diff --git a/.classpath b/.classpath index a630c18d..2217d1f1 100644 --- a/.classpath +++ b/.classpath @@ -25,5 +25,10 @@ + + + + + diff --git a/.gitignore b/.gitignore index 7be9efe9..7599d847 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /*.class /classes* -/product* -/bin/ +/product* \ No newline at end of file diff --git a/README.md b/README.md index 22bcc018..8c1e9dc4 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,19 @@ -# sdrtrunk # -A cross-platform java application for decoding, monitoring and recording trunked -mobile and related radio protocols using Software Defined Radios (SDR). +# sdrtrunk +A cross-platform java application for decoding, monitoring, recording and streaming trunked mobile and related radio protocols using Software Defined Radios (SDR). -# Documentation # * [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted) -* [User's Manual (Version 0.2.0)](https://github.com/DSheirer/sdrtrunk/wiki/UserManual) -* [Wiki](https://github.com/DSheirer/sdrtrunk/wiki) - -# Support Group and Error Reporting # -You can get help with setting up the sdrtrunk application or report any errors -that you encounter while running the application in the sdrtrunk Google Group at: - +* [User's Manual](https://github.com/DSheirer/sdrtrunk/wiki/UserManual) +* [Download](https://drive.google.com/folderview?id=0B7BHsssXUq8eOHBTNndCczZTd0k&usp=sharing) * [Support](https://groups.google.com/forum/#!forum/sdrtrunk) -# Updates # -Subscribe to this group to receive updates about new releases and the ongoing -application development effort. - -* [Subscribe for Updates](https://groups.google.com/forum/#!forum/sdrtrunk) - ![](https://github.com/DSheirer/sdrtrunk/wiki/images/SDRTrunk_application.png) + diff --git a/build/build.xml b/build/build.xml index b325e828..695f81bc 100644 --- a/build/build.xml +++ b/build/build.xml @@ -5,11 +5,11 @@ - + - + - + @@ -33,9 +33,9 @@ - + - + @@ -48,68 +48,68 @@ - + - - - - - - - + + + + + + + - + - - - + + + - + - - - + + + - + - - + + - + - - - - - - - - + + + + + + + + diff --git a/images/control_play.png b/images/control_play.png new file mode 100644 index 00000000..0846555d Binary files /dev/null and b/images/control_play.png differ diff --git a/images/control_stop.png b/images/control_stop.png new file mode 100644 index 00000000..893bb60e Binary files /dev/null and b/images/control_stop.png differ diff --git a/images/no_icon.png b/images/no_icon.png index 3b2b90c4..bab7cc9b 100644 Binary files a/images/no_icon.png and b/images/no_icon.png differ diff --git a/imports/tablefilter-swing-5.2.1.jar b/imports/tablefilter-swing-5.2.1.jar new file mode 100644 index 00000000..a7d26029 Binary files /dev/null and b/imports/tablefilter-swing-5.2.1.jar differ diff --git a/license/copyright_header.txt b/license/copyright_header.txt index 62c7ab8b..3eaea8b8 100644 --- a/license/copyright_header.txt +++ b/license/copyright_header.txt @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2015 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/scripts/run_sdrtrunk_windows.bat b/scripts/run_sdrtrunk_windows.bat index 27c0b763..1206e039 100644 --- a/scripts/run_sdrtrunk_windows.bat +++ b/scripts/run_sdrtrunk_windows.bat @@ -1 +1 @@ -java -XX:+UseG1GC -cp "sdrtrunk.jar;*;libs/*;config/*;images/*" gui.SDRTrunk \ No newline at end of file +java -XX:+UseG1GC -cp "SDRTrunk.jar;*;libs/*;config/*;images/*" gui.SDRTrunk \ No newline at end of file diff --git a/src/alias/Alias.java b/src/alias/Alias.java index 6bb82983..b17a92b9 100644 --- a/src/alias/Alias.java +++ b/src/alias/Alias.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,29 +22,44 @@ import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSeeAlso; +import javax.xml.bind.annotation.XmlTransient; import alias.action.AliasAction; -import alias.priority.Priority; -import audio.inverted.AudioType; +import alias.id.AliasID; +import alias.id.AliasIDType; +import alias.id.nonrecordable.NonRecordable; +import alias.id.priority.Priority; +@XmlSeeAlso( { AliasID.class, AliasAction.class } ) +@XmlRootElement( name = "alias" ) public class Alias { + private String mList; + private String mGroup; private String mName; private int mColor; private String mIconName; - private ArrayList mAliasIDs = new ArrayList(); + private List mAliasIDs = new ArrayList(); private List mAliasActions = new ArrayList(); public Alias() { } + public Alias( String name ) + { + mName = name; + } + public String toString() { return getName(); } - @XmlAttribute + @XmlAttribute( name="name" ) public String getName() { return mName; @@ -55,6 +70,38 @@ public class Alias mName = name; } + @XmlAttribute( name="list" ) + public String getList() + { + return mList; + } + + public void setList( String list ) + { + mList = list; + } + + public boolean hasList() + { + return mList != null; + } + + @XmlAttribute( name="group" ) + public String getGroup() + { + return mGroup; + } + + public void setGroup( String group ) + { + mGroup = group; + } + + public boolean hasGroup() + { + return mGroup != null; + } + @XmlAttribute public int getColor() { @@ -82,7 +129,8 @@ public class Alias mIconName = iconName; } - public ArrayList getId() + @XmlElement( name="id" ) + public List getId() { return mAliasIDs; } @@ -102,6 +150,7 @@ public class Alias mAliasIDs.remove( id ); } + @XmlElement( name="action" ) public List getAction() { return mAliasActions; @@ -127,35 +176,10 @@ public class Alias return !mAliasActions.isEmpty(); } - public boolean hasAudioType() - { - for( AliasID id: mAliasIDs ) - { - if( id.hasAudioType() ) - { - return true; - } - } - - return false; - } - - public AudioType getAudioType() - { - for( AliasID id: mAliasIDs ) - { - if( id.hasAudioType() ) - { - return id.getAudioType(); - } - } - - return AudioType.NORMAL; - } - /** * Returns the priority level of this alias, if defined, or the default priority */ + @XmlTransient public int getCallPriority() { for( AliasID id: mAliasIDs ) @@ -169,7 +193,7 @@ public class Alias return Priority.DEFAULT_PRIORITY; } - public boolean hasPriority() + public boolean hasCallPriority() { for( AliasID id: mAliasIDs ) { @@ -181,10 +205,35 @@ public class Alias return false; } + + /** + * Sets or updates the call priority + */ + public void setCallPriority( int priority ) + { + if( priority == Priority.DO_NOT_MONITOR || + ( Priority.MIN_PRIORITY <= priority && priority <= Priority.MAX_PRIORITY ) ) + { + for( AliasID id: mAliasIDs ) + { + if( id.getType() == AliasIDType.Priority ) + { + ((Priority)id).setPriority( priority ); + return; + } + } + + //If we don't find a priority id, create one + Priority p = new Priority(); + p.setPriority( priority ); + addAliasID( p ); + } + } /** * Inspects the alias for a non-recordable alias id. Default is true; */ + @XmlTransient public boolean isRecordable() { for( AliasID id: getId() ) @@ -197,4 +246,41 @@ public class Alias return true; } + + /** + * Sets or removes the non-recordable alias ID for this alias. + */ + public void setRecordable( boolean recordable ) + { + if( recordable ) + { + AliasID toRemove = null; + + for( AliasID id: getId() ) + { + if( id.getType() == AliasIDType.NonRecordable ) + { + toRemove = id; + break; + } + } + + if( toRemove != null ) + { + removeAliasID( toRemove ); + } + } + else + { + for( AliasID id: getId() ) + { + if( id.getType() == AliasIDType.NonRecordable ) + { + return; + } + } + + addAliasID( new NonRecordable() ); + } + } } diff --git a/src/alias/AliasController.java b/src/alias/AliasController.java new file mode 100644 index 00000000..60696bc6 --- /dev/null +++ b/src/alias/AliasController.java @@ -0,0 +1,368 @@ +package alias; + +import gui.editor.Editor; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.DefaultTableCellRenderer; + +import net.coderazzi.filters.gui.AutoChoices; +import net.coderazzi.filters.gui.TableFilterHeader; +import net.miginfocom.swing.MigLayout; +import settings.SettingsManager; + +import com.jidesoft.swing.JideSplitPane; + +public class AliasController extends JPanel + implements ActionListener, ListSelectionListener +{ + private static final long serialVersionUID = 1L; + + private AliasModel mAliasModel; + private JTable mAliasTable; + private TableFilterHeader mTableFilterHeader; + private AliasEditor mAliasEditor; + private MultipleAliasEditor mMultipleAliasEditor; + private JideSplitPane mSplitPane; + + private static final String NEW_ALIAS = "New"; + private static final String COPY_ALIAS = "Copy"; + private static final String DELETE_ALIAS = "Delete"; + + private JButton mNewButton = new JButton( NEW_ALIAS ); + private JButton mCopyButton = new JButton( COPY_ALIAS ); + private JButton mDeleteButton = new JButton( DELETE_ALIAS ); + + private IconCellRenderer mIconCellRenderer; + + public AliasController( AliasModel aliasModel, SettingsManager settingsManager ) + { + mAliasModel = aliasModel; + + mAliasEditor = new AliasEditor( mAliasModel, settingsManager ); + mMultipleAliasEditor = new MultipleAliasEditor( mAliasModel, settingsManager ); + + mIconCellRenderer = new IconCellRenderer( settingsManager ); + + init(); + + mAliasModel.addListener( mAliasEditor ); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0", + "[grow,fill]", + "[grow,fill]") ); + + //System Configuration View and Editor + mAliasTable = new JTable( mAliasModel ); + mAliasTable.setSelectionMode( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION ); + mAliasTable.getSelectionModel().addListSelectionListener( this ); + mAliasTable.setAutoCreateRowSorter( true ); + + mAliasTable.getColumnModel().getColumn( AliasModel.COLUMN_COLOR ) + .setCellRenderer( new ColorCellRenderer() ); + + mAliasTable.getColumnModel().getColumn( AliasModel.COLUMN_ICON ) + .setCellRenderer( mIconCellRenderer ); + + mTableFilterHeader = new TableFilterHeader( mAliasTable, AutoChoices.ENABLED ); + mTableFilterHeader.setFilterOnUpdates( true ); + + JScrollPane tableScroller = new JScrollPane( mAliasTable ); + + JPanel buttonsPanel = new JPanel(); + + buttonsPanel.setLayout( + new MigLayout( "insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]") ); + + mNewButton.addActionListener( this ); + mNewButton.setToolTipText( "Adds a new alias" ); + buttonsPanel.add( mNewButton ); + + mCopyButton.addActionListener( this ); + mCopyButton.setEnabled( false ); + mCopyButton.setToolTipText( "Creates a copy of the currently selected alias and adds it" ); + buttonsPanel.add( mCopyButton ); + + mDeleteButton.addActionListener( this ); + mDeleteButton.setEnabled( false ); + mDeleteButton.setToolTipText( "Deletes the currently selected alias" ); + buttonsPanel.add( mDeleteButton ); + + JPanel listAndButtonsPanel = new JPanel(); + + listAndButtonsPanel.setLayout( + new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill][]") ); + + listAndButtonsPanel.add( tableScroller, "wrap" ); + listAndButtonsPanel.add( buttonsPanel ); + + mSplitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); + mSplitPane.setDividerSize( 5 ); + mSplitPane.setShowGripper( true ); + mSplitPane.add( listAndButtonsPanel ); + mSplitPane.add( mAliasEditor ); + + add( mSplitPane ); + } + + /** + * Sets the editor argument as the currently visible alias editor + */ + private void setEditor( Editor editor ) + { + if( mSplitPane.getComponentCount() == 3 ) + { + Component component = mSplitPane.getComponent( 2 ); + + if( component instanceof Editor && component != editor ) + { + //Get current divider location so that we can reapply it after + //changing out the editors, so that the interface doesn't move + int location = mSplitPane.getDividerLocation( 0 ); + + ((Editor)component).setItem( null ); + + mSplitPane.remove( component ); + editor.setPreferredSize( new Dimension( 100, 100 ) ); + mSplitPane.add( editor ); + + mSplitPane.validate(); + mSplitPane.setDividerLocation( 0, location ); + mSplitPane.repaint(); + } + } + } + + private Alias getAlias( int selectedRow ) + { + if( selectedRow >= 0 ) + { + int index = mAliasTable.convertRowIndexToModel( selectedRow ); + + return mAliasModel.getAliasAtIndex( index ); + } + + return null; + } + + @Override + public void valueChanged( ListSelectionEvent event ) + { + //Limits event firing to only when selection is complete + if( !event.getValueIsAdjusting() ) + { + int[] selectedRows = mAliasTable.getSelectedRows(); + + if( selectedRows.length == 0 ) + { + mAliasEditor.setItem( null ); + mCopyButton.setEnabled( false ); + mDeleteButton.setEnabled( false ); + } + else if( selectedRows.length == 1 ) + { + setEditor( mAliasEditor ); + + Alias selectedAlias = getAlias( selectedRows[ 0 ] ); + + mAliasEditor.setItem( selectedAlias ); + + if( selectedAlias != null ) + { + mCopyButton.setEnabled( true ); + mDeleteButton.setEnabled( true ); + } + else + { + mCopyButton.setEnabled( false ); + mDeleteButton.setEnabled( false ); + } + } + else + { + setEditor( mMultipleAliasEditor ); + + mCopyButton.setEnabled( true ); + mDeleteButton.setEnabled( true ); + + List selectedAliases = new ArrayList(); + + for( int selectedRow: selectedRows ) + { + Alias alias = getAlias( selectedRow ); + + selectedAliases.add( alias ); + } + + mMultipleAliasEditor.setItem( selectedAliases ); + } + } + } + + /** + * Adds the alias to the alias table/model and scrolls the view it + */ + private void addAlias( Alias alias ) + { + //HACK: when inserting a row to the model, the JTable gets + //notified and attempts to tell the coderazzi table filter + //adaptive choices filter to refresh before the table filter is + //notified of the row additions, causing an index out of bounds + //exception. We turn off adaptive choices temporarily, add the + //channel, and turn on adaptive choices again. + mTableFilterHeader.setAdaptiveChoices( false ); + + int index = mAliasModel.addAlias( alias ); + + mTableFilterHeader.setAdaptiveChoices( true ); + + if( index >= 0 ) + { + int translatedIndex = mAliasTable.convertRowIndexToView( index ); + mAliasTable.setRowSelectionInterval( translatedIndex, translatedIndex ); + mAliasTable.scrollRectToVisible( + new Rectangle( mAliasTable.getCellRect( translatedIndex, 0, true ) ) ); + } + } + + private List getSelectedAliases() + { + List selected = new ArrayList<>(); + + int[] rows = mAliasTable.getSelectedRows(); + + for( int row: rows ) + { + if( row >= 0 ) + { + Alias alias = mAliasModel.getAliasAtIndex( mAliasTable.convertRowIndexToModel( row ) ); + + if( alias != null ) + { + selected.add( alias ); + } + } + } + + return selected; + } + + /** + * Responds to New, Copy and Delete Channel button invocations + */ + @Override + public void actionPerformed( ActionEvent event ) + { + switch( event.getActionCommand() ) + { + case NEW_ALIAS: + addAlias( new Alias( "New Alias" ) ); + break; + case COPY_ALIAS: + for( Alias alias: getSelectedAliases() ) + { + addAlias( AliasFactory.copyOf( alias ) ); + } + break; + case DELETE_ALIAS: + List toDelete = getSelectedAliases(); + + if( toDelete != null && !toDelete.isEmpty() ) + { + String title = toDelete.size() == 1 ? "Delete Alias?" : "Delete Aliases?"; + String prompt = toDelete.size() == 1 ? "Do you want to delete this alias?" : + "Do you want to delete these " + toDelete.size() + " aliases?"; + + int choice = JOptionPane.showConfirmDialog( AliasController.this, + prompt, title, JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE ); + + if( choice == JOptionPane.YES_OPTION ) + { + for( Alias alias: toDelete ) + { + mAliasModel.removeAlias( alias ); + } + } + } + break; + default: + break; + } + } + + /** + * Colorizes the cell based on the cell's integer value + */ + public class ColorCellRenderer extends DefaultTableCellRenderer + { + private static final long serialVersionUID = 1L; + + @Override + public Component getTableCellRendererComponent( JTable table, + Object value, boolean isSelected, boolean hasFocus, int row, + int column ) + { + JLabel label = (JLabel)super.getTableCellRendererComponent( table, + value, isSelected, hasFocus, row, column ); + + label.setBackground( new Color( (int)value ) ); + + label.setText( "" ); + + return label; + } + } + + /** + * Displays cell's icon and name + */ + public class IconCellRenderer extends DefaultTableCellRenderer + { + private static final long serialVersionUID = 1L; + + private SettingsManager mSettingsManager; + + public IconCellRenderer( SettingsManager settingsManager ) + { + mSettingsManager = settingsManager; + } + + @Override + public Component getTableCellRendererComponent( JTable table, + Object value, boolean isSelected, boolean hasFocus, int row, + int column ) + { + JLabel label = (JLabel)super.getTableCellRendererComponent( table, + value, isSelected, hasFocus, row, column ); + + ImageIcon icon = mSettingsManager.getImageIcon( label.getText(), 12 ); + + if( icon != null ) + { + label.setIcon( icon ); + } + + return label; + } + } +} diff --git a/src/alias/AliasDirectoryNode.java b/src/alias/AliasDirectoryNode.java deleted file mode 100644 index a616b755..00000000 --- a/src/alias/AliasDirectoryNode.java +++ /dev/null @@ -1,91 +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 - ******************************************************************************/ -package alias; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; - -import controller.ConfigurableNode; - -public class AliasDirectoryNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - public AliasDirectoryNode( AliasDirectory directory ) - { - super( directory ); - } - - public AliasDirectory getAliasDirectory() - { - return (AliasDirectory)getUserObject(); - } - - public void init() - { - for( AliasList list: getAliasDirectory().getAliasList() ) - { - AliasListNode node = new AliasListNode( list ); - - getModel().addNode( node, AliasDirectoryNode.this, getChildCount() ); - - node.init(); - } - - sort(); - } - - public String toString() - { - return "Alias Lists [" + this.getChildCount() + "]"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addItem = new JMenuItem( "Add Alias List" ); - addItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - AliasList list = new AliasList(); - - getAliasDirectory().addAliasList( list ); - - AliasListNode node = new AliasListNode( list ); - - getModel().addNode( node, - AliasDirectoryNode.this, - AliasDirectoryNode.this.getChildCount() ); - - sort(); - - getModel().showNode( node ); - } - } ); - - retVal.add( addItem ); - - return retVal; - } -} diff --git a/src/alias/AliasEditor.java b/src/alias/AliasEditor.java index 77be321b..b642df01 100644 --- a/src/alias/AliasEditor.java +++ b/src/alias/AliasEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,189 +17,146 @@ ******************************************************************************/ package alias; +import gui.editor.Editor; + import java.awt.Color; -import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import javax.swing.BorderFactory; import javax.swing.JButton; -import javax.swing.JColorChooser; -import javax.swing.JComboBox; import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; -import javax.swing.tree.TreePath; +import javax.swing.JSeparator; -import map.MapIcon; -import map.MapIconListCellRenderer; import net.miginfocom.swing.MigLayout; +import sample.Listener; import settings.SettingsManager; -import controller.ConfigurableNode; -import controller.ResourceManager; +import alias.action.AliasActionEditor; +import alias.id.AliasIdentifierEditor; -public class AliasEditor extends JPanel implements ActionListener +import com.jidesoft.swing.JideTabbedPane; + +public class AliasEditor extends Editor + implements ActionListener, Listener { private static final long serialVersionUID = 1L; - - private AliasNode mAliasNode; - private ResourceManager mResourceManager; - - private JLabel mLabelName; - private JTextField mTextAlias; - private JLabel mLabelMapColor; - private JButton mButtonColor; - private JComboBox mComboMapIcon; - private JButton mBtnIconManager; - - public AliasEditor( AliasNode aliasNode, ResourceManager resourceManager ) - { - mAliasNode = aliasNode; - mResourceManager = resourceManager; - - initGUI(); - } + private static final String DEFAULT_NAME = "select an alias"; + + private AliasModel mAliasModel; - private void initGUI() + private JLabel mAliasLabel; + private AliasNameEditor mAliasNameEditor; + private AliasIdentifierEditor mAliasIdentifierEditor; + private AliasActionEditor mAliasActionEditor; + + public AliasEditor( AliasModel aliasModel, SettingsManager settingsManager ) { - setLayout( new MigLayout( "", "[right][grow,fill]", "[][][][][][grow][]" ) ); + mAliasModel = aliasModel; + mAliasNameEditor = new AliasNameEditor( mAliasModel, settingsManager ); + mAliasIdentifierEditor = new AliasIdentifierEditor( aliasModel ); + mAliasActionEditor = new AliasActionEditor( aliasModel ); + + mAliasModel.addListener( this ); - setBorder( BorderFactory.createTitledBorder( "Alias" ) ); + init(); + } - mLabelName = new JLabel( "Name:" ); - add( mLabelName ); + + @Override + public void setItem( Alias alias ) + { + super.setItem( alias ); - mTextAlias = new JTextField( mAliasNode.getAlias().getName() ); - add( mTextAlias, "wrap" ); - - mLabelMapColor = new JLabel( "Map Color:" ); - add( mLabelMapColor ); - - mButtonColor = new JButton( "Map Color" ); - mButtonColor.setBackground( mAliasNode.getAlias().getMapColor() ); - mButtonColor.setForeground( mAliasNode.getAlias().getMapColor() ); - mButtonColor.addActionListener( new ActionListener() + if( hasItem() ) { - @Override - public void actionPerformed( ActionEvent e ) - { - Color newColor = JColorChooser.showDialog( - AliasEditor.this, - "Choose color for map labels and routes", - mAliasNode.getAlias().getMapColor() ); - - mButtonColor.setForeground( newColor ); - mButtonColor.setBackground( newColor ); - } - } ); - add( mButtonColor, "wrap" ); - - add( new JLabel( "Icon:" ) ); - - mComboMapIcon = new JComboBox( - mResourceManager.getSettingsManager().getMapIcons() ); - - MapIconListCellRenderer renderer = new MapIconListCellRenderer(); - renderer.setPreferredSize( new Dimension( 200, 30 ) ); - mComboMapIcon.setRenderer( renderer ); - - String iconName = mAliasNode.getAlias().getIconName(); - - if( iconName == null ) + mAliasLabel.setText( getItem().getName() ); + } + else { - iconName = SettingsManager.DEFAULT_ICON; + mAliasLabel.setText( DEFAULT_NAME ); } - MapIcon savedIcon = mResourceManager.getSettingsManager() - .getMapIcon( iconName ); + mAliasNameEditor.setItem( alias ); + mAliasIdentifierEditor.setItem( alias ); + mAliasActionEditor.setItem( alias ); + } - if( savedIcon != null ) - { - mComboMapIcon.setSelectedItem( savedIcon ); - } - - add( mComboMapIcon, "wrap" ); - - //Dummy place holder - add( new JLabel() ); + private void init() + { + setLayout( new MigLayout( "fill,wrap 2", "[grow,fill][grow,fill]", + "[][][][][][grow,fill]" ) ); - mBtnIconManager = new JButton( "Icon Manager" ); - mBtnIconManager.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - mResourceManager.getController().showIconManager(); - } - } ); - add( mBtnIconManager, "span 2,wrap" ); + add( new JLabel( "Alias:" ), "align right" ); + mAliasLabel = new JLabel( DEFAULT_NAME ); + add( mAliasLabel, "align left" ); + add( new JSeparator( JSeparator.HORIZONTAL ), "span,growx,push" ); + + JideTabbedPane tabs = new JideTabbedPane(); + tabs.setFont( this.getFont() ); + tabs.setForeground( Color.BLACK ); + + tabs.addTab( "Alias", mAliasNameEditor ); + tabs.addTab( "Audio / Identifier", mAliasIdentifierEditor ); + tabs.addTab( "Action", mAliasActionEditor ); + add( tabs, "span,grow" ); + + //Playlist management buttons JButton btnSave = new JButton( "Save" ); + btnSave.setToolTipText( "Save alias changes to the playlist" ); btnSave.addActionListener( AliasEditor.this ); add( btnSave ); JButton btnReset = new JButton( "Reset" ); + btnReset.setToolTipText( "Reset alias changes since last save" ); btnReset.addActionListener( AliasEditor.this ); add( btnReset, "wrap" ); } + @Override + public void receive( AliasEvent event ) + { + //If this is the currently displayed alias, reload it + if( hasItem() && getItem() == event.getAlias() ) + { + switch( event.getEvent() ) + { + case CHANGE: + //Alias changed - reset the editor with changed alias + setItem( event.getAlias() ); + break; + case DELETE: + setItem( null ); + break; + default: + break; + } + } + } + @Override public void actionPerformed( ActionEvent e ) { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) + if( hasItem() ) { - String alias = mTextAlias.getText(); + String command = e.getActionCommand(); - if( alias != null ) + if( command.contentEquals( "Save" ) ) { - boolean expanded = mAliasNode.getModel().getTree() - .isExpanded( new TreePath( mAliasNode ) ); - - mAliasNode.getAlias().setName( alias ); - ((ConfigurableNode)mAliasNode.getParent()).sort(); - - mAliasNode.getAlias().setColor( - mButtonColor.getBackground().getRGB() ); - - MapIcon selectedIcon = (MapIcon)mComboMapIcon.getSelectedItem(); - - if( selectedIcon != null ) - { - mAliasNode.getAlias().setIconName( selectedIcon.getName() ); - } - - mAliasNode.save(); - - mAliasNode.show(); - - if( expanded ) - { - mAliasNode.getModel().getTree() - .expandPath( new TreePath( mAliasNode ) ); - } + save(); } - else + else if( command.contentEquals( "Reset" ) ) { - JOptionPane.showMessageDialog( AliasEditor.this, "Please enter an alias name" ); + reset(); } } - else if( command.contentEquals( "Reset" ) ) - { - mTextAlias.setText( mAliasNode.getAlias().getName() ); - - mButtonColor.setBackground( mAliasNode.getAlias().getMapColor() ); - mButtonColor.setForeground( mAliasNode.getAlias().getMapColor() ); - - MapIcon savedIcon = mResourceManager.getSettingsManager() - .getMapIcon( mAliasNode.getAlias().getIconName() ); - - mComboMapIcon.setSelectedItem( savedIcon ); - } - - mAliasNode.refresh(); } + + @Override + public void save() + { + mAliasNameEditor.save(); + mAliasIdentifierEditor.save(); + mAliasActionEditor.save(); + } } diff --git a/src/alias/AliasID.java b/src/alias/AliasEvent.java similarity index 63% rename from src/alias/AliasID.java rename to src/alias/AliasEvent.java index 998896df..c1cacd74 100644 --- a/src/alias/AliasID.java +++ b/src/alias/AliasEvent.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,25 +17,39 @@ ******************************************************************************/ package alias; -import audio.inverted.AudioType; - -public abstract class AliasID +public class AliasEvent { - public AliasID() + private Alias mAlias; + private Event mEvent; + + /** + * AliasEvent - event describing any changes to an alias + * @param alias - alias that changed + * @param event - change event + */ + public AliasEvent( Alias alias, Event event ) { + mAlias = alias; + mEvent = event; } - public abstract AliasIDType getType(); - - public boolean hasAudioType() + public Alias getAlias() { - return false; + return mAlias; } - public AudioType getAudioType() + public Event getEvent() { - return AudioType.NORMAL; + return mEvent; } - public abstract boolean matches( AliasID id ); + /** + * Channel events to describe the specific event + */ + public enum Event + { + ADD, + CHANGE, + DELETE; + } } diff --git a/src/alias/AliasFactory.java b/src/alias/AliasFactory.java new file mode 100644 index 00000000..3f91ccbe --- /dev/null +++ b/src/alias/AliasFactory.java @@ -0,0 +1,292 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package alias; + +import gui.editor.Editor; +import gui.editor.EmptyEditor; +import alias.action.AliasAction; +import alias.action.AliasActionType; +import alias.action.beep.BeepAction; +import alias.action.beep.BeepActionEditor; +import alias.action.clip.ClipAction; +import alias.action.clip.ClipActionEditor; +import alias.action.script.ScriptAction; +import alias.action.script.ScriptActionEditor; +import alias.id.AliasID; +import alias.id.AliasIDType; +import alias.id.esn.ESNEditor; +import alias.id.esn.Esn; +import alias.id.fleetsync.FleetsyncID; +import alias.id.fleetsync.FleetsyncIDEditor; +import alias.id.lojack.LoJackFunctionAndID; +import alias.id.lojack.LoJackIDEditor; +import alias.id.mdc.MDC1200ID; +import alias.id.mdc.MDC1200IDEditor; +import alias.id.mobileID.MINEditor; +import alias.id.mobileID.Min; +import alias.id.mpt1327.MPT1327ID; +import alias.id.mpt1327.MPT1327IDEditor; +import alias.id.nonrecordable.NonRecordable; +import alias.id.nonrecordable.NonRecordableEditor; +import alias.id.priority.Priority; +import alias.id.priority.PriorityEditor; +import alias.id.siteID.SiteID; +import alias.id.siteID.SiteIDEditor; +import alias.id.status.StatusID; +import alias.id.status.StatusIDEditor; +import alias.id.talkgroup.TalkgroupID; +import alias.id.talkgroup.TalkgroupIDEditor; +import alias.id.uniqueID.UniqueID; +import alias.id.uniqueID.UniqueIDEditor; + +public class AliasFactory +{ + public static AliasID copyOf( AliasID id ) + { + switch( id.getType() ) + { + case ESN: + Esn originalESN = (Esn)id; + Esn copyESN = new Esn(); + copyESN.setEsn( originalESN.getEsn() ); + return copyESN; + case Fleetsync: + FleetsyncID originalFleetsyncID = (FleetsyncID)id; + FleetsyncID copyFleetsyncID = new FleetsyncID(); + copyFleetsyncID.setIdent( originalFleetsyncID.getIdent() ); + return copyFleetsyncID; + case LTRNetUID: + UniqueID originalUniqueID = (UniqueID)id; + UniqueID copyUniqueID = new UniqueID(); + copyUniqueID.setUid( originalUniqueID.getUid() ); + return copyUniqueID; + case LoJack: + LoJackFunctionAndID originalLoJackFunctionAndID = (LoJackFunctionAndID)id; + LoJackFunctionAndID copyLoJackFunctionAndID = new LoJackFunctionAndID(); + copyLoJackFunctionAndID.setFunction( originalLoJackFunctionAndID.getFunction() ); + copyLoJackFunctionAndID.setID( originalLoJackFunctionAndID.getID() ); + return copyLoJackFunctionAndID; + case MDC1200: + MDC1200ID originalMDC1200ID = (MDC1200ID)id; + MDC1200ID copyMDC1200ID = new MDC1200ID(); + copyMDC1200ID.setIdent( originalMDC1200ID.getIdent() ); + return copyMDC1200ID; + case MIN: + Min originalMin = (Min)id; + Min copyMin = new Min(); + copyMin.setMin( originalMin.getMin() ); + return copyMin; + case MPT1327: + MPT1327ID originalMPT1327ID = (MPT1327ID)id; + MPT1327ID copyMPT1327ID = new MPT1327ID(); + copyMPT1327ID.setIdent( originalMPT1327ID.getIdent() ); + return copyMPT1327ID; + case NonRecordable: + return new NonRecordable(); + case Priority: + Priority originalPriority = (Priority)id; + Priority copyPriority = new Priority(); + copyPriority.setPriority( originalPriority.getPriority() ); + return copyPriority; + case Site: + SiteID originalSiteID = (SiteID)id; + SiteID copySiteID = new SiteID(); + copySiteID.setSite( originalSiteID.getSite() ); + return copySiteID; + case Talkgroup: + TalkgroupID originalTalkgroupID = (TalkgroupID)id; + TalkgroupID copyTalkgroupID = new TalkgroupID(); + copyTalkgroupID.setTalkgroup( originalTalkgroupID.getTalkgroup() ); + return copyTalkgroupID; + case Status: + StatusID originalStatusID = (StatusID)id; + StatusID copyStatusID = new StatusID(); + copyStatusID.setStatus( originalStatusID.getStatus() ); + return copyStatusID; + default: + } + + return null; + } + + public static AliasAction copyOf( AliasAction action ) + { + if( action instanceof BeepAction ) + { + return new BeepAction(); + } + else if( action instanceof ClipAction ) + { + ClipAction originalClip = (ClipAction)action; + ClipAction copyClip = new ClipAction(); + copyClip.setInterval( originalClip.getInterval() ); + copyClip.setPath( originalClip.getPath() ); + copyClip.setPeriod( originalClip.getPeriod() ); + return copyClip; + } + else if( action instanceof ScriptAction ) + { + ScriptAction originalScript = (ScriptAction)action; + ScriptAction copyScript = new ScriptAction(); + copyScript.setInterval( originalScript.getInterval() ); + copyScript.setPeriod( originalScript.getPeriod() ); + copyScript.setScript( originalScript.getScript() ); + return copyScript; + } + + return null; + } + + public static Alias copyOf( Alias original ) + { + Alias copy = new Alias( original.getName() ); + copy.setList( original.getList() ); + copy.setGroup( original.getGroup() ); + copy.setColor( original.getColor() ); + copy.setIconName( original.getIconName() ); + + for( AliasID id: original.getId() ) + { + AliasID copyID = copyOf( id ); + + if( copyID != null ) + { + copy.addAliasID( copyID ); + } + } + + for( AliasAction action: original.getAction() ) + { + AliasAction copyAction = copyOf( action ); + + if( copyAction != null ) + { + copy.addAliasAction( copyAction ); + } + } + + return copy; + } + + public static Editor getEditor( AliasID aliasID ) + { + if( aliasID != null ) + { + switch( aliasID.getType() ) + { + case ESN: + return new ESNEditor( aliasID ); + case Fleetsync: + return new FleetsyncIDEditor( aliasID ); + case LTRNetUID: + return new UniqueIDEditor( aliasID ); + case LoJack: + return new LoJackIDEditor( aliasID ); + case MDC1200: + return new MDC1200IDEditor( aliasID ); + case MIN: + return new MINEditor( aliasID ); + case MPT1327: + return new MPT1327IDEditor( aliasID ); + case NonRecordable: + return new NonRecordableEditor( aliasID ); + case Priority: + return new PriorityEditor( aliasID ); + case Site: + return new SiteIDEditor( aliasID ); + case Status: + return new StatusIDEditor( aliasID ); + case Talkgroup: + return new TalkgroupIDEditor( aliasID ); + default: + break; + } + } + + return new EmptyEditor(); + } + + public static Editor getEditor( AliasAction aliasAction ) + { + if( aliasAction != null ) + { + switch( aliasAction.getType() ) + { + case BEEP: + return new BeepActionEditor( aliasAction ); + case CLIP: + return new ClipActionEditor( aliasAction ); + case SCRIPT: + return new ScriptActionEditor( aliasAction ); + default: + break; + } + } + + return new EmptyEditor(); + } + + public static AliasID getAliasID( AliasIDType type ) + { + switch( type ) + { + case ESN: + return new Esn(); + case Fleetsync: + return new FleetsyncID(); + case LTRNetUID: + return new UniqueID(); + case LoJack: + return new LoJackFunctionAndID(); + case MDC1200: + return new MDC1200ID(); + case MIN: + return new Min(); + case MPT1327: + return new MPT1327ID(); + case NonRecordable: + return new NonRecordable(); + case Priority: + return new Priority(); + case Site: + return new SiteID(); + case Status: + return new StatusID(); + case Talkgroup: + return new TalkgroupID(); + default: + throw new IllegalArgumentException( "Unrecognized Alias ID type: " + type ); + } + } + + public static AliasAction getAliasAction( AliasActionType type ) + { + switch( type ) + { + case BEEP: + return new BeepAction(); + case CLIP: + return new ClipAction(); + case SCRIPT: + return new ScriptAction(); + default: + throw new IllegalArgumentException( "Unrecognized Alias Action type: " + type ); + } + } + +} diff --git a/src/alias/AliasIDNode.java b/src/alias/AliasIDNode.java deleted file mode 100644 index c861c56a..00000000 --- a/src/alias/AliasIDNode.java +++ /dev/null @@ -1,35 +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 - ******************************************************************************/ -package alias; - -import controller.ConfigurableNode; - -public class AliasIDNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - public AliasIDNode( AliasID id ) - { - super( id ); - } - - public void save() - { - getModel().getResourceManager().getPlaylistManager().save(); - } -} diff --git a/src/alias/AliasList.java b/src/alias/AliasList.java index 312e9d6f..59f7dc9a 100644 --- a/src/alias/AliasList.java +++ b/src/alias/AliasList.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,69 +19,35 @@ package alias; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlSeeAlso; import module.decode.lj1200.LJ1200Message.Function; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import alias.action.AliasAction; -import alias.action.beep.BeepAction; -import alias.action.clip.ClipAction; -import alias.action.script.ScriptAction; +import sample.Listener; +import alias.id.AliasID; import alias.id.esn.Esn; import alias.id.fleetsync.FleetsyncID; -import alias.id.fleetsync.StatusID; import alias.id.lojack.LoJackFunctionAndID; import alias.id.mdc.MDC1200ID; import alias.id.mobileID.Min; import alias.id.mpt1327.MPT1327ID; import alias.id.siteID.SiteID; +import alias.id.status.StatusID; import alias.id.talkgroup.TalkgroupID; import alias.id.uniqueID.UniqueID; -import alias.priority.Priority; -import alias.record.NonRecordable; import audio.metadata.MetadataType; -@XmlSeeAlso( { Alias.class, - - /* Alias ID's */ - AliasID.class, - FleetsyncID.class, - Esn.class, - Group.class, - LoJackFunctionAndID.class, - MDC1200ID.class, - Min.class, - MPT1327ID.class, - UniqueID.class, - SiteID.class, - StatusID.class, - TalkgroupID.class, - - /* Alias Attributes */ - NonRecordable.class, - Priority.class, - - /* Alias Actions */ - AliasAction.class, - BeepAction.class, - ClipAction.class, - ScriptAction.class } ) - -@XmlRootElement( name = "alias_list" ) -public class AliasList implements Comparable +public class AliasList implements Listener { private final static Logger mLog = LoggerFactory.getLogger( AliasList.class ); - private String mName; - private ArrayList mGroups = new ArrayList(); - + private List mAliases = new ArrayList<>(); private Map mESN = new HashMap<>(); private Map mFleetsync = new HashMap<>(); private Map mLoJack = new HashMap<>(); @@ -101,167 +67,326 @@ public class AliasList implements Comparable private boolean mSiteWildcard = false; private boolean mTalkgroupWildcard = false; - public AliasList() - { - this( null ); - } + private String mName; + /** + * List of aliases where all aliases share the same list name. Contains + * several methods for alias lookup from identifier values, like talkgroups. + * + * Responds to alias change events to keep the internal alias list updated. + */ public AliasList( String name ) { mName = name; - update(); } - + /** - * Load/Reload all lookup hashmaps + * Adds the alias to this list */ - public void update() + public void addAlias( Alias alias ) { - //Clear hashmaps - mESN.clear(); - mFleetsync.clear(); - mLoJack.clear(); - mMDC1200.clear(); - mMobileID.clear(); - mMPT1327.clear(); - mSiteID.clear(); - mStatus.clear(); - mTalkgroup.clear(); - mUniqueID.clear(); - - for( Group group: mGroups ) + if( alias != null ) { - for( Alias alias: group.getAlias() ) + mAliases.add( alias ); + + for( AliasID aliasID: alias.getId() ) { - for( AliasID id: alias.getId() ) + addAliasID( aliasID, alias ); + } + } + } + + /** + * Adds the alias and alias identifier to the internal type mapping. + */ + private void addAliasID( AliasID id, Alias alias ) + { + switch( id.getType() ) + { + case ESN: + Esn esn = (Esn)id; + if( esn.getEsn().contains( "*" ) ) { - switch( id.getType() ) + mESNWildcard = true; + + mESN.put( fixWildcard( esn.getEsn() ), alias ); + } + else + { + mESN.put( esn.getEsn(), alias ); + } + break; + case Fleetsync: + FleetsyncID fs = (FleetsyncID)id; + + if( fs.getIdent().contains( "*" ) ) + { + mFleetsyncWildcard = true; + + mFleetsync.put( fixWildcard( fs.getIdent() ), alias ); + } + else + { + mFleetsync.put( fs.getIdent(), alias ); + } + break; + case LoJack: + mLoJack.put( (LoJackFunctionAndID)id, alias ); + break; + case MDC1200: + MDC1200ID mdc = (MDC1200ID)id; + + if( mdc.getIdent().contains( "*" ) ) + { + mMDC1200Wildcard = true; + + //Replace (*) wildcard with regex wildcard (.) + mMDC1200.put( fixWildcard( mdc.getIdent() ), alias ); + } + else + { + mMDC1200.put( mdc.getIdent(), alias ); + } + break; + case MPT1327: + MPT1327ID mpt = (MPT1327ID)id; + + String ident = mpt.getIdent(); + + if( ident != null ) + { + if( ident.contains( "*" ) ) { - case ESN: - Esn esn = (Esn)id; - if( esn.getEsn().contains( "*" ) ) - { - mESNWildcard = true; - - mESN.put( fixWildcard( esn.getEsn() ), alias ); - } - else - { - mESN.put( esn.getEsn(), alias ); - } - break; - case Fleetsync: - FleetsyncID fs = (FleetsyncID)id; - - if( fs.getIdent().contains( "*" ) ) - { - mFleetsyncWildcard = true; - - mFleetsync.put( fixWildcard( fs.getIdent() ), alias ); - } - else - { - mFleetsync.put( fs.getIdent(), alias ); - } - break; - case LoJack: - mLoJack.put( (LoJackFunctionAndID)id, alias ); - break; - case MDC1200: - MDC1200ID mdc = (MDC1200ID)id; - - if( mdc.getIdent().contains( "*" ) ) - { - mMDC1200Wildcard = true; - - //Replace (*) wildcard with regex wildcard (.) - mMDC1200.put( fixWildcard( mdc.getIdent() ), alias ); - } - else - { - mMDC1200.put( mdc.getIdent(), alias ); - } - break; - case MPT1327: - MPT1327ID mpt = (MPT1327ID)id; - - String ident = mpt.getIdent(); - - if( ident != null ) - { - if( ident.contains( "*" ) ) - { - mMPT1327Wildcard = true; - - //Replace (*) wildcard with regex wildcard (.) - mMPT1327.put( fixWildcard( ident ), alias ); - } - else - { - mMPT1327.put( ident, alias ); - } - } - break; - case MIN: - Min min = (Min)id; - - if( min.getMin().contains( "*" ) ) - { - mMobileIDWildcard = true; - mMobileID.put( fixWildcard( min.getMin() ), alias ); - } - else - { - mMobileID.put( min.getMin(), alias ); - } - break; - case LTRNetUID: - UniqueID uid = (UniqueID)id; - - mUniqueID.put( uid.getUid(), alias ); - break; - case Site: - SiteID siteID = (SiteID)id; - - if( siteID.getSite().contains( "*" ) ) - { - mSiteWildcard = true; - mSiteID.put( fixWildcard( siteID.getSite() ), alias ); - } - else - { - mSiteID.put( siteID.getSite(), alias ); - } - - break; - case Status: - mStatus.put( ((StatusID)id).getStatus(), alias ); - break; - case Talkgroup: - TalkgroupID tgid = (TalkgroupID)id; - - if( tgid.getTalkgroup().contains( "*" ) ) - { - mTalkgroupWildcard = true; - - //Replace (*) wildcard with regex wildcard (.) - mTalkgroup.put( fixWildcard( tgid.getTalkgroup() ), alias ); - } - else - { - mTalkgroup.put( tgid.getTalkgroup(), alias ); - } - break; - case NonRecordable: - case Priority: - //We don't maintain lookups for these items - break; - default: - mLog.warn( "Unrecognized Alias ID Type:" + id.getType().name() ); - break; + mMPT1327Wildcard = true; + + //Replace (*) wildcard with regex wildcard (.) + mMPT1327.put( fixWildcard( ident ), alias ); + } + else + { + mMPT1327.put( ident, alias ); } } + break; + case MIN: + Min min = (Min)id; + + if( min.getMin().contains( "*" ) ) + { + mMobileIDWildcard = true; + mMobileID.put( fixWildcard( min.getMin() ), alias ); + } + else + { + mMobileID.put( min.getMin(), alias ); + } + break; + case LTRNetUID: + UniqueID uid = (UniqueID)id; + + mUniqueID.put( uid.getUid(), alias ); + break; + case Site: + SiteID siteID = (SiteID)id; + + if( siteID.getSite().contains( "*" ) ) + { + mSiteWildcard = true; + mSiteID.put( fixWildcard( siteID.getSite() ), alias ); + } + else + { + mSiteID.put( siteID.getSite(), alias ); + } + + break; + case Status: + mStatus.put( ((StatusID)id).getStatus(), alias ); + break; + case Talkgroup: + TalkgroupID tgid = (TalkgroupID)id; + + if( tgid.getTalkgroup().contains( "*" ) ) + { + mTalkgroupWildcard = true; + + //Replace (*) wildcard with regex wildcard (.) + mTalkgroup.put( fixWildcard( tgid.getTalkgroup() ), alias ); + } + else + { + mTalkgroup.put( tgid.getTalkgroup(), alias ); + } + break; + case NonRecordable: + case Priority: + //We don't maintain lookups for these items + break; + default: + mLog.warn( "Unrecognized Alias ID Type:" + id.getType().name() ); + break; + } + } + + /** + * Removes the alias from this list + */ + public void removeAlias( Alias alias ) + { + if( alias != null ) + { + for( AliasID aliasID: alias.getId() ) + { + removeAliasID( aliasID, alias ); } + + mAliases.remove( alias ); + } + } + + /** + * Removes the alias and alias identifier from internal mappings. + */ + private void removeAliasID( AliasID id, Alias alias ) + { + switch( id.getType() ) + { + case ESN: + Esn esn = (Esn)id; + if( mESN.containsKey( esn.getEsn() ) ) + { + Alias esnAlias = mESN.get( esn.getEsn() ); + + if( esnAlias != null && esnAlias == alias ) + { + mESN.remove( esn.getEsn() ); + } + } + break; + case Fleetsync: + FleetsyncID fs = (FleetsyncID)id; + + if( mFleetsync.containsKey( fs.getIdent() ) ) + { + Alias fsAlias = mFleetsync.get( fs.getIdent() ); + + if( fsAlias != null && fsAlias == alias ) + { + mFleetsync.remove( fs.getIdent() ); + } + } + break; + case LoJack: + LoJackFunctionAndID lj = (LoJackFunctionAndID)id; + + if( mLoJack.containsKey( lj ) ) + { + Alias ljAlias = mLoJack.get( lj ); + + if( ljAlias != null && ljAlias == alias ) + { + mLoJack.remove( lj ); + } + } + break; + case MDC1200: + MDC1200ID mdc = (MDC1200ID)id; + + if( mMDC1200.containsKey( mdc.getIdent() ) ) + { + Alias mdcAlias = mMDC1200.get( mdc.getIdent() ); + + if( mdcAlias != null && mdcAlias == alias ) + { + mMDC1200.remove( mdc.getIdent() ); + } + } + break; + case MPT1327: + MPT1327ID mpt = (MPT1327ID)id; + + if( mMPT1327.containsKey( mpt.getIdent() ) ) + { + Alias mptAlias = mMDC1200.get( mpt.getIdent() ); + + if( mptAlias != null && mptAlias == alias ) + { + mMPT1327.remove( mpt.getIdent() ); + } + } + break; + case MIN: + Min min = (Min)id; + + if( mMobileID.containsKey( min.getMin() ) ) + { + Alias minAlias = mMobileID.get( min.getMin() ); + + if( minAlias != null && minAlias == alias ) + { + mMobileID.remove( min.getMin() ); + } + } + break; + case LTRNetUID: + UniqueID uid = (UniqueID)id; + + if( mUniqueID.containsKey( uid.getUid() ) ) + { + Alias uidAlias = mUniqueID.get( uid.getUid() ); + + if( uidAlias != null && uidAlias == alias ) + { + mUniqueID.remove( uid.getUid() ); + } + } + break; + case Site: + SiteID sid = (SiteID)id; + + if( mSiteID.containsKey( sid.getSite() ) ) + { + Alias sidAlias = mSiteID.get( sid.getSite() ); + + if( sidAlias != null && sidAlias == alias ) + { + mSiteID.remove( sid.getSite() ); + } + } + break; + case Status: + StatusID stat = (StatusID)id; + + if( mStatus.containsKey( stat.getStatus() ) ) + { + Alias statAlias = mStatus.get( stat.getStatus() ); + + if( statAlias != null && statAlias == alias ) + { + mStatus.remove( stat.getStatus() ); + } + } + break; + case Talkgroup: + TalkgroupID tg = (TalkgroupID)id; + + if( mTalkgroup.containsKey( tg.getTalkgroup() ) ) + { + Alias tgAlias = mTalkgroup.get( tg.getTalkgroup() ); + + if( tgAlias != null && tgAlias == alias ) + { + mTalkgroup.remove( tg.getTalkgroup() ); + } + } + break; + case NonRecordable: + case Priority: + //We don't maintain lookups for these items + break; + default: + mLog.warn( "Unrecognized Alias ID Type:" + id.getType().name() ); + break; } } @@ -278,7 +403,10 @@ public class AliasList implements Comparable return value; } - + + /** + * Lookup alias by site ID + */ public Alias getSiteID( String siteID ) { if( siteID != null ) @@ -301,17 +429,26 @@ public class AliasList implements Comparable return null; } - + + /** + * Lookup alias by status ID + */ public Alias getStatus( int status ) { return mStatus.get( status ); } + /** + * Lookup alias by Unique ID (UID) + */ public Alias getUniqueID( int uniqueID ) { return mUniqueID.get( uniqueID ); } + /** + * Lookup alias by ESN + */ public Alias getESNAlias( String esn ) { if( esn != null ) @@ -335,7 +472,9 @@ public class AliasList implements Comparable return null; } - + /** + * Lookup alias by fleetsync ID + */ public Alias getFleetsyncAlias( String ident ) { if( ident != null ) @@ -359,6 +498,9 @@ public class AliasList implements Comparable return null; } + /** + * Lookup alias by lojack function and ID + */ public Alias getLoJackAlias( Function function, String id ) { if( id != null ) @@ -375,6 +517,9 @@ public class AliasList implements Comparable return null; } + /** + * Lookup alias by MDC1200 ID + */ public Alias getMDC1200Alias( String ident ) { if( ident != null ) @@ -398,6 +543,9 @@ public class AliasList implements Comparable return null; } + /** + * Lookup alias by MPT1327 Ident + */ public Alias getMPT1327Alias( String ident ) { if( ident != null ) @@ -421,22 +569,9 @@ public class AliasList implements Comparable return null; } - public Group getGroup( Alias alias ) - { - if( alias != null ) - { - for( Group group: mGroups ) - { - if( group.contains( alias ) ) - { - return group; - } - } - } - - return null; - } - + /** + * Lookup alias by Mobile ID Number (MIN) + */ public Alias getMobileIDNumberAlias( String ident ) { if( ident != null ) @@ -460,6 +595,9 @@ public class AliasList implements Comparable return null; } + /** + * Lookup alias by talkgroup + */ public Alias getTalkgroupAlias( String tgid ) { if( tgid != null ) @@ -482,49 +620,27 @@ public class AliasList implements Comparable return null; } - + + /** + * Alias list name + */ public String toString() { return mName; } + /** + * Alias list name + */ @XmlAttribute public String getName() { return mName; } - - public void setName( String name ) - { - mName = name; - } - - public ArrayList getGroup() - { - return mGroups; - } - - public void setGroup( ArrayList groups ) - { - mGroups = groups; - } - - public void addGroup( Group group ) - { - mGroups.add( group ); - } - - public void removeGroup( Group group ) - { - mGroups.remove( group ); - } - - @Override - public int compareTo( AliasList otherAliasList ) - { - return getName().compareTo( otherAliasList.getName() ); - } + /** + * Lookup an alias by metadata type and string value + */ public Alias getAlias( String value, MetadataType type ) { switch( type ) @@ -576,5 +692,48 @@ public class AliasList implements Comparable return null; } - + + /** + * Receive alias change event notifications and modify this list accordingly + */ + @Override + public void receive( AliasEvent event ) + { + Alias alias = event.getAlias(); + + switch( event.getEvent() ) + { + case ADD: + if( alias.getList() != null && + getName().equalsIgnoreCase( alias.getList() ) ) + { + addAlias( alias ); + } + break; + case CHANGE: + if( alias.getList() != null && + getName().equalsIgnoreCase( alias.getList() ) ) + { + if( !mAliases.contains( alias ) ) + { + //Alias was moved to this list - add it + addAlias( alias ); + } + } + else if( mAliases.contains( alias ) ) + { + //An alias in this list was moved to another list + removeAlias( alias ); + } + break; + case DELETE: + if( mAliases.contains( alias ) ) + { + removeAlias( alias ); + } + break; + default: + break; + } + } } diff --git a/src/alias/AliasListNode.java b/src/alias/AliasListNode.java deleted file mode 100644 index 65632971..00000000 --- a/src/alias/AliasListNode.java +++ /dev/null @@ -1,126 +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 - ******************************************************************************/ -package alias; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JSeparator; - -import controller.ConfigurableNode; - -public class AliasListNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - public AliasListNode( AliasList list ) - { - super( list ); - } - - @Override - public JPanel getEditor() - { - return new ListEditor( this ); - } - - public AliasList getList() - { - return (AliasList)getUserObject(); - } - - public void init() - { - for( Group group: getList().getGroup() ) - { - GroupNode node = new GroupNode( group ); - - getModel().addNode( node, AliasListNode.this, getChildCount() ); - - node.init(); - } - - sort(); - } - - public String toString() - { - return getList().getName() + " [" + this.getChildCount() + "]"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addItem = new JMenuItem( "Add Group" ); - addItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - Group group = new Group(); - - getList().addGroup( group ); - - GroupNode node = new GroupNode( group ); - - getModel().addNode( node, - AliasListNode.this, - AliasListNode.this.getChildCount() ); - - sort(); - - getModel().showNode( node ); - } - } ); - - retVal.add( addItem ); - - retVal.add( new JSeparator() ); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasDirectoryNode parent = (AliasDirectoryNode)getParent(); - - parent.getAliasDirectory().removeAliasList( getList() ); - - save(); - - getModel().removeNodeFromParent( AliasListNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/AliasModel.java b/src/alias/AliasModel.java new file mode 100644 index 00000000..a051a0d0 --- /dev/null +++ b/src/alias/AliasModel.java @@ -0,0 +1,311 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package alias; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +import javax.swing.table.AbstractTableModel; + +import sample.Broadcaster; +import sample.Listener; +import alias.AliasEvent.Event; + +/** + * Alias Model + */ +public class AliasModel extends AbstractTableModel +{ + private static final long serialVersionUID = 1L; + + public static final int COLUMN_LIST = 0; + public static final int COLUMN_GROUP = 1; + public static final int COLUMN_NAME = 2; + public static final int COLUMN_ICON = 3; + public static final int COLUMN_COLOR = 4; + + private List mAliases = new CopyOnWriteArrayList<>(); + private Broadcaster mAliasEventBroadcaster = new Broadcaster<>(); + private Map mAliasListMap = new HashMap<>(); + + public AliasModel() + { + } + + /** + * Unmodifiable list of all aliases currently in the model + */ + public List getAliases() + { + return Collections.unmodifiableList( mAliases ); + } + + public Alias getAliasAtIndex( int row ) + { + if( mAliases.size() >= row ) + { + return mAliases.get( row ); + } + + return null; + } + + public AliasList getAliasList( String name ) + { + if( mAliasListMap.containsKey( name ) ) + { + return mAliasListMap.get( name ); + } + + AliasList aliasList = new AliasList( name ); + + for( Alias alias: mAliases ) + { + if( alias.getList().equalsIgnoreCase( name ) ) + { + aliasList.addAlias( alias ); + } + } + + mAliasListMap.put( name, aliasList ); + + //Register the new alias list to receive updates from this model + addListener( aliasList ); + + return aliasList; + } + + /** + * Returns a list of unique alias list names from across the alias set + */ + public List getListNames() + { + List listNames = new ArrayList<>(); + + for( Alias alias: mAliases ) + { + if( alias.hasList() && !listNames.contains( alias.getList() ) ) + { + listNames.add( alias.getList() ); + } + } + + Collections.sort( listNames ); + + return listNames; + } + + /** + * Returns a list of alias group names for all aliases + */ + public List getGroupNames() + { + List groupNames = new ArrayList<>(); + + for( Alias alias: mAliases ) + { + if( alias.hasGroup() && !groupNames.contains( alias.getGroup() ) ) + { + groupNames.add( alias.getGroup() ); + } + } + + Collections.sort( groupNames ); + + return groupNames; + } + + /** + * Returns a list of alias group names for all aliases that have a matching + * list name value + */ + public List getGroupNames( String listName ) + { + List groupNames = new ArrayList<>(); + + if( listName != null ) + { + for( Alias alias: mAliases ) + { + if( alias.hasList() && + alias.hasGroup() && + listName.equals( alias.getList() ) && + !groupNames.contains( alias.getGroup() ) ) + { + groupNames.add( alias.getGroup() ); + } + } + } + + Collections.sort( groupNames ); + + return groupNames; + } + + /** + * Bulk loading of aliases + */ + public void addAliases( List aliases ) + { + for( Alias alias: aliases ) + { + addAlias( alias ); + } + } + + /** + * Adds the alias to the model + */ + public int addAlias( Alias alias ) + { + if( alias != null ) + { + mAliases.add( alias ); + + int index = mAliases.size() - 1; + + fireTableRowsInserted( index, index ); + + broadcast( new AliasEvent( alias, Event.ADD ) ); + + return index; + } + + return -1; + } + + /** + * Removes the channel from the model and broadcasts a channel remove event + */ + public void removeAlias( Alias alias ) + { + if( alias != null ) + { + int index = mAliases.indexOf( alias ); + + mAliases.remove( alias ); + + fireTableRowsDeleted( index, index ); + + broadcast( new AliasEvent( alias, Event.DELETE ) ); + } + } + + @Override + public int getRowCount() + { + return mAliases.size(); + } + + @Override + public int getColumnCount() + { + return 5; + } + + @Override + public String getColumnName( int columnIndex ) + { + switch( columnIndex ) + { + case COLUMN_LIST: + return "List"; + case COLUMN_GROUP: + return "Group"; + case COLUMN_NAME: + return "Name"; + case COLUMN_ICON: + return "Icon"; + case COLUMN_COLOR: + return "Color"; + } + + return null; + } + + @Override + public Class getColumnClass( int columnIndex ) + { + if( columnIndex == COLUMN_COLOR ) + { + return Integer.class; + } + + return String.class; + } + + @Override + public boolean isCellEditable( int rowIndex, int columnIndex ) + { + return false; + } + + @Override + public Object getValueAt( int rowIndex, int columnIndex ) + { + Alias alias = mAliases.get( rowIndex ); + + switch( columnIndex ) + { + case COLUMN_LIST: + return alias.getList(); + case COLUMN_GROUP: + return alias.getGroup(); + case COLUMN_NAME: + return alias.getName(); + case COLUMN_ICON: + return alias.getIconName(); + case COLUMN_COLOR: + return alias.getColor(); + } + + return null; + } + + @Override + public void setValueAt( Object aValue, int rowIndex, int columnIndex ) + { + throw new IllegalArgumentException( "Not yet implemented" ); + } + + public void broadcast( AliasEvent event ) + { + if( event.getEvent() == Event.CHANGE ) + { + int index = mAliases.indexOf( event.getAlias() ); + + fireTableRowsUpdated( index, index ); + } + + mAliasEventBroadcaster.broadcast( event ); + } + + public void addListener( Listener listener ) + { + mAliasEventBroadcaster.addListener( listener ); + } + + public void removeListener( Listener listener ) + { + mAliasEventBroadcaster.removeListener( listener ); + } +} diff --git a/src/alias/AliasNameEditor.java b/src/alias/AliasNameEditor.java new file mode 100644 index 00000000..f628cd59 --- /dev/null +++ b/src/alias/AliasNameEditor.java @@ -0,0 +1,291 @@ +package alias; + +import gui.editor.Editor; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +import javax.swing.ComboBoxModel; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JColorChooser; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import map.IconManager; +import map.MapIcon; +import map.MapIconListCellRenderer; +import net.miginfocom.swing.MigLayout; +import settings.SettingsManager; +import alias.AliasEvent.Event; + +public class AliasNameEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + private static ComboBoxModel EMPTY_MODEL = new DefaultComboBoxModel<>(); + + private JComboBox mListCombo = new JComboBox<>( EMPTY_MODEL ); + private JComboBox mGroupCombo = new JComboBox<>( EMPTY_MODEL ); + private JComboBox mMapIconCombo; + private JTextField mName; + private JButton mButtonColor; + private JButton mBtnIconManager; + + private AliasModel mAliasModel; + private SettingsManager mSettingsManager; + + public AliasNameEditor( AliasModel aliasModel, SettingsManager settingsManager ) + { + mAliasModel = aliasModel; + mSettingsManager = settingsManager; + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 2", "[right][grow,fill]", + "[][][][][][][grow]" ) ); + + add( new JLabel( "Name:" ) ); + mName = new JTextField(); + mName.getDocument().addDocumentListener( new DocumentListener() + { + @Override + public void removeUpdate( DocumentEvent e ) + { + setModified( true ); + } + + @Override + public void insertUpdate( DocumentEvent e ) + { + setModified( true ); + } + + @Override + public void changedUpdate( DocumentEvent e ){} + } ); + add( mName, "wrap" ); + + add( new JLabel( "List:" ) ); + mListCombo.setEditable( true ); + mListCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + if( mListCombo.getSelectedItem() != null ) + { + List groups = mAliasModel + .getGroupNames( (String)mListCombo.getSelectedItem() ); + + if( groups.isEmpty() ) + { + mGroupCombo.setModel( EMPTY_MODEL ); + } + else + { + mGroupCombo.setModel( new DefaultComboBoxModel( + groups.toArray( new String[ groups.size() ] ) ) );; + } + } + + setModified( true ); + } + } ); + add( mListCombo, "wrap" ); + + add( new JLabel( "Group:" ) ); + mGroupCombo.setEditable( true ); + mGroupCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mGroupCombo, "wrap" ); + + add( new JLabel( "Color:" ) ); + + mButtonColor = new JButton( "Select ..." ); + mButtonColor.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + Color newColor = JColorChooser.showDialog( + AliasNameEditor.this, + "Choose color for this alias", + ( hasItem() ? getItem().getMapColor() : null ) ); + + if( newColor != null ) + { + mButtonColor.setForeground( newColor ); + mButtonColor.setBackground( newColor ); + + setModified( true ); + } + } + } ); + add( mButtonColor, "wrap" ); + + add( new JLabel( "Icon:" ) ); + + mMapIconCombo = new JComboBox( mSettingsManager.getMapIcons() ); + + MapIconListCellRenderer renderer = new MapIconListCellRenderer(); + renderer.setPreferredSize( new Dimension( 200, 30 ) ); + mMapIconCombo.setRenderer( renderer ); + mMapIconCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + + add( mMapIconCombo, "wrap" ); + + //Dummy place holder + add( new JLabel() ); + + mBtnIconManager = new JButton( "Icon Manager" ); + mBtnIconManager.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + final IconManager iconManager = + new IconManager( mSettingsManager, AliasNameEditor.this ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + iconManager.setVisible( true ); + } + } ); + } + } ); + + add( mBtnIconManager, "span 2,wrap" ); + + setModified( false ); + } + + @Override + public void setItem( Alias alias ) + { + super.setItem( alias ); + + if( hasItem() ) + { + mName.setText( alias.getName() ); + + List listNames = mAliasModel.getListNames(); + + if( listNames.isEmpty() ) + { + mListCombo.setModel( EMPTY_MODEL ); + } + else + { + mListCombo.setModel( new DefaultComboBoxModel( + listNames.toArray( new String[ listNames.size() ] ) ) );; + } + + mListCombo.setSelectedItem( alias.getList() ); + + List groupNames = mAliasModel.getGroupNames( alias.getList() ); + + if( groupNames.isEmpty() ) + { + mGroupCombo.setModel( EMPTY_MODEL ); + } + else + { + mGroupCombo.setModel( new DefaultComboBoxModel( + groupNames.toArray( new String[ groupNames.size() ] ) ) );; + } + + mGroupCombo.setSelectedItem( alias.getGroup() ); + + Color color = alias.getMapColor(); + + mButtonColor.setBackground( color ); + mButtonColor.setForeground( color ); + + String iconName = alias.getIconName(); + + if( iconName == null ) + { + iconName = SettingsManager.DEFAULT_ICON; + } + + MapIcon savedIcon = mSettingsManager.getMapIcon( iconName ); + + if( savedIcon != null ) + { + mMapIconCombo.setSelectedItem( savedIcon ); + } + } + else + { + mListCombo.setModel( EMPTY_MODEL ); + mGroupCombo.setModel( EMPTY_MODEL ); + mName.setText( null ); + + mButtonColor.setBackground( getBackground() ); + mButtonColor.setForeground( getForeground() ); + } + + repaint(); + + setModified( false ); + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + Alias alias = getItem(); + + if( mListCombo.getSelectedItem() != null ) + { + alias.setList( (String)mListCombo.getSelectedItem() ); + } + + if( mGroupCombo.getSelectedItem() != null ) + { + alias.setGroup( (String)mGroupCombo.getSelectedItem() ); + } + + alias.setName( mName.getText() ); + + alias.setColor( mButtonColor.getBackground().getRGB() ); + + if( mMapIconCombo.getSelectedItem() != null ) + { + alias.setIconName( ((MapIcon)mMapIconCombo.getSelectedItem()).getName() ); + } + + setModified( false ); + + //Broadcast an alias change event to save the updates + mAliasModel.broadcast( new AliasEvent( getItem(), Event.CHANGE ) ); + } + } +} diff --git a/src/alias/AliasNode.java b/src/alias/AliasNode.java deleted file mode 100644 index 43de7703..00000000 --- a/src/alias/AliasNode.java +++ /dev/null @@ -1,562 +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 - ******************************************************************************/ -package alias; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import map.MapIcon; -import alias.action.AliasAction; -import alias.action.beep.BeepAction; -import alias.action.beep.BeepActionNode; -import alias.action.clip.ClipAction; -import alias.action.clip.ClipActionNode; -import alias.action.script.ScriptAction; -import alias.action.script.ScriptActionNode; -import alias.id.esn.ESNNode; -import alias.id.esn.Esn; -import alias.id.fleetsync.FleetsyncID; -import alias.id.fleetsync.FleetsyncIDNode; -import alias.id.fleetsync.StatusID; -import alias.id.fleetsync.StatusIDNode; -import alias.id.lojack.LoJackFunctionAndID; -import alias.id.lojack.LoJackIDNode; -import alias.id.mdc.MDC1200ID; -import alias.id.mdc.MDC1200IDNode; -import alias.id.mobileID.MINNode; -import alias.id.mobileID.Min; -import alias.id.mpt1327.MPT1327ID; -import alias.id.mpt1327.MPT1327IDNode; -import alias.id.siteID.SiteID; -import alias.id.siteID.SiteIDNode; -import alias.id.talkgroup.TalkgroupID; -import alias.id.talkgroup.TalkgroupIDNode; -import alias.id.uniqueID.UniqueID; -import alias.id.uniqueID.UniqueIDNode; -import alias.priority.Priority; -import alias.priority.PriorityNode; -import alias.record.NonRecordable; -import alias.record.NonRecordableNode; -import controller.ConfigurableNode; - -public class AliasNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - public AliasNode( Alias alias ) - { - super( alias ); - } - - @Override - public JPanel getEditor() - { - return new AliasEditor( this, getModel().getResourceManager() ); - } - - public Alias getAlias() - { - return (Alias)getUserObject(); - } - - public String getIconPath() - { - String icon = getAlias().getIconName(); - - MapIcon mapIcon = getModel().getResourceManager() - .getSettingsManager().getMapIcon( icon ); - - if( mapIcon != null ) - { - return mapIcon.getPath(); - } - else - { - return null; - } - } - - public void init() - { - for( AliasID aliasID: getAlias().getId() ) - { - switch( aliasID.getType() ) - { - case ESN: - getModel().addNode( new ESNNode( (Esn)aliasID ), - AliasNode.this, getChildCount() ); - break; - case Fleetsync: - getModel().addNode( new FleetsyncIDNode( (FleetsyncID)aliasID ), - AliasNode.this, getChildCount() ); - break; - case LoJack: - getModel().addNode( new LoJackIDNode( (LoJackFunctionAndID)aliasID ), - AliasNode.this, getChildCount() ); - break; - case LTRNetUID: - getModel().addNode( new UniqueIDNode( (UniqueID)aliasID ), - AliasNode.this, getChildCount() ); - break; - case MDC1200: - getModel().addNode( new MDC1200IDNode( (MDC1200ID)aliasID ), - AliasNode.this, getChildCount() ); - break; - case MIN: - getModel().addNode( new MINNode( (Min)aliasID ), - AliasNode.this, getChildCount() ); - break; - case MPT1327: - getModel().addNode( new MPT1327IDNode( (MPT1327ID)aliasID ), - AliasNode.this, getChildCount() ); - break; - case NonRecordable: - getModel().addNode( new NonRecordableNode( (NonRecordable)aliasID ), - AliasNode.this, getChildCount() ); - break; - case Priority: - getModel().addNode( new PriorityNode( (Priority)aliasID ), - AliasNode.this, getChildCount() ); - break; - case Site: - getModel().addNode( new SiteIDNode( (SiteID)aliasID ), - AliasNode.this, getChildCount() ); - break; - case Status: - getModel().addNode( new StatusIDNode( (StatusID)aliasID ), - AliasNode.this, getChildCount() ); - break; - case Talkgroup: - getModel().addNode( new TalkgroupIDNode( (TalkgroupID)aliasID ), - AliasNode.this, getChildCount() ); - break; - default: - throw new IllegalArgumentException( "Unrecognized alias ID [" + - aliasID.getType() + "]"); - } - } - - for( AliasAction action: getAlias().getAction() ) - { - if( action instanceof BeepAction ) - { - getModel().addNode( new BeepActionNode( (BeepAction)action ), - AliasNode.this, getChildCount() ); - } - else if( action instanceof ClipAction ) - { - getModel().addNode( new ClipActionNode( (ClipAction)action ), - AliasNode.this, getChildCount() ); - } - else if( action instanceof ScriptAction ) - { - getModel().addNode( new ScriptActionNode( (ScriptAction)action ), - AliasNode.this, getChildCount() ); - } - } - - sort(); - } - - public String toString() - { - return getAlias().getName() + " [" + this.getChildCount() + "]"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenu addIDMenu = new JMenu( "Add ID" ); - - JMenuItem addESNItem = new JMenuItem( "ESN" ); - addESNItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - Esn esn = new Esn(); - - getAlias().addAliasID( esn ); - - ESNNode node = new ESNNode( esn ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addESNItem ); - - JMenuItem addFleetsyncItem = new JMenuItem( "Fleetsync" ); - addFleetsyncItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - FleetsyncID fs = new FleetsyncID(); - - getAlias().addAliasID( fs ); - - FleetsyncIDNode node = new FleetsyncIDNode( fs ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addFleetsyncItem ); - - JMenuItem addLoJackItem = new JMenuItem( "LoJack" ); - addLoJackItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - LoJackFunctionAndID lj = new LoJackFunctionAndID(); - - getAlias().addAliasID( lj ); - - LoJackIDNode node = new LoJackIDNode( lj ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addLoJackItem ); - - JMenuItem addMDCItem = new JMenuItem( "MDC-1200" ); - addMDCItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - MDC1200ID mdc = new MDC1200ID(); - - getAlias().addAliasID( mdc ); - - MDC1200IDNode node = new MDC1200IDNode( mdc ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addMDCItem ); - - JMenuItem addMINItem = new JMenuItem( "MIN" ); - addMINItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - Min min = new Min(); - - getAlias().addAliasID( min ); - - MINNode node = new MINNode( min ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addMINItem ); - - JMenuItem addMPTItem = new JMenuItem( "MPT-1327" ); - addMPTItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - MPT1327ID mpt = new MPT1327ID(); - - getAlias().addAliasID( mpt ); - - MPT1327IDNode node = new MPT1327IDNode( mpt ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addMPTItem ); - - JMenuItem addSiteItem = new JMenuItem( "Site" ); - addSiteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - SiteID siteID = new SiteID(); - - getAlias().addAliasID( siteID ); - - SiteIDNode node = new SiteIDNode( siteID ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addSiteItem ); - - JMenuItem addStatusItem = new JMenuItem( "Status" ); - addStatusItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - StatusID statusID = new StatusID(); - - getAlias().addAliasID( statusID ); - - StatusIDNode node = new StatusIDNode( statusID ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - - addIDMenu.add( addStatusItem ); - - JMenuItem addTalkgroupItem = new JMenuItem( "Talkgroup" ); - addTalkgroupItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - TalkgroupID tg = new TalkgroupID(); - - getAlias().addAliasID( tg ); - - TalkgroupIDNode node = new TalkgroupIDNode( tg ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addTalkgroupItem ); - - JMenuItem addUniqueIDItem = new JMenuItem( "Unique ID" ); - addUniqueIDItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - UniqueID uid = new UniqueID(); - - getAlias().addAliasID( uid ); - - UniqueIDNode node = new UniqueIDNode( uid ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addUniqueIDItem ); - - retVal.add( addIDMenu ); - - JMenu addActionMenu = new JMenu( "Add Action" ); - - JMenuItem addClipItem = new JMenuItem( "Audio Clip" ); - - addClipItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - ClipAction clipAction = new ClipAction(); - - getAlias().addAliasAction( clipAction ); - - ClipActionNode node = new ClipActionNode( clipAction ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - - addActionMenu.add( addClipItem ); - - JMenuItem addBeepItem = new JMenuItem( "Beep" ); - - addBeepItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - BeepAction beepAction = new BeepAction(); - - getAlias().addAliasAction( beepAction ); - - BeepActionNode node = new BeepActionNode( beepAction ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - - addActionMenu.add( addBeepItem ); - - JMenuItem addScriptItem = new JMenuItem( "Script" ); - - addScriptItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - ScriptAction scriptAction = new ScriptAction(); - - getAlias().addAliasAction( scriptAction ); - - ScriptActionNode node = new ScriptActionNode( scriptAction ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - - addActionMenu.add( addScriptItem ); - - retVal.add( addActionMenu ); - - retVal.addSeparator(); - - if( getAlias().isRecordable() ) - { - JMenuItem nonRecordableItem = new JMenuItem( "Set Non-Recordable" ); - - nonRecordableItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - NonRecordable non = new NonRecordable(); - - getAlias().addAliasID( non ); - - NonRecordableNode node = new NonRecordableNode( non ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - - retVal.add( nonRecordableItem ); - retVal.addSeparator(); - } - - if( !getAlias().hasPriority() ) - { - JMenuItem priorityItem = new JMenuItem( "Set Call Priority" ); - - priorityItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - Priority priority = new Priority(); - - getAlias().addAliasID( priority ); - - PriorityNode node = new PriorityNode( priority ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - - retVal.add( priorityItem ); - retVal.addSeparator(); - } - - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - GroupNode parent = (GroupNode)getParent(); - - parent.getGroup().removeAlias( getAlias() ); - - save(); - - getModel().removeNodeFromParent( AliasNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/ComponentEditor.java b/src/alias/ComponentEditor.java new file mode 100644 index 00000000..9cc909bf --- /dev/null +++ b/src/alias/ComponentEditor.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package alias; + +import javax.swing.JPanel; +import javax.swing.event.DocumentListener; + +public abstract class ComponentEditor extends JPanel implements DocumentListener +{ + private static final long serialVersionUID = 1L; + private boolean mModified; + private ComponentModificationListener mListener; + protected T mComponent; + + /** + * Abstract class for editor with notifications when contents are modified + * and methods for saving or resetting the component values with the + * contents of the editor. + */ + public ComponentEditor( T component ) + { + mComponent = component; + } + + public ComponentEditor() + { + } + + public void dispose() + { + mComponent = null; + mListener = null; + } + + public T getComponent() + { + return mComponent; + } + + protected void setModified( boolean modified ) + { + mModified = modified; + + if( modified && mListener != null ) + { + mListener.componentModified(); + } + } + + /** + * Indicates if the contents of the editor have been modified + */ + public boolean isModified() + { + return mModified; + } + + public void setComponentModificationListener( ComponentModificationListener listener ) + { + mListener = listener; + } + + /** + * Sets or resets the editable component + */ + public abstract void setComponent( T t ); + + /** + * Saves changed editor values to the component + */ + public abstract void save(); + + /** + * Reloads the editor contents from the source component and resets the + * modified flag + */ + public void reset() + { + setComponent( getComponent() ); + } + + /** + * Validate contents of other editors against this component editor before + * saving. Override this method to allow controller to pass other editors + * to this editor for content inspection/validation before indicating that + * this editor passes validation. This allows for validating editor contents + * that are contingent on settings in other editors. + * + * @throws ComponentValidationException if the contents of the editor + * argument cannot be validated, with the exception containing a detailed + * description of the issue. + */ + public void validate( ComponentEditor editor ) throws ComponentValidationException + { + } + + /** + * Listener interface to receive notification that the contents of the + * editor have been modified from the original component value and either + * need to be saved or reset + */ + public interface ComponentModificationListener + { + public void componentModified(); + } +} diff --git a/src/alias/ComponentValidationException.java b/src/alias/ComponentValidationException.java new file mode 100644 index 00000000..bfa8d925 --- /dev/null +++ b/src/alias/ComponentValidationException.java @@ -0,0 +1,14 @@ +package alias; + +public class ComponentValidationException extends Exception +{ + private static final long serialVersionUID = 1L; + + /** + * Component editor contents validation error + */ + public ComponentValidationException( String message ) + { + super( message ); + } +} diff --git a/src/alias/GroupEditor.java b/src/alias/GroupEditor.java deleted file mode 100644 index 6cb6ab1d..00000000 --- a/src/alias/GroupEditor.java +++ /dev/null @@ -1,110 +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 - ******************************************************************************/ -package alias; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; -import javax.swing.tree.TreePath; - -import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; - -public class GroupEditor extends JPanel implements ActionListener -{ - private static final long serialVersionUID = 1L; - private GroupNode mGroupNode; - - private JLabel mLabelName; - private JTextField mTextGroup; - - public GroupEditor( GroupNode groupNode ) - { - mGroupNode = groupNode; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "", "[right][grow,fill]", "[][][grow]" ) ); - - setBorder( BorderFactory.createTitledBorder( "Group" ) ); - - mLabelName = new JLabel( "Name:" ); - add( mLabelName, "align right" ); - - mTextGroup = new JTextField( mGroupNode.getGroup().getName() ); - add( mTextGroup, "wrap" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( GroupEditor.this ); - add( btnSave ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( GroupEditor.this ); - add( btnReset, "wrap" ); - } - - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) - { - String group = mTextGroup.getText(); - - if( group != null ) - { - boolean expanded = mGroupNode.getModel().getTree() - .isExpanded( new TreePath( mGroupNode ) ); - - mGroupNode.getGroup().setName( group ); - - ((ConfigurableNode)mGroupNode.getParent()).sort(); - - mGroupNode.save(); - - mGroupNode.show(); - - if( expanded ) - { - mGroupNode.getModel().getTree() - .expandPath( new TreePath( mGroupNode ) ); - } - } - else - { - JOptionPane.showMessageDialog( GroupEditor.this, "Please enter a group name" ); - } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextGroup.setText( mGroupNode.getGroup().getName() ); - } - - mGroupNode.refresh(); - } -} diff --git a/src/alias/GroupNode.java b/src/alias/GroupNode.java deleted file mode 100644 index ac0ca894..00000000 --- a/src/alias/GroupNode.java +++ /dev/null @@ -1,126 +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 - ******************************************************************************/ -package alias; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JSeparator; - -import controller.ConfigurableNode; - -public class GroupNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - public GroupNode( Group group ) - { - super( group ); - } - - @Override - public JPanel getEditor() - { - return new GroupEditor( this ); - } - - public Group getGroup() - { - return (Group)getUserObject(); - } - - public void init() - { - for( Alias alias: getGroup().getAlias() ) - { - AliasNode node = new AliasNode( alias ); - - getModel().addNode( node, GroupNode.this, getChildCount() ); - - node.init(); - } - - sort(); - } - - public String toString() - { - return getGroup().getName() + " [" + this.getChildCount() + "]"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addItem = new JMenuItem( "Add Alias" ); - addItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - Alias alias = new Alias(); - - getGroup().addAlias( alias ); - - AliasNode node = new AliasNode( alias ); - - getModel().addNode( node, - GroupNode.this, - GroupNode.this.getChildCount() ); - - sort(); - - getModel().showNode( node ); - } - } ); - - retVal.add( addItem ); - - retVal.add( new JSeparator() ); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasListNode parent = (AliasListNode)getParent(); - - parent.getList().removeGroup( getGroup() ); - - save(); - - getModel().removeNodeFromParent( GroupNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/ListEditor.java b/src/alias/ListEditor.java deleted file mode 100644 index b4649a5c..00000000 --- a/src/alias/ListEditor.java +++ /dev/null @@ -1,107 +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 - ******************************************************************************/ -package alias; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; -import javax.swing.tree.TreePath; - -import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; - -public class ListEditor extends JPanel implements ActionListener -{ - private static final long serialVersionUID = 1L; - private AliasListNode mListNode; - - private JLabel mLabelName; - private JTextField mTextGroup; - - public ListEditor( AliasListNode listNode ) - { - mListNode = listNode; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "", "[right][grow,fill]", "[][][grow]" ) ); - - setBorder( BorderFactory.createTitledBorder( "List" ) ); - - mLabelName = new JLabel( "Name:" ); - add( mLabelName, "align right" ); - - mTextGroup = new JTextField( mListNode.getList().getName() ); - add( mTextGroup, "grow, wrap" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( ListEditor.this ); - add( btnSave ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( ListEditor.this ); - add( btnReset, "wrap" ); - } - - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) - { - String group = mTextGroup.getText(); - - if( group != null ) - { - boolean expanded = mListNode.getModel().getTree() - .isExpanded( new TreePath( mListNode ) ); - - mListNode.getList().setName( group ); - ((ConfigurableNode)mListNode.getParent()).sort(); - mListNode.save(); - - mListNode.show(); - - if( expanded ) - { - mListNode.getModel().getTree().expandPath( new TreePath( mListNode ) ); - } - } - else - { - JOptionPane.showMessageDialog( ListEditor.this, "Please enter a list name" ); - } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextGroup.setText( mListNode.getList().getName() ); - } - - mListNode.refresh(); - } -} diff --git a/src/alias/MultipleAliasEditor.java b/src/alias/MultipleAliasEditor.java new file mode 100644 index 00000000..e22e0b1b --- /dev/null +++ b/src/alias/MultipleAliasEditor.java @@ -0,0 +1,417 @@ +package alias; + +import gui.editor.Editor; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JColorChooser; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JSeparator; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import map.IconManager; +import map.MapIcon; +import map.MapIconListCellRenderer; +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import settings.SettingsManager; +import alias.AliasEvent.Event; +import alias.id.priority.Priority; + +public class MultipleAliasEditor extends Editor> + implements Listener +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( MultipleAliasEditor.class ); + + private static String HELP_TEXT = + "Select attributes below to change for all selected aliases"; + + private JLabel mAliasCountLabel; + private JLabel mAliasCount; + + private JCheckBox mListCheckBox = new JCheckBox( "List" ); + private JComboBox mListCombo = new JComboBox<>(); + private JCheckBox mGroupCheckBox = new JCheckBox( "Group" ); + private JComboBox mGroupCombo = new JComboBox<>(); + private JCheckBox mIconCheckBox = new JCheckBox( "Icon" ); + private JComboBox mMapIconCombo; + private JCheckBox mColorCheckBox = new JCheckBox( "Color" ); + private JButton mButtonColor; + private JButton mBtnIconManager; + private JCheckBox mRecordCheckBox = new JCheckBox( "Record" ); + private JComboBox mRecordActionCombo = new JComboBox<>( Record.values() ); + private JCheckBox mPriorityCheckBox = new JCheckBox( "Priority" ); + private JSlider mPrioritySlider; + private JLabel mPrioritySliderLabel; + + private AliasModel mAliasModel; + private SettingsManager mSettingsManager; + + public MultipleAliasEditor( AliasModel aliasModel, SettingsManager settingsManager ) + { + mAliasModel = aliasModel; + mAliasModel.addListener( this ); + + mSettingsManager = settingsManager; + + init(); + } + + public List getAliases() + { + if( hasItem() ) + { + return (List)getItem(); + } + + return null; + } + + public void init() + { + setLayout( new MigLayout( "fill,wrap 2", "[grow,fill][grow,fill]", + "[][][][][][][][][][][][][grow,fill]" ) ); + + mAliasCountLabel = new JLabel( "Alias:" ); + add( mAliasCountLabel ); + + mAliasCount = new JLabel( "Multiple" ); + add( mAliasCount, "span" ); + + add( new JSeparator(), "span" ); + + add( new JLabel( HELP_TEXT ), "span" ); + + add( mListCheckBox ); + + mListCombo.setEditable( true ); + add( mListCombo, "wrap" ); + + add( mGroupCheckBox ); + + mGroupCombo.setEditable( true ); + add( mGroupCombo, "wrap" ); + + add( mColorCheckBox ); + + mButtonColor = new JButton( "Select ..." ); + mButtonColor.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + Color newColor = JColorChooser.showDialog( + MultipleAliasEditor.this, + "Choose color for this alias", null ); + + if( newColor != null ) + { + mButtonColor.setForeground( newColor ); + mButtonColor.setBackground( newColor ); + } + } + } ); + add( mButtonColor, "wrap" ); + + add( mIconCheckBox ); + + mMapIconCombo = new JComboBox( mSettingsManager.getMapIcons() ); + + MapIconListCellRenderer renderer = new MapIconListCellRenderer(); + renderer.setPreferredSize( new Dimension( 200, 30 ) ); + mMapIconCombo.setRenderer( renderer ); + add( mMapIconCombo, "wrap" ); + + //Dummy place holder + add( new JLabel() ); + + mBtnIconManager = new JButton( "Icon Manager" ); + mBtnIconManager.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + final IconManager iconManager = + new IconManager( mSettingsManager, MultipleAliasEditor.this ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + iconManager.setVisible( true ); + } + } ); + } + } ); + + add( mBtnIconManager, "span,wrap" ); + + add( mRecordCheckBox ); + add( mRecordActionCombo ); + + //Placeholder + add( new JLabel( " " ) ); + mPrioritySliderLabel = new JLabel( "Priority: " + Priority.MIN_PRIORITY ); + add( mPrioritySliderLabel ); + + add( mPriorityCheckBox ); + + mPrioritySlider = new JSlider( JSlider.HORIZONTAL, + Priority.MIN_PRIORITY, + Priority.MAX_PRIORITY + 1, + Priority.MIN_PRIORITY ); + + mPrioritySlider.setMajorTickSpacing( 20 ); + mPrioritySlider.setMinorTickSpacing( 5 ); + mPrioritySlider.setPaintTicks( true ); + mPrioritySlider.setLabelTable( mPrioritySlider.createStandardLabels( 25, 25 ) ); + mPrioritySlider.setPaintLabels( true ); + mPrioritySlider.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + int priority = mPrioritySlider.getValue(); + + if( priority > Priority.MAX_PRIORITY ) + { + mPrioritySliderLabel.setText( "Priority: Do Not Monitor" ); + } + else + { + mPrioritySliderLabel.setText( "Priority: " + mPrioritySlider.getValue() ); + } + } + } ); + mPrioritySlider.setToolTipText( HELP_TEXT ); + + add( mPrioritySlider ); + + JButton saveButton = new JButton( "Save" ); + saveButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + save(); + } + } ); + add( saveButton ); + + JButton resetButton = new JButton( "Reset" ); + resetButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + reset(); + } + } ); + add( resetButton ); + + setModified( false ); + } + + @Override + public void setItem( List item ) + { + super.setItem( item ); + + StringBuilder sb = new StringBuilder(); + sb.append( "Multiple Selected [ " ); + + if( hasItem() ) + { + sb.append( String.valueOf( getAliases().size() ) ); + } + else + { + sb.append( "0" ); + } + + sb.append( " ]" ); + + mAliasCount.setText( sb.toString() ); + + List listNames = mAliasModel.getListNames(); + listNames.add( 0, "" ); + mListCombo.setModel( new DefaultComboBoxModel( + listNames.toArray( new String[ listNames.size() ] ) ) ); + + List groupNames = mAliasModel.getGroupNames(); + groupNames.add( 0, "" ); + mGroupCombo.setModel( new DefaultComboBoxModel( + groupNames.toArray( new String[ groupNames.size() ] ) ) ); + + setCheckBoxesUnselected(); + + setModified( false ); + } + + private void setCheckBoxesUnselected() + { + mListCheckBox.setSelected( false ); + mGroupCheckBox.setSelected( false ); + mColorCheckBox.setSelected( false ); + mIconCheckBox.setSelected( false ); + mRecordCheckBox.setSelected( false ); + mPriorityCheckBox.setSelected( false ); + } + + @Override + public void reset() + { + List listNames = mAliasModel.getListNames(); + listNames.add( 0, "" ); + mListCombo.setModel( new DefaultComboBoxModel( + listNames.toArray( new String[ listNames.size() ] ) ) ); + + List groupNames = mAliasModel.getGroupNames(); + groupNames.add( 0, "" ); + mGroupCombo.setModel( new DefaultComboBoxModel( + groupNames.toArray( new String[ groupNames.size() ] ) ) ); + + mButtonColor.setForeground( getForeground() ); + mButtonColor.setBackground( getBackground() ); + + super.reset(); + } + + private boolean hasRequestedChanges() + { + return mListCheckBox.isSelected() || + mGroupCheckBox.isSelected() || + mColorCheckBox.isSelected() || + mIconCheckBox.isSelected() || + mRecordCheckBox.isSelected() || + mPriorityCheckBox.isSelected(); + } + + @Override + public void save() + { + if( hasRequestedChanges() && hasItem() ) + { + List aliases = getAliases(); + + for( Alias alias: aliases ) + { + if( mListCheckBox.isSelected() ) + { + String list = null; + + if( mListCombo.getSelectedItem() != null ) + { + list = (String)mListCombo.getSelectedItem(); + } + + alias.setList( list ); + } + + if( mGroupCheckBox.isSelected() ) + { + String group = null; + + if( mGroupCombo.getSelectedItem() != null ) + { + group = (String)mGroupCombo.getSelectedItem(); + } + + alias.setGroup( group ); + } + + if( mColorCheckBox.isSelected() ) + { + alias.setColor( mButtonColor.getForeground().getRGB() ); + } + + if( mIconCheckBox.isSelected() ) + { + if( mMapIconCombo.getSelectedItem() != null ) + { + alias.setIconName( ((MapIcon)mMapIconCombo.getSelectedItem()).getName() ); + } + else + { + alias.setIconName( null ); + } + } + + if( mRecordCheckBox.isSelected() ) + { + Record action = + (Record)mRecordActionCombo.getSelectedItem(); + + alias.setRecordable( action == Record.RECORDABLE ); + } + + if( mPriorityCheckBox.isSelected() ) + { + int priority = mPrioritySlider.getValue(); + + //This is a work-around -- we use max priority + 1 in the + //gui to indicate do not monitor, and change the value here + if( priority == Priority.MAX_PRIORITY + 1 ) + { + priority = Priority.DO_NOT_MONITOR; + } + + alias.setCallPriority( priority ); + } + } + + for( Alias alias: aliases ) + { + //Broadcast an alias change event to save the updates + mAliasModel.broadcast( new AliasEvent( alias, Event.CHANGE ) ); + } + } + } + + @Override + public void receive( AliasEvent event ) + { + if( event.getEvent() == Event.DELETE && + hasItem() && + getAliases().contains( event.getAlias() ) ) + { + getAliases().remove( event.getAlias() ); + setItem( getAliases() ); + } + } + + public enum Record + { + NON_RECORDABLE( "Non-Recordable" ), + RECORDABLE( "Recordable" ); + + private String mLabel; + + private Record( String label ) + { + mLabel = label; + } + + public String toString() + { + return mLabel; + } + } +} diff --git a/src/alias/action/AliasAction.java b/src/alias/action/AliasAction.java index 08308c82..4d10dd29 100644 --- a/src/alias/action/AliasAction.java +++ b/src/alias/action/AliasAction.java @@ -1,17 +1,46 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ package alias.action; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSeeAlso; +import javax.xml.bind.annotation.XmlTransient; + import message.Message; import alias.Alias; +import alias.action.beep.BeepAction; +import alias.action.clip.ClipAction; +import alias.action.script.ScriptAction; import controller.ThreadPoolManager; /** * Alias action defines an action to execute when an alias is detected active. */ +@XmlSeeAlso( { BeepAction.class, ClipAction.class, ScriptAction.class } ) +@XmlRootElement( name = "action" ) public abstract class AliasAction { public AliasAction() { } + + @XmlTransient + public abstract AliasActionType getType(); /** * Task to execute when an alias action is defined. The message argument is diff --git a/src/alias/action/AliasActionEditor.java b/src/alias/action/AliasActionEditor.java new file mode 100644 index 00000000..f4d41aa9 --- /dev/null +++ b/src/alias/action/AliasActionEditor.java @@ -0,0 +1,350 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package alias.action; + +import gui.editor.Editor; +import gui.editor.EmptyEditor; + +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JList; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.ListModel; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import net.miginfocom.swing.MigLayout; +import alias.Alias; +import alias.AliasEvent; +import alias.AliasEvent.Event; +import alias.AliasFactory; +import alias.AliasModel; + +public class AliasActionEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + private static ListModel EMPTY_MODEL = new DefaultListModel<>(); + private JList mAliasActionList = new JList<>( EMPTY_MODEL ); + private JButton mNewActionButton; + private JButton mCloneActionButton; + private JButton mDeleteActionButton; + private EditorContainer mEditorContainer = new EditorContainer(); + + private AliasModel mAliasModel; + + public AliasActionEditor( AliasModel model ) + { + mAliasModel = model; + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 3", + "[grow,fill][grow,fill][grow,fill]", "[][grow,fill][]" ) ); + + mAliasActionList.setVisibleRowCount( 6 ); + mAliasActionList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + mAliasActionList.setLayoutOrientation( JList.VERTICAL ); + mAliasActionList.addListSelectionListener( new ListSelectionListener() + { + @Override + public void valueChanged( ListSelectionEvent event ) + { + if( !event.getValueIsAdjusting() ) + { + JList list = (JList)event.getSource(); + + Object selectedItem = list.getSelectedValue(); + + if( selectedItem != null && selectedItem instanceof AliasAction ) + { + AliasAction selected = (AliasAction)selectedItem; + + mEditorContainer.setAliasAction( selected ); + + mCloneActionButton.setEnabled( true ); + mDeleteActionButton.setEnabled( true ); + } + else + { + mCloneActionButton.setEnabled( false ); + mDeleteActionButton.setEnabled( false ); + } + } + } + } ); + + JScrollPane scroller = new JScrollPane( mAliasActionList ); + add( scroller, "span,grow" ); + + add( mEditorContainer, "span,grow" ); + + mNewActionButton = new JButton( "New ..." ); + mNewActionButton.setToolTipText( "Create a new Alias Action" ); + mNewActionButton.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JPopupMenu menu = new JPopupMenu(); + + menu.add( new AddAliasActionItem( AliasActionType.BEEP ) ); + menu.add( new AddAliasActionItem( AliasActionType.CLIP ) ); + menu.add( new AddAliasActionItem( AliasActionType.SCRIPT ) ); + + menu.show( e.getComponent(), e.getX(), e.getY() ); + } + } ); + + add( mNewActionButton ); + + mCloneActionButton = new JButton( "Clone" ); + mCloneActionButton.setToolTipText( "Create a copy of the currently selected action" ); + mCloneActionButton.setEnabled( false ); + mCloneActionButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + AliasAction selected = mAliasActionList.getSelectedValue(); + + if( selected != null ) + { + AliasAction clone = AliasFactory.copyOf( selected ); + + if( clone != null ) + { + addAliasActionToList( clone ); + } + } + } + } ); + add( mCloneActionButton ); + + mDeleteActionButton = new JButton( "Delete" ); + mDeleteActionButton.setToolTipText( "Delete the currently selected action" ); + mDeleteActionButton.setEnabled( false ); + mDeleteActionButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + final AliasAction selected = mAliasActionList.getSelectedValue(); + + if( selected != null ) + { + int choice = JOptionPane.showConfirmDialog( mDeleteActionButton, + "Do you want to delete [" + selected.toString() + "]", + "Are you sure?", JOptionPane.YES_NO_OPTION ); + + if( choice == JOptionPane.YES_OPTION && hasItem() ) + { + Alias alias = getItem(); + + alias.removeAliasAction( selected ); + + mAliasModel.broadcast( new AliasEvent( alias, Event.CHANGE ) ); + + setItem( alias ); + } + } + } + } ); + add( mDeleteActionButton, "wrap" ); + } + + @Override + public void setItem( Alias alias ) + { + super.setItem( alias ); + + if( alias == null || alias.getAction().isEmpty() ) + { + mAliasActionList.setModel( EMPTY_MODEL ); + } + else + { + DefaultListModel model = new DefaultListModel<>(); + + List actions = alias.getAction(); + + Collections.sort( actions, new Comparator() + { + @Override + public int compare( AliasAction o1, AliasAction o2 ) + { + return o1.toString().compareTo( o2.toString() ); + } + } ); + + for( AliasAction action: actions ) + { + model.addElement( action ); + } + + mAliasActionList.setModel( model ); + } + + mEditorContainer.setAliasAction( null ); + } + + @Override + public void save() + { + if( isModified() || mEditorContainer.isModified() ) + { + if( mEditorContainer.isModified() ) + { + mEditorContainer.save(); + } + + setModified( false ); + + if( hasItem() ) + { + mAliasModel.broadcast( new AliasEvent( getItem(), Event.CHANGE ) ); + } + } + } + + public class EditorContainer extends JPanel + { + private static final long serialVersionUID = 1L; + + private Editor mEditor = new EmptyEditor<>(); + + public EditorContainer() + { + setLayout( new MigLayout( "","[grow,fill]", "[grow,fill]" ) ); + + add( mEditor ); + } + + public boolean isModified() + { + if( mEditor != null ) + { + return mEditor.isModified(); + } + + return false; + } + + public void save() + { + if( mEditor != null ) + { + mEditor.save(); + } + } + + public void setAliasAction( AliasAction aliasAction ) + { + if( mEditor != null ) + { + if( mEditor.isModified() ) + { + int option = JOptionPane.showConfirmDialog( + EditorContainer.this, + "Action settings have changed. Do you want to save these changes?", + "Save Changes?", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE ); + + if( option == JOptionPane.YES_OPTION ) + { + mEditor.save(); + } + } + } + removeAll(); + + //This will always give us an editor + mEditor = AliasFactory.getEditor( aliasAction ); + + add( mEditor ); + + revalidate(); + } + } + + private void addAliasActionToList( final AliasAction action ) + { + if( action != null && hasItem() ) + { + final Alias alias = getItem(); + + alias.addAliasAction( action ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + setItem( alias ); + + mAliasActionList.setSelectedValue( action, true ); + + AliasActionEditor.this.setModified( true ); + } + } ); + } + } + + public class AddAliasActionItem extends JMenuItem + { + private static final long serialVersionUID = 1L; + + private AliasActionType mAliasActionType; + + public AddAliasActionItem( AliasActionType type ) + { + super( type.toString() ); + + mAliasActionType = type; + + addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + final AliasAction action = + AliasFactory.getAliasAction( mAliasActionType ); + + addAliasActionToList( action ); + } + } ); + } + } +} diff --git a/src/buffer/ByteBufferListener.java b/src/alias/action/AliasActionListener.java similarity index 70% rename from src/buffer/ByteBufferListener.java rename to src/alias/action/AliasActionListener.java index 7df629a0..a967e446 100644 --- a/src/buffer/ByteBufferListener.java +++ b/src/alias/action/AliasActionListener.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,11 +15,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package buffer; +package alias.action; -import java.nio.ByteBuffer; +import alias.AliasEvent; -public interface ByteBufferListener +/** + * Interface for receiving alias events. Each listener will receive + * notification of all alias events. + */ +public interface AliasActionListener { - public void receive( ByteBuffer buffer ); + /** + * Notifies the listener that an alias event has occurred + */ + public void aliasChanged( AliasEvent event ); } diff --git a/src/alias/action/AliasActionManager.java b/src/alias/action/AliasActionManager.java index 3451dbdb..20a63440 100644 --- a/src/alias/action/AliasActionManager.java +++ b/src/alias/action/AliasActionManager.java @@ -3,13 +3,9 @@ package alias.action; import java.util.List; import message.Message; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sample.Listener; import alias.Alias; -import controller.ResourceManager; +import controller.ThreadPoolManager; /** * Manages all alias action events. Each received message is interrogated for @@ -20,14 +16,11 @@ import controller.ResourceManager; */ public class AliasActionManager implements Listener { - private final static Logger mLog = - LoggerFactory.getLogger( AliasActionManager.class ); - - private ResourceManager mResourceManager; + private ThreadPoolManager mThreadPoolManager; - public AliasActionManager( ResourceManager resourceManager ) + public AliasActionManager( ThreadPoolManager threadPoolManager ) { - mResourceManager = resourceManager; + mThreadPoolManager = threadPoolManager; } @Override @@ -51,8 +44,7 @@ public class AliasActionManager implements Listener * action needs to setup a timer, and provide the original * message to be used as part of the action (e.g. sending * the message as a text message to a cell phone */ - action.execute( mResourceManager.getThreadPoolManager(), - alias, message ); + action.execute( mThreadPoolManager, alias, message ); } } } diff --git a/src/alias/action/AliasActionNode.java b/src/alias/action/AliasActionNode.java deleted file mode 100644 index 79ee2484..00000000 --- a/src/alias/action/AliasActionNode.java +++ /dev/null @@ -1,40 +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 - ******************************************************************************/ -package alias.action; - -import controller.ConfigurableNode; - -public class AliasActionNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - public AliasActionNode( AliasAction action ) - { - super( action ); - } - - public void save() - { - getModel().getResourceManager().getPlaylistManager().save(); - } - - public String getIconPath() - { - return "images/action.png"; - } -} diff --git a/src/source/tuner/frequency/FrequencyChangeBroadcaster.java b/src/alias/action/AliasActionType.java similarity index 74% rename from src/source/tuner/frequency/FrequencyChangeBroadcaster.java rename to src/alias/action/AliasActionType.java index 89c03bf6..b54ddb06 100644 --- a/src/source/tuner/frequency/FrequencyChangeBroadcaster.java +++ b/src/alias/action/AliasActionType.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,12 +15,23 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package source.tuner.frequency; +package alias.action; - -public interface FrequencyChangeBroadcaster +public enum AliasActionType { - public abstract void addListener( FrequencyChangeListener listener ); + BEEP( "Beep" ), + CLIP( "Play Clip" ), + SCRIPT( "Run Script" ); - public abstract void removeListener( FrequencyChangeListener listener ); + private String mLabel; + + private AliasActionType( String label ) + { + mLabel = label; + } + + public String toString() + { + return mLabel; + } } diff --git a/src/alias/action/beep/BeepAction.java b/src/alias/action/beep/BeepAction.java index b260adbb..65ebd2bd 100644 --- a/src/alias/action/beep/BeepAction.java +++ b/src/alias/action/beep/BeepAction.java @@ -2,13 +2,27 @@ package alias.action.beep; import message.Message; import alias.Alias; +import alias.action.AliasActionType; import alias.action.RecurringAction; public class BeepAction extends RecurringAction { + + @Override + public AliasActionType getType() + { + return AliasActionType.BEEP; + } + @Override public void performAction( Alias alias, Message message ) { System.out.println( "\007" ); } + + @Override + public String toString() + { + return "Beep"; + } } diff --git a/src/alias/action/beep/BeepActionEditor.java b/src/alias/action/beep/BeepActionEditor.java index 7b5844f9..1f47f9aa 100644 --- a/src/alias/action/beep/BeepActionEditor.java +++ b/src/alias/action/beep/BeepActionEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,59 +17,75 @@ ******************************************************************************/ package alias.action.beep; +import gui.editor.DocumentListenerEditor; + +import java.awt.Color; +import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; -import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JSpinner; -import javax.swing.JTextArea; -import javax.swing.JTextField; +import javax.swing.JOptionPane; +import javax.swing.JSlider; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import alias.action.AliasAction; import alias.action.RecurringAction.Interval; -public class BeepActionEditor extends JPanel implements ActionListener +public class BeepActionEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( BeepActionEditor.class ); - - private BeepActionNode mBeepActionNode; - private JTextField mTextSiteID; - private JComboBox mComboInterval; - private JSpinner mSpinnerPeriod; + private static final String HELP_TEXT = + "

Beep Action

" + + "This action will sound a beep according to the interval
" + + "that you select:

" + + "Once: Beep the first time the alias is active and
" + + "never again

" + + "Once, Reset After Delay: Beep once and suppress
" + + "subsequent beeps for the specified period in seconds.
" + + "After the reset period, beep again when the alias is active.

" + + "Until Dismissed: Beep every period seconds until you
" + + "click OK on the dialog that appears. Alerting is suppressed
" + + "for 15 seconds after you click OK.
" + + ""; - public BeepActionEditor( BeepActionNode beepActionNode ) + private JComboBox mComboInterval; + private JSlider mPeriodSlider; + private JLabel mPeriodSliderLabel; + + public BeepActionEditor( AliasAction aliasAction ) { - mBeepActionNode = beepActionNode; - - initGUI(); + init(); + + setItem( aliasAction ); } - private void initGUI() + public BeepAction getBeepAction() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][grow]" ) ); - - add( new JLabel( "Action: Beep" ), "span,align center" ); + if( getItem() instanceof BeepAction ) + { + return (BeepAction)getItem(); + } - add( new JLabel( "Interval:" ) ); + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][]" ) ); + + add( new JLabel( "Beep Interval:" ) ); mComboInterval = new JComboBox( Interval.values() ); - - mComboInterval.setSelectedItem( mBeepActionNode.getBeepAction().getInterval() ); - + mComboInterval.setToolTipText( HELP_TEXT ); mComboInterval.addActionListener( new ActionListener() { @Override @@ -81,71 +97,97 @@ public class BeepActionEditor extends JPanel implements ActionListener if( selected != null ) { /* Enable/disable period spinner based on selection */ - mSpinnerPeriod.setEnabled( selected != Interval.ONCE ); + boolean enabled = selected != Interval.ONCE; - mBeepActionNode.getBeepAction().setInterval( selected ); + mPeriodSlider.setEnabled( enabled ); + mPeriodSliderLabel.setEnabled( enabled ); } + + setModified( true ); } }); - add( mComboInterval, "wrap" ); - final SpinnerModel model = new SpinnerNumberModel( - mBeepActionNode.getBeepAction().getPeriod(), 1, 30, 1 ); - + final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 ); model.addChangeListener( new ChangeListener() { @Override public void stateChanged( ChangeEvent e ) { - mBeepActionNode.getBeepAction().setPeriod( (int)model.getValue() ); + setModified( true ); } } ); - mSpinnerPeriod = new JSpinner( model ); - - if( mBeepActionNode.getBeepAction().getInterval() == Interval.ONCE ) + mPeriodSlider = new JSlider( JSlider.HORIZONTAL, 1, 60, 1 ); + mPeriodSlider.setMajorTickSpacing( 10 ); + mPeriodSlider.setMinorTickSpacing( 2 ); + mPeriodSlider.setPaintTicks( true ); + mPeriodSlider.setLabelTable( mPeriodSlider.createStandardLabels( 10, 10 ) ); + mPeriodSlider.setPaintLabels( true ); + mPeriodSlider.addChangeListener( new ChangeListener() { - mSpinnerPeriod.setEnabled( false ); + @Override + public void stateChanged( ChangeEvent e ) + { + mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() ); + setModified( true ); + } + } ); + mPeriodSlider.setToolTipText( HELP_TEXT ); + + mPeriodSliderLabel = new JLabel( "Period: " + mPeriodSlider.getValue() + " " ); + add( mPeriodSliderLabel ); + add( mPeriodSlider, "wrap,grow" ); + + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( BeepActionEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( help, "align left" ); + } + + @Override + public void setItem( AliasAction item ) + { + super.setItem( item ); + + if( hasItem() ) + { + BeepAction beep = getBeepAction(); + + Interval interval = beep.getInterval(); + + mComboInterval.setSelectedItem( interval ); + + boolean enabled = interval != Interval.ONCE; + + mPeriodSliderLabel.setEnabled( enabled ); + mPeriodSlider.setEnabled( enabled ); + mPeriodSlider.setValue( beep.getPeriod() ); } - add( new JLabel( "Period:" ) ); - add( mSpinnerPeriod, "wrap" ); - - StringBuilder sb = new StringBuilder(); - - sb.append( "This action will sound a beep according to the interval that you select.\n\n" ); - sb.append( "Once - Beep the first time the alias is active and never again.\n\n" ); - sb.append( "Once, Reset After Delay - Beep once and suppress subsequent beeps for the specified period in seconds. After the reset period, it will beep again when the alias is active.\n\n" ); - sb.append( "Until Dismissed - Beep every period seconds until you click OK on the dialog that appears. Alerting is suppressed for 15 seconds after you click OK." ); - - JTextArea description = new JTextArea( sb.toString() ); - - description.setLineWrap( true ); - description.setBackground( getBackground() ); - - add( description, "growx,span" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( BeepActionEditor.this ); - add( btnSave, "growx,push" ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( BeepActionEditor.this ); - add( btnReset, "growx,push" ); + setModified( false ); } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) + public void save() + { + if( hasItem() && isModified() ) { - mBeepActionNode.save(); - mBeepActionNode.show(); + BeepAction beep = getBeepAction(); + + beep.setInterval( (Interval)mComboInterval.getSelectedItem() ); + beep.setPeriod( mPeriodSlider.getValue() ); } - mBeepActionNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/action/beep/BeepActionNode.java b/src/alias/action/beep/BeepActionNode.java deleted file mode 100644 index 817e3949..00000000 --- a/src/alias/action/beep/BeepActionNode.java +++ /dev/null @@ -1,88 +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 - ******************************************************************************/ -package alias.action.beep; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import alias.AliasNode; -import alias.action.AliasActionNode; - -public class BeepActionNode extends AliasActionNode -{ - private static final long serialVersionUID = 1L; - - public BeepActionNode( BeepAction beepAction ) - { - super( beepAction ); - } - - @Override - public JPanel getEditor() - { - return new BeepActionEditor( this ); - } - - public BeepAction getBeepAction() - { - return (BeepAction)getUserObject(); - } - - public String toString() - { - return "Action: Beep"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasAction( getBeepAction() ); - - /* Save before removing the node ... so that we still have - * a reference to the parent's resources */ - save(); - - getModel().removeNodeFromParent( BeepActionNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/action/clip/ClipAction.java b/src/alias/action/clip/ClipAction.java index 30e4c113..dd338d96 100644 --- a/src/alias/action/clip/ClipAction.java +++ b/src/alias/action/clip/ClipAction.java @@ -13,6 +13,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.Alias; +import alias.action.AliasActionType; import alias.action.RecurringAction; public class ClipAction extends RecurringAction @@ -28,9 +29,14 @@ public class ClipAction extends RecurringAction { } + @Override + public AliasActionType getType() + { + return AliasActionType.CLIP; + } + public String getPath() { - return mFilePath; } @@ -87,4 +93,18 @@ public class ClipAction extends RecurringAction throw e; } } + + + @Override + public String toString() + { + if( mFilePath == null ) + { + return "Play Clip"; + } + else + { + return "Play Clip: " + mFilePath; + } + } } diff --git a/src/alias/action/clip/ClipActionEditor.java b/src/alias/action/clip/ClipActionEditor.java index e07b4aee..83b09cc2 100644 --- a/src/alias/action/clip/ClipActionEditor.java +++ b/src/alias/action/clip/ClipActionEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,9 +17,14 @@ ******************************************************************************/ package alias.action.clip; -import java.awt.EventQueue; +import gui.editor.DocumentListenerEditor; + +import java.awt.Color; +import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.io.File; import javax.swing.JButton; @@ -27,9 +32,7 @@ import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JSpinner; -import javax.swing.JTextArea; +import javax.swing.JSlider; import javax.swing.JTextField; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; @@ -37,43 +40,58 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import alias.action.AliasAction; import alias.action.RecurringAction.Interval; -public class ClipActionEditor extends JPanel implements ActionListener +public class ClipActionEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( ClipActionEditor.class ); - - private ClipActionNode mClipActionNode; + private static final String PLEASE_SELECT_A_FILE = "Please select a file"; + private static final String HELP_TEXT = + "

Clip Action

" + + "This action will play an audio clip according to the interval
" + + "that you select:

" + + "Once: play the first time the alias is active and
" + + "never again

" + + "Once, Reset After Delay: play once and suppress
" + + "subsequent runs for the specified period in seconds.
" + + "After the reset period, play again when the alias is active.

" + + "Until Dismissed: play every period seconds until you
" + + "click OK on the dialog that appears. Alerting is suppressed
" + + "for 15 seconds after you click OK.
" + + ""; + private JComboBox mComboInterval; - private JSpinner mSpinnerPeriod; + private JSlider mPeriodSlider; + private JLabel mPeriodSliderLabel; private JTextField mTextFilePath; - public ClipActionEditor( ClipActionNode clipActionNode ) + public ClipActionEditor( AliasAction aliasAction ) { - mClipActionNode = clipActionNode; - - initGUI(); + init(); + + setItem( aliasAction ); } - private void initGUI() + public ClipAction getClipAction() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][grow]" ) ); - - add( new JLabel( "Action: Audio Clip" ), "span,align center" ); + if( getItem() instanceof ClipAction ) + { + return (ClipAction)getItem(); + } - add( new JLabel( "Interval:" ) ); + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][]" ) ); + + add( new JLabel( "Play Interval:" ) ); mComboInterval = new JComboBox( Interval.values() ); - - mComboInterval.setSelectedItem( mClipActionNode.getClipAction().getInterval() ); - + mComboInterval.setToolTipText( HELP_TEXT ); mComboInterval.addActionListener( new ActionListener() { @Override @@ -84,37 +102,51 @@ public class ClipActionEditor extends JPanel implements ActionListener if( selected != null ) { - /* Enable/disable period spinner based on selection */ - mSpinnerPeriod.setEnabled( selected != Interval.ONCE ); + /* Enable/disable period slider based on selection */ + boolean enabled = selected != Interval.ONCE; - mClipActionNode.getClipAction().setInterval( selected ); + mPeriodSlider.setEnabled( enabled ); + mPeriodSliderLabel.setEnabled( enabled ); } + + setModified( true ); } }); - - add( mComboInterval, "wrap" ); + add( mComboInterval ); - final SpinnerModel model = new SpinnerNumberModel( - mClipActionNode.getClipAction().getPeriod(), 1, 30, 1 ); - + final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 ); model.addChangeListener( new ChangeListener() { @Override public void stateChanged( ChangeEvent e ) { - mClipActionNode.getClipAction().setPeriod( (int)model.getValue() ); + setModified( true ); } } ); - mSpinnerPeriod = new JSpinner( model ); - - if( mClipActionNode.getClipAction().getInterval() == Interval.ONCE ) + mPeriodSlider = new JSlider( JSlider.HORIZONTAL, 1, 60, 1 ); + mPeriodSlider.setMajorTickSpacing( 10 ); + mPeriodSlider.setMinorTickSpacing( 2 ); + mPeriodSlider.setPaintTicks( true ); + mPeriodSlider.setLabelTable( mPeriodSlider.createStandardLabels( 10, 10 ) ); + mPeriodSlider.setPaintLabels( true ); + mPeriodSlider.addChangeListener( new ChangeListener() { - mSpinnerPeriod.setEnabled( false ); - } + @Override + public void stateChanged( ChangeEvent e ) + { + mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() ); + setModified( true ); + } + } ); + mPeriodSlider.setToolTipText( HELP_TEXT ); - add( new JLabel( "Period:" ) ); - add( mSpinnerPeriod, "wrap" ); + mPeriodSliderLabel = new JLabel( "Period: " + mPeriodSlider.getValue() + " " ); + add( mPeriodSliderLabel ); + add( mPeriodSlider, "wrap,grow" ); + + mTextFilePath = new JTextField( PLEASE_SELECT_A_FILE ); + add( mTextFilePath, "growx,span" ); JButton fileButton = new JButton( "File" ); @@ -131,88 +163,134 @@ public class ClipActionEditor extends JPanel implements ActionListener { File file = chooser.getSelectedFile(); - mClipActionNode.getClipAction().setPath( file.getAbsolutePath() ); mTextFilePath.setText( file.getAbsolutePath() ); + + setModified( true ); } } } ); - add( fileButton ); + add( fileButton, "grow" ); JButton testButton = new JButton( "Test" ); - testButton.addActionListener( new ActionListener() + + testButton.addActionListener( new ActionListener() { @Override - public void actionPerformed( ActionEvent actionEvent ) + public void actionPerformed( ActionEvent e ) { - String path = mClipActionNode.getClipAction().getPath(); - - if( path == null || path.trim().isEmpty() ) + if( hasItem() ) { - JOptionPane.showMessageDialog( ClipActionEditor.this, - "Please select an audio file", "Please select file", - JOptionPane.ERROR_MESSAGE ); - } - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() + String path = mTextFilePath.getText(); + + if( path == null || path.trim().isEmpty() ) { - try - { - mClipActionNode.getClipAction().play(); - } - catch( Exception e ) - { - JOptionPane.showMessageDialog( ClipActionEditor.this, - "Couldn't play audio clip [" + e.getMessage() + "]", - "Error", JOptionPane.ERROR_MESSAGE ); - } + JOptionPane.showMessageDialog( ClipActionEditor.this, + "Please select an audio file", PLEASE_SELECT_A_FILE, + JOptionPane.ERROR_MESSAGE ); } - } ); + else + { + if( isModified() ) + { + int option = JOptionPane.showConfirmDialog( + ClipActionEditor.this, + "Settings have changed. Do you want to save these changes?", + "Save Changes?", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE ); + + if( option == JOptionPane.YES_OPTION ) + { + save(); + } + } + + new Thread( new Runnable() + { + @Override + public void run() + { + try + { + getClipAction().play(); + } + catch( Exception e1 ) + { + JOptionPane.showMessageDialog( ClipActionEditor.this, + "Couldn't play audio clip [" + e1.getMessage() + "]", + "Error", JOptionPane.ERROR_MESSAGE ); + } + } + }).start(); + } + } } } ); - - add( testButton, "wrap" ); - - mTextFilePath = new JTextField( mClipActionNode.getClipAction().getPath() ); - add( mTextFilePath, "growx,span" ); - StringBuilder sb = new StringBuilder(); + add( testButton, "grow,wrap" ); - sb.append( "This action will play the audio clip according to the interval that you select.\n\n" ); - sb.append( "Once - Play the first time the alias is active and never again.\n\n" ); - sb.append( "Once, Reset After Delay - Play once and suppress subsequent plays for the specified period in seconds. After the reset period, it will play again when the alias is active.\n\n" ); - sb.append( "Until Dismissed - Play audio clip every period seconds until you click OK on the dialog that appears. Alerting is suppressed for 15 seconds after you click OK." ); + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( ClipActionEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( help, "align left" ); + } + + @Override + public void setItem( AliasAction item ) + { + super.setItem( item ); - JTextArea description = new JTextArea( sb.toString() ); - - description.setLineWrap( true ); - description.setBackground( getBackground() ); - - add( description, "growx,span" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( ClipActionEditor.this ); - add( btnSave, "growx,push" ); + if( hasItem() ) + { + ClipAction clip = getClipAction(); - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( ClipActionEditor.this ); - add( btnReset, "growx,push" ); + Interval interval = clip.getInterval(); + + mComboInterval.setSelectedItem( interval ); + + boolean enabled = interval != Interval.ONCE; + + mPeriodSliderLabel.setEnabled( enabled ); + mPeriodSlider.setEnabled( enabled ); + mPeriodSlider.setValue( clip.getPeriod() ); + + String filepath = clip.getPath(); + + if( filepath != null && !filepath.isEmpty() ) + { + mTextFilePath.setText( filepath ); + } + else + { + mTextFilePath.setText( PLEASE_SELECT_A_FILE ); + } + } + + setModified( false ); } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) + public void save() + { + if( hasItem() && isModified() ) { - mClipActionNode.save(); - mClipActionNode.show(); + ClipAction script = getClipAction(); + + script.setInterval( (Interval)mComboInterval.getSelectedItem() ); + script.setPeriod( mPeriodSlider.getValue() ); + script.setPath( mTextFilePath.getText() ); } - mClipActionNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/action/clip/ClipActionNode.java b/src/alias/action/clip/ClipActionNode.java deleted file mode 100644 index e3255cd0..00000000 --- a/src/alias/action/clip/ClipActionNode.java +++ /dev/null @@ -1,88 +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 - ******************************************************************************/ -package alias.action.clip; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import alias.AliasNode; -import alias.action.AliasActionNode; - -public class ClipActionNode extends AliasActionNode -{ - private static final long serialVersionUID = 1L; - - public ClipActionNode( ClipAction clipAction ) - { - super( clipAction ); - } - - @Override - public JPanel getEditor() - { - return new ClipActionEditor( this ); - } - - public ClipAction getClipAction() - { - return (ClipAction)getUserObject(); - } - - public String toString() - { - return "Action: Audio Clip"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasAction( getClipAction() ); - - /* Save before removing the node ... so that we still have - * a reference to the parent's resources */ - save(); - - getModel().removeNodeFromParent( ClipActionNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/action/script/ScriptAction.java b/src/alias/action/script/ScriptAction.java index de94ffc1..8dcfb55f 100644 --- a/src/alias/action/script/ScriptAction.java +++ b/src/alias/action/script/ScriptAction.java @@ -9,6 +9,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.Alias; +import alias.action.AliasActionType; import alias.action.RecurringAction; public class ScriptAction extends RecurringAction @@ -21,6 +22,12 @@ public class ScriptAction extends RecurringAction { } + @Override + public AliasActionType getType() + { + return AliasActionType.SCRIPT; + } + public String getScript() { return mScript; @@ -75,4 +82,17 @@ public class ScriptAction extends RecurringAction } } } + + @Override + public String toString() + { + if( mScript == null ) + { + return "Run Script"; + } + else + { + return "Run Script: " + mScript; + } + } } diff --git a/src/alias/action/script/ScriptActionEditor.java b/src/alias/action/script/ScriptActionEditor.java index 22f2c77d..a0593e42 100644 --- a/src/alias/action/script/ScriptActionEditor.java +++ b/src/alias/action/script/ScriptActionEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,9 +17,14 @@ ******************************************************************************/ package alias.action.script; -import java.awt.EventQueue; +import gui.editor.DocumentListenerEditor; + +import java.awt.Color; +import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.io.File; import javax.swing.JButton; @@ -27,9 +32,7 @@ import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JSpinner; -import javax.swing.JTextArea; +import javax.swing.JSlider; import javax.swing.JTextField; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; @@ -37,43 +40,58 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import alias.action.AliasAction; import alias.action.RecurringAction.Interval; -public class ScriptActionEditor extends JPanel implements ActionListener +public class ScriptActionEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( ScriptActionEditor.class ); - - private ScriptActionNode mScriptActionNode; + private static final String PLEASE_SELECT_A_FILE = "Please select a file"; + private static final String HELP_TEXT = + "

Script Action

" + + "This action will run a script according to the interval
" + + "that you select:

" + + "Once: run the first time the alias is active and
" + + "never again

" + + "Once, Reset After Delay: run once and suppress
" + + "subsequent runs for the specified period in seconds.
" + + "After the reset period, run again when the alias is active.

" + + "Until Dismissed: run every period seconds until you
" + + "click OK on the dialog that appears. Alerting is suppressed
" + + "for 15 seconds after you click OK.
" + + ""; + private JComboBox mComboInterval; - private JSpinner mSpinnerPeriod; + private JSlider mPeriodSlider; + private JLabel mPeriodSliderLabel; private JTextField mTextFilePath; - public ScriptActionEditor( ScriptActionNode clipActionNode ) + public ScriptActionEditor( AliasAction aliasAction ) { - mScriptActionNode = clipActionNode; - - initGUI(); + init(); + + setItem( aliasAction ); } - private void initGUI() + public ScriptAction getScriptAction() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][grow]" ) ); - - add( new JLabel( "Action: Script" ), "span,align center" ); + if( getItem() instanceof ScriptAction ) + { + return (ScriptAction)getItem(); + } - add( new JLabel( "Interval:" ) ); + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][]" ) ); + + add( new JLabel( "Run Interval:" ) ); mComboInterval = new JComboBox( Interval.values() ); - - mComboInterval.setSelectedItem( mScriptActionNode.getScriptAction().getInterval() ); - + mComboInterval.setToolTipText( HELP_TEXT ); mComboInterval.addActionListener( new ActionListener() { @Override @@ -84,37 +102,51 @@ public class ScriptActionEditor extends JPanel implements ActionListener if( selected != null ) { - /* Enable/disable period spinner based on selection */ - mSpinnerPeriod.setEnabled( selected != Interval.ONCE ); + /* Enable/disable period slider based on selection */ + boolean enabled = selected != Interval.ONCE; - mScriptActionNode.getScriptAction().setInterval( selected ); + mPeriodSlider.setEnabled( enabled ); + mPeriodSliderLabel.setEnabled( enabled ); } + + setModified( true ); } }); - - add( mComboInterval, "wrap" ); + add( mComboInterval ); - final SpinnerModel model = new SpinnerNumberModel( - mScriptActionNode.getScriptAction().getPeriod(), 1, 30, 1 ); - + final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 ); model.addChangeListener( new ChangeListener() { @Override public void stateChanged( ChangeEvent e ) { - mScriptActionNode.getScriptAction().setPeriod( (int)model.getValue() ); + setModified( true ); } } ); - mSpinnerPeriod = new JSpinner( model ); - - if( mScriptActionNode.getScriptAction().getInterval() == Interval.ONCE ) + mPeriodSlider = new JSlider( JSlider.HORIZONTAL, 1, 60, 1 ); + mPeriodSlider.setMajorTickSpacing( 10 ); + mPeriodSlider.setMinorTickSpacing( 2 ); + mPeriodSlider.setPaintTicks( true ); + mPeriodSlider.setLabelTable( mPeriodSlider.createStandardLabels( 10, 10 ) ); + mPeriodSlider.setPaintLabels( true ); + mPeriodSlider.addChangeListener( new ChangeListener() { - mSpinnerPeriod.setEnabled( false ); - } + @Override + public void stateChanged( ChangeEvent e ) + { + mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() ); + setModified( true ); + } + } ); + mPeriodSlider.setToolTipText( HELP_TEXT ); - add( new JLabel( "Period:" ) ); - add( mSpinnerPeriod, "wrap" ); + mPeriodSliderLabel = new JLabel( "Period: " + mPeriodSlider.getValue() + " " ); + add( mPeriodSliderLabel ); + add( mPeriodSlider, "wrap,grow" ); + + mTextFilePath = new JTextField( PLEASE_SELECT_A_FILE ); + add( mTextFilePath, "growx,span" ); JButton fileButton = new JButton( "File" ); @@ -131,13 +163,14 @@ public class ScriptActionEditor extends JPanel implements ActionListener { File file = chooser.getSelectedFile(); - mScriptActionNode.getScriptAction().setScript( file.getAbsolutePath() ); mTextFilePath.setText( file.getAbsolutePath() ); + + setModified( true ); } } } ); - add( fileButton ); + add( fileButton, "grow" ); JButton testButton = new JButton( "Test" ); testButton.addActionListener( new ActionListener() @@ -145,74 +178,115 @@ public class ScriptActionEditor extends JPanel implements ActionListener @Override public void actionPerformed( ActionEvent actionEvent ) { - String script = mScriptActionNode.getScriptAction().getScript(); + String script = mTextFilePath.getText(); if( script == null || script.trim().isEmpty() ) { JOptionPane.showMessageDialog( ScriptActionEditor.this, - "Please select a script file", "Please select file", + "Please select a script file", PLEASE_SELECT_A_FILE, JOptionPane.ERROR_MESSAGE ); } - - EventQueue.invokeLater( new Runnable() + else { - @Override - public void run() + if( isModified() ) { - try + int option = JOptionPane.showConfirmDialog( + ScriptActionEditor.this, + "Settings have changed. Do you want to save these changes?", + "Save Changes?", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE ); + + if( option == JOptionPane.YES_OPTION ) { - mScriptActionNode.getScriptAction().play(); - } - catch( Exception e ) - { - JOptionPane.showMessageDialog( ScriptActionEditor.this, - "Couldn't run script [" + e.getMessage() + "]", - "Error", JOptionPane.ERROR_MESSAGE ); + save(); } } - } ); + + new Thread( new Runnable() + { + @Override + public void run() + { + try + { + getScriptAction().play(); + } + catch( Exception e1 ) + { + JOptionPane.showMessageDialog( ScriptActionEditor.this, + e1.getMessage(), "Error", JOptionPane.ERROR_MESSAGE ); + } + } + }).start(); + } + } } ); - add( testButton, "wrap" ); + add( testButton, "grow,wrap" ); - mTextFilePath = new JTextField( mScriptActionNode.getScriptAction().getScript() ); - add( mTextFilePath, "growx,span" ); + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( ScriptActionEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( help, "align left" ); + } + + @Override + public void setItem( AliasAction item ) + { + super.setItem( item ); + + if( hasItem() ) + { + ScriptAction script = getScriptAction(); - StringBuilder sb = new StringBuilder(); - - sb.append( "This action will run the script according to the interval that you select.\n\n" ); - sb.append( "Once - Run script the first time the alias is active and never again.\n\n" ); - sb.append( "Once, Reset After Delay - Run once and suppress subsequent runs for the specified period in seconds. After the reset period, it will play again when the alias is active.\n\n" ); - sb.append( "Until Dismissed - Run script every period seconds until you click OK on the dialog that appears. Alerting is suppressed for 15 seconds after you click OK." ); - - JTextArea description = new JTextArea( sb.toString() ); - - description.setLineWrap( true ); - description.setBackground( getBackground() ); - - add( description, "growx,span" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( ScriptActionEditor.this ); - add( btnSave, "growx,push" ); + Interval interval = script.getInterval(); + + mComboInterval.setSelectedItem( interval ); - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( ScriptActionEditor.this ); - add( btnReset, "growx,push" ); + boolean enabled = interval != Interval.ONCE; + + mPeriodSliderLabel.setEnabled( enabled ); + mPeriodSlider.setEnabled( enabled ); + mPeriodSlider.setValue( script.getPeriod() ); + + String filepath = script.getScript(); + + if( filepath != null && !filepath.isEmpty() ) + { + mTextFilePath.setText( filepath ); + } + else + { + mTextFilePath.setText( PLEASE_SELECT_A_FILE ); + } + } + + setModified( false ); } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) + public void save() + { + if( hasItem() && isModified() ) { - mScriptActionNode.save(); - mScriptActionNode.show(); + ScriptAction script = getScriptAction(); + + script.setInterval( (Interval)mComboInterval.getSelectedItem() ); + script.setPeriod( mPeriodSlider.getValue() ); + script.setScript( mTextFilePath.getText() ); } - mScriptActionNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/action/script/ScriptActionNode.java b/src/alias/action/script/ScriptActionNode.java deleted file mode 100644 index b0385e3f..00000000 --- a/src/alias/action/script/ScriptActionNode.java +++ /dev/null @@ -1,88 +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 - ******************************************************************************/ -package alias.action.script; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import alias.AliasNode; -import alias.action.AliasActionNode; - -public class ScriptActionNode extends AliasActionNode -{ - private static final long serialVersionUID = 1L; - - public ScriptActionNode( ScriptAction scriptAction ) - { - super( scriptAction ); - } - - @Override - public JPanel getEditor() - { - return new ScriptActionEditor( this ); - } - - public ScriptAction getScriptAction() - { - return (ScriptAction)getUserObject(); - } - - public String toString() - { - return "Action: Script"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasAction( getScriptAction() ); - - /* Save before removing the node ... so that we still have - * a reference to the parent's resources */ - save(); - - getModel().removeNodeFromParent( ScriptActionNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/id/AliasID.java b/src/alias/id/AliasID.java new file mode 100644 index 00000000..688004d9 --- /dev/null +++ b/src/alias/id/AliasID.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package alias.id; + +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSeeAlso; + +import playlist.version1.Group; +import alias.id.esn.Esn; +import alias.id.fleetsync.FleetsyncID; +import alias.id.lojack.LoJackFunctionAndID; +import alias.id.mdc.MDC1200ID; +import alias.id.mobileID.Min; +import alias.id.mpt1327.MPT1327ID; +import alias.id.nonrecordable.NonRecordable; +import alias.id.priority.Priority; +import alias.id.siteID.SiteID; +import alias.id.status.StatusID; +import alias.id.talkgroup.TalkgroupID; +import alias.id.uniqueID.UniqueID; + +@SuppressWarnings( "deprecation" ) +@XmlSeeAlso( { Esn.class, FleetsyncID.class, Group.class, LoJackFunctionAndID.class, + MDC1200ID.class, Min.class, MPT1327ID.class, NonRecordable.class, + Priority.class, SiteID.class, StatusID.class, TalkgroupID.class, + UniqueID.class } ) +@XmlRootElement( name = "id" ) +public abstract class AliasID +{ + public AliasID() + { + } + + public abstract AliasIDType getType(); + + public abstract boolean matches( AliasID id ); +} diff --git a/src/alias/AliasIDType.java b/src/alias/id/AliasIDType.java similarity index 61% rename from src/alias/AliasIDType.java rename to src/alias/id/AliasIDType.java index 83bc887b..7417b66a 100644 --- a/src/alias/AliasIDType.java +++ b/src/alias/id/AliasIDType.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,20 +15,35 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias; +package alias.id; public enum AliasIDType { - ESN, - Fleetsync, - LoJack, - LTRNetUID, - MDC1200, - MIN, - MPT1327, - NonRecordable, - Priority, - Site, - Status, - Talkgroup; + ESN( "ESN" ), + Fleetsync( "Fleetsync" ), + LoJack( "LoJack" ), + LTRNetUID( "LTR-Net UID" ), + MDC1200( "MDC-1200" ), + MIN( "Passport MIN" ), + MPT1327( "MPT-1327" ), + Site( "Site" ), + Status( "Status" ), + Talkgroup( "Talkgroup" ), + + Invert( "Audio Inversion" ), + Priority( "Audio Priority" ), + NonRecordable( "Audio Non-Recordable" ), + Streaming( "Audio Streaming" ); + + private String mLabel; + + private AliasIDType( String label ) + { + mLabel = label; + } + + public String toString() + { + return mLabel; + } } diff --git a/src/alias/id/AliasIdentifierEditor.java b/src/alias/id/AliasIdentifierEditor.java new file mode 100644 index 00000000..935d59be --- /dev/null +++ b/src/alias/id/AliasIdentifierEditor.java @@ -0,0 +1,385 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package alias.id; + +import gui.editor.Editor; +import gui.editor.EmptyEditor; + +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JList; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.ListModel; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import net.miginfocom.swing.MigLayout; +import alias.Alias; +import alias.AliasEvent; +import alias.AliasEvent.Event; +import alias.AliasFactory; +import alias.AliasModel; + +public class AliasIdentifierEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + private static ListModel EMPTY_MODEL = new DefaultListModel<>(); + private JList mAliasIDList = new JList<>( EMPTY_MODEL ); + private JButton mNewIDButton; + private JButton mCloneIDButton; + private JButton mDeleteIDButton; + private EditorContainer mEditorContainer = new EditorContainer(); + + private AliasModel mAliasModel; + + public AliasIdentifierEditor( AliasModel model ) + { + mAliasModel = model; + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 3", + "[grow,fill][grow,fill][grow,fill]", "[][grow,fill][]" ) ); + + mAliasIDList.setVisibleRowCount( 6 ); + mAliasIDList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + mAliasIDList.setLayoutOrientation( JList.VERTICAL ); + mAliasIDList.addListSelectionListener( new ListSelectionListener() + { + @Override + public void valueChanged( ListSelectionEvent event ) + { + if( !event.getValueIsAdjusting() ) + { + JList list = (JList)event.getSource(); + + Object selectedItem = list.getSelectedValue(); + + if( selectedItem != null && selectedItem instanceof AliasID ) + { + AliasID selected = (AliasID)selectedItem; + + mEditorContainer.setAliasID( selected ); + + mCloneIDButton.setEnabled( true ); + mDeleteIDButton.setEnabled( true ); + } + else + { + mCloneIDButton.setEnabled( false ); + mDeleteIDButton.setEnabled( false ); + } + } + } + } ); + + JScrollPane scroller = new JScrollPane( mAliasIDList ); + add( scroller, "span,grow" ); + + add( mEditorContainer, "span,grow" ); + + mNewIDButton = new JButton( "New ..." ); + mNewIDButton.setToolTipText( "Create a new Alias Identifier" ); + mNewIDButton.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JPopupMenu menu = new JPopupMenu(); + + menu.add( new AddAliasIdentifierItem( AliasIDType.ESN ) ); + menu.add( new AddAliasIdentifierItem( AliasIDType.Fleetsync ) ); + menu.add( new AddAliasIdentifierItem( AliasIDType.LoJack ) ); + menu.add( new AddAliasIdentifierItem( AliasIDType.LTRNetUID ) ); + menu.add( new AddAliasIdentifierItem( AliasIDType.MDC1200 ) ); + menu.add( new AddAliasIdentifierItem( AliasIDType.MPT1327 ) ); + menu.add( new AddAliasIdentifierItem( AliasIDType.MIN ) ); + menu.add( new AddAliasIdentifierItem( AliasIDType.Site ) ); + menu.add( new AddAliasIdentifierItem( AliasIDType.Status ) ); + menu.add( new AddAliasIdentifierItem( AliasIDType.Talkgroup ) ); + + boolean separatorAdded = false; + + if( hasItem() ) + { + Alias alias = getItem(); + + if( !alias.hasCallPriority() ) + { + if( !separatorAdded ) + { + menu.addSeparator(); + separatorAdded = true; + } + + menu.add( new AddAliasIdentifierItem( AliasIDType.Priority ) ); + } + + if( alias.isRecordable() ) + { + if( !separatorAdded ) + { + menu.addSeparator(); + separatorAdded = true; + } + + menu.add( new AddAliasIdentifierItem( AliasIDType.NonRecordable ) ); + } + } + + menu.show( e.getComponent(), e.getX(), e.getY() ); + } + } ); + + add( mNewIDButton ); + + mCloneIDButton = new JButton( "Clone" ); + mCloneIDButton.setToolTipText( "Create a copy of the currently selected identifier" ); + mCloneIDButton.setEnabled( false ); + mCloneIDButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + AliasID selected = mAliasIDList.getSelectedValue(); + + if( selected != null ) + { + AliasID clone = AliasFactory.copyOf( selected ); + + if( clone != null ) + { + addAliasIDToList( clone ); + } + } + } + } ); + add( mCloneIDButton ); + + mDeleteIDButton = new JButton( "Delete" ); + mDeleteIDButton.setToolTipText( "Delete the currently selected identifier" ); + mDeleteIDButton.setEnabled( false ); + mDeleteIDButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + final AliasID selected = mAliasIDList.getSelectedValue(); + + if( selected != null ) + { + int choice = JOptionPane.showConfirmDialog( mDeleteIDButton, + "Do you want to delete [" + selected.toString() + "]", + "Are you sure?", JOptionPane.YES_NO_OPTION ); + + if( choice == JOptionPane.YES_OPTION && hasItem() ) + { + Alias alias = getItem(); + + alias.removeAliasID( selected ); + + mAliasModel.broadcast( new AliasEvent( alias, Event.CHANGE ) ); + + setItem( alias ); + } + } + } + } ); + add( mDeleteIDButton, "wrap" ); + } + + @Override + public void setItem( Alias alias ) + { + super.setItem( alias ); + + if( alias == null || alias.getId().isEmpty() ) + { + mAliasIDList.setModel( EMPTY_MODEL ); + } + else + { + DefaultListModel model = new DefaultListModel(); + + List ids = alias.getId(); + + Collections.sort( ids, new Comparator() + { + @Override + public int compare( AliasID o1, AliasID o2 ) + { + return o1.toString().compareTo( o2.toString() ); + } + } ); + + for( AliasID id: ids ) + { + model.addElement( id ); + } + + mAliasIDList.setModel( model ); + } + + mEditorContainer.setAliasID( null ); + } + + @Override + public void save() + { + if( isModified() || mEditorContainer.isModified() ) + { + if( mEditorContainer.isModified() ) + { + mEditorContainer.save(); + } + + setModified( false ); + + if( hasItem() ) + { + mAliasModel.broadcast( new AliasEvent( getItem(), Event.CHANGE ) ); + } + } + } + + public class EditorContainer extends JPanel + { + private static final long serialVersionUID = 1L; + + private Editor mEditor = new EmptyEditor<>(); + + public EditorContainer() + { + setLayout( new MigLayout( "","[grow,fill]", "[grow,fill]" ) ); + + add( mEditor ); + } + + public boolean isModified() + { + if( mEditor != null ) + { + return mEditor.isModified(); + } + + return false; + } + + public void save() + { + if( mEditor != null ) + { + mEditor.save(); + } + } + + public void setAliasID( AliasID aliasID ) + { + if( mEditor != null ) + { + if( mEditor.isModified() ) + { + int option = JOptionPane.showConfirmDialog( + EditorContainer.this, + "Identifier settings have changed. Do you want to save these changes?", + "Save Changes?", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE ); + + if( option == JOptionPane.YES_OPTION ) + { + mEditor.save(); + } + } + } + removeAll(); + + //This will always give us an editor + mEditor = AliasFactory.getEditor( aliasID ); + + add( mEditor ); + + revalidate(); + } + } + + private void addAliasIDToList( final AliasID id ) + { + if( id != null && hasItem() ) + { + final Alias alias = getItem(); + + alias.addAliasID( id ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + setItem( alias ); + + mAliasIDList.setSelectedValue( id, true ); + + AliasIdentifierEditor.this.setModified( true ); + } + } ); + } + } + + public class AddAliasIdentifierItem extends JMenuItem + { + private static final long serialVersionUID = 1L; + + private AliasIDType mAliasIDType; + + public AddAliasIdentifierItem( AliasIDType type ) + { + super( type.toString() ); + + mAliasIDType = type; + + addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + final AliasID id = AliasFactory.getAliasID( mAliasIDType ); + + addAliasIDToList( id ); + } + } ); + } + } +} diff --git a/src/alias/id/esn/ESNEditor.java b/src/alias/id/esn/ESNEditor.java index e2a55f2b..fd36ed47 100644 --- a/src/alias/id/esn/ESNEditor.java +++ b/src/alias/id/esn/ESNEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,79 +17,102 @@ ******************************************************************************/ package alias.id.esn; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import gui.editor.DocumentListenerEditor; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; -import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; +import alias.id.AliasID; -public class ESNEditor extends JPanel implements ActionListener +public class ESNEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; - private ESNNode mESNNode; - private JTextField mTextESN; - public ESNEditor( ESNNode esnNode ) + private static final String HELP_TEXT = + "

Electronic Serial Number (ESN)

" + + "ESN: hexadecimal 0-9, A-F (e.g. ABCD1234 )
" + + "Wildcard: use an asterisk (*) to wildcard individual
" + + "digits (e.g. ABCD123* or AB**1**4)" + + ""; + + private JTextField mTextField; + + public ESNEditor( AliasID aliasID ) { - mESNNode = esnNode; - initGUI(); + + setItem( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); - - add( new JLabel( "Electronic Serial Number ID" ), "span,align center" ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); add( new JLabel( "ESN:" ) ); - mTextESN = new JTextField( mESNNode.getESN().getEsn() ); - add( mTextESN, "growx,push" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( ESNEditor.this ); - add( btnSave, "growx,push" ); + mTextField = new JTextField(); + mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); + add( mTextField, "growx,push" ); - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( ESNEditor.this ); - add( btnReset, "growx,push" ); + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( ESNEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( help, "align left" ); + } + + public Esn getEsn() + { + if( getItem() instanceof Esn ) + { + return (Esn)getItem(); + } + + return null; } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setItem( AliasID aliasID ) + { + super.setItem( aliasID ); - if( command.contentEquals( "Save" ) ) + Esn esn = getEsn(); + + if( esn != null ) { - String esn = mTextESN.getText(); - - if( esn != null ) - { - mESNNode.getESN().setEsn( esn ); - - ((ConfigurableNode)mESNNode.getParent()).sort(); - - mESNNode.save(); - - mESNNode.show(); - } - else - { - JOptionPane.showMessageDialog( ESNEditor.this, "Please enter an ESN" ); - } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextESN.setText( mESNNode.getESN().getEsn() ); + mTextField.setText( esn.getEsn() ); } - mESNNode.refresh(); - } + setModified( false ); + + repaint(); + } + + @Override + public void save() + { + Esn esn = getEsn(); + + if( esn != null ) + { + esn.setEsn( mTextField.getText() ); + } + + setModified( false ); + } } diff --git a/src/alias/id/esn/ESNNode.java b/src/alias/id/esn/ESNNode.java deleted file mode 100644 index 1b6234d0..00000000 --- a/src/alias/id/esn/ESNNode.java +++ /dev/null @@ -1,86 +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 - ******************************************************************************/ -package alias.id.esn; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import alias.AliasIDNode; -import alias.AliasNode; - -public class ESNNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public ESNNode( Esn esn ) - { - super( esn ); - } - - @Override - public JPanel getEditor() - { - return new ESNEditor( this ); - } - - public Esn getESN() - { - return (Esn)getUserObject(); - } - - public String toString() - { - return "ESN " + getESN().getEsn(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getESN() ); - - save(); - - getModel().removeNodeFromParent( ESNNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/alias/id/esn/Esn.java b/src/alias/id/esn/Esn.java index 09198031..91d5396d 100644 --- a/src/alias/id/esn/Esn.java +++ b/src/alias/id/esn/Esn.java @@ -19,8 +19,8 @@ package alias.id.esn; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class Esn extends AliasID { diff --git a/src/alias/id/fleetsync/FleetsyncID.java b/src/alias/id/fleetsync/FleetsyncID.java index ca92a75c..57de648e 100644 --- a/src/alias/id/fleetsync/FleetsyncID.java +++ b/src/alias/id/fleetsync/FleetsyncID.java @@ -19,8 +19,8 @@ package alias.id.fleetsync; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class FleetsyncID extends AliasID diff --git a/src/alias/id/fleetsync/FleetsyncIDEditor.java b/src/alias/id/fleetsync/FleetsyncIDEditor.java index 03b36c81..64f8f531 100644 --- a/src/alias/id/fleetsync/FleetsyncIDEditor.java +++ b/src/alias/id/fleetsync/FleetsyncIDEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,92 +17,123 @@ ******************************************************************************/ package alias.id.fleetsync; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import gui.editor.DocumentListenerEditor; -import javax.swing.JButton; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextArea; import javax.swing.JTextField; +import javax.swing.text.MaskFormatter; import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; -public class FleetsyncIDEditor extends JPanel implements ActionListener +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import alias.id.AliasID; + +public class FleetsyncIDEditor extends DocumentListenerEditor { - private static final long serialVersionUID = 1L; - private FleetsyncIDNode mFleetsyncIDNode; - - private JLabel mLabelName; - private JTextField mTextIdent; + private final static Logger mLog = LoggerFactory.getLogger( FleetsyncIDEditor.class ); - private String mHelpText = "Enter a formatted fleetsync identifier.\n\n" - + "Format: PPP-IIII (P=Prefix, I=Identifier)\n\n" - + "Wildcard: use one or more asterisks (*) for any talkgroup " - + "digits.\n\n"; + private static final long serialVersionUID = 1L; - public FleetsyncIDEditor( FleetsyncIDNode fsNode ) + private static final String HELP_TEXT = "" + + "

Fleetsync Identifier

" + + "Fleetsync: ggg-uuuu where g=Group and u=Unit (e.g. 001-0001)
" + + "Wildcard: use an asterisk (*) for each digit (e.g. 001-****)" + + ""; + + private JTextField mTextField; + + public FleetsyncIDEditor( AliasID aliasID ) { - mFleetsyncIDNode = fsNode; initGUI(); + + setItem( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); - add( new JLabel( "Fleetsync ID" ), "span,align center" ); + add( new JLabel( "Fleetsync ID:" ) ); - add( new JLabel( "Ident:" ) ); - mTextIdent = new JTextField( mFleetsyncIDNode.getFleetsyncID().getIdent() ); + MaskFormatter formatter = null; - add( mTextIdent, "grow, wrap" ); + try + { + //Mask: 3 digits - 4 digits + formatter = new MaskFormatter( "###-####" ); + } + catch( Exception e ) + { + //Do nothing, the mask was invalid + } - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( FleetsyncIDEditor.this ); - add( btnSave, "growx,push" ); + mTextField = new JFormattedTextField( formatter ); + mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( FleetsyncIDEditor.this ); - add( btnReset, "growx,push" ); + add( mTextField, "growx,push" ); - JTextArea helpText = new JTextArea( mHelpText ); - helpText.setLineWrap( true ); - helpText.setBackground( getBackground() ); - add( helpText, "span,grow,push" ); + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( FleetsyncIDEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( help, "align left" ); + } + + public FleetsyncID getFleetsyncID() + { + if( getItem() instanceof FleetsyncID ) + { + return (FleetsyncID)getItem(); + } + + return null; } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setItem( AliasID aliasID ) + { + super.setItem( aliasID ); - if( command.contentEquals( "Save" ) ) + FleetsyncID fleetsync = getFleetsyncID(); + + if( fleetsync != null ) { - String ident = mTextIdent.getText(); - - if( ident != null ) - { - mFleetsyncIDNode.getFleetsyncID().setIdent( ident ); + mTextField.setText( fleetsync.getIdent() ); + } + + setModified( false ); + + repaint(); + } - ((ConfigurableNode)mFleetsyncIDNode.getParent()).sort(); - - mFleetsyncIDNode.save(); - - mFleetsyncIDNode.show(); - } - else - { - JOptionPane.showMessageDialog( FleetsyncIDEditor.this, "Please enter an ident" ); - } - } - else if( command.contentEquals( "Reset" ) ) + @Override + public void save() + { + FleetsyncID fleetsync = getFleetsyncID(); + + if( fleetsync != null ) { - mTextIdent.setText( mFleetsyncIDNode.getFleetsyncID().getIdent() ); + fleetsync.setIdent( mTextField.getText() ); } - mFleetsyncIDNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/id/fleetsync/FleetsyncIDNode.java b/src/alias/id/fleetsync/FleetsyncIDNode.java deleted file mode 100644 index 9a16c1d9..00000000 --- a/src/alias/id/fleetsync/FleetsyncIDNode.java +++ /dev/null @@ -1,97 +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 - ******************************************************************************/ -package alias.id.fleetsync; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import alias.AliasIDNode; -import alias.AliasNode; - -public class FleetsyncIDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public FleetsyncIDNode( FleetsyncID id ) - { - super( id ); - } - - @Override - public JPanel getEditor() - { - return new FleetsyncIDEditor( this ); - } - - public FleetsyncID getFleetsyncID() - { - return (FleetsyncID)getUserObject(); - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "Fleetsync " ); - - String ident = getFleetsyncID().getIdent(); - - if( ident != null ) - { - sb.append( ident ); - } - - return sb.toString(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getFleetsyncID() ); - - save(); - - getModel().removeNodeFromParent( FleetsyncIDNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/id/fleetsync/StatusIDEditor.java b/src/alias/id/fleetsync/StatusIDEditor.java deleted file mode 100644 index fe3a6348..00000000 --- a/src/alias/id/fleetsync/StatusIDEditor.java +++ /dev/null @@ -1,120 +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 - ******************************************************************************/ -package alias.id.fleetsync; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import controller.ConfigurableNode; - -public class StatusIDEditor extends JPanel implements ActionListener -{ - private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( StatusIDEditor.class ); - - private StatusIDNode mStatusIDNode; - - private JLabel mLabelName; - private JTextField mTextStatusID; - - public StatusIDEditor( StatusIDNode statusIDNode ) - { - mStatusIDNode = statusIDNode; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout() ); - - setBorder( BorderFactory.createTitledBorder( "Status Code" ) ); - - mLabelName = new JLabel( "Status Code:" ); - add( mLabelName, "align right" ); - - mTextStatusID = new JTextField( - String.valueOf( mStatusIDNode.getStatusID().getStatus() ) ); - add( mTextStatusID, "grow, wrap" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( StatusIDEditor.this ); - add( btnSave ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( StatusIDEditor.this ); - add( btnReset, "wrap" ); - } - - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) - { - String statusIDString = mTextStatusID.getText(); - - if( statusIDString != null ) - { - try - { - int id = Integer.parseInt( statusIDString ); - - mStatusIDNode.getStatusID().setStatus( id ); - - ((ConfigurableNode)mStatusIDNode.getParent()).sort(); - - mStatusIDNode.save(); - - mStatusIDNode.show(); - } - catch( Exception ex ) - { - mLog.error( "StatusIDEditor - error parsing int status " + - "id from [" + statusIDString + "]" ); - } - } - else - { - JOptionPane.showMessageDialog( StatusIDEditor.this, - "Please enter a status ID code" ); - } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextStatusID.setText( - String.valueOf( mStatusIDNode.getStatusID().getStatus() ) ); - } - - mStatusIDNode.refresh(); - } -} diff --git a/src/alias/id/fleetsync/StatusIDNode.java b/src/alias/id/fleetsync/StatusIDNode.java deleted file mode 100644 index 621626fb..00000000 --- a/src/alias/id/fleetsync/StatusIDNode.java +++ /dev/null @@ -1,86 +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 - ******************************************************************************/ -package alias.id.fleetsync; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import alias.AliasIDNode; -import alias.AliasNode; - -public class StatusIDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public StatusIDNode( StatusID id ) - { - super( id ); - } - - @Override - public JPanel getEditor() - { - return new StatusIDEditor( this ); - } - - public StatusID getStatusID() - { - return (StatusID)getUserObject(); - } - - public String toString() - { - return "Status Code " + getStatusID().getStatus(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getStatusID() ); - - save(); - - getModel().removeNodeFromParent( StatusIDNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/alias/id/lojack/LoJackFunctionAndID.java b/src/alias/id/lojack/LoJackFunctionAndID.java index 99ee74b1..065db0f4 100644 --- a/src/alias/id/lojack/LoJackFunctionAndID.java +++ b/src/alias/id/lojack/LoJackFunctionAndID.java @@ -21,8 +21,8 @@ import javax.xml.bind.annotation.XmlAttribute; import module.decode.lj1200.LJ1200Message; import module.decode.lj1200.LJ1200Message.Function; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class LoJackFunctionAndID extends AliasID { diff --git a/src/alias/id/lojack/LoJackIDEditor.java b/src/alias/id/lojack/LoJackIDEditor.java index 9cc121da..8c13e60e 100644 --- a/src/alias/id/lojack/LoJackIDEditor.java +++ b/src/alias/id/lojack/LoJackIDEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2015 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,112 +17,151 @@ ******************************************************************************/ package alias.id.lojack; +import gui.editor.DocumentListenerEditor; + +import java.awt.Color; +import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; -import javax.swing.JButton; import javax.swing.JComboBox; +import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextArea; import javax.swing.JTextField; +import javax.swing.text.MaskFormatter; import module.decode.lj1200.LJ1200Message; import module.decode.lj1200.LJ1200Message.Function; import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; +import alias.id.AliasID; -public class LoJackIDEditor extends JPanel implements ActionListener +public class LoJackIDEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; - private LoJackIDNode mLoJackIDNode; - + + private JTextField mTextField; + private JComboBox mFunctionCombo; - private JTextField mTextIdent; - private String mHelpText = "Select a LoJack message function code and enter" - + " a five character reply code/identifier in the ID field.\n\n" - + "Wildcard: use an asterisk (*) to wildcard any character in the " - + "five character reply code (e.g. AB*CD or ***12 or *****)\n\n" - + "Reply codes are five characters where the middle character" - + " identifies the entity (tower/site or transponder) as follows:\n" - + "X,Y\t\tTower\n" - + "0-9,A,C-H,J-N,P-W\tTransponder\n" - + "B,I,O,Z\t\tNot Used"; + private static final String HELP_TEXT = "" + + "

LoJack Function and Identifier

" + + "Function Code: 1Y-SITE ID
" + + "ID: 5 numbers or characters (e.g. 1BN47)
" + + "
" + + "Wildcard: use an asterisk (*) to wildcard ID
" + + "characters (e.g. AB*CD or ***12 or *****)

" + + "The middle character in a reply ID code identifies the
" + + "entity. Valid ID middle characters are:
" + + "
  • Tower: X,Y
  • " + + "
  • Transponder: 0-9,A,C-H,J-N,P-W
  • " + + "
  • Not Used: B,I,O,Z
  • " + + ""; - public LoJackIDEditor( LoJackIDNode lojackNode ) + public LoJackIDEditor( AliasID aliasID ) { - mLoJackIDNode = lojackNode; initGUI(); + + setItem( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); - - add( new JLabel( "LoJack Function and ID" ), "span,align center" ); - - LoJackFunctionAndID lojack = mLoJackIDNode.getLoJackID(); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][]" ) ); add( new JLabel( "Function:" ) ); mFunctionCombo = new JComboBox( Function.values() ); - mFunctionCombo.setSelectedItem( lojack.getFunction() ); - add( mFunctionCombo, "grow, wrap" ); + mFunctionCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + mFunctionCombo.setToolTipText( HELP_TEXT ); + add( mFunctionCombo, "growx, push" ); add( new JLabel( "ID:" ) ); - mTextIdent = new JTextField( lojack.getID() ); - - add( mTextIdent, "grow, wrap" ); - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( LoJackIDEditor.this ); - add( btnSave, "growx,push" ); + MaskFormatter formatter = null; - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( LoJackIDEditor.this ); - add( btnReset, "growx,push" ); + try + { + //Mask: any character or number, 5 places + formatter = new MaskFormatter( "AAAAA" ); + } + catch( Exception e ) + { + //Do nothing, the mask was invalid + } - JTextArea helpText = new JTextArea( mHelpText ); - helpText.setLineWrap( true ); - helpText.setBackground( getBackground() ); - add( helpText, "span,grow,push" ); + mTextField = new JFormattedTextField( formatter ); + mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); + add( mTextField, "growx,push" ); + + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( LoJackIDEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( help, "align left" ); + } + + public LoJackFunctionAndID getLoJackID() + { + if( getItem() instanceof LoJackFunctionAndID ) + { + return (LoJackFunctionAndID)getItem(); + } + + return null; } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setItem( AliasID aliasID ) + { + super.setItem( aliasID ); - final LoJackFunctionAndID lojack = mLoJackIDNode.getLoJackID(); - - if( command.contentEquals( "Save" ) ) - { - String id = mTextIdent.getText(); - - if( id != null ) - { - - lojack.setID( id ); - lojack.setFunction( (Function)mFunctionCombo.getSelectedItem() ); - - ((ConfigurableNode)mLoJackIDNode.getParent()).sort(); - - mLoJackIDNode.save(); - - mLoJackIDNode.show(); - } - else - { - JOptionPane.showMessageDialog( LoJackIDEditor.this, "Please enter an ident" ); - } - } - else if( command.contentEquals( "Reset" ) ) + LoJackFunctionAndID lojack = getLoJackID(); + + if( lojack != null ) { mFunctionCombo.setSelectedItem( lojack.getFunction() ); - mTextIdent.setText( lojack.getID() ); + mTextField.setText( lojack.getID() ); + } + else + { + mFunctionCombo.setSelectedItem( null ); + mTextField.setText( null ); } - mLoJackIDNode.refresh(); - } + setModified( false ); + + repaint(); + } + + @Override + public void save() + { + LoJackFunctionAndID lojack = getLoJackID(); + + if( lojack != null ) + { + lojack.setID( mTextField.getText() ); + lojack.setFunction( (Function)mFunctionCombo.getSelectedItem() ); + } + + setModified( false ); + } } diff --git a/src/alias/id/lojack/LoJackIDNode.java b/src/alias/id/lojack/LoJackIDNode.java deleted file mode 100644 index 3b4f3ed0..00000000 --- a/src/alias/id/lojack/LoJackIDNode.java +++ /dev/null @@ -1,86 +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 - ******************************************************************************/ -package alias.id.lojack; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import alias.AliasIDNode; -import alias.AliasNode; - -public class LoJackIDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public LoJackIDNode( LoJackFunctionAndID id ) - { - super( id ); - } - - @Override - public JPanel getEditor() - { - return new LoJackIDEditor( this ); - } - - public LoJackFunctionAndID getLoJackID() - { - return (LoJackFunctionAndID)getUserObject(); - } - - public String toString() - { - return getLoJackID().toString(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getLoJackID() ); - - save(); - - getModel().removeNodeFromParent( LoJackIDNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/id/mdc/MDC1200ID.java b/src/alias/id/mdc/MDC1200ID.java index cf866e35..af0ed12e 100644 --- a/src/alias/id/mdc/MDC1200ID.java +++ b/src/alias/id/mdc/MDC1200ID.java @@ -19,8 +19,8 @@ package alias.id.mdc; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class MDC1200ID extends AliasID diff --git a/src/alias/id/mdc/MDC1200IDEditor.java b/src/alias/id/mdc/MDC1200IDEditor.java index 6003fd56..21437cb9 100644 --- a/src/alias/id/mdc/MDC1200IDEditor.java +++ b/src/alias/id/mdc/MDC1200IDEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,79 +17,117 @@ ******************************************************************************/ package alias.id.mdc; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import gui.editor.DocumentListenerEditor; -import javax.swing.JButton; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; import javax.swing.JTextField; +import javax.swing.text.MaskFormatter; import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; +import alias.id.AliasID; -public class MDC1200IDEditor extends JPanel implements ActionListener +public class MDC1200IDEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; - private MDC1200IDNode mMDC1200IDNode; - - private JTextField mTextIdent; - public MDC1200IDEditor( MDC1200IDNode fsNode ) + private static final String HELP_TEXT = "" + + "

    MDC-1200 Identifier

    " + + "MDC-1200: four digit [0-9] value (e.g. 1234)
    " + + "Wildcard: use an asterisk (*) to wildcard individual
    " + + "digits (e.g. 123* or **34)" + + ""; + + private JTextField mTextField; + + public MDC1200IDEditor( AliasID aliasID ) { - mMDC1200IDNode = fsNode; initGUI(); + + setItem( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); - add( new JLabel( "MDC-1200 " ), "span,align center" ); + add( new JLabel( "MDC-1200 ID:" ) ); - add( new JLabel( "Unit ID:" ) ); - mTextIdent = new JTextField( mMDC1200IDNode.getMDC1200ID().getIdent() ); - add( mTextIdent, "growx,push" ); + MaskFormatter formatter = null; + + try + { + //Mask: 4 digits + formatter = new MaskFormatter( "####" ); + } + catch( Exception e ) + { + //Do nothing, the mask was invalid + } - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( MDC1200IDEditor.this ); - add( btnSave, "growx,push" ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( MDC1200IDEditor.this ); - add( btnReset, "growx,push" ); + mTextField = new JFormattedTextField( formatter ); + mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); + add( mTextField, "growx,push" ); + + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( MDC1200IDEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( help, "align left" ); + } + + public MDC1200ID getMDC1200ID() + { + if( getItem() instanceof MDC1200ID ) + { + return (MDC1200ID)getItem(); + } + + return null; } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setItem( AliasID aliasID ) + { + super.setItem( aliasID ); - if( command.contentEquals( "Save" ) ) + MDC1200ID mdc = getMDC1200ID(); + + if( mdc != null ) { - String ident = mTextIdent.getText(); - - if( ident != null ) - { - mMDC1200IDNode.getMDC1200ID().setIdent( ident ); + mTextField.setText( mdc.getIdent() ); + } + + setModified( false ); + + repaint(); + } - ((ConfigurableNode)mMDC1200IDNode.getParent()).sort(); - - mMDC1200IDNode.save(); - - mMDC1200IDNode.show(); - } - else - { - JOptionPane.showMessageDialog( MDC1200IDEditor.this, "Please enter a unit ID" ); - } - } - else if( command.contentEquals( "Reset" ) ) + @Override + public void save() + { + MDC1200ID mdc = getMDC1200ID(); + + if( mdc != null ) { - mTextIdent.setText( mMDC1200IDNode.getMDC1200ID().getIdent() ); + mdc.setIdent( mTextField.getText() ); } - mMDC1200IDNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/id/mdc/MDC1200IDNode.java b/src/alias/id/mdc/MDC1200IDNode.java deleted file mode 100644 index 5a5a837b..00000000 --- a/src/alias/id/mdc/MDC1200IDNode.java +++ /dev/null @@ -1,91 +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 - ******************************************************************************/ -package alias.id.mdc; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import alias.AliasIDNode; -import alias.AliasNode; - -public class MDC1200IDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public MDC1200IDNode( MDC1200ID id ) - { - super( id ); - } - - @Override - public JPanel getEditor() - { - return new MDC1200IDEditor( this ); - } - - public MDC1200ID getMDC1200ID() - { - return (MDC1200ID)getUserObject(); - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "MDC-1200 " ); - sb.append( getMDC1200ID().getIdent() ); - - return sb.toString(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getMDC1200ID() ); - - save(); - - getModel().removeNodeFromParent( MDC1200IDNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/id/mobileID/MINEditor.java b/src/alias/id/mobileID/MINEditor.java index 8b0f982a..6244ff69 100644 --- a/src/alias/id/mobileID/MINEditor.java +++ b/src/alias/id/mobileID/MINEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,82 +17,117 @@ ******************************************************************************/ package alias.id.mobileID; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import gui.editor.DocumentListenerEditor; -import javax.swing.JButton; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; import javax.swing.JTextField; +import javax.swing.text.MaskFormatter; import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; +import alias.id.AliasID; -public class MINEditor extends JPanel implements ActionListener +public class MINEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; - private MINNode mMINNode; - - private JLabel mLabelName; - private JTextField mTextMIN; - public MINEditor( MINNode minNode ) + private static final String HELP_TEXT = "" + + "

    Passport Mobile ID (MIN)

    " + + "MIN: six character hex value (e.g. AB12CD)
    " + + "Wildcard: use an asterisk (*) to wildcard individual
    " + + "digits (e.g. AB**CD or AB12**)" + + ""; + + private JTextField mTextField; + + public MINEditor( AliasID aliasID ) { - mMINNode = minNode; - initGUI(); + + setItem( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); - - add( new JLabel( "Mobile ID Number (MIN)" ), "span,align center" ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); add( new JLabel( "MIN:" ) ); - - mTextMIN = new JTextField( mMINNode.getMIN().getMin() ); - add( mTextMIN, "growx,push" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( MINEditor.this ); - add( btnSave, "growx,push" ); - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( MINEditor.this ); - add( btnReset, "growx,push" ); + MaskFormatter formatter = null; + + try + { + //Mask: 6 hex characters + formatter = new MaskFormatter( "HHHHHH" ); + } + catch( Exception e ) + { + //Do nothing, the mask was invalid + } + + mTextField = new JFormattedTextField( formatter ); + mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); + add( mTextField, "growx,push" ); + + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( MINEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( help, "align left" ); + } + + public Min getMin() + { + if( getItem() instanceof Min ) + { + return (Min)getItem(); + } + + return null; } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setItem( AliasID aliasID ) + { + super.setItem( aliasID ); - if( command.contentEquals( "Save" ) ) + Min min = getMin(); + + if( min != null ) { - String esn = mTextMIN.getText(); - - if( esn != null ) - { - mMINNode.getMIN().setMin( esn ); - - ((ConfigurableNode)mMINNode.getParent()).sort(); - - mMINNode.save(); - - mMINNode.show(); - } - else - { - JOptionPane.showMessageDialog( MINEditor.this, "Please enter an ESN" ); - } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextMIN.setText( mMINNode.getMIN().getMin() ); + mTextField.setText( min.getMin() ); } - mMINNode.refresh(); - } + setModified( false ); + + repaint(); + } + + @Override + public void save() + { + Min min = getMin(); + + if( min != null ) + { + min.setMin( mTextField.getText() ); + } + + setModified( false ); + } } diff --git a/src/alias/id/mobileID/MINNode.java b/src/alias/id/mobileID/MINNode.java deleted file mode 100644 index cf832f91..00000000 --- a/src/alias/id/mobileID/MINNode.java +++ /dev/null @@ -1,86 +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 - ******************************************************************************/ -package alias.id.mobileID; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import alias.AliasIDNode; -import alias.AliasNode; - -public class MINNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public MINNode( Min min ) - { - super( min ); - } - - @Override - public JPanel getEditor() - { - return new MINEditor( this ); - } - - public Min getMIN() - { - return (Min)getUserObject(); - } - - public String toString() - { - return "MIN " + getMIN().getMin(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getMIN() ); - - save(); - - getModel().removeNodeFromParent( MINNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/alias/id/mobileID/Min.java b/src/alias/id/mobileID/Min.java index ca6b02dd..c6f8e4c2 100644 --- a/src/alias/id/mobileID/Min.java +++ b/src/alias/id/mobileID/Min.java @@ -19,8 +19,8 @@ package alias.id.mobileID; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; /** * Mobile ID Number diff --git a/src/alias/id/mpt1327/MPT1327ID.java b/src/alias/id/mpt1327/MPT1327ID.java index 5aa4c463..57535dbb 100644 --- a/src/alias/id/mpt1327/MPT1327ID.java +++ b/src/alias/id/mpt1327/MPT1327ID.java @@ -19,8 +19,8 @@ package alias.id.mpt1327; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class MPT1327ID extends AliasID { diff --git a/src/alias/id/mpt1327/MPT1327IDEditor.java b/src/alias/id/mpt1327/MPT1327IDEditor.java index ab6dcdb6..6c487814 100644 --- a/src/alias/id/mpt1327/MPT1327IDEditor.java +++ b/src/alias/id/mpt1327/MPT1327IDEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,80 +17,119 @@ ******************************************************************************/ package alias.id.mpt1327; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import gui.editor.DocumentListenerEditor; -import javax.swing.JButton; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; import javax.swing.JTextField; +import javax.swing.text.MaskFormatter; import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; +import alias.id.AliasID; -public class MPT1327IDEditor extends JPanel implements ActionListener +public class MPT1327IDEditor extends DocumentListenerEditor { - private static final long serialVersionUID = 1L; - private MPT1327IDNode mMPT1327IDNode; - - private JTextField mTextIdent; + private static final long serialVersionUID = 1L; - public MPT1327IDEditor( MPT1327IDNode fsNode ) + private static final String HELP_TEXT = "" + + "

    MPT-1327 Identifier

    " + + "MPT-1327: decimal (0-9) format ppp-iiii where
    " + + "p=Prefix and i=Ident (e.g. 123-0001)
    " + + "Wildcard: use an asterisk (*) to wildcard individual
    " + + "digits (e.g. ABCD123* or AB**1**4)" + + ""; + + private JTextField mTextField; + + public MPT1327IDEditor( AliasID aliasID ) { - mMPT1327IDNode = fsNode; initGUI(); + + setItem( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); - add( new JLabel( "MPT-1327 Radio or Group ID" ), "span,align center" ); + add( new JLabel( "MPT-1327 ID:" ) ); - add( new JLabel( "ID:" ) ); + MaskFormatter formatter = null; + + try + { + //Mask: 3 digits - 4 digits + formatter = new MaskFormatter( "###-####" ); + } + catch( Exception e ) + { + //Do nothing, the mask was invalid + } - mTextIdent = new JTextField( mMPT1327IDNode.getMPT1327ID().getIdent() ); - add( mTextIdent, "growx,push" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( MPT1327IDEditor.this ); - add( btnSave, "growx,push" ); + mTextField = new JFormattedTextField( formatter ); + mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( MPT1327IDEditor.this ); - add( btnReset, "growx,push" ); + add( mTextField, "growx,push" ); + + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( MPT1327IDEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( help, "align left" ); + } + + public MPT1327ID getMPT1327ID() + { + if( getItem() instanceof MPT1327ID ) + { + return (MPT1327ID)getItem(); + } + + return null; } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setItem( AliasID aliasID ) + { + super.setItem( aliasID ); - if( command.contentEquals( "Save" ) ) + MPT1327ID mpt = getMPT1327ID(); + + if( mpt != null ) { - String ident = mTextIdent.getText(); - - if( ident != null ) - { - mMPT1327IDNode.getMPT1327ID().setIdent( ident ); + mTextField.setText( mpt.getIdent() ); + } + + setModified( false ); + + repaint(); + } - ((ConfigurableNode)mMPT1327IDNode.getParent()).sort(); - - mMPT1327IDNode.save(); - - mMPT1327IDNode.show(); - } - else - { - JOptionPane.showMessageDialog( MPT1327IDEditor.this, "Please enter a MPT1327 unit ID" ); - } - } - else if( command.contentEquals( "Reset" ) ) + @Override + public void save() + { + MPT1327ID mpt = getMPT1327ID(); + + if( mpt != null ) { - mTextIdent.setText( mMPT1327IDNode.getMPT1327ID().getIdent() ); + mpt.setIdent( mTextField.getText() ); } - mMPT1327IDNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/id/mpt1327/MPT1327IDNode.java b/src/alias/id/mpt1327/MPT1327IDNode.java deleted file mode 100644 index ab591365..00000000 --- a/src/alias/id/mpt1327/MPT1327IDNode.java +++ /dev/null @@ -1,91 +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 - ******************************************************************************/ -package alias.id.mpt1327; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import alias.AliasIDNode; -import alias.AliasNode; - -public class MPT1327IDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public MPT1327IDNode( MPT1327ID id ) - { - super( id ); - } - - @Override - public JPanel getEditor() - { - return new MPT1327IDEditor( this ); - } - - public MPT1327ID getMPT1327ID() - { - return (MPT1327ID)getUserObject(); - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "MPT-1327 " ); - sb.append( getMPT1327ID().getIdent() ); - - return sb.toString(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getMPT1327ID() ); - - save(); - - getModel().removeNodeFromParent( MPT1327IDNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/record/NonRecordable.java b/src/alias/id/nonrecordable/NonRecordable.java similarity index 55% rename from src/alias/record/NonRecordable.java rename to src/alias/id/nonrecordable/NonRecordable.java index 00c69482..97128880 100644 --- a/src/alias/record/NonRecordable.java +++ b/src/alias/id/nonrecordable/NonRecordable.java @@ -1,7 +1,7 @@ -package alias.record; +package alias.id.nonrecordable; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class NonRecordable extends AliasID { @@ -16,4 +16,10 @@ public class NonRecordable extends AliasID { return false; } + + @Override + public String toString() + { + return "Audio Non-Recordable"; + } } diff --git a/src/alias/id/nonrecordable/NonRecordableEditor.java b/src/alias/id/nonrecordable/NonRecordableEditor.java new file mode 100644 index 00000000..6f402371 --- /dev/null +++ b/src/alias/id/nonrecordable/NonRecordableEditor.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package alias.id.nonrecordable; + +import gui.editor.DocumentListenerEditor; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JLabel; +import javax.swing.JOptionPane; + +import net.miginfocom.swing.MigLayout; +import alias.id.AliasID; + +public class NonRecordableEditor extends DocumentListenerEditor +{ + private static final long serialVersionUID = 1L; + + private static final String HELP_TEXT = "" + + "

    Audio Non-Recordable

    " + + "This identifies an alias as non-recordable.

    " + + "The alias can still be monitored but associated audio will
    " + + "not be recorded.

    " + + "The default behavior when audio recording is enabled for a
    " + + "channel is to record all aliases except any alias with a
    " + + "non-recordable identifier"; + + public NonRecordableEditor( AliasID aliasID ) + { + initGUI(); + + setItem( aliasID ); + } + + private void initGUI() + { + setLayout( new MigLayout( "fill,wrap 1", "[]", "[][]" ) ); + + add( new JLabel( "Non-Recordable" ), "wrap" ); + + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( NonRecordableEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( help, "align left" ); + } + + @Override + public void save() + { + } +} diff --git a/src/alias/priority/Priority.java b/src/alias/id/priority/Priority.java similarity index 91% rename from src/alias/priority/Priority.java rename to src/alias/id/priority/Priority.java index 6a9aa177..e29be8a0 100644 --- a/src/alias/priority/Priority.java +++ b/src/alias/id/priority/Priority.java @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias.priority; +package alias.id.priority; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; /** * Specifies an (audio) priority level for this alias. @@ -60,7 +60,7 @@ public class Priority extends AliasID public String toString() { - return "Priority: " + ( isDoNotMonitor() ? "Do Not Monitor" : mPriority ); + return "Audio Priority: " + ( isDoNotMonitor() ? "Do Not Monitor" : mPriority ); } @Override diff --git a/src/alias/id/priority/PriorityEditor.java b/src/alias/id/priority/PriorityEditor.java new file mode 100644 index 00000000..5b797991 --- /dev/null +++ b/src/alias/id/priority/PriorityEditor.java @@ -0,0 +1,168 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package alias.id.priority; + +import gui.editor.DocumentListenerEditor; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import net.miginfocom.swing.MigLayout; +import alias.id.AliasID; + +public class PriorityEditor extends DocumentListenerEditor +{ + private static final long serialVersionUID = 1L; + + public static final String DO_NOT_MONITOR = "Do Not Monitor"; + + private static final String HELP_TEXT = "" + + "

    Call Audio Priority

    " + + "Priority determines which calls have priority for playback
    " + + "over your computer speakers, or designates an alias for
    " + + "no-monitoring if you don't want to hear calls from an alias.

    " + + "Lower values indicate higher priority levels.

    " + + "Do Not Monitor: slide priority all the way to the right" + + ""; + + private JSlider mPrioritySlider; + private JLabel mPrioritySliderLabel; + + public PriorityEditor( AliasID aliasID ) + { + initGUI(); + + setItem( aliasID ); + } + + private void initGUI() + { + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); + + mPrioritySlider = new JSlider( JSlider.HORIZONTAL, + Priority.MIN_PRIORITY, + Priority.MAX_PRIORITY + 1, + Priority.MIN_PRIORITY ); + + mPrioritySlider.setMajorTickSpacing( 20 ); + mPrioritySlider.setMinorTickSpacing( 5 ); + mPrioritySlider.setPaintTicks( true ); + mPrioritySlider.setLabelTable( mPrioritySlider.createStandardLabels( 20, 20 ) ); + mPrioritySlider.setPaintLabels( true ); + mPrioritySlider.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + int priority = mPrioritySlider.getValue(); + + if( priority == Priority.MAX_PRIORITY + 1 ) + { + mPrioritySliderLabel.setText( "Priority: Do Not Monitor" ); + } + else + { + mPrioritySliderLabel.setText( "Priority: " + priority ); + } + + setModified( true ); + } + } ); + mPrioritySlider.setToolTipText( HELP_TEXT ); + + mPrioritySliderLabel = new JLabel( "Priority: " + mPrioritySlider.getValue() + " " ); + add( mPrioritySliderLabel, "span,align center" ); + add( mPrioritySlider, "span,grow" ); + + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( PriorityEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( help, "align left" ); + } + + public Priority getPriority() + { + if( getItem() instanceof Priority ) + { + return (Priority)getItem(); + } + + return null; + } + + @Override + public void setItem( AliasID aliasID ) + { + super.setItem( aliasID ); + + Priority priority = getPriority(); + + if( priority != null ) + { + int value = priority.getPriority(); + + if( value == Priority.DO_NOT_MONITOR ) + { + value = Priority.MAX_PRIORITY + 1; + } + + mPrioritySlider.setValue( value ); + } + + setModified( false ); + + repaint(); + } + + @Override + public void save() + { + Priority priority = getPriority(); + + if( priority != null ) + { + int value = mPrioritySlider.getValue(); + + if( value == Priority.MAX_PRIORITY + 1 ) + { + value = Priority.DO_NOT_MONITOR; + } + + priority.setPriority( value ); + } + + setModified( false ); + } +} diff --git a/src/alias/id/siteID/SiteID.java b/src/alias/id/siteID/SiteID.java index ba5f57e0..a36d2fd5 100644 --- a/src/alias/id/siteID/SiteID.java +++ b/src/alias/id/siteID/SiteID.java @@ -19,8 +19,8 @@ package alias.id.siteID; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class SiteID extends AliasID { diff --git a/src/alias/id/siteID/SiteIDEditor.java b/src/alias/id/siteID/SiteIDEditor.java index 526e89c2..7df5fa7a 100644 --- a/src/alias/id/siteID/SiteIDEditor.java +++ b/src/alias/id/siteID/SiteIDEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,95 +17,105 @@ ******************************************************************************/ package alias.id.siteID; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import gui.editor.DocumentListenerEditor; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; -import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextArea; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; +import alias.id.AliasID; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SiteIDEditor extends JPanel implements ActionListener +public class SiteIDEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( SiteIDEditor.class ); - private SiteIDNode mSiteIDNode; - private JTextField mTextSiteID; - public SiteIDEditor( SiteIDNode siteIDNode ) + private static final String HELP_TEXT = "" + + "

    Site Identifier

    " + + "LTR-Net: decimal (0-9) (e.g. 019)
    " + + "MPT-1327: 5 digits (0-9) (e.g. 23619)
    " + + "Passport: 3 digits (0-9) (e.g. 019)
    " + + "P25: hex (0-9, A-F) format RR-SS where RR = RF Subsystem
    " + + "and SS = Site Number (e.g. RFSS 1 Site 1F: 01-1F)" + + ""; + + private JTextField mTextField; + + public SiteIDEditor( AliasID aliasID ) { - mSiteIDNode = siteIDNode; - initGUI(); + + setItem( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); - add( new JLabel( "Site ID" ), "span,align center" ); + add( new JLabel( "Site ID:" ) ); + mTextField = new JTextField(); + mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); + add( mTextField, "growx,push" ); - add( new JLabel( "Site:" ) ); + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( SiteIDEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); - mTextSiteID = new JTextField( - String.valueOf( mSiteIDNode.getSiteID().getSite() ) ); - add( mTextSiteID, "growx,push" ); - - JTextArea description = new JTextArea( "Enter a site number. " - + "For P25 systems, use the format RR-SS, where RR = RF " - + "Subsystem and SS = Site Number. For example, RFSS 1 and " - + "Site 3 would be: 01-03"); + add( help, "align left" ); + } + + public SiteID getSiteID() + { + if( getItem() instanceof SiteID ) + { + return (SiteID)getItem(); + } - description.setLineWrap( true ); - description.setBackground( getBackground() ); - - add( description, "growx,span" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( SiteIDEditor.this ); - add( btnSave, "growx,push" ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( SiteIDEditor.this ); - add( btnReset, "growx,push" ); + return null; } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setItem( AliasID aliasID ) + { + super.setItem( aliasID ); - if( command.contentEquals( "Save" ) ) + SiteID siteID = getSiteID(); + + if( siteID != null ) { - String siteIDString = mTextSiteID.getText(); - - if( siteIDString != null ) - { - mSiteIDNode.getSiteID().setSite( siteIDString ); + mTextField.setText( siteID.getSite() ); + } + + setModified( false ); + + repaint(); + } - mSiteIDNode.save(); - - mSiteIDNode.show(); - } - else - { - JOptionPane.showMessageDialog( SiteIDEditor.this, - "Please enter a site ID" ); - } - } - else if( command.contentEquals( "Reset" ) ) + @Override + public void save() + { + SiteID siteID = getSiteID(); + + if( siteID != null ) { - mTextSiteID.setText( mSiteIDNode.getSiteID().getSite() ); + siteID.setSite( mTextField.getText() ); } - mSiteIDNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/id/siteID/SiteIDNode.java b/src/alias/id/siteID/SiteIDNode.java deleted file mode 100644 index 26569699..00000000 --- a/src/alias/id/siteID/SiteIDNode.java +++ /dev/null @@ -1,86 +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 - ******************************************************************************/ -package alias.id.siteID; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import alias.AliasIDNode; -import alias.AliasNode; - -public class SiteIDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public SiteIDNode( SiteID id ) - { - super( id ); - } - - @Override - public JPanel getEditor() - { - return new SiteIDEditor( this ); - } - - public SiteID getSiteID() - { - return (SiteID)getUserObject(); - } - - public String toString() - { - return "Site " + getSiteID().getSite(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getSiteID() ); - - save(); - - getModel().removeNodeFromParent( SiteIDNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/alias/id/fleetsync/StatusID.java b/src/alias/id/status/StatusID.java similarity index 92% rename from src/alias/id/fleetsync/StatusID.java rename to src/alias/id/status/StatusID.java index bbcd694c..b93e0ad8 100644 --- a/src/alias/id/fleetsync/StatusID.java +++ b/src/alias/id/status/StatusID.java @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias.id.fleetsync; +package alias.id.status; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class StatusID extends AliasID { @@ -43,7 +43,7 @@ public class StatusID extends AliasID public String toString() { - return "Status: " + mStatus; + return "Status: " + String.format( "%03d", mStatus ); } @Override diff --git a/src/alias/id/status/StatusIDEditor.java b/src/alias/id/status/StatusIDEditor.java new file mode 100644 index 00000000..e4dc8e3d --- /dev/null +++ b/src/alias/id/status/StatusIDEditor.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package alias.id.status; + +import gui.editor.DocumentListenerEditor; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JTextField; +import javax.swing.text.MaskFormatter; + +import net.miginfocom.swing.MigLayout; +import alias.id.AliasID; + +public class StatusIDEditor extends DocumentListenerEditor +{ + private static final long serialVersionUID = 1L; + + private static final String HELP_TEXT = "" + + "

    Status Identifier

    " + + "Status numbers are used in some protocols like Fleetsync.

    " + + "The status number is assigned a meaning in the radio. You
    " + + "can assign a 3 digit status code (use leading zeros) to an
    " + + "alias where the alias contains the status meaning.

    " + + "Status: 001 engine start" + + ""; + + private JTextField mTextField; + + public StatusIDEditor( AliasID aliasID ) + { + initGUI(); + + setItem( aliasID ); + } + + private void initGUI() + { + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); + + add( new JLabel( "Status:" ) ); + + MaskFormatter formatter = null; + + try + { + //Mask: 3 digits + formatter = new MaskFormatter( "###" ); + } + catch( Exception e ) + { + //Do nothing, the mask was invalid + } + + mTextField = new JFormattedTextField( formatter ); + mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); + add( mTextField, "growx,push" ); + + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( StatusIDEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( help, "align left" ); + } + + public StatusID getStatusID() + { + if( getItem() instanceof StatusID ) + { + return (StatusID)getItem(); + } + + return null; + } + + @Override + public void setItem( AliasID aliasID ) + { + super.setItem( aliasID ); + + StatusID statusID = getStatusID(); + + if( statusID != null ) + { + mTextField.setText( String.format( "%03d", statusID.getStatus() ) ); + } + + setModified( false ); + + repaint(); + } + + @Override + public void save() + { + StatusID statusID = getStatusID(); + + if( statusID != null ) + { + int status = 0; + + try + { + status = Integer.valueOf( mTextField.getText() ); + } + catch( Exception e ) + { + //Do nothing, we couldn't parse the value + } + + statusID.setStatus( status ); + } + + setModified( false ); + } +} diff --git a/src/alias/id/talkgroup/TalkgroupID.java b/src/alias/id/talkgroup/TalkgroupID.java index 5ca1df8a..71764535 100644 --- a/src/alias/id/talkgroup/TalkgroupID.java +++ b/src/alias/id/talkgroup/TalkgroupID.java @@ -19,9 +19,8 @@ package alias.id.talkgroup; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; -import audio.inverted.AudioType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class TalkgroupID extends AliasID @@ -43,11 +42,6 @@ public class TalkgroupID extends AliasID mTalkgroup = talkgroup; } - public boolean hasAudioType() - { - return false; - } - public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/src/alias/id/talkgroup/TalkgroupIDEditor.java b/src/alias/id/talkgroup/TalkgroupIDEditor.java index 1feda866..6a63faf6 100644 --- a/src/alias/id/talkgroup/TalkgroupIDEditor.java +++ b/src/alias/id/talkgroup/TalkgroupIDEditor.java @@ -17,99 +17,105 @@ ******************************************************************************/ package alias.id.talkgroup; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import gui.editor.DocumentListenerEditor; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; -import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextArea; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; +import alias.id.AliasID; -public class TalkgroupIDEditor extends JPanel implements ActionListener +public class TalkgroupIDEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; - private TalkgroupIDNode mTalkgroupIDNode; - - private JTextField mTextTalkgroupID; - - private String mHelpText = "Enter a formatted talkgroup identifier.\n\n" - + "P25 Talkgroup Format: hexadecimal value, 4 characters for " - + "talkgroup or 6 characters for a mobile radio\n\n" - + "LTR Talkgroup Format: A-HH-TTT (A=Area, H=Home Repeater, " - + "T=Talkgroup)\n\n" - + "Passport Talkgroup Format: xxxxx (up to 5-digit number)\n\n" - + "Wildcard: use one or more asterisks (*) for any talkgroup digits."; - public TalkgroupIDEditor( TalkgroupIDNode talkgroupIDNode ) + private static final String HELP_TEXT = + "

    Talkgroup Identifier

    " + + "P25: 4 or 6 hex characters (e.g. AB12 or ABC123)
    " + + "LTR: A-HH-TTT where A=Area H=Home T=Talkgroup (0-01-128)
    " + + "Passport: 5-digit number (12345 or 00023)
    " + + "
    " + + "Wildcard: use an asterisk (*) in place of each talkgroup digit (e.g. 0*1*5)" + + ""; + + private JTextField mTextField; + + public TalkgroupIDEditor( AliasID aliasID ) { - mTalkgroupIDNode = talkgroupIDNode; - initGUI(); + + setItem( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); + + add( new JLabel( "Talkgroup:" ) ); - add( new JLabel( "Talkgroup ID" ), "span,align center" ); + mTextField = new JTextField(); + mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); + add( mTextField, "growx,push" ); + + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( TalkgroupIDEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( help, "align left" ); + } + + public TalkgroupID getTalkgroupID() + { + if( getItem() instanceof TalkgroupID ) + { + return (TalkgroupID)getItem(); + } - add( new JLabel( "TGID:" ) ); - mTextTalkgroupID = new JTextField(); - - mTextTalkgroupID.setText( mTalkgroupIDNode.getTalkgroupID().getTalkgroup() ); - - add( mTextTalkgroupID, "growx,push" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( TalkgroupIDEditor.this ); - add( btnSave, "growx,push" ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( TalkgroupIDEditor.this ); - add( btnReset, "growx,push" ); - - JTextArea helpText = new JTextArea( mHelpText ); - helpText.setBackground( getBackground() ); - helpText.setLineWrap( true ); - add( helpText, "span,grow,push" ); + return null; } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setItem( AliasID aliasID ) + { + super.setItem( aliasID ); - if( command.contentEquals( "Save" ) ) + TalkgroupID talkgroup = getTalkgroupID(); + + if( talkgroup != null ) { - String tgid = mTextTalkgroupID.getText(); - - if( tgid != null ) - { - mTalkgroupIDNode.getTalkgroupID().setTalkgroup( tgid ); - - ((ConfigurableNode)mTalkgroupIDNode.getParent()).sort(); + mTextField.setText( talkgroup.getTalkgroup() ); + } + + setModified( false ); + + repaint(); + } - mTalkgroupIDNode.save(); - - mTalkgroupIDNode.show(); - } - else - { - JOptionPane.showMessageDialog( TalkgroupIDEditor.this, - "Please enter a talkgroup ID" ); - } - } - else if( command.contentEquals( "Reset" ) ) + @Override + public void save() + { + TalkgroupID talkgroup = getTalkgroupID(); + + if( talkgroup != null ) { - mTextTalkgroupID.setText( - mTalkgroupIDNode.getTalkgroupID().getTalkgroup() ); + talkgroup.setTalkgroup( mTextField.getText() ); } - mTalkgroupIDNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/id/talkgroup/TalkgroupIDNode.java b/src/alias/id/talkgroup/TalkgroupIDNode.java deleted file mode 100644 index 0a8e7e0a..00000000 --- a/src/alias/id/talkgroup/TalkgroupIDNode.java +++ /dev/null @@ -1,86 +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 - ******************************************************************************/ -package alias.id.talkgroup; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import alias.AliasIDNode; -import alias.AliasNode; - -public class TalkgroupIDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public TalkgroupIDNode( TalkgroupID id ) - { - super( id ); - } - - @Override - public JPanel getEditor() - { - return new TalkgroupIDEditor( this ); - } - - public TalkgroupID getTalkgroupID() - { - return (TalkgroupID)getUserObject(); - } - - public String toString() - { - return "Talkgroup " + getTalkgroupID().getTalkgroup(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getTalkgroupID() ); - - save(); - - getModel().removeNodeFromParent( TalkgroupIDNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/alias/id/uniqueID/UniqueID.java b/src/alias/id/uniqueID/UniqueID.java index f6596294..ab7aedd0 100644 --- a/src/alias/id/uniqueID/UniqueID.java +++ b/src/alias/id/uniqueID/UniqueID.java @@ -19,8 +19,8 @@ package alias.id.uniqueID; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class UniqueID extends AliasID { diff --git a/src/alias/id/uniqueID/UniqueIDEditor.java b/src/alias/id/uniqueID/UniqueIDEditor.java index 7b46f176..ea0aa3be 100644 --- a/src/alias/id/uniqueID/UniqueIDEditor.java +++ b/src/alias/id/uniqueID/UniqueIDEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,101 +17,112 @@ ******************************************************************************/ package alias.id.uniqueID; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import gui.editor.DocumentListenerEditor; + +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; -import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; +import alias.id.AliasID; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import controller.ConfigurableNode; - -public class UniqueIDEditor extends JPanel implements ActionListener +public class UniqueIDEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( UniqueIDEditor.class ); - private UniqueIDNode mUniqueIDNode; - - private JLabel mLabelUniqueID; - private JTextField mTextUniqueID; - public UniqueIDEditor( UniqueIDNode uniqueIDNode ) + private static final String HELP_TEXT = "" + + "

    LTR-Net Unique ID (UID)

    " + + "UID: identifier assigned to each radio in
    " + + "the range 1 - 2097152" + + ""; + + private JTextField mTextField; + + public UniqueIDEditor( AliasID aliasID ) { - mUniqueIDNode = uniqueIDNode; - initGUI(); + + setItem( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); - - add( new JLabel( "LTR-Net Radio Unique ID" ), "span,align center" ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); add( new JLabel( "Unique ID:" ) ); - - mTextUniqueID = new JTextField( - String.valueOf( mUniqueIDNode.getUniqueID().getUid() ) ); - add( mTextUniqueID, "growx,push" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( UniqueIDEditor.this ); - add( btnSave, "growx,push" ); + mTextField = new JTextField(); + mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); + add( mTextField, "growx,push" ); - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( UniqueIDEditor.this ); - add( btnReset, "growx,push" ); + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( UniqueIDEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( help, "align left" ); + } + + public UniqueID getUniqueID() + { + if( getItem() instanceof UniqueID ) + { + return (UniqueID)getItem(); + } + + return null; } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setItem( AliasID aliasID ) + { + super.setItem( aliasID ); - if( command.contentEquals( "Save" ) ) + UniqueID uid = getUniqueID(); + + if( uid != null ) { - String uid = mTextUniqueID.getText(); + mTextField.setText( String.valueOf( uid.getUid() ) ); + } + + setModified( false ); + + repaint(); + } + + @Override + public void save() + { + UniqueID uid = getUniqueID(); + + if( uid != null ) + { + int id = 0; - if( uid != null ) + try { - try - { - int uidInt = Integer.parseInt( uid ); - - mUniqueIDNode.getUniqueID().setUid( uidInt ); - - ((ConfigurableNode)mUniqueIDNode.getParent()).sort(); - - mUniqueIDNode.save(); - - mUniqueIDNode.show(); - } - catch( Exception ex ) - { - mLog.error( "UniqueIDEditor - exception trying to parse " + - "int from uid [" + uid + "]", ex ); - } + id = Integer.parseInt( mTextField.getText() ); } - else + catch( Exception e ) { - JOptionPane.showMessageDialog( UniqueIDEditor.this, - "Please enter a site number and unique ID" ); + //Do nothing, we couldn't parse the value } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextUniqueID.setText( - String.valueOf( mUniqueIDNode.getUniqueID().getUid() ) ); + + uid.setUid( id ); } - mUniqueIDNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/id/uniqueID/UniqueIDNode.java b/src/alias/id/uniqueID/UniqueIDNode.java deleted file mode 100644 index 5c850f15..00000000 --- a/src/alias/id/uniqueID/UniqueIDNode.java +++ /dev/null @@ -1,86 +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 - ******************************************************************************/ -package alias.id.uniqueID; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import alias.AliasIDNode; -import alias.AliasNode; - -public class UniqueIDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public UniqueIDNode( UniqueID id ) - { - super( id ); - } - - @Override - public JPanel getEditor() - { - return new UniqueIDEditor( this ); - } - - public UniqueID getUniqueID() - { - return (UniqueID)getUserObject(); - } - - public String toString() - { - return "Unique ID " + getUniqueID().getUid(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getUniqueID() ); - - save(); - - getModel().removeNodeFromParent( UniqueIDNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/alias/priority/PriorityEditor.java b/src/alias/priority/PriorityEditor.java deleted file mode 100644 index 1028ae74..00000000 --- a/src/alias/priority/PriorityEditor.java +++ /dev/null @@ -1,191 +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 - ******************************************************************************/ -package alias.priority; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JSlider; -import javax.swing.JTextArea; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import controller.ConfigurableNode; - -public class PriorityEditor extends JPanel implements ActionListener -{ - private final static Logger mLog = LoggerFactory.getLogger( PriorityEditor.class ); - - public static final String DO_NOT_MONITOR = "Do Not Monitor"; - public static final String SAVE = "Save"; - public static final String RESET = "Reset"; - - private static final long serialVersionUID = 1L; - private PriorityNode mPriorityNode; - private JCheckBox mDoNotMonitorCheckBox = new JCheckBox( DO_NOT_MONITOR ); - private JSlider mPrioritySlider; - private JLabel mPrioritySliderLabel; - - public PriorityEditor( PriorityNode priorityNode ) - { - mPriorityNode = priorityNode; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); - - add( new JLabel( "Priority" ), "span,align center" ); - - boolean doNotMonitor = mPriorityNode.getPriority().isDoNotMonitor(); - - mDoNotMonitorCheckBox.addActionListener( this ); - - if( doNotMonitor ) - { - mDoNotMonitorCheckBox.setSelected( true ); - } - - add( mDoNotMonitorCheckBox, "span,align center" ); - - int priority = mPriorityNode.getPriority().getPriority(); - - /* Adjust the displayable priority value to within the min/max bounds - * so we don't get an error in the jslider control. If the actual - * priority is -1 (ie do not follow), the controls will be updated - * to reflect that state */ - if( priority < Priority.MIN_PRIORITY ) - { - priority = Priority.DEFAULT_PRIORITY; - } - else if( priority > Priority.MAX_PRIORITY ) - { - priority = Priority.DEFAULT_PRIORITY; - } - - mPrioritySlider = new JSlider( JSlider.HORIZONTAL, - Priority.MIN_PRIORITY, - Priority.MAX_PRIORITY, - priority ); - - mPrioritySlider.setMajorTickSpacing( 20 ); - mPrioritySlider.setMinorTickSpacing( 5 ); - mPrioritySlider.setPaintTicks( true ); - - mPrioritySlider.setLabelTable( mPrioritySlider.createStandardLabels( 20, 20 ) ); - mPrioritySlider.setPaintLabels( true ); - - mPrioritySliderLabel = new JLabel( "Priority: " + mPrioritySlider.getValue() + " " ); - - mPrioritySlider.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - mPrioritySliderLabel.setText( "Priority: " + mPrioritySlider.getValue() ); - } - } ); - - if( doNotMonitor ) - { - mPrioritySlider.setEnabled( false ); - mPrioritySliderLabel.setEnabled( false ); - } - - add( mPrioritySliderLabel ); - add( mPrioritySlider, "wrap,grow" ); - - JButton btnSave = new JButton( SAVE ); - btnSave.addActionListener( PriorityEditor.this ); - add( btnSave, "growx,push" ); - - JButton btnReset = new JButton( RESET ); - btnReset.addActionListener( PriorityEditor.this ); - add( btnReset, "growx,push" ); - - StringBuilder sb = new StringBuilder(); - sb.append( "Select a value for audio priority within the range " ); - sb.append( Priority.MIN_PRIORITY ); - sb.append( " and " ); - sb.append( Priority.MAX_PRIORITY ); - sb.append( ". Lower values indicate higher priority levels." ); - - JTextArea helpText = new JTextArea( sb.toString() ); - helpText.setLineWrap( true ); - add( helpText, "span,grow,push" ); - } - - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( SAVE ) ) - { - if( mDoNotMonitorCheckBox.isSelected() ) - { - mPriorityNode.getPriority().setPriority( Priority.DO_NOT_MONITOR ); - } - else - { - mPriorityNode.getPriority().setPriority( mPrioritySlider.getValue() ); - } - - ((ConfigurableNode)mPriorityNode.getParent()).sort(); - - mPriorityNode.save(); - - mPriorityNode.show(); - } - else if( command.contentEquals( RESET ) ) - { - mPrioritySlider.setValue( mPriorityNode.getPriority().getPriority() ); - } - else if( command.contentEquals( DO_NOT_MONITOR ) ) - { - if( mDoNotMonitorCheckBox.isSelected() ) - { - mPriorityNode.getPriority().setPriority( Priority.DO_NOT_MONITOR ); - mPrioritySlider.setEnabled( false ); - mPrioritySliderLabel.setEnabled( false ); - } - else - { - mPriorityNode.getPriority().setPriority( Priority.DEFAULT_PRIORITY ); - mPrioritySlider.setEnabled( true ); - mPrioritySliderLabel.setEnabled( true ); - } - - mPriorityNode.save(); - mPriorityNode.show(); - } - - mPriorityNode.refresh(); - } -} diff --git a/src/alias/priority/PriorityNode.java b/src/alias/priority/PriorityNode.java deleted file mode 100644 index 178860fe..00000000 --- a/src/alias/priority/PriorityNode.java +++ /dev/null @@ -1,86 +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 - ******************************************************************************/ -package alias.priority; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import alias.AliasIDNode; -import alias.AliasNode; - -public class PriorityNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public PriorityNode( Priority priority ) - { - super( priority ); - } - - @Override - public JPanel getEditor() - { - return new PriorityEditor( this ); - } - - public Priority getPriority() - { - return (Priority)getUserObject(); - } - - public String toString() - { - return getPriority().toString(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getPriority() ); - - save(); - - getModel().removeNodeFromParent( PriorityNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/alias/record/NonRecordableEditor.java b/src/alias/record/NonRecordableEditor.java deleted file mode 100644 index e9ab06e3..00000000 --- a/src/alias/record/NonRecordableEditor.java +++ /dev/null @@ -1,75 +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 - ******************************************************************************/ -package alias.record; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; -import javax.swing.JPanel; -import javax.swing.JTextArea; - -import net.miginfocom.swing.MigLayout; - -public class NonRecordableEditor extends JPanel implements ActionListener -{ - private static final long serialVersionUID = 1L; - - private NonRecordableNode mNode; - - public NonRecordableEditor( NonRecordableNode node ) - { - mNode = node; - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill", "[left]", "[grow]" ) ); - - JTextArea description = new JTextArea( "Non-Recordable. Any audio where" - + " this alias is a participant in the call will not be recorded." ); - - description.setLineWrap( true ); - description.setBackground( getBackground() ); - - add( description, "growx,span" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( NonRecordableEditor.this ); - add( btnSave, "growx,push" ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( NonRecordableEditor.this ); - add( btnReset, "growx,push" ); - } - - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) - { - mNode.save(); - mNode.show(); - } - - mNode.refresh(); - } -} diff --git a/src/alias/record/NonRecordableNode.java b/src/alias/record/NonRecordableNode.java deleted file mode 100644 index eb8ba572..00000000 --- a/src/alias/record/NonRecordableNode.java +++ /dev/null @@ -1,88 +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 - ******************************************************************************/ -package alias.record; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import alias.AliasID; -import alias.AliasIDNode; -import alias.AliasNode; - -public class NonRecordableNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public NonRecordableNode( NonRecordable id ) - { - super( id ); - } - - @Override - public JPanel getEditor() - { - return new NonRecordableEditor( this ); - } - - @Override - public String getIconPath() - { - return "images/no_record.png"; - } - - public String toString() - { - return "Non-Recordable"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( (AliasID)getUserObject() ); - - save(); - - getModel().removeNodeFromParent( NonRecordableNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/audio/AudioChannelPanel.java b/src/audio/AudioChannelPanel.java index d23c2024..58540f88 100644 --- a/src/audio/AudioChannelPanel.java +++ b/src/audio/AudioChannelPanel.java @@ -6,13 +6,8 @@ import java.awt.Font; import javax.swing.JLabel; import javax.swing.JPanel; -import javax.swing.JSeparator; import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sample.Listener; import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; @@ -30,8 +25,6 @@ public class AudioChannelPanel extends JPanel { private static final long serialVersionUID = 1L; - private static final Logger mLog = LoggerFactory.getLogger( AudioChannelPanel.class ); - private Font mFont = new Font( Font.MONOSPACED, Font.PLAIN, 12 ); private Color mLabelColor; private Color mDetailsColor; @@ -48,11 +41,8 @@ public class AudioChannelPanel extends JPanel private JLabel mFromLabel = new JLabel( "FROM:" ); private JLabel mFrom = new JLabel( "" ); private JLabel mFromAlias = new JLabel( "" ); - - private enum AudioStatus { STARTED,STOPPED }; - private AudioStatus mAudioStatus = AudioStatus.STOPPED; - private AudioMetadata mAudioMetadata; + private boolean mConfigured = false; public AudioChannelPanel( SettingsManager settingsManager, AudioOutput audioOutput ) { @@ -127,9 +117,6 @@ public class AudioChannelPanel extends JPanel mFromAlias.setForeground( mLabelColor ); add( mFromAlias,"wrap" ); - JSeparator separator = new JSeparator( JSeparator.HORIZONTAL ); - separator.setBackground( Color.DARK_GRAY ); - add( separator, "span,growx" ); } @Override @@ -137,19 +124,15 @@ public class AudioChannelPanel extends JPanel { switch( audioEvent.getType() ) { - case AUDIO_STARTED: - synchronized( mAudioStatus ) - { - mAudioStatus = AudioStatus.STARTED; - updateLabels(); - } - break; case AUDIO_STOPPED: - synchronized( mAudioStatus ) + EventQueue.invokeLater( new Runnable() { - mAudioStatus = AudioStatus.STOPPED; - resetLabels(); - } + @Override + public void run() + { + resetLabels(); + } + } ); break; case AUDIO_MUTED: case AUDIO_UNMUTED: @@ -173,55 +156,13 @@ public class AudioChannelPanel extends JPanel */ private void resetLabels() { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - mFrom.setText( "" ); - updateAlias( mFromAlias, null ); - - mTo.setText( "" ); - updateAlias( mToAlias, null ); - } - } ); - } - - private void updateLabels() - { - if( mAudioMetadata != null ) - { - final Metadata from = mAudioMetadata.getMetadata( MetadataType.FROM ); - - final Metadata to = mAudioMetadata.getMetadata( MetadataType.TO ); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - if( from != null ) - { - mFrom.setText( from.getValue() ); - updateAlias( mFromAlias, from.getAlias() ); - } - else - { - mFrom.setText( "-----" ); - } - - if( to != null ) - { - mTo.setText( to.getValue() ); - updateAlias( mToAlias, to.getAlias() ); - } - else - { - mTo.setText( "-----" ); - } - } - } ); - } + mFrom.setText( "" ); + updateAlias( mFromAlias, null ); + + mTo.setText( "" ); + updateAlias( mToAlias, null ); + + mConfigured = false; } /** @@ -263,17 +204,40 @@ public class AudioChannelPanel extends JPanel @Override public void receive( AudioMetadata audioMetadata ) { - mAudioMetadata = audioMetadata; - - if( audioMetadata.isUpdated() ) + if( !mConfigured || audioMetadata.isUpdated() ) { - synchronized( mAudioStatus ) + final Metadata from = audioMetadata.getMetadata( MetadataType.FROM ); + + final Metadata to = audioMetadata.getMetadata( MetadataType.TO ); + + EventQueue.invokeLater( new Runnable() { - if( mAudioStatus == AudioStatus.STARTED ) + @Override + public void run() { - updateLabels(); + if( from != null ) + { + mFrom.setText( from.getValue() ); + updateAlias( mFromAlias, from.getAlias() ); + } + else + { + mFrom.setText( "-----" ); + } + + if( to != null ) + { + mTo.setText( to.getValue() ); + updateAlias( mToAlias, to.getAlias() ); + } + else + { + mTo.setText( "-----" ); + } } - } + } ); + + mConfigured = true; } } } diff --git a/src/audio/AudioChannelsPanel.java b/src/audio/AudioChannelsPanel.java index 07c7604c..4043d4de 100644 --- a/src/audio/AudioChannelsPanel.java +++ b/src/audio/AudioChannelsPanel.java @@ -4,6 +4,7 @@ import java.awt.Color; import java.util.List; import javax.swing.JPanel; +import javax.swing.JSeparator; import net.miginfocom.swing.MigLayout; import settings.SettingsManager; @@ -21,17 +22,30 @@ public class AudioChannelsPanel extends JPanel setBackground( Color.BLACK ); List outputs = controller.getAudioOutputs(); - - for( AudioOutput audioOutput: controller.getAudioOutputs() ) + + for( int x = 0; x < outputs.size(); x++ ) { - add( new AudioChannelPanel( settingsManager, audioOutput ), "wrap" ); + add( new AudioChannelPanel( settingsManager, outputs.get( x ) ), "wrap" ); + + if( x < outputs.size() - 1 ) + { + addSeparator(); + } } /* Add an empty channel panel so that the panel is sized appropriately * for either a single channel or two channels */ if( outputs.size() == 1 ) { + addSeparator(); add( new AudioChannelPanel( settingsManager, null ), "wrap" ); } } + + private void addSeparator() + { + JSeparator separator = new JSeparator( JSeparator.HORIZONTAL ); + separator.setBackground( Color.DARK_GRAY ); + add( separator, "span,growx" ); + } } diff --git a/src/audio/AudioManager.java b/src/audio/AudioManager.java index f35499fb..fbc7e7b1 100644 --- a/src/audio/AudioManager.java +++ b/src/audio/AudioManager.java @@ -73,17 +73,19 @@ public class AudioManager implements Listener, IAudioController private ThreadPoolManager mThreadPoolManager; private ScheduledFuture mProcessingTask; - + private MixerManager mMixerManager; private MixerChannelConfiguration mMixerChannelConfiguration; + /** * Processes all audio produced by the decoding channels and routes audio * packets to any combination of outputs based on any alias audio routing * options specified by the user. */ - public AudioManager( ThreadPoolManager manager ) + public AudioManager( ThreadPoolManager manager, MixerManager mixerManager ) { mThreadPoolManager = manager; + mMixerManager = mixerManager; loadSettings(); } @@ -100,8 +102,7 @@ public class AudioManager implements Listener, IAudioController String channels = properties.get( AUDIO_CHANNELS_PROPERTY, MixerChannel.MONO.name() ); - MixerChannelConfiguration[] mixerConfigurations = - MixerManager.getInstance().getOutputMixers(); + MixerChannelConfiguration[] mixerConfigurations = mMixerManager.getOutputMixers(); for( MixerChannelConfiguration mixerConfig: mixerConfigurations ) { diff --git a/src/audio/AudioModule.java b/src/audio/AudioModule.java index 572b3b71..01ad02d4 100644 --- a/src/audio/AudioModule.java +++ b/src/audio/AudioModule.java @@ -1,6 +1,7 @@ package audio; import java.util.Arrays; +import java.util.concurrent.ScheduledExecutorService; import module.Module; @@ -8,7 +9,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import audio.metadata.AudioMetadata; import audio.metadata.IMetadataListener; @@ -18,7 +19,6 @@ import audio.squelch.SquelchState; import controller.channel.ChannelEvent; import controller.channel.IChannelEventListener; import dsp.filter.Filters; -import dsp.filter.dc.DCRemovalFilter_RB; import dsp.filter.fir.real.RealFIRFilter_R_R; /** @@ -32,49 +32,31 @@ import dsp.filter.fir.real.RealFIRFilter_R_R; public class AudioModule extends Module implements IAudioPacketProvider, IChannelEventListener, IMetadataListener, - IRealBufferListener, + IFilteredRealBufferListener, ISquelchStateListener, Listener { protected static final Logger mLog = LoggerFactory.getLogger( AudioModule.class ); - /* Determines responsiveness of DC filter to frequency changes */ - private static final float DC_REMOVAL_RATIO = 0.000003f; - /* Provides a unique identifier for this audio module instance to use as a * source identifier for all audio packets */ private static int UNIQUE_ID = 0; private int mSourceID; private AudioMetadata mAudioMetadata; - private ChannelEventListener mChannelEventListener = new ChannelEventListener(); - private SquelchStateListener mSquelchStateListener = new SquelchStateListener(); private SquelchState mSquelchState = SquelchState.SQUELCH; - - private RealFIRFilter_R_R mAudioFilter = new RealFIRFilter_R_R( - Filters.FIR_BANDPASS_AUDIO_48KHZ.getCoefficients(), 2.0f ); - - private DCRemovalFilter_RB mDCRemovalFilter; - private Listener mAudioPacketListener; + +//TODO: remove this -- is being replaced by the DemodulatedAudioFilterModule + private RealFIRFilter_R_R mAudioFilter = new RealFIRFilter_R_R( + Filters.FIR_BANDPASS_AUDIO_48KHZ.getCoefficients(), 2.0f ); - private boolean mRemoveDC = false; - - public AudioModule( boolean record, boolean removeDC ) + public AudioModule( boolean record ) { mSourceID = ++UNIQUE_ID; - mAudioMetadata = new AudioMetadata( mSourceID, record ); - - mRemoveDC = removeDC; - - if( mRemoveDC ) - { - mDCRemovalFilter = new DCRemovalFilter_RB( DC_REMOVAL_RATIO ); - mDCRemovalFilter.setListener( this ); - } } @Override @@ -91,7 +73,7 @@ public class AudioModule extends Module implements IAudioPacketProvider, } @Override - public void start() + public void start( ScheduledExecutorService executor ) { /* No start operations provided */ } @@ -132,13 +114,8 @@ public class AudioModule extends Module implements IAudioPacketProvider, } @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { - if( mRemoveDC ) - { - return mDCRemovalFilter; - } - return this; } @@ -183,10 +160,10 @@ public class AudioModule extends Module implements IAudioPacketProvider, { switch( event.getEvent() ) { - case CHANNEL_STATE_RESET: + case NOTIFICATION_STATE_RESET: mAudioMetadata.reset(); break; - case CHANGE_SELECTED: + case NOTIFICATION_SELECTION_CHANGE: mAudioMetadata.setSelected( event.getChannel().isSelected() ); break; default: diff --git a/src/audio/AudioPanel.java b/src/audio/AudioPanel.java index 0d008f01..a9e10b4d 100644 --- a/src/audio/AudioPanel.java +++ b/src/audio/AudioPanel.java @@ -27,8 +27,8 @@ import org.slf4j.LoggerFactory; import sample.Listener; import settings.SettingsManager; +import source.SourceManager; import source.mixer.MixerChannelConfiguration; -import source.mixer.MixerManager; import audio.output.AudioOutput; public class AudioPanel extends JPanel implements Listener @@ -43,14 +43,18 @@ public class AudioPanel extends JPanel implements Listener new ImageIcon( "images/audio_unmuted.png" ), 38 ); private SettingsManager mSettingsManager; + private SourceManager mSourceManager; private IAudioController mController; private JButton mMuteButton; private AudioChannelsPanel mAudioChannelsPanel; - public AudioPanel( SettingsManager settingsManager, IAudioController controller ) + public AudioPanel( SettingsManager settingsManager, + SourceManager sourceManager, + IAudioController controller ) { mSettingsManager = settingsManager; + mSourceManager = sourceManager; mController = controller; mController.addControllerListener( this ); @@ -148,7 +152,7 @@ public class AudioPanel extends JPanel implements Listener JMenu outputMenu = new JMenu( "Audio Output" ); MixerChannelConfiguration[] mixerConfigurations = - MixerManager.getInstance().getOutputMixers(); + mSourceManager.getMixerManager().getOutputMixers(); for( MixerChannelConfiguration mixerConfig: mixerConfigurations ) { diff --git a/src/audio/metadata/AudioMetadata.java b/src/audio/metadata/AudioMetadata.java index dc6f2472..521ffa79 100644 --- a/src/audio/metadata/AudioMetadata.java +++ b/src/audio/metadata/AudioMetadata.java @@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory; import sample.Listener; import alias.Alias; -import alias.priority.Priority; +import alias.id.priority.Priority; public class AudioMetadata implements Listener { @@ -305,6 +305,7 @@ public class AudioMetadata implements Listener sb.append( getPriority() ); sb.append( " updated:" + isUpdated() ); sb.append( " " ); + for( Entry entry: getMetadata().entrySet() ) { sb.append( entry.getValue().getKey() ); diff --git a/src/audio/metadata/Metadata.java b/src/audio/metadata/Metadata.java index 500daf8e..1f73c379 100644 --- a/src/audio/metadata/Metadata.java +++ b/src/audio/metadata/Metadata.java @@ -1,3 +1,20 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ package audio.metadata; import alias.Alias; @@ -142,5 +159,4 @@ public class Metadata return true; } - } diff --git a/src/audio/output/AudioOutput.java b/src/audio/output/AudioOutput.java index caa9396f..7acf46d7 100644 --- a/src/audio/output/AudioOutput.java +++ b/src/audio/output/AudioOutput.java @@ -1,3 +1,20 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ package audio.output; import java.nio.ByteBuffer; @@ -147,8 +164,7 @@ public abstract class AudioOutput implements Listener, LineListener public void reset() { - broadcast( new AudioEvent( AudioEvent.Type.AUDIO_STOPPED, - getChannelName() ) ); + broadcast( new AudioEvent( AudioEvent.Type.AUDIO_STOPPED, getChannelName() ) ); } public void dispose() diff --git a/src/audio/output/MonoAudioOutput.java b/src/audio/output/MonoAudioOutput.java index 33ecfe92..e146e507 100644 --- a/src/audio/output/MonoAudioOutput.java +++ b/src/audio/output/MonoAudioOutput.java @@ -1,3 +1,20 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ package audio.output; import java.nio.ByteBuffer; diff --git a/src/audio/output/StereoAudioOutput.java b/src/audio/output/StereoAudioOutput.java index 1ef7ae69..2ba8db41 100644 --- a/src/audio/output/StereoAudioOutput.java +++ b/src/audio/output/StereoAudioOutput.java @@ -1,3 +1,20 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ package audio.output; import java.nio.ByteBuffer; diff --git a/src/bits/BinaryMessage.java b/src/bits/BinaryMessage.java index 3a2e0744..66b9463e 100644 --- a/src/bits/BinaryMessage.java +++ b/src/bits/BinaryMessage.java @@ -19,15 +19,10 @@ package bits; import java.util.BitSet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import edac.CRC; public class BinaryMessage extends BitSet { - private final static Logger mLog = LoggerFactory.getLogger( BinaryMessage.class ); - private static final long serialVersionUID = 1L; /** diff --git a/src/bits/MessageFramer.java b/src/bits/MessageFramer.java index cd41737c..32b4ab03 100644 --- a/src/bits/MessageFramer.java +++ b/src/bits/MessageFramer.java @@ -64,9 +64,9 @@ public class MessageFramer implements Listener, { assembler.dispose(); } - - mMessageAssemblers.clear(); - } + + mMessageAssemblers.clear(); + } public void dispose() { @@ -154,8 +154,9 @@ public class MessageFramer implements Listener, { mMessageAssemblers.add( assembler ); } - - private void removeMessageAssembler( MessageAssembler assembler ) + + @SuppressWarnings( "unused" ) + private void removeMessageAssembler( MessageAssembler assembler ) { mMessageAssemblers.remove( assembler ); } diff --git a/src/buffer/BufferOverflowException.java b/src/buffer/BufferOverflowException.java deleted file mode 100644 index 4b6e1a57..00000000 --- a/src/buffer/BufferOverflowException.java +++ /dev/null @@ -1,33 +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 - ******************************************************************************/ -package buffer; - -public class BufferOverflowException extends Exception -{ - private static final long serialVersionUID = 1L; - - public BufferOverflowException( String text ) - { - super( text ); - } - - public BufferOverflowException( String text, Throwable exc ) - { - super( text, exc ); - } -} diff --git a/src/buffer/BufferOverflowListener.java b/src/buffer/BufferOverflowListener.java deleted file mode 100644 index 2f8acb03..00000000 --- a/src/buffer/BufferOverflowListener.java +++ /dev/null @@ -1,23 +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 - ******************************************************************************/ -package buffer; - -public interface BufferOverflowListener -{ - public void overflow( Object source, String message ); -} diff --git a/src/buffer/ByteArrayDelayBuffer.java b/src/buffer/ByteArrayDelayBuffer.java deleted file mode 100644 index fc60703a..00000000 --- a/src/buffer/ByteArrayDelayBuffer.java +++ /dev/null @@ -1,60 +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 - ******************************************************************************/ -package buffer; - -public class ByteArrayDelayBuffer -{ - private int mBufferSize; - private byte[][] mBuffer = null; - private int mBufferPointer = 0; - - /** - * Implements a delay buffer for byte arrays. As a new byte[] is placed - * into the buffer, the oldest byte[] is returned - * @param bufferSize - number of byte arrays to buffer - */ - public ByteArrayDelayBuffer( int bufferSize ) - { - mBufferSize = bufferSize; - } - - /** - * Inserts new bytes into the buffer - * - * @return - oldest byte[] from the buffer, or null if the buffer is not - * yet full - */ - public byte[] get( byte[] bytes ) - { - if( mBuffer == null ) - { - mBuffer = new byte[ mBufferSize ][ bytes.length ]; - } - - byte[] returnBytes = mBuffer[ mBufferPointer ]; - - mBuffer[ mBufferPointer++ ] = bytes; - - if( mBufferPointer >= mBufferSize ) - { - mBufferPointer = 0; - } - - return returnBytes; - } -} diff --git a/src/buffer/ByteArrayListener.java b/src/buffer/ByteArrayListener.java deleted file mode 100644 index 021b8e2a..00000000 --- a/src/buffer/ByteArrayListener.java +++ /dev/null @@ -1,23 +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 - ******************************************************************************/ -package buffer; - -public interface ByteArrayListener -{ - public void receive( byte[] data ); -} diff --git a/src/buffer/ByteBufferProvider.java b/src/buffer/ByteBufferProvider.java deleted file mode 100644 index f85962c3..00000000 --- a/src/buffer/ByteBufferProvider.java +++ /dev/null @@ -1,33 +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 - ******************************************************************************/ -package buffer; - - - -public interface ByteBufferProvider -{ - /** - * Adds a sample listener to receive samples - */ - public void addByteBufferListener( ByteBufferListener listener ); - - /** - * Removes the sample listener, if it is currently registered as a listener - */ - public void removeByteBufferListener( ByteBufferListener listener ); -} diff --git a/src/buffer/ComplexByteBuffer.java b/src/buffer/ComplexByteBuffer.java deleted file mode 100644 index 1abb25dc..00000000 --- a/src/buffer/ComplexByteBuffer.java +++ /dev/null @@ -1,91 +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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; -import java.util.Enumeration; - -import sample.complex.Complex; - -/** - * Provides a complex sample enumeration wrapper around a byte array containing - * interleaved byte-length I & Q sample values in the range of 0 - 255, converting - * those samples into complex samples with value range of -128 to 0 to 127. - */ -public class ComplexByteBuffer implements Enumeration -{ - private static float[] mValues; - - /** - * Creates a lookup table that converts the 8-bit valued range from 0 - 255 - * into scaled float values of -128 to 0 to 127 - */ - static - { - mValues = new float[ 256 ]; - - float scale = (float)( 1.0f / 127.0f ); - - for( int x = 0; x < 256; x++ ) - { - mValues[ x ] = (float)( x - 128 ) * scale; - } - } - - private ByteBuffer mBytes; -// private byte[] mBytes; -// private int mPointer; - - public ComplexByteBuffer( byte[] bytes ) - { - mBytes = ByteBuffer.wrap( bytes ); -// mBytes = bytes; -// mPointer = 0; - } - - public byte[] toArray() - { -// return mBytes; - return mBytes.array(); - } - - /** - * Indicates if there are more complex samples remaining. - */ - @Override - public boolean hasMoreElements() - { -// return mPointer < mBytes.length; - return mBytes.hasRemaining(); - } - - /** - * Returns the next complex sample. - * - * Note: does not check for buffer overrun. Use the hasMoreElements() - * method to ensure you do not read past the end of the buffer. - */ - @Override - public Complex nextElement() - { -// return new ComplexSample( mValues[ ( mBytes[ mPointer++ ] & 0xFF ) ], -// mValues[ ( mBytes[ mPointer++ ] & 0xFF ) ] ); - return new Complex( mValues[ ( mBytes.get() & 0xFF ) ], - mValues[ ( mBytes.get() & 0xFF ) ] ); - } -} diff --git a/src/buffer/ComplexByteBufferListener.java b/src/buffer/ComplexByteBufferListener.java deleted file mode 100644 index efdb3a1a..00000000 --- a/src/buffer/ComplexByteBufferListener.java +++ /dev/null @@ -1,23 +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 - ******************************************************************************/ -package buffer; - -public interface ComplexByteBufferListener -{ - public void receive( ComplexByteBuffer buffer ); -} diff --git a/src/buffer/ComplexSampleBufferAssembler.java b/src/buffer/ComplexSampleBufferAssembler.java deleted file mode 100644 index 935696f1..00000000 --- a/src/buffer/ComplexSampleBufferAssembler.java +++ /dev/null @@ -1,73 +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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Arrays; - -import sample.complex.Complex; - -public class ComplexSampleBufferAssembler -{ - private ByteBuffer mByteBuffer; - private int mSizeInBytes; - - public ComplexSampleBufferAssembler( int size ) - { - mSizeInBytes = size * 4; - - //Allocate a byte buffer with 4 bytes per sample (ie 2 x 16-bit samples) - mByteBuffer = ByteBuffer.allocate( mSizeInBytes ); - mByteBuffer.order( ByteOrder.LITTLE_ENDIAN ); - } - - /** - * Load the sample's bytes, left then right, little endian, into the - * backing byte array - */ - public void put( Complex sample ) - { - mByteBuffer.putShort( (short)( sample.left() * Short.MAX_VALUE ) ); - mByteBuffer.putShort( (short)( sample.right() * Short.MAX_VALUE ) ); - } - - /** - * Indicates if there is room for more samples in the buffer, or if it is full - */ - public boolean hasRemaining() - { - return mByteBuffer.hasRemaining(); - } - - /** - * Returns a copy of the byte array backing this buffer - */ - public ByteBuffer get() - { - return ByteBuffer.wrap( Arrays.copyOf( mByteBuffer.array(), mSizeInBytes ) ); - } - - /** - * Clear the bytes from the underlying buffer and reset the pointer to 0. - */ - public void clear() - { - mByteBuffer.clear(); - } -} diff --git a/src/buffer/DelayBuffer.java b/src/buffer/DelayBuffer.java deleted file mode 100644 index 001058db..00000000 --- a/src/buffer/DelayBuffer.java +++ /dev/null @@ -1,67 +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 - ******************************************************************************/ -package buffer; - -import sample.Listener; - -/** - * Delay Buffer - implements a circular delay buffer backed by an object array - */ -public class DelayBuffer implements Listener -{ - private Object[] mBuffer; - private int mBufferPointer = 0; - private Listener mListener; - - public DelayBuffer( int length ) - { - mBuffer = new Object[ length ]; - } - - @Override - @SuppressWarnings( "unchecked" ) - public void receive( T value ) - { - Object delayed = mBuffer[ mBufferPointer ]; - - mBuffer[ mBufferPointer ] = value; - - mBufferPointer++; - - /* Wrap the buffer pointer around to 0 when necessary */ - if( mBufferPointer >= mBuffer.length ) - { - mBufferPointer = 0; - } - - if( mListener != null && delayed != null ) - { - mListener.receive( (T)delayed ); - } - } - - public void setListener( Listener listener ) - { - mListener = listener; - } - - public void removeListener( Listener listener ) - { - mListener = null; - } -} diff --git a/src/buffer/FloatArrayCircularAveragingBuffer.java b/src/buffer/FloatArrayCircularAveragingBuffer.java deleted file mode 100644 index 5003d789..00000000 --- a/src/buffer/FloatArrayCircularAveragingBuffer.java +++ /dev/null @@ -1,97 +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 - ******************************************************************************/ -package buffer; - -import java.util.ArrayList; -import java.util.Arrays; - -public class FloatArrayCircularAveragingBuffer -{ - private ArrayList mBuffer; - private int mBufferPointer; - private int mBufferSize; - private float[] mAverage; - - public FloatArrayCircularAveragingBuffer( int bufferSize ) - { - mBufferSize = bufferSize; - mBuffer = new ArrayList(); - } - - /** - * Replaces the oldest float array element in the buffer with this new - * float array element, and then returns the average of the buffer contents - * with the newElement values included in the average array - */ - public float[] get( float[] newElement ) - { - /** - * If we've never setup the average array, or the newElements length is - * different than what we've been processing, reset the buffer & averages - */ - if( mAverage == null || mAverage.length != newElement.length ) - { - reset( newElement.length ); - } - - float[] oldElement = mBuffer.get( mBufferPointer ); - - for( int x = 0; x < newElement.length; x++ ) - { - /** - * Change the current average value in each array element by the - * difference of the new element minus the old element, divided - * by the buffer size ... average it - */ - mAverage[ x ] += - ( ( newElement[ x ] - oldElement[ x ] ) / mBufferSize ); - } - - mBuffer.set( mBufferPointer, newElement ); - - mBufferPointer++; - - if( mBufferPointer >= mBufferSize ) - { - mBufferPointer = 0; - } - - return mAverage; - } - - /** - * Clears buffer and average and fills buffer with zero-valued float arrays - */ - private void reset( int arrayLength ) - { - mAverage = new float[ arrayLength ]; - Arrays.fill( mAverage, 0.0f ); - - mBuffer.clear(); - mBufferPointer = 0; - - float[] emptyArray = new float[ arrayLength ]; - Arrays.fill( emptyArray, 0.0f ); - - for( int x = 0; x < mBufferSize; x++ ) - { - mBuffer.add( emptyArray ); - } - } - -} diff --git a/src/buffer/FloatBufferAssembler.java b/src/buffer/FloatBufferAssembler.java deleted file mode 100644 index 651a1794..00000000 --- a/src/buffer/FloatBufferAssembler.java +++ /dev/null @@ -1,70 +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 - ******************************************************************************/ -package buffer; - -import java.util.Arrays; - -import sample.Listener; - - -/** - * Assembles single samples into sample blocks (ie arrays) for broadcast to - * registered SampleBlockListeners - */ -public class FloatBufferAssembler implements Listener -{ - private Float[] mFloatBuffer; - private int mBufferSize; - private int mBufferPointer = 0; - private Listener mListener; - - public FloatBufferAssembler( int bufferSize ) - { - mBufferSize = bufferSize; - mFloatBuffer = new Float[ mBufferSize ]; - } - - /** - * Stores received samples in the sample block and manages the storage - * pointer. Sends sample blocks when full. - */ - @Override - public void receive( Float sample ) - { - mFloatBuffer[ mBufferPointer++ ] = sample; - - if( mBufferPointer == mBufferSize ) - { - if( mListener != null ) - { - mListener.receive( Arrays.copyOf( mFloatBuffer, mBufferSize ) ); - } - - mBufferPointer = 0; - } - } - - /** - * Adds a new sampleblock listener to receive sample blocks as they are - * assembled from the sample stream - */ - public void setListener( Listener listener ) - { - mListener = listener; - } -} diff --git a/src/buffer/FloatCircularAveragingBuffer.java b/src/buffer/FloatCircularAveragingBuffer.java deleted file mode 100644 index 6a05ea61..00000000 --- a/src/buffer/FloatCircularAveragingBuffer.java +++ /dev/null @@ -1,110 +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 - ******************************************************************************/ -package buffer; - -import java.util.Arrays; - -/** - * Circular sample buffer - allocates a buffer and stores samples in a circular - * fashion, overwriting older samples with newly arrived samples. Initially - * fills buffer with 0-valued samples - * - * Produces no output. Obtain the average buffer value on-demand via the - * getAverage() method - */ -public class FloatCircularAveragingBuffer -{ - private float[] mBuffer; - private int mBufferPointer = 0; - private int mSampleCountForAveraging; - private int mCurrentSampleCount = 0; - private float mAverage = 0.0f; - - public FloatCircularAveragingBuffer( int size, int sampleCountForAveraging ) - { - mBuffer = new float[ size ]; - mSampleCountForAveraging = sampleCountForAveraging; - Arrays.fill( mBuffer, 0.0f ); - } - - /** - * Convenience wrapper to throw away to put the new sample and throwaway - * the oldest sample - */ - public void put( float newestSample ) - { - get( newestSample ); - } - - /** - * Loads the newest sample into the buffer and returns the oldest sample - */ - public float get( float newestSample ) - { - float oldestSample = mBuffer[ mBufferPointer ]; - - mBuffer[ mBufferPointer ] = newestSample; - - mBufferPointer++; - - if( mBufferPointer >= mBuffer.length ) - { - mBufferPointer = 0; - } - - mCurrentSampleCount++; - - if( mCurrentSampleCount >= mSampleCountForAveraging ) - { - calculateAverage(); - - mCurrentSampleCount = 0; - } - - return oldestSample; - } - - /** - * Calculates the average of the maximum and minimum values present in - * the buffer - */ - private void calculateAverage() - { - float min = mBuffer[ 0 ]; - float max = mBuffer[ 0 ]; - - for( int x = 0; x < mBuffer.length; x++ ) - { - if( mBuffer[ x ] < min ) - { - min = mBuffer[ x ]; - } - if( mBuffer[ x ] > max ) - { - max = mBuffer[ x ]; - } - } - - mAverage = (float)( ( min + max ) / 2.0f ); - } - - public float getAverage() - { - return mAverage; - } -} diff --git a/src/buffer/FloatCircularAveragingBuffer2.java b/src/buffer/FloatCircularAveragingBuffer2.java deleted file mode 100644 index da27d207..00000000 --- a/src/buffer/FloatCircularAveragingBuffer2.java +++ /dev/null @@ -1,68 +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 - ******************************************************************************/ -package buffer; - -import java.util.Arrays; - -/** - * Circular sample buffer - stores samples in a circular buffer - * overwriting older samples with newly arrived samples. Initially - * fills buffer with 0-valued samples - * - * Continuously calculates the average of the samples in the buffer, with the - * average available from the average() method. - */ -public class FloatCircularAveragingBuffer2 -{ - private float[] mBuffer; - private int mBufferPointer = 0; - private float mAverage = 0.0f; - - public FloatCircularAveragingBuffer2( int size ) - { - mBuffer = new float[ size ]; - Arrays.fill( mBuffer, 0.0f ); - } - - /** - * Loads the newest sample into the buffer and returns the oldest sample - */ - public float get( float newestSample ) - { - float oldestSample = mBuffer[ mBufferPointer ]; - - mBuffer[ mBufferPointer ] = newestSample; - - mBufferPointer++; - - if( mBufferPointer >= mBuffer.length ) - { - mBufferPointer = 0; - } - - mAverage = mAverage - ( oldestSample / mBuffer.length ) + - ( newestSample / mBuffer.length ); - - return oldestSample; - } - - public float average() - { - return mAverage; - } -} diff --git a/src/buffer/FloatSampleBuffer.java b/src/buffer/FloatSampleBuffer.java deleted file mode 100644 index bca43189..00000000 --- a/src/buffer/FloatSampleBuffer.java +++ /dev/null @@ -1,66 +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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; - - -public class FloatSampleBuffer -{ - private static final long serialVersionUID = 1L; - - private int mSize; - private float[] mBuffer; - private int mBufferPointer = 0; - - public FloatSampleBuffer( int size ) - { - super(); - mBuffer = new float[ size ]; - } - - public void put( float sample ) - { - if( !isFull() ) - { - mBuffer[ mBufferPointer++ ] = sample; - } - } - - public void reset() - { - mBufferPointer = 0; - } - - public boolean isFull() - { - return mBufferPointer >= mBuffer.length; - } - - public byte[] getBytes() - { - ByteBuffer bb = ByteBuffer.allocate( mBuffer.length * 2 ); - - for( int x = 0; x < mBuffer.length; x++ ) - { - bb.putShort( ((Float)mBuffer[ x ]).shortValue() ); - } - - return bb.array(); - } -} diff --git a/src/buffer/FloatSampleBufferAssembler.java b/src/buffer/FloatSampleBufferAssembler.java deleted file mode 100644 index de4d53c2..00000000 --- a/src/buffer/FloatSampleBufferAssembler.java +++ /dev/null @@ -1,72 +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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -public class FloatSampleBufferAssembler -{ - private ByteBuffer mByteBuffer; - private int mSizeInBytes; - - public FloatSampleBufferAssembler( int size ) - { - mSizeInBytes = size * 2; - - //Allocate a byte buffer with 2 bytes per sample (ie 1 x 16-bit sample) - mByteBuffer = ByteBuffer.allocate( mSizeInBytes ); - mByteBuffer.order( ByteOrder.LITTLE_ENDIAN ); - } - - /** - * Load the sample's bytes, left then right, little endian, into the - * backing byte array - */ - public void put( float sample ) - { - mByteBuffer.putShort( (short)( sample * 32768 ) ); - } - - /** - * Indicates if there is room for more samples in the buffer, or if it is full - */ - public boolean hasRemaining() - { - return mByteBuffer.hasRemaining(); - } - - /** - * Returns a copy of the byte array backing this buffer - */ - public ByteBuffer get() - { - return mByteBuffer; -// return ByteBuffer.wrap( Arrays.copyOf( mByteBuffer.array(), mSizeInBytes ) ); - } - - /** - * Clear the bytes from the underlying buffer and reset the pointer to 0. - */ - public void reset() - { -// mByteBuffer.clear(); - mByteBuffer = ByteBuffer.allocate( mSizeInBytes ); - mByteBuffer.order( ByteOrder.LITTLE_ENDIAN ); - } -} diff --git a/src/buffer/FloatTappedCircularBuffer.java b/src/buffer/FloatTappedCircularBuffer.java deleted file mode 100644 index 5d0c3e7f..00000000 --- a/src/buffer/FloatTappedCircularBuffer.java +++ /dev/null @@ -1,86 +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 - ******************************************************************************/ -package buffer; - - -/** - * Implements a FIFO circular buffer for floats that provides access - * to each sample by index number, where index 0 is the newest/latest - * complex sample and index (length - 1) is the oldest sample in the buffer. - */ -public class FloatTappedCircularBuffer -{ - private Float[] mBuffer; - private int mBufferPointer = 0; - - public FloatTappedCircularBuffer( int length ) - { - mBuffer = new Float[ length ]; - - //Pre-load the buffer with 0-valued samples - for( int x = 0; x < length; x++ ) - { - mBuffer[ x ] = Float.valueOf( 0.0f ); - } - } - - /** - * Adds the sample to the buffer and removes the oldest sample - * @param sample - */ - public void add( Float sample ) - { - mBuffer[ mBufferPointer++ ] = sample; - - //Reset the buffer pointer once it points past the end of the buffer - if( mBufferPointer >= mBuffer.length ) - { - mBufferPointer = 0; - } - } - - /** - * Retrieves a sample currently in the buffer by index position. - * - * Index 0 is the newest/latest sample in the buffer and index (length - 1) - * is the oldest. - * - * Note: the buffer is preloaded with complex samples of 0,0 value. - * - * @param index where 0 is the newest/latest sample and length-1 is the oldest - * @return complex sample from the buffer. - * @throws ArrayIndexOutOfBoundsException if you attempt to access a sample - * outside of the buffer size - */ - public Float get( int index ) - { - /* Since the samples are stored in a forward fashion, we have to access - * the samples in reverse to comply with the access contract, where - * index 0 is the newest and index (length -1) is the oldest, all - * relative to the current buffer pointer */ - - int pointer = mBuffer.length - 1 + mBufferPointer - index; - - if( pointer >= mBuffer.length ) - { - pointer -= mBuffer.length; - } - - return mBuffer[ pointer ]; - } -} diff --git a/src/buffer/FloatToByteBufferAssembler.java b/src/buffer/FloatToByteBufferAssembler.java deleted file mode 100644 index 5f24ffce..00000000 --- a/src/buffer/FloatToByteBufferAssembler.java +++ /dev/null @@ -1,69 +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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import sample.Broadcaster; -import sample.Listener; - -public class FloatToByteBufferAssembler implements Listener -{ - private Broadcaster mBroadcaster = new Broadcaster(); - - private int mBufferSize = 0; - private ByteBuffer mBuffer; - - public FloatToByteBufferAssembler( int bufferSize ) - { - mBufferSize = bufferSize; - resetBuffer(); - } - - public void addByteBufferListener( Listener listener ) - { - mBroadcaster.addListener( listener ); - } - - public void removeByteBufferListener( Listener listener ) - { - mBroadcaster.removeListener( listener ); - } - - private void resetBuffer() - { - mBuffer = ByteBuffer.allocate( mBufferSize ); - mBuffer.order( ByteOrder.BIG_ENDIAN ); - } - - @Override - public void receive( Float sample ) - { - mBuffer.putShort( Float.valueOf( sample ).shortValue() ); - - if( mBuffer.position() >= mBufferSize ) - { - //Buffer is full ... send it - mBroadcaster.receive( mBuffer ); - - //Reset the buffer - resetBuffer(); - } - } -} diff --git a/src/buffer/ShortComplexByteBuffer.java b/src/buffer/ShortComplexByteBuffer.java deleted file mode 100644 index 2aff0558..00000000 --- a/src/buffer/ShortComplexByteBuffer.java +++ /dev/null @@ -1,53 +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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Enumeration; - -import sample.complex.Complex; - -public class ShortComplexByteBuffer implements Enumeration -{ - private ByteBuffer mByteBuffer; - - public ShortComplexByteBuffer( byte[] bytes, ByteOrder endianness ) - { - mByteBuffer = ByteBuffer.wrap( bytes ); - mByteBuffer.order( endianness ); - } - - public byte[] array() - { - return mByteBuffer.array(); - } - - @Override - public boolean hasMoreElements() - { - return mByteBuffer.hasRemaining(); - } - - @Override - public Complex nextElement() - { - return new Complex( (float)mByteBuffer.getShort(), - (float)mByteBuffer.getShort() ); - } -} diff --git a/src/buffer/StereoByteBuffer.java b/src/buffer/StereoByteBuffer.java deleted file mode 100644 index 6e4e6db4..00000000 --- a/src/buffer/StereoByteBuffer.java +++ /dev/null @@ -1,59 +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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Enumeration; - -import sample.complex.Complex; -import source.mixer.MixerChannel; - -public class StereoByteBuffer implements Enumeration -{ - private ByteBuffer mByteBuffer; - - public StereoByteBuffer( byte[] bytes, int frames ) - { - mByteBuffer = ByteBuffer.wrap( bytes ); - mByteBuffer.order( ByteOrder.LITTLE_ENDIAN ); - } - - public byte[] array() - { - return mByteBuffer.array(); - } - - public ByteBuffer getChannel( MixerChannel channel ) - { - return StereoToMonoByteBufferConverter.getChannel( mByteBuffer, channel ); - } - - @Override - public boolean hasMoreElements() - { - return mByteBuffer.hasRemaining(); - } - - @Override - public Complex nextElement() - { - return new Complex( (float)mByteBuffer.getShort(), - (float)mByteBuffer.getShort() ); - } -} diff --git a/src/buffer/StereoByteBufferListener.java b/src/buffer/StereoByteBufferListener.java deleted file mode 100644 index 641305c1..00000000 --- a/src/buffer/StereoByteBufferListener.java +++ /dev/null @@ -1,24 +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 - ******************************************************************************/ -package buffer; - - -public interface StereoByteBufferListener -{ - public void receive( StereoByteBuffer buffer ); -} diff --git a/src/buffer/StereoByteBufferProvider.java b/src/buffer/StereoByteBufferProvider.java deleted file mode 100644 index 76f91930..00000000 --- a/src/buffer/StereoByteBufferProvider.java +++ /dev/null @@ -1,32 +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 - ******************************************************************************/ -package buffer; - - -public interface StereoByteBufferProvider -{ - /** - * Adds a sample listener to receive samples - */ - public void addStereoByteBufferListener( StereoByteBufferListener listener ); - - /** - * Removes the sample listener, if it is currently registered as a listener - */ - public void removeStereoByteBufferListener( StereoByteBufferListener listener ); -} diff --git a/src/buffer/StereoToMonoByteBufferConverter.java b/src/buffer/StereoToMonoByteBufferConverter.java deleted file mode 100644 index 55e43aca..00000000 --- a/src/buffer/StereoToMonoByteBufferConverter.java +++ /dev/null @@ -1,58 +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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import source.mixer.MixerChannel; - -public class StereoToMonoByteBufferConverter -{ - public static ByteBuffer getChannel( ByteBuffer buffer, MixerChannel channel ) - { - byte[] channelBuffer = new byte[ buffer.capacity() / 2 ]; - - - int channelBufferPointer = 0; - - if( channel == MixerChannel.RIGHT ) - { - //throw away the first two bytes - buffer.get(); - buffer.get(); - } - - while( channelBufferPointer < channelBuffer.length ) - { - if( buffer.hasRemaining() ) - { - channelBuffer[ channelBufferPointer++ ] = buffer.get(); - channelBuffer[ channelBufferPointer++ ] = buffer.get(); - } - - if( buffer.hasRemaining() ) - { - buffer.get(); - buffer.get(); - } - } - - return ByteBuffer.wrap( channelBuffer ).order( ByteOrder.LITTLE_ENDIAN ); - } -} diff --git a/src/controller/BaseNode.java b/src/controller/BaseNode.java deleted file mode 100644 index 89bbdfa6..00000000 --- a/src/controller/BaseNode.java +++ /dev/null @@ -1,196 +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 - ******************************************************************************/ -package controller; - -import java.awt.Color; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.TreeNode; - -public class BaseNode extends DefaultMutableTreeNode - implements Comparable -{ - private static final long serialVersionUID = 1L; - - /** - * Model backing the JTree and nodes. Since we're primarily interacting - * with the model via JTree tree nodes via context menus, injecting a - * reference to the model facilitates nodal access to the model's methods - */ - protected ConfigurationControllerModel mModel = null; - - /** - * Implements a base node that has a reference to the underlying - * tree data model to facilitate tree node operations from nodal context - * menus. - */ - public BaseNode( Object object ) - { - super( object ); - } - - /** - * Override this method to return a custom icon for the node - * @return - */ - public String getIconPath() - { - return null; - } - - public Color getBackgroundColor() - { - return null; - } - - public String toString() - { - if( getUserObject() != null ) - { - return (String)getUserObject(); - } - else - { - return "SDRTrunk"; - } - } - - public JPanel getEditor() - { - return new EmptyEditor(); - } - - /** - * Comparator to support nodal sorting/ordering - */ - @Override - public int compareTo( BaseNode node ) - { - return toString().compareTo( node.toString() ); - } - - /** - * This method only needs to be called once on the root node and all nodes - * in the tree will then have a reference to the model via getModel() - */ - public void setModel( ConfigurationControllerModel model ) - { - mModel = model; - } - - /** - * Returns reference to the SystemTreeModel. Only ConfigurableNode types - * or children are allowed in this tree, so this method assumes that the - * node or parent is of ConfigurableNode class or subclass. - * - * Reference to the model is required to enable node context operations - * like adding/deleting/stopping/starting. - */ - public ConfigurationControllerModel getModel() - { - if( mModel == null ) - { - TreeNode parent = getParent(); - - if( parent != null ) - { - return ((BaseNode)parent).getModel(); - } - else - { - return null; - } - } - else - { - return mModel; - } - } - - /** - * Supports a context menu for any children of the BaseNode. Override the - * method in the child node to provide a custom context menu for that node. - */ - public JPopupMenu getContextMenu() - { - return null; - } - - /** - * Invokes a recursive delete() on all children to perform any cleanup - * operations prior to deleting. Override this method to perform cleanup - * operations specific to the node. Remember to also either invoke - * super.delete(), or specifically call .delete() on any children of the - * node with the custom delete method. - */ - public void delete() - { - for( int x = 0; x < getChildCount(); x++ ) - { - BaseNode node = (BaseNode)getChildAt( x ); - - //Tell the node to delete its children - node.delete(); - - //Delete the child node - getModel().deleteNode( node ); - } - } - - public void show() - { - getModel().showNode( this ); - } - - public void refresh() - { - if( getModel() != null ) - { - getModel().nodeChanged( this ); - } - } - - public void sort() - { - List nodesToSort = new ArrayList(); - - while( children().hasMoreElements() ) - { - BaseNode child = (BaseNode)children().nextElement(); - - nodesToSort.add( child ); - - getModel().removeNodeFromParent( child ); - } - - if( !nodesToSort.isEmpty() ) - { - Collections.sort( nodesToSort ); - - for( BaseNode node: nodesToSort ) - { - getModel().insertNodeInto( node, this, getChildCount() ); - } - } - } -} diff --git a/src/controller/ConfigurableNode.java b/src/controller/ConfigurableNode.java deleted file mode 100644 index f9a6e3be..00000000 --- a/src/controller/ConfigurableNode.java +++ /dev/null @@ -1,44 +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 - ******************************************************************************/ -package controller; - - -public abstract class ConfigurableNode extends BaseNode -{ - private static final long serialVersionUID = 1L; - - public ConfigurableNode( Object object ) - { - super( object ); - } - - /** - * Saves changes to the node - */ - public void save() - { - getModel().getResourceManager().getPlaylistManager().save(); - - BaseNode parent = (BaseNode)getParent(); - - if( parent instanceof ConfigurableNode ) - { - ((ConfigurableNode)parent).sort(); - } - } -} diff --git a/src/controller/ConfigurationControllerModel.java b/src/controller/ConfigurationControllerModel.java deleted file mode 100644 index 99115842..00000000 --- a/src/controller/ConfigurationControllerModel.java +++ /dev/null @@ -1,285 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,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 - ******************************************************************************/ -package controller; - -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; - -import javax.swing.JTree; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.MutableTreeNode; -import javax.swing.tree.TreePath; - -import map.IconManager; -import module.decode.DecoderType; -import module.decode.config.DecodeConfigFactory; -import playlist.PlaylistNode; -import source.config.SourceConfigTuner; -import source.tuner.Tuner; -import source.tuner.TunerGroupNode; -import source.tuner.TunerNode; -import source.tuner.TunerSelectionListener; -import controller.channel.Channel; -import controller.channel.ChannelEvent; -import controller.channel.ChannelEvent.Event; -import controller.channel.ChannelNode; -import controller.site.Site; -import controller.system.System; -import controller.system.SystemListNode; -import controller.system.SystemNode; - -public class ConfigurationControllerModel extends DefaultTreeModel -{ - private static final long serialVersionUID = 1L; - - private JTree mTree; - - private ResourceManager mResourceManager; - - private ArrayList mTunerSelectionListeners = - new ArrayList(); - - private PlaylistNode mPlaylistNode; - private TunerGroupNode mTunerGroupNode; -// private RecordingGroupNode mRecordingGroupNode; - - private IconManager mIconManagerFrame; - - /** - * Implements a merged system controller and (tree) model. The user will - * primarily interact with the system nodes via the tree view, so from a - * design perspective, the model and the control are necessarily merged to - * work well with the JTree as the system view. - */ - public ConfigurationControllerModel( ResourceManager resourceManager ) - { - super( new BaseNode( null ) ); - - mResourceManager = resourceManager; - - /** - * Give the root node a reference to this model, so that all nodes can - * invoke add/delete operations on the tree via context menu - */ - ((BaseNode)getRoot()).setModel( this ); - } - - public JTree getTree() - { - return mTree; - } - - public void showIconManager() - { - if( mIconManagerFrame == null ) - { - mIconManagerFrame = new IconManager( mResourceManager, mTree ); - - mResourceManager.getSettingsManager().addListener( mIconManagerFrame ); - } - - if( !mIconManagerFrame.isVisible() ) - { - mIconManagerFrame.setVisible( true ); - } - - mIconManagerFrame.toFront(); - } - - /** - * init() gets invoked manually/externally, after all of the references - * are established in the constructor - */ - public void init() - { - /** - * Add the tuner group node - */ - mTunerGroupNode = new TunerGroupNode(); - insertNodeInto( mTunerGroupNode, (MutableTreeNode)root, 0 ); - addTuners( mTunerGroupNode ); - - /** - * Add the recording group node - */ -// mRecordingGroupNode = new RecordingGroupNode(); -// insertNodeInto( mRecordingGroupNode, (MutableTreeNode)root, 1 ); -// mRecordingGroupNode.loadRecordings(); - - /** - * Add the playlist node - */ - mPlaylistNode = new PlaylistNode(); - insertNodeInto( mPlaylistNode, (MutableTreeNode)root, 1 ); - mPlaylistNode.loadPlaylist(); - } - - public void showNode( DefaultMutableTreeNode node ) - { - if( mTree != null && node != null ) - { - mTree.setSelectionPath( new TreePath( node.getPath() ) ); - } - } - - public ResourceManager getResourceManager() - { - return mResourceManager; - } - - public void setTree( JTree tree ) - { - mTree = tree; - } - - private void addTuners( TunerGroupNode parent ) - { - List tuners = mResourceManager.getTunerManager().getTuners(); - - for( Tuner tuner: tuners ) - { - TunerNode child = new TunerNode( tuner ); - - insertNodeInto( child, parent, parent.getChildCount() ); - - parent.sort(); - - show( child ); - } - - } - - /** - * Convenience method to add a new default system, site and channel to - * the tree - */ - public void createChannel( long frequency, DecoderType decoder ) - { - System system = new System(); - - Site site = new Site(); - system.addSite( site ); - - Channel channel = new Channel(); - channel.setDecodeConfiguration( - DecodeConfigFactory.getDecodeConfiguration( decoder ) ); - SourceConfigTuner sourceConfig = new SourceConfigTuner(); - sourceConfig.setFrequency( frequency ); - channel.setSourceConfiguration( sourceConfig ); - mResourceManager.getChannelManager().channelChanged( new ChannelEvent( channel, Event.CHANNEL_ADDED ) ); - channel.setEnabled( true, true ); - site.addChannel( channel ); - - mPlaylistNode.getPlaylist().getSystemList().addSystem( system ); - - SystemNode systemNode = new SystemNode( system ); - SystemListNode systemListNode = mPlaylistNode.getSystemListNode(); - addNode( systemNode, systemListNode, systemListNode.getChildCount() ); - systemNode.init(); - - systemNode.save(); - } - - public void addNode( BaseNode child, BaseNode parent, int index ) - { - insertNodeInto( child, parent, index ); - show( child ); - } - - public void deleteNode( BaseNode node ) - { - node.delete(); - } - - private void show( BaseNode node ) - { - if( mTree != null ) - { - mTree.scrollPathToVisible( new TreePath( node.getPath() ) ); - } - } - - public void addListener( TunerSelectionListener listener ) - { - mTunerSelectionListeners.add( listener ); - } - - public void removeListener( TunerSelectionListener listener ) - { - mTunerSelectionListeners.remove( listener ); - } - - /** - * Notifies registered listeners that the tuner has been selected - */ - public void fireTunerSelectedEvent( Tuner tuner ) - { - Iterator it = - mTunerSelectionListeners.iterator(); - - while( it.hasNext() ) - { - it.next().tunerSelected( tuner ); - } - } - - /** - * Fires a tuner selection event for the first tuner listed in the tree. - * This is mainly used on application startup to show the spectral display - * of the first tuner in the spectrum panel. - */ - public void fireFirstTunerSelected() - { - if( mTunerGroupNode.getChildCount() > 0 ) - { - TunerNode node = (TunerNode)mTunerGroupNode.getChildAt( 0 ); - - fireTunerSelectedEvent( node.getTuner() ); - } - } - - public ArrayList getCurrentChannelConfigs() - { - return getChannelConfigs( mPlaylistNode ); - } - - private ArrayList getChannelConfigs( DefaultMutableTreeNode parent ) - { - ArrayList configs = new ArrayList(); - - Enumeration nodes = parent.children(); - - while( nodes.hasMoreElements() ) - { - DefaultMutableTreeNode node = nodes.nextElement(); - - if( node instanceof ChannelNode ) - { - configs.add( ((ChannelNode)node).getChannel() ); - } - - //Recursive call - configs.addAll( getChannelConfigs( node ) ); - } - - return configs; - } -} diff --git a/src/controller/ConfigurationEditor.java b/src/controller/ConfigurationEditor.java deleted file mode 100644 index 84a99a30..00000000 --- a/src/controller/ConfigurationEditor.java +++ /dev/null @@ -1,74 +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 - ******************************************************************************/ -package controller; - - -import java.awt.BorderLayout; - -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTree; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; - -public class ConfigurationEditor extends JPanel - implements TreeSelectionListener -{ - private static final long serialVersionUID = 1L; - - /** - * Host for configuration editor display panels - */ - public ConfigurationEditor() - { - init(); - } - - public void init() - { - setPanel( new EmptyEditor() ); - } - - public void setPanel( JPanel display ) - { - removeAll(); - - setLayout( new BorderLayout() ); - - JScrollPane scroll = new JScrollPane( display ); - - add( scroll, BorderLayout.CENTER ); - - revalidate(); - repaint(); - } - - @Override - public void valueChanged( TreeSelectionEvent e ) - { - JTree tree = (JTree)e.getSource(); - - BaseNode node = - (BaseNode)tree.getLastSelectedPathComponent(); - - if( node != null ) - { - setPanel( node.getEditor() ); - } - } -} diff --git a/src/controller/ConfigurationTreePanel.java b/src/controller/ConfigurationTreePanel.java deleted file mode 100644 index 9644f1b8..00000000 --- a/src/controller/ConfigurationTreePanel.java +++ /dev/null @@ -1,117 +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 - ******************************************************************************/ -package controller; - -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; - -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; -import javax.swing.JTree; -import javax.swing.SwingUtilities; -import javax.swing.event.TreeSelectionListener; -import javax.swing.tree.TreeSelectionModel; - -import net.miginfocom.swing.MigLayout; - -public class ConfigurationTreePanel extends JPanel -{ - private static final long serialVersionUID = 1L; - - protected JTree mSystemTree; - protected ConfigurationControllerModel mTreeModel; - - public ConfigurationTreePanel( ConfigurationControllerModel model ) - { - mTreeModel = model; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill][]") ); - - mSystemTree = new JTree( mTreeModel ); - mSystemTree.getSelectionModel().setSelectionMode( - TreeSelectionModel.SINGLE_TREE_SELECTION ); - mSystemTree.setShowsRootHandles( true ); - mSystemTree.addMouseListener( new MouseHandler() ); - mSystemTree.setCellRenderer( new ControllerTreeCellRenderer() ); - - //Give the model a reference to the tree, so that it can force newly - //added nodes to be displayed - mTreeModel.setTree( mSystemTree ); - - JScrollPane treeScroll = new JScrollPane( mSystemTree ); - add( treeScroll, "cell 0 0,span,grow" ); - } - - public void addTreeSelectionListener( TreeSelectionListener listener ) - { - mSystemTree.addTreeSelectionListener( listener ); - } - - public class MouseHandler implements MouseListener - { - public MouseHandler() - { - } - - @Override - public void mouseClicked( MouseEvent event ) - { - if( SwingUtilities.isRightMouseButton( event ) ) - { - int row = mSystemTree.getRowForLocation( event.getX(), - event.getY() ); - - if( row != -1 ) - { - mSystemTree.setSelectionRow( row ); - - Object selectedNode = - mSystemTree.getLastSelectedPathComponent(); - - if( selectedNode instanceof BaseNode ) - { - JPopupMenu contextMenu = - ((BaseNode)selectedNode).getContextMenu(); - - if( contextMenu != null ) - { - contextMenu.show( mSystemTree, - event.getX(), - event.getY() ); - } - } - } - } - } - - @Override - public void mousePressed( MouseEvent e ){} - @Override - public void mouseReleased( MouseEvent e ){} - @Override - public void mouseEntered( MouseEvent e ){} - @Override - public void mouseExited( MouseEvent e ){} - } -} diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index cb409dda..1243b164 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,63 +33,75 @@ import module.decode.event.CallEventPanel; import module.decode.event.MessageActivityPanel; import module.decode.state.ChannelList; import net.miginfocom.swing.MigLayout; -import playlist.PlaylistManager; import settings.SettingsManager; +import source.SourceManager; +import source.tuner.TunerModel; +import source.tuner.TunerViewPanel; import spectrum.ChannelSpectrumPanel; +import alias.AliasController; +import alias.AliasModel; import audio.AudioManager; import audio.AudioPanel; import com.jidesoft.swing.JideSplitPane; import com.jidesoft.swing.JideTabbedPane; -import controller.channel.ChannelManager; +import controller.channel.ChannelController; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; +import controller.channel.map.ChannelMapModel; public class ControllerPanel extends JPanel { private static final long serialVersionUID = 1L; - private ChannelList mChannelStateList; - - private CallEventPanel mCallEventPanel; - - private MessageActivityPanel mMessageActivityPanel = - new MessageActivityPanel(); - - private ChannelSpectrumPanel mChannelSpectrumPanel; - - private JideTabbedPane mTabbedPane; - - protected ConfigurationTreePanel mSystemControlViewPanel; - protected ConfigurationEditor mConfigurationEditor; - protected JideSplitPane mSystemControlSplitPane; - - protected JTable mChannelActivityTable = new JTable(); + private AliasController mAliasController; private AudioPanel mAudioPanel; + private CallEventPanel mCallEventPanel; + private ChannelController mChannelController; + private ChannelList mChannelStateList; + private ChannelModel mChannelModel; + private ChannelSpectrumPanel mChannelSpectrumPanel; private MapPanel mMapPanel; + private MessageActivityPanel mMessageActivityPanel; + private TunerViewPanel mTunerManagerPanel; - private ChannelManager mChannelManager; - private ConfigurationControllerModel mController; - protected SettingsManager mSettingsManager; + private JideTabbedPane mTabbedPane; + protected JTable mChannelActivityTable = new JTable(); public ControllerPanel( AudioManager audioManager, - ConfigurationControllerModel controller, - ChannelManager channelManager, + AliasModel aliasModel, + ChannelModel channelModel, + ChannelMapModel channelMapModel, + ChannelProcessingManager channelProcessingManager, MapService mapService, - PlaylistManager playlistManager, - SettingsManager settingsManager ) + SettingsManager settingsManager, + SourceManager sourceManager, + TunerModel tunerModel ) { - mChannelManager = channelManager; - mController = controller; - mSettingsManager = settingsManager; + mChannelModel = channelModel; - mAudioPanel = new AudioPanel( mSettingsManager, audioManager ); + mAudioPanel = new AudioPanel( settingsManager, sourceManager, audioManager ); - mMapPanel = new MapPanel( mapService, mSettingsManager, mChannelManager ); + mMapPanel = new MapPanel( mapService, settingsManager ); - mCallEventPanel = new CallEventPanel( mSettingsManager ); - - mChannelStateList = new ChannelList( playlistManager, mSettingsManager ); + mMessageActivityPanel = new MessageActivityPanel( channelProcessingManager ); + + mCallEventPanel = new CallEventPanel( settingsManager, channelProcessingManager ); + mChannelSpectrumPanel = new ChannelSpectrumPanel( settingsManager, + channelProcessingManager ); + + mChannelStateList = new ChannelList( channelModel, channelProcessingManager, + settingsManager, mAudioPanel ); + + mChannelController = new ChannelController( channelModel, channelMapModel, + sourceManager, aliasModel ); + + mAliasController = new AliasController( aliasModel, settingsManager ); + + mTunerManagerPanel = new TunerViewPanel( tunerModel ); + init(); } @@ -99,27 +111,17 @@ public class ControllerPanel extends JPanel "[grow,fill]", "[grow,fill]") ); - //System Configuration View and Editor - mConfigurationEditor = new ConfigurationEditor(); - - mSystemControlViewPanel = new ConfigurationTreePanel( mController ); - mSystemControlViewPanel.addTreeSelectionListener( mConfigurationEditor ); - - mSystemControlSplitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); - mSystemControlSplitPane.setDividerSize( 5 ); - mSystemControlSplitPane.add( mSystemControlViewPanel ); - mSystemControlSplitPane.add( mConfigurationEditor ); - - mChannelSpectrumPanel = new ChannelSpectrumPanel( mSettingsManager ); - //Tabbed View - configuration, calls, messages, map mTabbedPane = new JideTabbedPane(); mTabbedPane.setFont( this.getFont() ); mTabbedPane.setForeground( Color.BLACK ); - mTabbedPane.addTab( "Configuration", mSystemControlSplitPane ); + mTabbedPane.addTab( "Tuners", mTunerManagerPanel ); + mTabbedPane.addTab( "Channels", mChannelController ); + mTabbedPane.addTab( "Aliases", mAliasController ); mTabbedPane.addTab( "Channel Spectrum", mChannelSpectrumPanel ); mTabbedPane.addTab( "Events", mCallEventPanel ); mTabbedPane.addTab( "Messages", mMessageActivityPanel ); + mTabbedPane.addTab( "Map", mMapPanel ); /** * Change listener to enable/disable the channel spectrum display @@ -145,38 +147,31 @@ public class ControllerPanel extends JPanel } } ); - /** - * Add mapping services and map panel to a new tab - */ - mTabbedPane.addTab( "Map", mMapPanel ); - /* Register each of the components to receive channel events when the * channels are selected or change */ - mChannelManager.addListener( mCallEventPanel ); - mChannelManager.addListener( mChannelStateList ); - mChannelManager.addListener( mChannelSpectrumPanel ); - mChannelManager.addListener( mMessageActivityPanel ); + mChannelModel.addListener( mCallEventPanel ); + mChannelModel.addListener( mChannelStateList ); + mChannelModel.addListener( mChannelSpectrumPanel ); + mChannelModel.addListener( mMessageActivityPanel ); JScrollPane channelStateListScroll = new JScrollPane(); channelStateListScroll.getViewport().setView( mChannelStateList ); - channelStateListScroll.setPreferredSize( new Dimension( 200, 300 ) ); + channelStateListScroll.setPreferredSize( new Dimension( 400, 500 ) ); +// JideSplitPane audioChannelListSplit = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT ); +// audioChannelListSplit.setDividerSize( 5 ); +// audioChannelListSplit.add( mAudioPanel ); +// audioChannelListSplit.add( channelStateListScroll ); - JideSplitPane audioChannelListSplit = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT ); - audioChannelListSplit.setDividerSize( 5 ); - audioChannelListSplit.add( mAudioPanel ); - audioChannelListSplit.add( channelStateListScroll ); + //Set preferred size to influence the split between these panels +// audioChannelListSplit.setPreferredSize( new Dimension( 400, 500 ) ); + mTabbedPane.setPreferredSize( new Dimension( 880, 500 ) ); JideSplitPane channelSplit = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); channelSplit.setDividerSize( 5 ); - channelSplit.add( audioChannelListSplit ); + channelSplit.add( channelStateListScroll ); channelSplit.add( mTabbedPane ); add( channelSplit ); } - - public ConfigurationControllerModel getController() - { - return mController; - } } diff --git a/src/controller/ControllerTreeCellRenderer.java b/src/controller/ControllerTreeCellRenderer.java deleted file mode 100644 index 42a791d4..00000000 --- a/src/controller/ControllerTreeCellRenderer.java +++ /dev/null @@ -1,127 +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 - ******************************************************************************/ -package controller; - -import java.awt.Color; -import java.awt.Component; -import java.awt.Image; -import java.util.HashMap; - -import javax.swing.ImageIcon; -import javax.swing.JTree; -import javax.swing.tree.DefaultTreeCellRenderer; - -public class ControllerTreeCellRenderer extends DefaultTreeCellRenderer -{ - private static final long serialVersionUID = 1L; - - public static final int MAX_ICON_HEIGHT = 16; - private HashMap mIcons = new HashMap(); - - public ControllerTreeCellRenderer() - { - } - - @Override - public Component getTreeCellRendererComponent( JTree tree, - Object value, boolean selected, boolean expanded, boolean isLeaf, - int row, boolean focused ) - { - Component c = super.getTreeCellRendererComponent( tree, value, selected, - expanded, isLeaf, row, focused ); - - if( value instanceof BaseNode ) - { - BaseNode node = (BaseNode)value; - - ImageIcon icon = getIcon( node ); - - setIcon( icon ); - - Color background = node.getBackgroundColor(); - - if( background != null ) - { - setBackground( background ); - setOpaque( true ); - - if( selected ) - { - if( background == Color.CYAN ) - { - setForeground( Color.ORANGE ); - } - else if( background == Color.GREEN ) - { - setForeground( Color.MAGENTA ); - } - else if( background == Color.RED ) - { - setForeground( Color.YELLOW ); - } - } - } - else - { - setOpaque( false ); - } - } - - return c; - } - - private ImageIcon getIcon( BaseNode node ) - { - ImageIcon icon = null; - - String filepath = node.getIconPath(); - - if( filepath != null ) - { - if( mIcons.containsKey( filepath ) ) - { - icon = mIcons.get( filepath ); - } - else - { - icon = new ImageIcon( filepath ); - - if( icon.getIconHeight() > MAX_ICON_HEIGHT ) - { - Image image = icon.getImage(); - - double scale = (double)icon.getIconHeight() / (double)MAX_ICON_HEIGHT; - - int scaledWidth = (int)( (double)icon.getIconWidth() / scale ); - - Image scaledImage = image.getScaledInstance( scaledWidth, - MAX_ICON_HEIGHT, java.awt.Image.SCALE_SMOOTH ); - - mIcons.put( filepath, new ImageIcon( scaledImage ) ); - } - else - { - mIcons.put( filepath, icon ); - } - - } - } - - return icon; - } -} diff --git a/src/controller/Editor.java b/src/controller/Editor.java deleted file mode 100644 index 56411ce1..00000000 --- a/src/controller/Editor.java +++ /dev/null @@ -1,51 +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 - ******************************************************************************/ -package controller; - -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; -import controller.channel.ChannelValidationException; - -public abstract class Editor extends JPanel -{ - private static final long serialVersionUID = 1L; - - public Editor() - { - setLayout( new MigLayout() ); - } - - public abstract void save(); - - public abstract void reset(); - - /** - * Override this method to allow the editor to inspect the argument for - * valid configuration - * - * @param editor - any editor - * - * @throws ChannelValidationException if the configuration is not compatible - * with this editor's configuration. Supplies a validation text value for - * display to the user - */ - public void validate( Editor editor ) throws ChannelValidationException - { - } -} diff --git a/src/controller/EmptyEditor.java b/src/controller/EmptyEditor.java deleted file mode 100644 index 83f21de3..00000000 --- a/src/controller/EmptyEditor.java +++ /dev/null @@ -1,59 +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 - ******************************************************************************/ -package controller; - -import java.awt.BorderLayout; -import java.awt.Dimension; - -import javax.swing.JLabel; -import javax.swing.JPanel; - -public class EmptyEditor extends JPanel -{ - private static final long serialVersionUID = 1L; - - public EmptyEditor() - { - setLayout( new BorderLayout() ); - setPreferredSize( new Dimension( 200, (int)getPreferredSize().getHeight() ) ); - - String text = "" - + "

    SDR Trunk

    " - + "
    " - + "

    Use the tree structure to the left to configure SDRTrunk" - + " to use multiple SDR tuners and sound card sources to" - + " decode multiple trunked and digital radio channels.

    " - + "
    " - + "

    Each tree branch can be accessed by clicking on the branch" - + " and a configuration editor will appear here.

    " - + "
    " - + "

    Many of the branches contain context sensitive menus that " - + " you can access by right-clicking on the node.

    " - + "
    " - + "

    To begin, right-click on the 'Systems' branch under 'Playlist'" - + "and create a new 'System'. Right-click the 'System' node to create " - + "a new 'Site'. Right-click on the 'Site' node to create a new Channel.

    " - + "
    " - + "

    In the new channel, select a channel source and decoder " - + "and select event logging and recording options. Check 'Enabled'" - + " and hit the 'Save' button to begin decoding.

    " - + ""; - - add( new JLabel( text ), BorderLayout.NORTH ); - } -} diff --git a/src/controller/ResourceManager.java b/src/controller/ResourceManager.java deleted file mode 100644 index 91256fda..00000000 --- a/src/controller/ResourceManager.java +++ /dev/null @@ -1,183 +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 - ******************************************************************************/ -package controller; - -import map.MapService; -import module.log.EventLogManager; -import playlist.PlaylistManager; -import record.RecorderManager; -import settings.SettingsManager; -import source.SourceManager; -import source.recording.RecordingSourceManager; -import source.tuner.TunerManager; -import audio.AudioManager; -import controller.channel.ChannelManager; - -/** - * Manager for system wide resources - */ -public class ResourceManager -{ - private AudioManager mAudioManager; - private ChannelManager mChannelManager; - private EventLogManager mEventLogManager; - private PlaylistManager mPlaylistManager; - private RecorderManager mRecorderManager; - private RecordingSourceManager mRecordingSourceManager; - private SettingsManager mSettingsManager; - private SourceManager mSourceManager; - private TunerManager mTunerManager; - private ConfigurationControllerModel mController; - private MapService mMapService; - private ThreadPoolManager mThreadPoolManager; - - public ResourceManager() - { - this( new SettingsManager(), - new PlaylistManager(), - new EventLogManager() ); - } - - public ResourceManager( SettingsManager settingsManager, - PlaylistManager playlistManager, - EventLogManager eventLogManager ) - { - mSettingsManager = settingsManager; - mPlaylistManager = playlistManager; - mEventLogManager = eventLogManager; - - /** - * ChannelManager requires a reference to the ResourceManager so that - * channel processing chains can access system wide resources - */ - mThreadPoolManager = new ThreadPoolManager(); - mAudioManager = new AudioManager( mThreadPoolManager ); - mRecorderManager = new RecorderManager( mThreadPoolManager ); - mChannelManager = new ChannelManager( this ); - mSourceManager = new SourceManager( this ); - mRecordingSourceManager = new RecordingSourceManager( this ); - mTunerManager = new TunerManager( this ); - mController = new ConfigurationControllerModel( this ); - mMapService = new MapService( this ); - } - - public AudioManager getAudioManager() - { - return mAudioManager; - } - - public void setAudioManager( AudioManager audioManager ) - { - mAudioManager = audioManager; - } - - public ConfigurationControllerModel getController() - { - return mController; - } - - public ThreadPoolManager getThreadPoolManager() - { - return mThreadPoolManager; - } - - public void setController( ConfigurationControllerModel controller ) - { - mController = controller; - } - - public TunerManager getTunerManager() - { - return mTunerManager; - } - - public MapService getMapService() - { - return mMapService; - } - - public SettingsManager getSettingsManager() - { - return mSettingsManager; - } - - public void setSettingsManager( SettingsManager manager ) - { - mSettingsManager = manager; - } - - public PlaylistManager getPlaylistManager() - { - return mPlaylistManager; - } - - public void setPlaylistManager( PlaylistManager aliasManager ) - { - mPlaylistManager = aliasManager; - } - - public EventLogManager getEventLogManager() - { - return mEventLogManager; - } - - public void setEventLogManager( EventLogManager eventLogManager ) - { - mEventLogManager = eventLogManager; - } - - public RecorderManager getRecorderManager() - { - return mRecorderManager; - } - - public void setRecorderManager( RecorderManager recorderManager ) - { - mRecorderManager = recorderManager; - } - - public SourceManager getSourceManager() - { - return mSourceManager; - } - - public void setSourceManager( SourceManager sourceManager ) - { - mSourceManager = sourceManager; - } - - public RecordingSourceManager getRecordingSourceManager() - { - return mRecordingSourceManager; - } - - public void setRecordingSourceManager( RecordingSourceManager manager ) - { - mRecordingSourceManager = manager; - } - - public ChannelManager getChannelManager() - { - return mChannelManager; - } - - public void setChannelManager( ChannelManager channelManager ) - { - mChannelManager = channelManager; - } -} diff --git a/src/controller/ThreadPoolManager.java b/src/controller/ThreadPoolManager.java index 0ca9a00b..ca993295 100644 --- a/src/controller/ThreadPoolManager.java +++ b/src/controller/ThreadPoolManager.java @@ -36,20 +36,29 @@ public class ThreadPoolManager private HashMap,ThreadType> mTasks = new HashMap,ThreadType>(); - private ScheduledExecutorService mExecutor = - Executors.newScheduledThreadPool( THREAD_POOL_SIZE, - new NamingThreadFactory( "sdrtrunk" ) ); + private ScheduledExecutorService mExecutor; public ThreadPoolManager() { } + public ScheduledExecutorService getScheduledExecutorService() + { + return mExecutor; + } + public ScheduledFuture scheduleFixedRate( ThreadType type, Runnable command, long period, TimeUnit unit ) throws RejectedExecutionException { + if( mExecutor == null ) + { + mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE, + new NamingThreadFactory( "sdrtrunk" ) ); + } + ScheduledFuture task = mExecutor.scheduleAtFixedRate( command, 0, period, unit ); @@ -61,6 +70,12 @@ public class ThreadPoolManager public void scheduleOnce( Runnable command, long delay, TimeUnit unit ) throws RejectedExecutionException { + if( mExecutor == null ) + { + mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE, + new NamingThreadFactory( "sdrtrunk" ) ); + } + mExecutor.schedule( command, delay, unit ); } diff --git a/src/controller/channel/AbstractChannelEditor.java b/src/controller/channel/AbstractChannelEditor.java deleted file mode 100644 index 41c3b4aa..00000000 --- a/src/controller/channel/AbstractChannelEditor.java +++ /dev/null @@ -1,42 +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 - ******************************************************************************/ -package controller.channel; - -import controller.Editor; - -public abstract class AbstractChannelEditor extends Editor -{ - private static final long serialVersionUID = 1L; - - protected ChannelNode mChannelNode; - - public AbstractChannelEditor( ChannelNode channel ) - { - super(); - mChannelNode = channel; - } - - public ChannelNode getChannelNode() - { - return mChannelNode; - } - - public abstract void save(); - - public abstract void reset(); -} diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java index 2b150c4f..81ced518 100644 --- a/src/controller/channel/Channel.java +++ b/src/controller/channel/Channel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,41 +17,20 @@ ******************************************************************************/ package controller.channel; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; - import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.bind.annotation.XmlTransient; -import message.Message; -import module.Module; -import module.ProcessingChain; import module.decode.DecoderFactory; +import module.decode.DecoderType; import module.decode.config.AuxDecodeConfiguration; -import module.decode.config.DecodeConfigFactory; import module.decode.config.DecodeConfiguration; -import module.decode.event.CallEventModel; -import module.decode.event.MessageActivityModel; -import module.decode.state.ChannelState; -import module.log.MessageEventLogger; +import module.log.EventLogType; import module.log.config.EventLogConfiguration; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import record.RecorderManager; import record.RecorderType; import record.config.RecordConfiguration; -import sample.Listener; -import source.Source; -import source.SourceException; import source.SourceType; import source.config.SourceConfigFactory; import source.config.SourceConfigRecording; @@ -59,43 +38,23 @@ import source.config.SourceConfigTuner; import source.config.SourceConfiguration; import source.tuner.TunerChannel; import source.tuner.TunerChannel.Type; -import alias.AliasList; -import audio.metadata.Metadata; -import audio.metadata.MetadataType; -import controller.ResourceManager; -import controller.channel.ChannelEvent.Event; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeEvent.Event; +import source.tuner.frequency.IFrequencyChangeProcessor; import controller.config.Configuration; -import controller.site.Site; -import controller.system.System; -import filter.FilterSet; @XmlSeeAlso( { Configuration.class } ) @XmlRootElement( name = "channel" ) -public class Channel extends Configuration +public class Channel extends Configuration implements IFrequencyChangeProcessor { - private final static Logger mLog = LoggerFactory.getLogger( Channel.class ); + // Standard channels are persisted and traffic channels are temporary + public enum ChannelType { STANDARD, TRAFFIC }; + - private static final boolean ENABLED = true; - private static final boolean DISABLED = false; - private static final boolean BROADCAST_CHANGE = true; + // Static unique channel identifier tracking + private static int UNIQUE_ID = 0; - public enum ChannelType - { - STANDARD, - TRAFFIC - }; - - - private CopyOnWriteArrayList mChannelListeners = - new CopyOnWriteArrayList(); - - private CopyOnWriteArrayList> mMessageListeners = - new CopyOnWriteArrayList>(); - - private Map mTrafficChannels = new HashMap<>(); - - private DecodeConfiguration mDecodeConfiguration = - DecodeConfigFactory.getDefaultDecodeConfiguration(); + private DecodeConfiguration mDecodeConfiguration = DecoderFactory.getDefaultDecodeConfiguration(); private AuxDecodeConfiguration mAuxDecodeConfiguration = new AuxDecodeConfiguration(); private SourceConfiguration mSourceConfiguration = @@ -104,134 +63,107 @@ public class Channel extends Configuration new EventLogConfiguration(); private RecordConfiguration mRecordConfiguration = new RecordConfiguration(); - private ResourceManager mResourceManager; private String mAliasListName; - private System mSystem; - private Site mSite; - private String mName; + private String mSystem = "System"; + private String mSite = "Site"; + private String mName = "Channel"; + private boolean mEnabled; private boolean mSelected; - private ChannelType mChannelType; - - private CallEventModel mCallEventModel; - private MessageActivityModel mMessageActivityModel; - private MessageEventLogger mEventLogger; - private ProcessingChain mProcessingChain; + private TunerChannel mTunerChannel = null; - private ChannelState mChannelState; - private long mTrafficChannelTimeout = - DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS; + private ChannelType mChannelType = ChannelType.STANDARD; - /** - * Constructs a new standard channel with a default name of "New Channel" - */ - public Channel() - { - this( "New Channel", ChannelType.STANDARD ); - } + private int mChannelID; + private int mChannelFrequencyCorrection = 0; /** - * Constructs a new standard channel with the specified channel name - */ - public Channel( String channelName ) - { - this( channelName, ChannelType.STANDARD ); - } - - /** - * Constructs a new channel with the specified channel name and type. + * Channel represents a complete set of configurations needed to setup and + * manage a processing chain and/or manage as a set of business objects that + * can be displayed in graphical components. * * @param channelName * @param channelType */ public Channel( String channelName, ChannelType channelType ) { - mName = channelName; + this( channelName ); mChannelType = channelType; } - + /** - * Indicates if the channel has Automatic or Direct Frequency Control + * Constructs a new standard channel with the specified channel name */ - public boolean hasFrequencyControl() + public Channel( String channelName ) { - return mProcessingChain != null && - mProcessingChain.hasFrequencyCorrectionControl(); + this(); + mName = channelName; } - - public long getFrequencyCorrection() + + /** + * Constructs a new standard channel with a default name of "Channel" + */ + public Channel() { - if( hasFrequencyControl() ) + mChannelID = UNIQUE_ID++; + } + + /** + * Creates a (new) deep copy of this channel + */ + public Channel copyOf() + { + Channel channel = new Channel( mName ); + channel.setSystem( mSystem ); + channel.setSite( mSite ); + channel.setAliasListName( mAliasListName ); + + AuxDecodeConfiguration aux = new AuxDecodeConfiguration(); + + for( DecoderType auxType: aux.getAuxDecoders() ) { - return mProcessingChain.getFrequencyCorrectionControl().getErrorCorrection(); + aux.addAuxDecoder( auxType ); } - return 0; - } - - /** - * Sets the channel state traffic channel call end timer to the specified - * value. - */ - public void setTrafficChannelTimeout( long milliseconds ) - { - mTrafficChannelTimeout = milliseconds; + channel.setAuxDecodeConfiguration( aux ); - if( mChannelState != null ) - { - mChannelState.setTrafficChannelTimeout( milliseconds ); - } - } - - /** - * Sets the resource manager responsible for this channel and registers - * the channel manager as a listener to this channel. Fires channel add - * channel event to the newly added channel manager channel listener. - * - * Note: resource manager instance is applied after object construction - * since we construct the channel objects from the xml playlist - */ - public void setResourceManager( ResourceManager resourceManager ) - { - mResourceManager = resourceManager; - - /* Add system-wide channel listeners onto this channel */ - addListeners( mResourceManager.getChannelManager().getChannelListeners() ); + channel.setDecodeConfiguration( DecoderFactory.copy( mDecodeConfiguration ) ); - /* If we're enabled, fire enable changed event to get processing started */ - if( mEnabled ) + EventLogConfiguration log = new EventLogConfiguration(); + + for( EventLogType logType: mEventLogConfiguration.getLoggers() ) { - enableChanged(); + log.addLogger( logType ); } + + channel.setEventLogConfiguration( log ); + + RecordConfiguration record = new RecordConfiguration(); + + for( RecorderType recordType: mRecordConfiguration.getRecorders() ) + { + record.addRecorder( recordType ); + } + + channel.setRecordConfiguration( record ); + + channel.setSourceConfiguration( SourceConfigFactory.copy( mSourceConfiguration ) ); + + return channel; } - @XmlTransient - public ResourceManager getResourceManager() - { - return mResourceManager; - } - /** - * Returns the processing chain for this channel. If the processing chain - * is not currently constructed and running, this method will return null. + * Unique identifier for this channel. Value is transient (not persisted). */ @XmlTransient - public ProcessingChain getProcessingChain() + public int getChannelID() { - return mProcessingChain; + return mChannelID; } /** - * Indicates if this channel has a processing chain constructed and it - * is processing. - */ - public boolean isProcessing() - { - return mProcessingChain != null && mProcessingChain.processing(); - } - - /** - * Indicates if this channel has been selected for audio output + * Indicates if this channel has been selected for prioritized audio output + * and display of processing chain products */ @XmlTransient public boolean isSelected() @@ -239,46 +171,18 @@ public class Channel extends Configuration return mSelected; } - public void setSelected( boolean selected ) + /** + * Sets selection status of the channel. + * + * NOTE: this method is package private so that only the ChannelSelectionManager + * can toggle the selection state. Use the channel model to broadcast a + * request select/deselect channel event. + * + * @param selected + */ + void setSelected( boolean selected ) { mSelected = selected; - - fireChannelEvent( Event.CHANGE_SELECTED ); - } - - @XmlTransient - public CallEventModel getCallEventModel() - { - return mCallEventModel; - } - - /** - * Returns the MessageEventLogger for this processing chain - */ - @XmlTransient - public MessageEventLogger getEventLogger() - { - return mEventLogger; - } - - /** - * Sets the MessageEventLogger for this processing chain - */ - public void setEventLogger( MessageEventLogger eventLogger ) - { - mEventLogger = eventLogger; - } - - @XmlTransient - public MessageActivityModel getMessageActivityModel() - { - return mMessageActivityModel; - } - - @XmlTransient - public ChannelState getChannelState() - { - return mChannelState; } @XmlTransient @@ -286,193 +190,104 @@ public class Channel extends Configuration { return mChannelType; } - - /** - * Orderly shutdown method when this channel is going to be deleted. - */ - public void dispose() - { - setEnabled( DISABLED, BROADCAST_CHANGE ); - - /* Broadcast channel deleted event */ - fireChannelEvent( Event.CHANNEL_DELETED ); - - mProcessingChain.dispose(); - mProcessingChain = null; - } - - /** - * Returns a full channel name containing the system, site and channel name. - */ - public String getChannelDisplayName() - { - StringBuilder sb = new StringBuilder(); - - if( mSystem != null ) - { - sb.append( mSystem.getName() ); - } - else - { - sb.append( "-" ); - } - - sb.append( "/" ); - - if( mSite != null ) - { - sb.append( mSite.getName() ); - } - else - { - sb.append( "-" ); - } - - sb.append( "/" ); - - sb.append( mName ); - - return sb.toString(); - } - - /** - * Returns the tuner channel requirement for this channel when the source - * configuration specifies a tuner and frequency as the source for this - * channel. - * - * If the source configuration is anything else, this method returns null. - */ - public TunerChannel getTunerChannel() - { - TunerChannel retVal = null; - - if( mSourceConfiguration.getSourceType() == SourceType.TUNER ) - { - SourceConfigTuner config = (SourceConfigTuner)mSourceConfiguration; - - retVal = new TunerChannel( Type.LOCKED, config.getFrequency(), - mDecodeConfiguration.getDecoderType().getChannelBandwidth() ); - } - else if( mSourceConfiguration.getSourceType() == SourceType.RECORDING ) - { - SourceConfigRecording config = - (SourceConfigRecording)mSourceConfiguration; - - retVal = new TunerChannel( Type.LOCKED, config.getFrequency(), - mDecodeConfiguration.getDecoderType().getChannelBandwidth() ); - } - - return retVal; - } - /** * Returns the owning system for this channel. */ - public System getSystem() + @XmlAttribute( name = "system" ) + public String getSystem() { return mSystem; } + + public void setSystem( String system ) + { + mSystem = system; + } public boolean hasSystem() { return mSystem != null; } - /** - * Sets the owning system for this channel and optionally broadcasts a - * system change channel event. - */ - public void setSystem( System system, boolean fireChangeEvent ) - { - mSystem = system; - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_SYSTEM ); - } - } - /** * Returns the owning site for this channel. */ - public Site getSite() + @XmlAttribute( name = "site" ) + public String getSite() { - return mSite; + return mSite; } - + + public void setSite( String site ) + { + mSite = site; + } + public boolean hasSite() { return mSite != null; } /** - * Sets the owning site for this channel and optionally broadcasts a site - * change channel event. + * Returns the name of this channel. */ - public void setSite( Site site, boolean fireChangeEvent ) - { - mSite = site; - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_SITE ); - } - } + @XmlAttribute( name = "name" ) + public String getName() + { + return mName; + } + + /** + * Sets the name of this channel. + */ + public void setName( String name ) + { + mName = name; + } /** - * Default display string for this channel -- the channel name. + * Default display string for this channel: SYSTEM_SITE_NAME */ public String toString() { - return mName; + StringBuilder sb = new StringBuilder(); + sb.append( hasSystem() ? getSystem() : "SYSTEM" ); + sb.append( "_" ); + sb.append( hasSite() ? getSite() : "SITE" ); + sb.append( "_" ); + sb.append( getName() ); + + return sb.toString(); } /** * Indicates if this channel is enabled for processing. */ - @XmlAttribute + @XmlAttribute( name = "enabled" ) public boolean getEnabled() { return mEnabled; } /** - * Enables/disables this channel for processing. Automatically starts or - * stops channel processing in response to this change. + * Indicates if this channel will automatically start processing on + * application startup or if this channel is currently processing after + * application startup. + * + * Note: this method is package private and is intended to be managed + * by a co-package central processing manager + * + * @see controller.ChannelProcessingManager */ - public void setEnabled( boolean enabled ) + void setEnabled( boolean enabled ) { mEnabled = enabled; - - enableChanged(); - } - - /** - * Enables/disables this channel for processing. Automatically starts or - * stops channel processing in response to this change. Optionally - * broadcasts a enabled change channel event. - */ - public void setEnabled( boolean enabled, boolean fireChannelEvent ) - { - setEnabled( enabled ); - - if( fireChannelEvent ) - { - if( enabled ) - { - fireChannelEvent( Event.CHANNEL_ENABLED ); - } - else - { - fireChannelEvent( Event.CHANNEL_DISABLED ); - } - } } /** * Returns the alias list that is used by this channel for looking up alias - * values for the various identifiers produced by the decoder. + * values for the various identifiers produced by the decoder(s). */ @XmlElement( name = "alias_list_name" ) public String getAliasListName() @@ -488,25 +303,9 @@ public class Channel extends Configuration { mAliasListName = name; } - - /** - * Sets the alias list to be used for looking up the alias values for the - * various identifiers produced by the decoder. This method is the same - * as setAliasName(name) and optionally broadcasts an alias list change - * channel event. - */ - public void setAliasListName( String name, boolean fireChangeEvent ) - { - mAliasListName = name; - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_ALIAS_LIST ); - } - } /** - * Gets the decoder configuration used by this channel + * Gets the primary decoder configuration used by this channel */ @XmlElement( name = "decode_configuration" ) public DecodeConfiguration getDecodeConfiguration() @@ -515,7 +314,7 @@ public class Channel extends Configuration } /** - * Sets the decoder configuration used by this channel + * Sets the primary decoder configuration used by this channel */ public void setDecodeConfiguration( DecodeConfiguration config ) { @@ -523,7 +322,8 @@ public class Channel extends Configuration } /** - * Gets the aux decoder configuration used by this channel + * Gets the aux decoder configuration used by this channel. Aux decoders + * operate on demodulated audio */ @XmlElement( name = "aux_decode_configuration" ) public AuxDecodeConfiguration getAuxDecodeConfiguration() @@ -540,22 +340,8 @@ public class Channel extends Configuration } /** - * Sets the decoder configuration used by this channel and optionally - * broadcasts a decoder change channel event. - */ - public void setDecodeConfiguration( DecodeConfiguration config, - boolean fireChangeEvent ) - { - setDecodeConfiguration( config ); - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_DECODER ); - } - } - - /** - * Returns the source configuration for this channel. + * Returns the source configuration for this channel. A channel source + * identifies where the processing chain will get source sample data from. */ @XmlElement( name = "source_configuration" ) public SourceConfiguration getSourceConfiguration() @@ -569,23 +355,12 @@ public class Channel extends Configuration public void setSourceConfiguration( SourceConfiguration config ) { mSourceConfiguration = config; + + //Clear the tune channel object so that it can be recreated if the + //source configuration changes + mTunerChannel = null; } - /** - * Sets the source configuration for this channel and optionally broadcasts - * a source change channel event. - */ - public void setSourceConfiguration( SourceConfiguration config, - boolean fireChangeEvent ) - { - setSourceConfiguration( config ); - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_SOURCE ); - } - } - /** * Returns the event logger configuration for this channel. */ @@ -603,21 +378,6 @@ public class Channel extends Configuration mEventLogConfiguration = config; } - /** - * Sets the event logger configuration for this channel and optionally - * broadcasts an event logger change channel event. - */ - public void setEventLogConfiguration( EventLogConfiguration config, - boolean fireChangeEvent ) - { - setEventLogConfiguration( config ); - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_EVENT_LOGGER ); - } - } - /** * Returns the recorder configuration for this channel. */ @@ -636,370 +396,41 @@ public class Channel extends Configuration } /** - * Sets the recorder configuration for this channel and optionally broadcasts - * a recorder configuration change channel event. - */ - public void setRecordConfiguration( RecordConfiguration config, - boolean fireChangeEvent ) - { - setRecordConfiguration( config ); - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_RECORDER ); - } - } - - /** - * Returns the name of this channel. - */ - @XmlAttribute - public String getName() - { - return mName; - } - - /** - * Sets the name of this channel. - */ - public void setName( String name ) - { - mName = name; - } - - /** - * Sets the name of this channel and optionally broadcasts a name change - * channel event. - */ - public void setName( String name, boolean fireChangeEvent ) - { - mName = name; - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_NAME ); - } - } - - /** - * Responds to changes in the enabled setting by starting the processing - * change if the channel is now enabled, or stopping the procesing chain - * if it was already started. - */ - private void enableChanged() - { - if( mEnabled ) - { - start(); - } - else - { - stop(); - } - } - - /** - * Starts the channel processing chain. - */ - private void start() - { - if( mEnabled && mResourceManager != null ) - { - setupChannelState(); - - try - { - setup(); - - mProcessingChain.start(); - - fireChannelEvent( Event.CHANNEL_PROCESSING_STARTED ); - } - catch( SourceException se ) - { - //Do nothing, we couldn't get a source for the channel - } - } - } - - private void setupChannelState() - { - if( mChannelState == null ) - { - mChannelState = new ChannelState( mResourceManager - .getThreadPoolManager(), mChannelType ); - - mChannelState.setTrafficChannelTimeout( mTrafficChannelTimeout ); - } - } - - /** - * Sets up the channel and prepares to start processing. Sets up the - * processing chain and requests a source. + * Convenience method to construct a tuner channel object representing a + * tuner or recording source frequency and bandwidth that can be used by + * application components for graphically representing this channel. * - * @throws SourceException - if no source is available or if there is an - * error obtaining a source + * If the source configuration is not a tuner or recording, this method + * returns null. */ - public void setup() throws SourceException - { - setupProcessingChain(); - setupSource(); - } + @XmlTransient + public TunerChannel getTunerChannel() + { + if( mTunerChannel == null ) + { + if( mSourceConfiguration.getSourceType() == SourceType.TUNER ) + { + SourceConfigTuner config = (SourceConfigTuner)mSourceConfiguration; + + mTunerChannel = new TunerChannel( Type.LOCKED, config.getFrequency(), + mDecodeConfiguration.getDecoderType().getChannelBandwidth() ); + } + else if( mSourceConfiguration.getSourceType() == SourceType.RECORDING ) + { + SourceConfigRecording config = + (SourceConfigRecording)mSourceConfiguration; + + mTunerChannel = new TunerChannel( Type.LOCKED, config.getFrequency(), + mDecodeConfiguration.getDecoderType().getChannelBandwidth() ); + } + } + + return mTunerChannel; + } /** - * Setup recording options. Adds RecorderManager as a audio packet listener - * to receive and record decoded audio. - */ - private void setupRecording() - { - List recorders = mRecordConfiguration.getRecorders(); - - if( !recorders.isEmpty() ) - { - if( recorders.contains( RecorderType.AUDIO ) ) - { - mProcessingChain.addAudioPacketListener( - mResourceManager.getRecorderManager() ); - } - - /* Add baseband recorder */ - if( ( recorders.contains( RecorderType.BASEBAND ) && - mChannelType == ChannelType.STANDARD ) ) - { - mProcessingChain.addModule( RecorderManager.getBasebandRecorder( - mResourceManager.getThreadPoolManager(), getChannelName() ) ); - } - - /* Add traffic channel baseband recorder */ - if( recorders.contains( RecorderType.TRAFFIC_BASEBAND ) && - mChannelType == ChannelType.TRAFFIC ) - { - mProcessingChain.addModule( RecorderManager.getBasebandRecorder( - mResourceManager.getThreadPoolManager(), getChannelName() ) ); - } - } - } - - private String getChannelName() - { - StringBuilder sb = new StringBuilder(); - sb.append( mSystem ); - sb.append( "_" ); - sb.append( mSite ); - sb.append( "_" ); - sb.append( mName ); - - return sb.toString(); - } - - private void setupLogging() - { - if( mProcessingChain != null ) - { - String channelName = getChannelName(); - - List loggers = mResourceManager.getEventLogManager() - .getLoggers( mEventLogConfiguration, channelName ); - - if( !loggers.isEmpty() ) - { - mProcessingChain.addModules( loggers ); - } - } - } - - private void setupProcessingChain() - { - if( mProcessingChain == null ) - { - mProcessingChain = new ProcessingChain( getName() ); - - /* Get the optional alias list for the decode modules to use */ - AliasList aliasList = mResourceManager.getPlaylistManager() - .getPlayist().getAliasDirectory().getAliasList( mAliasListName ); - - /* Add the channel state as a module */ - mProcessingChain.addModule( mChannelState ); - - /* Processing Modules */ - List modules = DecoderFactory.getModules( mChannelType, - mResourceManager, mDecodeConfiguration, mRecordConfiguration, - mAuxDecodeConfiguration, aliasList, mSystem, - mSite, mName ); - - mProcessingChain.addModules( modules ); - - /* Get message filters for the set of processing modules */ - FilterSet messageFilter = DecoderFactory.getMessageFilters( modules ); - - /* Setup the message activity model and add message listeners */ - mMessageActivityModel = new MessageActivityModel( messageFilter ); - mProcessingChain.addMessageListener( mMessageActivityModel ); - - /* Add audio manager as listener for audio packets */ - mProcessingChain.addAudioPacketListener( - mResourceManager.getAudioManager() ); - - /* Add system-wide message listeners */ - mProcessingChain.addMessageListeners( - mResourceManager.getChannelManager().getMessageListeners() ); - - /* Add individual message listeners */ - mProcessingChain.addMessageListeners( mMessageListeners ); - - /* Create the call event model to receive call events */ - mCallEventModel = new CallEventModel(); - mProcessingChain.addCallEventListener( mCallEventModel ); - - setupLogging(); - - setupRecording(); - - /* Inject channel metadata that will be inserted into audio packets - * for the recorder manager and streaming */ - mProcessingChain.broadcast( - new Metadata( MetadataType.SYSTEM, mSystem.getName() ) ); - mProcessingChain.broadcast( - new Metadata( MetadataType.SITE_ID, mSite.getName() ) ); - mProcessingChain.broadcast( - new Metadata( MetadataType.CHANNEL_NAME, mName ) ); - } - } - - /** - * Attempts to obtain a source and apply it to the processing chain. - * - * @throws SourceException - if the processing chain is not setup or if - * there is an error produced while obtaining the source - */ - private void setupSource() throws SourceException - { - if( mProcessingChain != null ) - { - Source source = mResourceManager.getSourceManager() - .getSource( getSourceConfiguration(), getDecodeConfiguration() - .getDecoderType().getChannelBandwidth() ); - - if( source != null ) - { - mProcessingChain.setSource( source ); - } - else - { - throw new SourceException( "Couldn't obtain tuner channel source" ); - } - } - else - { - throw new SourceException( "Cannot obtain source for a null " - + "processing chain - setup the processing chain first" ); - } - } - - /** - * Stops the channel processing chain - */ - private void stop() - { - if( !mEnabled && mProcessingChain != null ) - { - fireChannelEvent( Event.CHANNEL_PROCESSING_STOPPED ); - - mProcessingChain.stop(); - - if( mChannelType == ChannelType.STANDARD ) - { - mProcessingChain.dispose(); - mProcessingChain = null; - } - - mSelected = false; - } - } - - /** - * Broadcasts a channel change event to all registered listeners - */ - public void fireChannelEvent( Event event ) - { - ChannelEvent channelEvent = new ChannelEvent( this, event ); - - for( ChannelEventListener listener: mChannelListeners ) - { - listener.channelChanged( channelEvent ); - } - - /* Send event to processing chain so modules can respond to channel - * selection events */ - if( mProcessingChain != null ) - { - mProcessingChain.getChannelEventListener().receive( channelEvent ); - } - } - - /** - * Adds a channel listener to receive all channel events from this channel - * and automatically sends the listener a channel add event. - */ - public void addListener( ChannelEventListener listener ) - { - if( !mChannelListeners.contains( listener ) ) - { - mChannelListeners.add( listener ); - - listener.channelChanged( - new ChannelEvent( this, Event.CHANNEL_ADDED ) ); - } - else - { - mLog.error( "Channel - attempt to add already existing channel " - + "listener [" + listener.getClass() + "]" ); - } - } - - /** - * Adds a list of channel listeners to receive all channel events from this - * channel and automatically sends each one a channel add event. - */ - public void addListeners( List listeners ) - { - for( ChannelEventListener listener: listeners ) - { - addListener( listener ); - } - } - - /** - * Removes a channel listener from receiving channel events from this channel - */ - public void removeListener( ChannelEventListener listener ) - { - mChannelListeners.remove( listener ); - } - - public void addListener( Listener listener ) - { - mMessageListeners.add( listener ); - - if( isProcessing() ) - { - mProcessingChain.addMessageListener( listener ); - } - } - - public void removeListener( Listener listener ) - { - mMessageListeners.remove( listener ); - - if( mProcessingChain != null ) - { - mProcessingChain.removeMessageListener( listener ); - } - } - - /** - * Indicates if any part of this channel is contained within the - * minimum and maximum frequency values. + * Convenience method to indicate if any part of this channel is contained + * within the minimum and maximum frequency values. */ public boolean isWithin( long minimum, long maximum ) { @@ -1009,34 +440,24 @@ public class Channel extends Configuration tunerChannel.overlaps( minimum, maximum ); } - public void addTrafficChannel( String channelID, Channel channel ) + /** + * Convenience method to make the current channel frequency correction value + * available for use outside of a constructed processing chain while maintaining + * linkage to the source channel. This hack allows the correction value to + * be used to visually show in the spectral display. + */ + @Override + public void frequencyChanged( FrequencyChangeEvent event ) { - mTrafficChannels.put( channelID, channel ); - } - - public boolean hasTrafficChannel( Channel channel ) - { - return mTrafficChannels.containsValue( channel ); - } - - public void removeTrafficChannel( Channel channel ) - { - String name = channel.getChannelDisplayName(); - - if( mTrafficChannels.containsKey( name ) ) + if( event.getEvent() == Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE ) { - mTrafficChannels.remove( name ); + mChannelFrequencyCorrection = event.getValue().intValue(); } } - - public Collection getTrafficChannels() + + @XmlTransient + public int getChannelFrequencyCorrection() { - return Collections.unmodifiableCollection( mTrafficChannels.values() ); + return mChannelFrequencyCorrection; } - - public boolean hasTrafficChannel( String name ) - { - return mTrafficChannels.containsKey( name ); - } - } diff --git a/src/controller/channel/ChannelController.java b/src/controller/channel/ChannelController.java new file mode 100644 index 00000000..60d0623f --- /dev/null +++ b/src/controller/channel/ChannelController.java @@ -0,0 +1,279 @@ +package controller.channel; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.DefaultTableCellRenderer; + +import net.coderazzi.filters.gui.AutoChoices; +import net.coderazzi.filters.gui.TableFilterHeader; +import net.miginfocom.swing.MigLayout; +import source.SourceManager; +import alias.AliasModel; + +import com.jidesoft.swing.JideSplitPane; + +import controller.channel.map.ChannelMapModel; + +public class ChannelController extends JPanel + implements ActionListener, ListSelectionListener +{ + private static final long serialVersionUID = 1L; + + private ChannelModel mChannelModel; + private JTable mChannelTable; + private TableFilterHeader mTableFilterHeader; + private ChannelEditor mEditor; + + private static final String NEW_CHANNEL = "New"; + private static final String COPY_CHANNEL = "Copy"; + private static final String DELETE_CHANNEL = "Delete"; + + private JButton mNewChannelButton = new JButton( NEW_CHANNEL ); + private JButton mCopyChannelButton = new JButton( COPY_CHANNEL ); + private JButton mDeleteChannelButton = new JButton( DELETE_CHANNEL ); + + public ChannelController( ChannelModel channelModel, + ChannelMapModel channelMapModel, + SourceManager sourceManager, + AliasModel aliasModel ) + { + mChannelModel = channelModel; + + mEditor = new ChannelEditor( channelModel, channelMapModel, sourceManager, aliasModel ); + mChannelModel.addListener( mEditor ); + + init(); + } + + private void init() + { + setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]") ); + + //System Configuration View and Editor + mChannelTable = new JTable( mChannelModel ); + mChannelTable.setDefaultRenderer( String.class, new ChannelTableCellRenderer() ); + mChannelTable.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + mChannelTable.getSelectionModel().addListSelectionListener( this ); + mChannelTable.setAutoCreateRowSorter( true ); + + mTableFilterHeader = new TableFilterHeader( mChannelTable, AutoChoices.ENABLED ); + mTableFilterHeader.setFilterOnUpdates( true ); + + JScrollPane channelScroller = new JScrollPane( mChannelTable ); + + JPanel buttonsPanel = new JPanel(); + + buttonsPanel.setLayout( + new MigLayout( "insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]") ); + + mNewChannelButton.addActionListener( this ); + mNewChannelButton.setToolTipText( "Create a new channel configuration" ); + buttonsPanel.add( mNewChannelButton ); + + mCopyChannelButton.addActionListener( this ); + mCopyChannelButton.setEnabled( false ); + mCopyChannelButton.setToolTipText( "Create a copy of the currently selected channel configuration" ); + buttonsPanel.add( mCopyChannelButton ); + + mDeleteChannelButton.addActionListener( this ); + mDeleteChannelButton.setEnabled( false ); + mDeleteChannelButton.setToolTipText( "Delete the currently selected channel configuration" ); + buttonsPanel.add( mDeleteChannelButton ); + + JPanel listAndButtonsPanel = new JPanel(); + + listAndButtonsPanel.setLayout( + new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill][]") ); + + listAndButtonsPanel.add( channelScroller, "wrap" ); + listAndButtonsPanel.add( buttonsPanel ); + + JideSplitPane splitPane = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT ); + splitPane.setDividerSize( 5 ); + splitPane.setShowGripper( true ); + + //Shrink this guy so that he'll eventually be expanded to the scroller preferred size + listAndButtonsPanel.setPreferredSize( new Dimension( 10, 10 ) ); + + //Attempt to get a 60:40 vertical sizing preference + JScrollPane listScroller = new JScrollPane( listAndButtonsPanel ); + listScroller.setPreferredSize( new Dimension( 800, 58 ) ); + + JScrollPane editorScroller = new JScrollPane( mEditor ); + editorScroller.setPreferredSize( new Dimension( 800, 42 ) ); + + splitPane.add( listScroller ); + splitPane.add( editorScroller ); + + add( splitPane ); + } + + @Override + public void valueChanged( ListSelectionEvent event ) + { + //This limits event firing to only when selection is complete + if( !event.getValueIsAdjusting() ) + { + int selectedRow = mChannelTable.getSelectedRow(); + + if( selectedRow != -1 ) + { + int index = mChannelTable.convertRowIndexToModel( mChannelTable.getSelectedRow() ); + + Channel channel = mChannelModel.getChannelAtIndex( index ); + + if( channel != null ) + { + mEditor.setItem( channel ); + mCopyChannelButton.setEnabled( true ); + mDeleteChannelButton.setEnabled( true ); + } + } + else + { + mEditor.setItem( null ); + mCopyChannelButton.setEnabled( false ); + mDeleteChannelButton.setEnabled( false ); + } + } + } + + /** + * Adds the channel to the channel model/table and scrolls to view it + */ + private void addChannel( Channel channel ) + { + //HACK: when inserting a row to the model, the JTable gets + //notified and attempts to tell the coderazzi table filter + //adaptive choices filter to refresh before the table filter is + //notified of the row additions, causing an index out of bounds + //exception. We turn off adaptive choices temporarily, add the + //channel, and turn on adaptive choices again. + mTableFilterHeader.setAdaptiveChoices( false ); + + int index = mChannelModel.addChannel( channel ); + + mTableFilterHeader.setAdaptiveChoices( true ); + + if( index >= 0 ) + { + int translatedIndex = mChannelTable.convertRowIndexToView( index ); + mChannelTable.setRowSelectionInterval( translatedIndex, translatedIndex ); + mChannelTable.scrollRectToVisible( + new Rectangle( mChannelTable.getCellRect( translatedIndex, 0, true ) ) ); + } + } + + private Channel getSelectedChannel() + { + int index = mChannelTable.getSelectedRow(); + + if( index >= 0 ) + { + return mChannelModel.getChannelAtIndex( + mChannelTable.convertRowIndexToModel( index ) ); + } + + return null; + } + + /** + * Responds to New, Copy and Delete Channel button invocations + */ + @Override + public void actionPerformed( ActionEvent event ) + { + switch( event.getActionCommand() ) + { + case NEW_CHANNEL: + addChannel( new Channel( "New Channel" ) ); + break; + case COPY_CHANNEL: + Channel selected = getSelectedChannel(); + + if( selected != null ) + { + addChannel( selected.copyOf() ); + } + break; + case DELETE_CHANNEL: + Channel toDelete = getSelectedChannel(); + + if( toDelete != null ) + { + int choice = JOptionPane.showConfirmDialog( ChannelController.this, + "Do you want to delete this channel?", "Delete Channel?", + JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE ); + + if( choice == JOptionPane.YES_OPTION ) + { + mChannelModel.removeChannel( toDelete ); + } + } + break; + default: + break; + } + } + + public class ChannelTableCellRenderer extends DefaultTableCellRenderer + { + private static final long serialVersionUID = 1L; + + @Override + public Component getTableCellRendererComponent( JTable table, + Object value, boolean isSelected, boolean hasFocus, int row, + int column ) + { + Component component = super.getTableCellRendererComponent( + table, value, isSelected, hasFocus, row, column ); + + ChannelModel model = (ChannelModel)table.getModel(); + + int index = table.convertRowIndexToModel( row ); + + Channel channel = model.getChannelAtIndex( index ); + + boolean enabled = channel.getEnabled(); + + if( isSelected ) + { + if( enabled ) + { + component.setBackground( Color.YELLOW ); + } + else + { + component.setBackground( table.getSelectionBackground() ); + } + } + else + { + if( enabled ) + { + component.setBackground( Color.GREEN ); + } + else + { + component.setBackground( table.getBackground() ); + } + } + + return component; + } + + } +} diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index 21306577..46edcd77 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,147 +17,145 @@ ******************************************************************************/ package controller.channel; +import gui.editor.Editor; +import gui.editor.EditorValidationException; import java.awt.Color; -import java.awt.ComponentOrientation; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.Collections; -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.JTextField; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreePath; -import module.decode.AuxDecodeComponentEditor; -import module.decode.DecodeComponentEditor; -import module.log.EventLogComponentEditor; +import module.decode.AuxDecodeConfigurationEditor; +import module.decode.DecodeConfigurationEditor; +import module.log.EventLogConfigurationEditor; import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import playlist.PlaylistManager; -import record.RecordComponentEditor; -import source.SourceComponentEditor; -import alias.AliasList; +import record.RecordConfigurationEditor; +import source.SourceConfigurationEditor; +import source.SourceManager; +import alias.AliasModel; import com.jidesoft.swing.JideTabbedPane; -import controller.Editor; +import controller.channel.ChannelEvent.Event; +import controller.channel.map.ChannelMapModel; -public class ChannelEditor extends Editor implements ActionListener +public class ChannelEditor extends Editor implements ActionListener, ChannelEventListener { - private final static Logger mLog = LoggerFactory.getLogger( ChannelEditor.class ); - private static final long serialVersionUID = 1L; - private DefaultTreeModel mTreeModel; - private ChannelNode mChannelNode; + + private NameConfigurationEditor mNameConfigurationEditor; + private SourceConfigurationEditor mSourceConfigurationEditor; + private DecodeConfigurationEditor mDecodeConfigurationEditor; + private AuxDecodeConfigurationEditor mAuxDecodeConfigurationEditor; + private EventLogConfigurationEditor mEventLogConfigurationEditor; + private RecordConfigurationEditor mRecordConfigurationEditor; - private DecodeComponentEditor mDecodeEditor; - private AuxDecodeComponentEditor mAuxDecodeEditor; - private EventLogComponentEditor mEventLogEditor; - private RecordComponentEditor mRecordEditor; - private SourceComponentEditor mSourceEditor; + private JButton mEnableButton = new JButton( "Enable" ); + private JLabel mChannelName = new JLabel( "Channel:" ); - private JTextField mChannelName; - private JCheckBox mChannelEnabled = new JCheckBox(); - private JComboBox mComboAliasLists; + private ChannelModel mChannelModel; + private ChannelMapModel mChannelMapModel; + private SourceManager mSourceManager; - private PlaylistManager mPlaylistManager; + private boolean mChannelEnableRequested = false; - public ChannelEditor( ChannelNode channel, PlaylistManager playlistManager ) + public ChannelEditor( ChannelModel channelModel, + ChannelMapModel channelMapModel, + SourceManager sourceManager, + AliasModel aliasModel ) { - super(); + mChannelModel = channelModel; + mChannelMapModel = channelMapModel; + mSourceManager = sourceManager; + mNameConfigurationEditor = new NameConfigurationEditor( aliasModel, mChannelModel ); - mChannelNode = channel; - - mPlaylistManager = playlistManager; - - initGUI(); + init(); } - - private void initGUI() + + private void init() { - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][grow][]" ) ); + setLayout( new MigLayout( "fill,wrap 3", "[grow,fill][grow,fill][grow,fill]", "[grow,fill][]" ) ); - add( new JLabel( "Channel Configuration" ), "span, align center" ); - - add( new JLabel( "Enabled:" ) ); - - mChannelEnabled.setSelected( - mChannelNode.getChannel().getEnabled() ); - mChannelEnabled.setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT ); - add( mChannelEnabled ); - - add( new JLabel( "Name:" ) ); - - mChannelName = new JTextField( mChannelNode.getChannel().getName() ); - add( mChannelName, "growx,push" ); - - /** - * ComboBox: Alias Lists - */ - add( new JLabel( "Alias List:" ) ); - - mComboAliasLists = new JComboBox(); - - List lists = mChannelNode.getModel().getResourceManager() - .getPlaylistManager().getPlayist().getAliasDirectory().getAliasList(); - - Collections.sort( lists ); - - mComboAliasLists.setModel( new DefaultComboBoxModel( - lists.toArray( new AliasList[ lists.size() ] ) ) ); - - String aliasListName = - mChannelNode.getChannel().getAliasListName(); - - if( aliasListName != null ) - { - AliasList selected = mChannelNode.getModel().getResourceManager() - .getPlaylistManager().getPlayist().getAliasDirectory() - .getAliasList( aliasListName ); - - mComboAliasLists.setSelectedItem( selected ); - } - - add( mComboAliasLists, "growx,push" ); - JideTabbedPane tabs = new JideTabbedPane(); tabs.setFont( this.getFont() ); tabs.setForeground( Color.BLACK ); - mSourceEditor = new SourceComponentEditor( mChannelNode ); - tabs.addTab( "Source", mSourceEditor ); + tabs.setTabTrailingComponent( mChannelName ); + + mNameConfigurationEditor.setSaveRequestListener( this ); + tabs.addTab( "Name/Alias", mNameConfigurationEditor ); + + mSourceConfigurationEditor = new SourceConfigurationEditor( mSourceManager ); + mSourceConfigurationEditor.setSaveRequestListener( this ); + tabs.addTab( "Source", mSourceConfigurationEditor ); - mDecodeEditor = new DecodeComponentEditor( mChannelNode, mPlaylistManager ); - tabs.addTab( "Decoder", mDecodeEditor ); + mDecodeConfigurationEditor = new DecodeConfigurationEditor( mChannelMapModel ); + mDecodeConfigurationEditor.setSaveRequestListener( this ); + tabs.addTab( "Decoder", mDecodeConfigurationEditor ); - mAuxDecodeEditor = new AuxDecodeComponentEditor( mChannelNode ); - tabs.addTab( "Aux", mAuxDecodeEditor ); + mAuxDecodeConfigurationEditor = new AuxDecodeConfigurationEditor(); + mAuxDecodeConfigurationEditor.setSaveRequestListener( this ); + tabs.addTab( "Aux Decoders", mAuxDecodeConfigurationEditor ); - mEventLogEditor = new EventLogComponentEditor( mChannelNode ); - tabs.addTab( "Event Log", mEventLogEditor ); + mEventLogConfigurationEditor = new EventLogConfigurationEditor(); + mEventLogConfigurationEditor.setSaveRequestListener( this ); + tabs.addTab( "Logging", mEventLogConfigurationEditor ); - mRecordEditor = new RecordComponentEditor( mChannelNode ); - tabs.addTab( "Record", mRecordEditor ); + mRecordConfigurationEditor = new RecordConfigurationEditor(); + mRecordConfigurationEditor.setSaveRequestListener( this ); + tabs.addTab( "Recording", mRecordConfigurationEditor ); + + add( tabs, "span" ); + + mEnableButton.addActionListener( this ); + mEnableButton.setEnabled( false ); + mEnableButton.setToolTipText( "Start the currently selected channel running/decoding" ); + add( mEnableButton ); - add( tabs, "span,grow,push" ); - JButton btnSave = new JButton( "Save" ); + btnSave.setToolTipText( "Save changes to the currently selected channel" ); btnSave.addActionListener( ChannelEditor.this ); - add( btnSave, "growx,push" ); + add( btnSave ); JButton btnReset = new JButton( "Reset" ); + btnReset.setToolTipText( "Reload the currently selected channel" ); btnReset.addActionListener( ChannelEditor.this ); - add( btnReset, "growx,push" ); + add( btnReset ); + } + + @Override + public void channelChanged( ChannelEvent event ) + { + if( hasItem() && getItem() == event.getChannel() ) + { + switch( event.getEvent() ) + { + case NOTIFICATION_CONFIGURATION_CHANGE: + case NOTIFICATION_PROCESSING_START: + case NOTIFICATION_PROCESSING_STOP: + setItem( getItem() ); + mChannelEnableRequested = false; + break; + case NOTIFICATION_DELETE: + setItem( null ); + break; + case NOTIFICATION_ENABLE_REJECTED: + if( mChannelEnableRequested ) + { + JOptionPane.showMessageDialog( ChannelEditor.this, "Channel could not be " + + "enabled. This is likely because there are no tuner channels " + + "available", "Couldn't enable channel", JOptionPane.INFORMATION_MESSAGE ); + + mChannelEnableRequested = false; + } + break; + default: + break; + } + } } @Override @@ -165,115 +163,95 @@ public class ChannelEditor extends Editor implements ActionListener { String command = e.getActionCommand(); - if( command.contentEquals( "Save" ) ) + if( command.contentEquals( "Enable" ) ) + { + if( hasItem() ) + { + save(); + + mChannelEnableRequested = true; + mChannelModel.broadcast( new ChannelEvent( getItem(), Event.REQUEST_ENABLE ) ); + } + } + else if( command.contentEquals( "Disable" ) ) + { + if( hasItem() ) + { + mChannelModel.broadcast( new ChannelEvent( getItem(), Event.REQUEST_DISABLE ) ); + } + } + else if( command.contentEquals( "Save" ) ) { save(); } else if( command.contentEquals( "Reset" ) ) { - reset(); + setItem( getItem() ); } - - if( mTreeModel != null ) - { - mTreeModel.nodeChanged( mChannelNode ); - } - } - - @Override - public void save() - { - try - { - /** - * Validate the source configuration against the other component - * configuration editors - */ - validate( mSourceEditor.getSourceEditor() ); - - boolean expanded = mChannelNode.getModel().getTree() - .isExpanded( new TreePath( mChannelNode ) ); - - mSourceEditor.save(); - mDecodeEditor.save(); - mAuxDecodeEditor.save(); - mEventLogEditor.save(); - mRecordEditor.save(); - - mChannelNode.getChannel().setName( mChannelName.getText(), false ); - mChannelNode.getChannel().setEnabled( mChannelEnabled.isSelected(), false ); - - AliasList selected = mComboAliasLists.getItemAt( - mComboAliasLists.getSelectedIndex() ); - if( selected != null ) - { - mChannelNode.getChannel().setAliasListName( selected.getName(), false ); - } - - - mChannelNode.save(); - mChannelNode.show(); - - if( expanded ) - { - mChannelNode.getModel().getTree().expandPath( new TreePath( mChannelNode ) ); - } - } - catch ( ChannelValidationException validationException ) - { - /* ChannelValidationException can be thrown by any of the component - * editors. Show the validation error text in a popup menu to the - * user */ - JOptionPane.showMessageDialog( ChannelEditor.this, - validationException.getMessage(), - "Channel Configuration Error", - JOptionPane.ERROR_MESSAGE ); - } - } - - @Override - public void reset() - { - mChannelName.setText( - mChannelNode.getChannel().getName() ); - mChannelName.requestFocus(); - mChannelName.requestFocusInWindow(); - - mChannelEnabled.setSelected( - mChannelNode.getChannel().getEnabled() ); - mChannelEnabled.requestFocus(); - mChannelEnabled.requestFocusInWindow(); - - String aliasListName = - mChannelNode.getChannel().getAliasListName(); - - if( aliasListName != null ) - { - AliasList selected = mChannelNode.getModel().getResourceManager() - .getPlaylistManager().getPlayist().getAliasDirectory() - .getAliasList( aliasListName ); - - mComboAliasLists.setSelectedItem( selected ); - } - - - mSourceEditor.reset(); - mDecodeEditor.reset(); - mAuxDecodeEditor.reset(); - mEventLogEditor.reset(); - mRecordEditor.reset(); } /** - * Validate the specified editor against each of the component editors + * Sets the channel configuration in each of the channel editor components + * Note: this method must be invoked on the swing event dispatch thread */ - @Override - public void validate( Editor editor ) throws ChannelValidationException + public void setItem( final Channel channel ) { - mSourceEditor.validate( editor ); - mDecodeEditor.validate( editor ); - mAuxDecodeEditor.validate( editor ); - mEventLogEditor.validate( editor ); - mRecordEditor.validate( editor ); + super.setItem( channel ); + + mNameConfigurationEditor.setItem( channel ); + mSourceConfigurationEditor.setItem( channel ); + mDecodeConfigurationEditor.setItem( channel ); + mAuxDecodeConfigurationEditor.setItem( channel ); + mEventLogConfigurationEditor.setItem( channel ); + mRecordConfigurationEditor.setItem( channel ); + + if( channel != null ) + { + mChannelName.setText( "Channel: " + channel.getName() ); + mEnableButton.setText( channel.getEnabled() ? "Disable" : "Enable" ); + mEnableButton.setEnabled( true ); + mEnableButton.setBackground( channel.getEnabled() ? Color.GREEN : getBackground() ); + } + else + { + mChannelName.setText( "Channel: " ); + mEnableButton.setText( "Enable" ); + mEnableButton.setEnabled( false ); + mEnableButton.setBackground( getBackground() ); + } } + + public void save() + { + if( hasItem() ) + { + mNameConfigurationEditor.save(); + mSourceConfigurationEditor.save(); + mDecodeConfigurationEditor.save(); + mAuxDecodeConfigurationEditor.save(); + mEventLogConfigurationEditor.save(); + mRecordConfigurationEditor.save(); + + try + { + mDecodeConfigurationEditor.validate( mSourceConfigurationEditor ); + mDecodeConfigurationEditor.validate( mAuxDecodeConfigurationEditor ); + } + catch( EditorValidationException e ) + { + e.getEditor().requestFocus(); + + JOptionPane.showMessageDialog( e.getEditor(), e.getReason(), + "Configuration Error", JOptionPane.ERROR_MESSAGE ); + + return; + } + + + mChannelModel.broadcast( new ChannelEvent( getItem(), + Event.NOTIFICATION_CONFIGURATION_CHANGE ) ); + } + + setModified( false ); + } } diff --git a/src/controller/channel/ChannelEvent.java b/src/controller/channel/ChannelEvent.java index a8b88eab..b71b5c94 100644 --- a/src/controller/channel/ChannelEvent.java +++ b/src/controller/channel/ChannelEvent.java @@ -1,3 +1,20 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ package controller.channel; public class ChannelEvent @@ -27,33 +44,35 @@ public class ChannelEvent } /** - * Channel Events - used to specify channel events and changes to channel - * configurations and settings. + * Channel events to describe the specific event */ public enum Event { - CHANGE_ALIAS_LIST, - CHANGE_DECODER, - CHANGE_EVENT_LOGGER, - CHANGE_NAME, - CHANGE_SELECTED, - CHANGE_SITE, - CHANGE_RECORDER, - CHANGE_SOURCE, - CHANGE_SYSTEM, - - CHANNEL_ADDED, - CHANNEL_DELETED, - - CHANNEL_ENABLED, - CHANNEL_DISABLED, - - CHANNEL_PROCESSING_STARTED, - CHANNEL_PROCESSING_STOPPED, - - CHANNEL_STATE_RESET, - - TRAFFIC_CHANNEL_ADDED, - TRAFFIC_CHANNEL_DELETED; + //Channel is added + NOTIFICATION_ADD, + //Channel configuration has changed + NOTIFICATION_CONFIGURATION_CHANGE, + //Channel is deleted/removed + NOTIFICATION_DELETE, + //Channel enable request was rejected + NOTIFICATION_ENABLE_REJECTED, + //Channel has started processing/decoding + NOTIFICATION_PROCESSING_START, + //Channel has stopped processing/decoding + NOTIFICATION_PROCESSING_STOP, + //Channel's selection state has changed + NOTIFICATION_SELECTION_CHANGE, + //Channel's channel-state has been reset + NOTIFICATION_STATE_RESET, + //Request to delete the channel + REQUEST_DELETE, + //Request to deselect the channel + REQUEST_DESELECT, + //Request to disable a channel - response will be a PROCESSING_STOP_NOTIFICATION + REQUEST_DISABLE, + //Request to enable a channel - response will be a PROCESSING_START_NOTIFICATION + REQUEST_ENABLE, + //Request to select the channel + REQUEST_SELECT; } } diff --git a/src/controller/channel/ChannelEventListener.java b/src/controller/channel/ChannelEventListener.java index 7c5405db..ad223435 100644 --- a/src/controller/channel/ChannelEventListener.java +++ b/src/controller/channel/ChannelEventListener.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/controller/channel/ChannelManager.java b/src/controller/channel/ChannelManager.java deleted file mode 100644 index 4c594824..00000000 --- a/src/controller/channel/ChannelManager.java +++ /dev/null @@ -1,143 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,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 - ******************************************************************************/ -package controller.channel; - -import java.util.ArrayList; -import java.util.List; - -import message.Message; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.Listener; -import alias.action.AliasActionManager; -import controller.ResourceManager; - -public class ChannelManager implements ChannelEventListener -{ - private final static Logger mLog = LoggerFactory.getLogger( ChannelManager.class ); - - private List mChannels = new ArrayList<>(); - private List mChannelListeners = new ArrayList<>(); - private List> mMessageListeners = new ArrayList<>(); - - private AliasActionManager mAliasActionManager; - - public ChannelManager( ResourceManager resourceManager ) - { - mAliasActionManager = new AliasActionManager( resourceManager ); - - mChannelListeners.add( this ); - - /* Add alias action manager as listener to all channels/messages */ - mMessageListeners.add( mAliasActionManager ); - } - - /** - * Notification that a channel has been added or deleted, so that we can - * keep track of all channels and manage system-wide listeners for each - * of those channels - */ - @SuppressWarnings( "incomplete-switch" ) - @Override - public void channelChanged( ChannelEvent event ) - { - switch( event.getEvent() ) - { - case CHANNEL_ADDED: - if( !mChannels.contains( event.getChannel() ) ) - { - mChannels.add( event.getChannel() ); - } - break; - case CHANNEL_DELETED: - mChannels.remove( event.getChannel() ); - break; - } - } - - /** - * Adds a channel listener that will be added to all channels to receive - * any channel change events. This listener will automatically receive - * a channel add event as it is added to each of the existing channels. - */ - public void addListener( ChannelEventListener listener ) - { - mChannelListeners.add( listener ); - - for( Channel channel: mChannels ) - { - channel.addListener( listener ); - } - } - - public List getChannels() - { - return mChannels; - } - - /** - * Removes a channel listener from being automatically added to all channels - * to receive channel change events. - */ - public void removeListener( ChannelEventListener listener ) - { - mChannelListeners.remove( listener ); - - for( Channel channel: mChannels ) - { - channel.removeListener( listener ); - } - } - - /** - * Returns the list of channel change listeners that will be automatically - * added to all channels to receive channel change events - */ - public List getChannelListeners() - { - return mChannelListeners; - } - - /** - * Returns the list of channel change listeners that will be automatically - * added to all channels to receive channel change events - */ - public List> getMessageListeners() - { - return mMessageListeners; - } - - /** - * Adds a message listener that will be added to all channels to receive - * any messages. - */ - public void addListener( Listener listener ) - { - mMessageListeners.add( listener ); - } - - /** - * Removes a message listener. - */ - public void removeListener( Listener listener ) - { - mMessageListeners.remove( listener ); - } -} diff --git a/src/controller/channel/ChannelModel.java b/src/controller/channel/ChannelModel.java new file mode 100644 index 00000000..9efe8cc2 --- /dev/null +++ b/src/controller/channel/ChannelModel.java @@ -0,0 +1,343 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package controller.channel; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import javax.swing.table.AbstractTableModel; + +import module.decode.DecoderType; +import controller.channel.Channel.ChannelType; +import controller.channel.ChannelEvent.Event; + +/** + * Channel Model + */ +public class ChannelModel extends AbstractTableModel implements IChannelEventBroadcaster +{ + private static final long serialVersionUID = 1L; + + private List mChannels = new CopyOnWriteArrayList<>(); + private List mTrafficChannels = new CopyOnWriteArrayList<>(); + private List mListeners = new CopyOnWriteArrayList<>(); + + public ChannelModel() + { + } + + /** + * Unmodifiable list of non-traffic channels currently in the model + */ + public List getChannels() + { + return Collections.unmodifiableList( mChannels ); + } + + public Channel getChannelAtIndex( int row ) + { + if( mChannels.size() >= row ) + { + return mChannels.get( row ); + } + + return null; + } + + /** + * Returns a list of unique system values from across the channel set + */ + public List getSystems() + { + List systems = new ArrayList<>(); + + for( Channel channel: mChannels ) + { + if( channel.hasSystem() && !systems.contains( channel.getSystem() ) ) + { + systems.add( channel.getSystem() ); + } + } + + Collections.sort( systems ); + + return systems; + } + + /** + * Returns a list of unique site values for all channels that have a matching + * system value + */ + public List getSites( String system ) + { + List sites = new ArrayList<>(); + + if( system != null ) + { + for( Channel channel: mChannels ) + { + if( channel.hasSystem() && + system.equals( channel.getSystem() ) && + channel.hasSite() && + !sites.contains( channel.getSite() ) ) + { + sites.add( channel.getSite() ); + } + } + } + + Collections.sort( sites ); + + return sites; + } + + /** + * Broadcasts the channel event to all registered listeners + */ + @Override + public void broadcast( ChannelEvent event ) + { + for( ChannelEventListener listener: mListeners ) + { + listener.channelChanged( event ); + } + + if( event.getChannel().getChannelType() == ChannelType.STANDARD ) + { + switch( event.getEvent() ) + { + case NOTIFICATION_CONFIGURATION_CHANGE: + case NOTIFICATION_PROCESSING_START: + case NOTIFICATION_PROCESSING_STOP: + case NOTIFICATION_SELECTION_CHANGE: + int index = mChannels.indexOf( event.getChannel() ); + fireTableRowsUpdated( index, index ); + break; + default: + break; + } + } + + if( event.getEvent() == Event.REQUEST_DELETE ) + { + removeChannel( event.getChannel() ); + } + } + + /** + * Bulk loading of channel list. Each channel is added and a channel add + * event is broadcast. + */ + public void addChannels( List channels ) + { + for( Channel channel: channels ) + { + addChannel( channel ); + } + } + + /** + * Adds the channel to the model and broadcasts a channel add event + */ + public int addChannel( Channel channel ) + { + int index = -1; + + switch( channel.getChannelType() ) + { + case STANDARD: + mChannels.add( channel ); + + index = mChannels.size() - 1; + + fireTableRowsInserted( index, index ); + break; + case TRAFFIC: + mTrafficChannels.add( channel ); + index = mChannels.size() - 1; + break; + default: + break; + } + + broadcast( new ChannelEvent( channel, Event.NOTIFICATION_ADD ) ); + + if( channel.getEnabled() ) + { + broadcast( new ChannelEvent( channel, Event.REQUEST_ENABLE ) ); + } + + return index; + } + + /** + * Removes the channel from the model and broadcasts a channel remove event + */ + public void removeChannel( Channel channel ) + { + if( channel != null ) + { + switch( channel.getChannelType() ) + { + case STANDARD: + int index = mChannels.indexOf( channel ); + + mChannels.remove( channel ); + + if( index >= 0 ) + { + fireTableRowsDeleted( index, index ); + } + break; + case TRAFFIC: + mTrafficChannels.remove( channel ); + break; + default: + break; + } + + broadcast( new ChannelEvent( channel, Event.NOTIFICATION_DELETE ) ); + } + } + + /** + * Returns a list of channels that fall within the frequency range + * + * @param start frequency of the range + * @param stop frequency of the range + * + * @return list of channels or an empty list if none fall within the range + */ + public List getChannelsInFrequencyRange( long start, long stop ) + { + List channels = new ArrayList<>(); + + for( Channel channel: mChannels ) + { + if( channel.isWithin( start, stop ) ) + { + channels.add( channel ); + } + } + + for( Channel channel: mTrafficChannels ) + { + if( channel.isWithin( start, stop ) ) + { + channels.add( channel ); + } + } + + return channels; + } + + public void addListener( ChannelEventListener listener ) + { + mListeners.add( listener ); + } + + public void removeListener( ChannelEventListener listener ) + { + mListeners.remove( listener ); + } + + //Table Model Interface Methods - standard channels only + @Override + public int getRowCount() + { + return mChannels.size(); + } + + @Override + public int getColumnCount() + { + return 6; + } + + @Override + public String getColumnName( int columnIndex ) + { + switch( columnIndex ) + { + case 0: + return "System"; + case 1: + return "Site"; + case 2: + return "Name"; + case 3: + return "Alias List"; + case 4: + return "Source"; + case 5: + return "Decoder"; + } + + return null; + } + + @Override + public Class getColumnClass( int columnIndex ) + { + return String.class; + } + + @Override + public boolean isCellEditable( int rowIndex, int columnIndex ) + { + return false; + } + + @Override + public Object getValueAt( int rowIndex, int columnIndex ) + { + Channel channel = mChannels.get( rowIndex ); + + switch( columnIndex ) + { + case 0: + return channel.getSystem(); + case 1: + return channel.getSite(); + case 2: + return channel.getName(); + case 3: + return channel.getAliasListName(); + case 4: + return channel.getSourceConfiguration().getDescription(); + case 5: + return channel.getDecodeConfiguration() + .getDecoderType().getShortDisplayString(); + } + + return null; + } + + @Override + public void setValueAt( Object aValue, int rowIndex, int columnIndex ) + { + throw new IllegalArgumentException( "Not yet implemented" ); + } + + public void createChannel( DecoderType decoderType, long frequency ) + { + throw new UnsupportedOperationException( "Not yet implemented" ); + } +} diff --git a/src/controller/channel/ChannelNode.java b/src/controller/channel/ChannelNode.java deleted file mode 100644 index 3c5d70ab..00000000 --- a/src/controller/channel/ChannelNode.java +++ /dev/null @@ -1,187 +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 - ******************************************************************************/ -package controller.channel; - -import java.awt.Color; -import java.text.DecimalFormat; - -import javax.swing.JMenu; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.tuner.TunerChannel; -import controller.ConfigurableNode; -import controller.site.SiteNode; -import controller.system.SystemNode; - -/** - * A channel has all of the pieces needed to wire together a source, decoder, - * event logger and recorder and be started and stopped. - */ -public class ChannelNode extends ConfigurableNode implements ChannelEventListener -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = LoggerFactory.getLogger( ChannelNode.class ); - - private static DecimalFormat sFORMAT = new DecimalFormat( "0.0000" ); - - public ChannelNode( Channel channel ) - { - super( channel ); - } - - public void init() - { - /* Set the owning system and site names for the channel */ - SiteNode siteNode = (SiteNode)getParent(); - - if( siteNode != null ) - { - getChannel().setSite( siteNode.getSite(), false ); - - SystemNode systemNode = (SystemNode)siteNode.getParent(); - - if( systemNode != null ) - { - getChannel().setSystem( systemNode.getSystem(), false ); - } - } - - /* Add this node as listener to receive changes from underlying channel */ - getChannel().addListener( this ); - - /* Add the resource manager to the channel so that the channel - * can provide channel change events to all system resources */ - getChannel().setResourceManager( getModel().getResourceManager() ); - } - - /** - * Listener method to receive channel changes from the underlying chnannel - */ - @Override - public void channelChanged( ChannelEvent event ) - { - switch( event.getEvent() ) - { - /* Refresh the node for each of these events */ - case CHANGE_DECODER: - case CHANGE_NAME: - case CHANGE_SELECTED: - case CHANGE_SITE: - case CHANGE_SYSTEM: - case CHANNEL_ENABLED: - case CHANNEL_DISABLED: - case CHANNEL_PROCESSING_STARTED: - case CHANNEL_PROCESSING_STOPPED: - this.refresh(); - break; - /* We're being deleted, so cleanup */ - case CHANNEL_DELETED: - delete(); - break; - default: - break; - } - } - - @Override - public String getIconPath() - { - return getChannel().getDecodeConfiguration().getDecoderType() - .getIconFilename(); - } - - @Override - public Color getBackgroundColor() - { - if( getChannel().getEnabled() ) - { - if( getChannel().isProcessing() ) - { - return Color.GREEN; - } - else - { - return Color.RED; - } - } - - return null; - } - - @Override - public JPanel getEditor() - { - return new ChannelEditor( this, - getChannel().getResourceManager() == null ? null : - getChannel().getResourceManager().getPlaylistManager() ); - } - - public Channel getChannel() - { - return (Channel)this.getUserObject(); - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getChannel().getName() ); - - TunerChannel channel = getChannel().getTunerChannel(); - - if( channel != null ) - { - sb.append( " (" ); - sb.append( sFORMAT.format( (double)channel.getFrequency() / 1000000.0d ) + " MHz" ); - sb.append( ")" ); - - } - - return sb.toString(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu popupMenu = new JPopupMenu(); - - JMenu channelMenu = ChannelUtils.getContextMenu( - getModel().getResourceManager().getPlaylistManager(), - getChannel(), getModel().getTree() ); - - if( channelMenu != null ) - { - popupMenu.add( channelMenu ); - } - - return popupMenu; - } - - public void delete() - { - ((SiteNode)getParent()).getSite().removeChannel( getChannel() ); - - save(); - - getModel().removeNodeFromParent( ChannelNode.this ); - } -} diff --git a/src/controller/channel/ChannelProcessingManager.java b/src/controller/channel/ChannelProcessingManager.java new file mode 100644 index 00000000..91ca81f2 --- /dev/null +++ b/src/controller/channel/ChannelProcessingManager.java @@ -0,0 +1,334 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package controller.channel; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +import message.Message; +import module.Module; +import module.ProcessingChain; +import module.decode.DecoderFactory; +import module.decode.event.MessageActivityModel; +import module.log.EventLogManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import record.RecorderManager; +import record.RecorderType; +import sample.Listener; +import source.Source; +import source.SourceException; +import source.SourceManager; +import alias.AliasModel; +import audio.AudioPacket; +import audio.metadata.Metadata; +import audio.metadata.MetadataType; +import controller.channel.Channel.ChannelType; +import controller.channel.ChannelEvent.Event; +import controller.channel.map.ChannelMapModel; +import filter.FilterSet; + +public class ChannelProcessingManager implements ChannelEventListener +{ + private final static Logger mLog = LoggerFactory.getLogger( ChannelProcessingManager.class ); + + private Map mProcessingChains = new HashMap<>(); + + private List> mAudioPacketListeners = new CopyOnWriteArrayList<>(); + private List> mMessageListeners = new CopyOnWriteArrayList<>(); + + private ChannelModel mChannelModel; + private ChannelMapModel mChannelMapModel; + private AliasModel mAliasModel; + private EventLogManager mEventLogManager; + private RecorderManager mRecorderManager; + private SourceManager mSourceManager; + + public ChannelProcessingManager( ChannelModel channelModel, + ChannelMapModel channelMapModel, + AliasModel aliasModel, + EventLogManager eventLogManager, + RecorderManager recorderManager, + SourceManager sourceManager ) + { + mChannelModel = channelModel; + mChannelMapModel = channelMapModel; + mAliasModel = aliasModel; + mEventLogManager = eventLogManager; + mRecorderManager = recorderManager; + mSourceManager = sourceManager; + } + + /** + * Indicates if a processing chain is constructed for the channel and that + * the processing chain is currently processing. + */ + public boolean isProcessing( Channel channel ) + { + if( mProcessingChains.containsKey( channel.getChannelID() ) ) + { + return mProcessingChains.get( channel.getChannelID() ).isProcessing(); + } + + return false; + } + + /** + * Returns the current processing chain associated with the channel, or + * null if a processing chain is not currently setup for the channel + */ + public ProcessingChain getProcessingChain( Channel channel ) + { + return mProcessingChains.get( channel.getChannelID() ); + } + + @Override + public synchronized void channelChanged( ChannelEvent event ) + { + Channel channel = event.getChannel(); + + switch( event.getEvent() ) + { + case REQUEST_ENABLE: + if( !mProcessingChains.containsKey( channel.getChannelID() ) || + !mProcessingChains.get( channel.getChannelID() ).isProcessing() ) + { + startProcessing( event ); + } + break; + case REQUEST_DISABLE: + if( channel.getEnabled() ) + { + switch( channel.getChannelType() ) + { + case STANDARD: + stopProcessing( channel, true ); + break; + case TRAFFIC: + //Don't remove traffic channel processing chains + //until explicitly deleted, so that we can reuse them + stopProcessing( channel, false ); + default: + break; + } + } + case NOTIFICATION_DELETE: + if( channel.getEnabled() ) + { + stopProcessing( channel, true ); + } + break; + case NOTIFICATION_CONFIGURATION_CHANGE: + if( isProcessing( channel ) ) + { + stopProcessing( channel, false ); + startProcessing( event ); + } + break; + default: + break; + } + } + + private void startProcessing( ChannelEvent event ) + { + Channel channel = event.getChannel(); + + ProcessingChain processingChain = mProcessingChains.get( channel.getChannelID() ); + + //If we're already processing, ignore the request + if( processingChain != null && processingChain.isProcessing() ) + { + return; + } + + //Ensure that we can get a source before we construct a new processing chain + Source source = null; + + try + { + source = mSourceManager.getSource( channel.getSourceConfiguration(), + channel.getDecodeConfiguration().getDecoderType().getChannelBandwidth() ); + } + catch( SourceException se ) + { + mLog.debug( "Error obtaining source for channel [" + channel.getName() + "]", se ); + } + + if( source == null ) + { + channel.setEnabled( false ); + + mChannelModel.broadcast( + new ChannelEvent( channel, Event.NOTIFICATION_ENABLE_REJECTED ) ); + + return; + } + + if( processingChain == null ) + { + processingChain = new ProcessingChain( channel.getName(), + channel.getChannelType() ); + + /* Register global listeners */ + for( Listener listener: mAudioPacketListeners ) + { + processingChain.addAudioPacketListener( listener ); + } + + for( Listener listener: mMessageListeners ) + { + processingChain.addMessageListener( listener ); + } + + /* Processing Modules */ + List modules = DecoderFactory.getModules( mChannelModel, + mChannelMapModel, this, mAliasModel, channel ); + processingChain.addModules( modules ); + + /* Setup message activity model with filtering */ + FilterSet messageFilter = DecoderFactory.getMessageFilters( modules ); + MessageActivityModel messageModel = new MessageActivityModel( messageFilter ); + processingChain.setMessageActivityModel( messageModel ); + + /* Setup event logging */ + List loggers = mEventLogManager.getLoggers( + channel.getEventLogConfiguration(), channel.getName() ); + + if( !loggers.isEmpty() ) + { + processingChain.addModules( loggers ); + } + + /* Setup recorders */ + List recorders = channel.getRecordConfiguration().getRecorders(); + + if( !recorders.isEmpty() ) + { + if( recorders.contains( RecorderType.AUDIO ) ) + { + processingChain.addAudioPacketListener( mRecorderManager ); + } + + /* Add baseband recorder */ + if( ( recorders.contains( RecorderType.BASEBAND ) && + channel.getChannelType() == ChannelType.STANDARD ) ) + { + processingChain.addModule( mRecorderManager.getBasebandRecorder( + channel.toString() ) ); + } + + /* Add traffic channel baseband recorder */ + if( recorders.contains( RecorderType.TRAFFIC_BASEBAND ) && + channel.getChannelType() == ChannelType.TRAFFIC ) + { + processingChain.addModule( mRecorderManager + .getBasebandRecorder( channel.toString() ) ); + } + } + + /* Inject channel metadata that will be inserted into audio packets + * for the recorder manager and streaming */ + processingChain.broadcast( + new Metadata( MetadataType.SYSTEM, channel.getSystem() ) ); + processingChain.broadcast( + new Metadata( MetadataType.SITE_ID, channel.getSite() ) ); + processingChain.broadcast( + new Metadata( MetadataType.CHANNEL_NAME, channel.getName() ) ); + } + + processingChain.setSource( source ); + + if( event instanceof TrafficChannelEvent ) + { + TrafficChannelEvent trafficChannelEvent = (TrafficChannelEvent)event; + + processingChain.getChannelState().configureAsTrafficChannel( + trafficChannelEvent.getTrafficChannelManager(), + trafficChannelEvent.getCallEvent() ); + } + + processingChain.start(); + + channel.setEnabled( true ); + + mProcessingChains.put( channel.getChannelID(), processingChain ); + + mChannelModel.broadcast( + new ChannelEvent( channel, Event.NOTIFICATION_PROCESSING_START ) ); + } + + private void stopProcessing( Channel channel, boolean remove ) + { + channel.setEnabled( false ); + + if( mProcessingChains.containsKey( channel.getChannelID() ) ) + { + ProcessingChain chain = mProcessingChains.get( channel.getChannelID() ); + + chain.stop(); + + mChannelModel.broadcast( new ChannelEvent( channel, Event.NOTIFICATION_PROCESSING_STOP ) ); + + if( remove ) + { + mProcessingChains.remove( channel.getChannelID() ); + + chain.dispose(); + } + } + } + + /** + * Adds a message listener that will be added to all channels to receive + * any messages. + */ + public void addAudioPacketListener( Listener listener ) + { + mAudioPacketListeners.add( listener ); + } + + /** + * Removes a message listener. + */ + public void removeAudioPacketListener( Listener listener ) + { + mAudioPacketListeners.remove( listener ); + } + + /** + * Adds a message listener that will be added to all channels to receive + * any messages. + */ + public void addMessageListener( Listener listener ) + { + mMessageListeners.add( listener ); + } + + /** + * Removes a message listener. + */ + public void removeMessageListener( Listener listener ) + { + mMessageListeners.remove( listener ); + } +} diff --git a/src/controller/channel/ChannelSelectionManager.java b/src/controller/channel/ChannelSelectionManager.java new file mode 100644 index 00000000..65ac9b91 --- /dev/null +++ b/src/controller/channel/ChannelSelectionManager.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ +package controller.channel; + +import controller.channel.ChannelEvent.Event; + +public class ChannelSelectionManager implements ChannelEventListener +{ + private IChannelEventBroadcaster mChannelEventBroadcaster; + private Channel mSelectedChannel = null; + + /** + * Manages channel selection state to ensure that only one channel is ever + * in a selected state. + */ + public ChannelSelectionManager( IChannelEventBroadcaster broadcaster ) + { + mChannelEventBroadcaster = broadcaster; + } + + @Override + public void channelChanged( ChannelEvent event ) + { + switch( event.getEvent() ) + { + case REQUEST_SELECT: + if( mSelectedChannel != null ) + { + mSelectedChannel.setSelected( false ); + mChannelEventBroadcaster.broadcast( new ChannelEvent( mSelectedChannel, + Event.NOTIFICATION_SELECTION_CHANGE ) ); + mSelectedChannel = null; + } + + mSelectedChannel = event.getChannel(); + mSelectedChannel.setSelected( true ); + mChannelEventBroadcaster.broadcast( new ChannelEvent( event.getChannel(), + Event.NOTIFICATION_SELECTION_CHANGE ) ); + break; + case NOTIFICATION_PROCESSING_STOP: + case REQUEST_DESELECT: + if( mSelectedChannel != null && + mSelectedChannel == event.getChannel() ) + { + mSelectedChannel.setSelected( false ); + mSelectedChannel = null; + mChannelEventBroadcaster.broadcast( new ChannelEvent( event.getChannel(), + Event.NOTIFICATION_SELECTION_CHANGE ) ); + } + break; + default: + break; + } + } + +} diff --git a/src/controller/channel/ChannelUtils.java b/src/controller/channel/ChannelUtils.java index 5f50f6e6..6434eb4c 100644 --- a/src/controller/channel/ChannelUtils.java +++ b/src/controller/channel/ChannelUtils.java @@ -9,23 +9,20 @@ import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JSeparator; +import module.ProcessingChain; import module.decode.event.ActivitySummaryFrame; import module.decode.state.DecoderState; -import playlist.PlaylistManager; import controller.channel.ChannelEvent.Event; public class ChannelUtils { - private static final boolean ENABLED = true; - private static final boolean DISABLED = false; - private static final boolean BROADCAST_CHANGE = true; - /** * Creates a context menu for the channel argument */ - public static JMenu getContextMenu( final PlaylistManager playlistManager, - final Channel channel, - final Component anchor ) + public static JMenu getContextMenu( final ChannelModel channelModel, + final ChannelProcessingManager channelProcessingManager, + final Channel channel, + final Component anchor ) { if( channel != null ) { @@ -39,12 +36,8 @@ public class ChannelUtils @Override public void actionPerformed( ActionEvent e ) { - channel.setEnabled( DISABLED, BROADCAST_CHANGE ); - - if( playlistManager != null ) - { - playlistManager.save(); - } + channelModel.broadcast( + new ChannelEvent( channel, Event.REQUEST_DISABLE ) ); } } ); @@ -62,10 +55,14 @@ public class ChannelUtils { StringBuilder sb = new StringBuilder(); - for( DecoderState decoderState: channel - .getProcessingChain().getDecoderStates() ) + ProcessingChain chain = channelProcessingManager.getProcessingChain( channel ); + + if( chain != null ) { - sb.append( decoderState.getActivitySummary() ); + for( DecoderState decoderState: chain.getDecoderStates() ) + { + sb.append( decoderState.getActivitySummary() ); + } } new ActivitySummaryFrame( sb.toString(), anchor ); @@ -82,12 +79,8 @@ public class ChannelUtils @Override public void actionPerformed( ActionEvent e ) { - channel.setEnabled( ENABLED, BROADCAST_CHANGE ); - - if( playlistManager != null ) - { - playlistManager.save(); - } + channelModel.broadcast( + new ChannelEvent( channel, Event.REQUEST_ENABLE ) ); } } ); @@ -108,16 +101,8 @@ public class ChannelUtils if( response == JOptionPane.YES_OPTION ) { - /* Disable the channel */ - channel.setEnabled( DISABLED, BROADCAST_CHANGE ); - - /* Broadcast channel deleted event */ - channel.fireChannelEvent( Event.CHANNEL_DELETED ); - - if( playlistManager != null ) - { - playlistManager.save(); - } + channelModel.broadcast( + new ChannelEvent( channel, Event.REQUEST_DELETE ) ); } } } ); diff --git a/src/controller/channel/ChannelValidationException.java b/src/controller/channel/ChannelValidationException.java deleted file mode 100644 index 53422a11..00000000 --- a/src/controller/channel/ChannelValidationException.java +++ /dev/null @@ -1,11 +0,0 @@ -package controller.channel; - -public class ChannelValidationException extends Exception -{ - private static final long serialVersionUID = 1L; - - public ChannelValidationException( String validationMessage ) - { - super( validationMessage ); - } -} diff --git a/src/controller/channel/ConfigurationValidationException.java b/src/controller/channel/ConfigurationValidationException.java new file mode 100644 index 00000000..a61862c5 --- /dev/null +++ b/src/controller/channel/ConfigurationValidationException.java @@ -0,0 +1,23 @@ +package controller.channel; + +import java.awt.Component; + +public class ConfigurationValidationException extends Exception +{ + private static final long serialVersionUID = 1L; + + private Component mComponent; + + public ConfigurationValidationException( Component component, + String validationMessage ) + { + super( validationMessage ); + + mComponent = component; + } + + public Component getComponent() + { + return mComponent; + } +} diff --git a/src/source/SampleProvider.java b/src/controller/channel/IChannelEventBroadcaster.java similarity index 78% rename from src/source/SampleProvider.java rename to src/controller/channel/IChannelEventBroadcaster.java index 3cc84749..ef7c89fd 100644 --- a/src/source/SampleProvider.java +++ b/src/controller/channel/IChannelEventBroadcaster.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 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 @@ -15,14 +15,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package source; +package controller.channel; -import sample.SampleType; - -public interface SampleProvider +/** + * Provides channel event broadcasting capability. + */ +public interface IChannelEventBroadcaster { /** - * Real or Complex + * Broadcasts the channel event */ - public abstract SampleType getSampleType(); + public void broadcast( ChannelEvent event ); } diff --git a/src/controller/channel/NameConfigurationEditor.java b/src/controller/channel/NameConfigurationEditor.java new file mode 100644 index 00000000..e0525bdf --- /dev/null +++ b/src/controller/channel/NameConfigurationEditor.java @@ -0,0 +1,269 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package controller.channel; + +import gui.editor.Editor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Collections; +import java.util.List; + +import javax.swing.ComboBoxModel; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.miginfocom.swing.MigLayout; +import alias.AliasModel; + +public class NameConfigurationEditor extends Editor implements DocumentListener +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = LoggerFactory.getLogger( NameConfigurationEditor.class ); + + private static ComboBoxModel EMPTY_MODEL = new DefaultComboBoxModel<>(); + + private AliasModel mAliasModel; + private ChannelModel mChannelModel; + + private JTextField mChannelName; + private JComboBox mSystemNameCombo; + private JComboBox mSiteNameCombo; + private JComboBox mAliasListCombo; + + public NameConfigurationEditor( AliasModel aliasModel, ChannelModel model ) + { + mAliasModel = aliasModel; + mChannelModel = model; + + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", "[][][grow]" ) ); + + mChannelName = new JTextField( "" ); + mChannelName.getDocument().addDocumentListener( this ); + mChannelName.setEnabled( false ); + + add( new JLabel( "Name:" ) ); + add( mChannelName ); + + mSystemNameCombo = new JComboBox<>( EMPTY_MODEL ); + mSystemNameCombo.setEnabled( false ); + mSystemNameCombo.setEditable( true ); + mSystemNameCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + Object selected = mSystemNameCombo.getSelectedItem(); + + if( selected != null ) + { + String system = (String)selected; + + List sites = mChannelModel.getSites( system ); + + if( !sites.contains( getItem().getSite() ) ) + { + sites.add( getItem().getSite() ); + } + + if( sites.isEmpty() ) + { + mSiteNameCombo.setModel( EMPTY_MODEL ); + } + else + { + mSiteNameCombo.setModel( new DefaultComboBoxModel( + sites.toArray( new String[ sites.size() ] ) ) );; + } + + mSiteNameCombo.setSelectedItem( getItem().getSite() ); + } + + setModified( true ); + } + } ); + + add( new JLabel( "System:" ) ); + add( mSystemNameCombo ); + + mAliasListCombo = new JComboBox(); + mAliasListCombo.setEnabled( false ); + mAliasListCombo.setEditable( true ); + mAliasListCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + + add( new JLabel( "Alias List:" ) ); + add( mAliasListCombo ); + + mSiteNameCombo = new JComboBox<>( EMPTY_MODEL ); + mSiteNameCombo.setEnabled( false ); + mSiteNameCombo.setEditable( true ); + mSiteNameCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + + add( new JLabel( "Site:" ) ); + add( mSiteNameCombo ); + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + getItem().setName( mChannelName.getText() ); + + Object system = mSystemNameCombo.getSelectedItem(); + getItem().setSystem( ( system == null ? null : (String)system ) ); + + Object site = mSiteNameCombo.getSelectedItem(); + getItem().setSite( ( site == null ? null : (String)site ) ); + + Object aliasList = mAliasListCombo.getSelectedItem(); + getItem().setAliasListName( aliasList == null ? null : (String)aliasList ); + } + + setModified( false ); + } + + private void setControlsEnabled( boolean enabled ) + { + if( mChannelName.isEnabled() != enabled ) + { + mChannelName.setEnabled( enabled ); + } + + if( mAliasListCombo.isEnabled() != enabled ) + { + mAliasListCombo.setEnabled( enabled ); + } + + if( mSystemNameCombo.isEnabled() != enabled ) + { + mSystemNameCombo.setEnabled( enabled ); + } + + if( mSiteNameCombo.isEnabled() != enabled ) + { + mSiteNameCombo.setEnabled( enabled ); + } + } + + @Override + public void setItem( Channel channel ) + { + super.setItem( channel ); + + if( hasItem() ) + { + setControlsEnabled( true ); + + mChannelName.setText( channel.getName() ); + + List systems = mChannelModel.getSystems(); + + if( systems.isEmpty() ) + { + mSystemNameCombo.setModel( EMPTY_MODEL ); + } + else + { + mSystemNameCombo.setModel( new DefaultComboBoxModel( + systems.toArray( new String[ systems.size() ] ) ) );; + } + + mSystemNameCombo.setSelectedItem( channel.getSystem() ); + + List sites = mChannelModel.getSites( channel.getSystem() ); + + if( sites.isEmpty() ) + { + mSiteNameCombo.setModel( EMPTY_MODEL ); + } + else + { + mSiteNameCombo.setModel( new DefaultComboBoxModel( + sites.toArray( new String[ sites.size() ] ) ) );; + } + + if( channel.getSite() != null ) + { + mSiteNameCombo.setSelectedItem( channel.getSite() ); + } + + List lists = mAliasModel.getListNames(); + Collections.sort( lists ); + mAliasListCombo.setModel( new DefaultComboBoxModel( + lists.toArray( new String[ lists.size() ] ) ) ); + + if( channel.getAliasListName() != null ) + { + mAliasListCombo.setSelectedItem( channel.getAliasListName() ); + } + } + else + { + setControlsEnabled( false ); + mChannelName.setText( null ); + } + + setModified( false ); + } + + @Override + public void insertUpdate( DocumentEvent e ) + { + setModified( true ); + } + + @Override + public void changedUpdate( DocumentEvent e ) + { + setModified( true ); + } + + @Override + public void removeUpdate( DocumentEvent e ) + { + setModified( true ); + } +} diff --git a/src/controller/channel/TrafficChannelEvent.java b/src/controller/channel/TrafficChannelEvent.java new file mode 100644 index 00000000..20c4aade --- /dev/null +++ b/src/controller/channel/TrafficChannelEvent.java @@ -0,0 +1,34 @@ +package controller.channel; + +import module.decode.event.CallEvent; +import module.decode.state.TrafficChannelManager; + +public class TrafficChannelEvent extends ChannelEvent +{ + private TrafficChannelManager mTrafficChannelManager; + private CallEvent mCallEvent; + + /** + * Call event with traffic channel manager for call back, and original creation event. + */ + public TrafficChannelEvent( TrafficChannelManager trafficChannelManager, + Channel channel, + Event event, + CallEvent callEvent ) + { + super( channel, event ); + + mTrafficChannelManager = trafficChannelManager; + mCallEvent = callEvent; + } + + public TrafficChannelManager getTrafficChannelManager() + { + return mTrafficChannelManager; + } + + public CallEvent getCallEvent() + { + return mCallEvent; + } +} diff --git a/src/controller/channel/map/ChannelMap.java b/src/controller/channel/map/ChannelMap.java index 3ec928a0..107280f8 100644 --- a/src/controller/channel/map/ChannelMap.java +++ b/src/controller/channel/map/ChannelMap.java @@ -18,17 +18,20 @@ package controller.channel.map; import java.util.ArrayList; +import java.util.List; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlSeeAlso; +import javax.xml.bind.annotation.XmlTransient; @XmlSeeAlso( { ChannelRange.class } ) public class ChannelMap { private String mName; + private boolean mInvalid = false; - private ArrayList mRanges = new ArrayList(); + private List mRanges = new ArrayList(); public ChannelMap() { @@ -39,17 +42,10 @@ public class ChannelMap { mName = name; } - - public String toString() - { - return mName; - } - /** - * Creates a copy of this channel map - */ + public ChannelMap copyOf() { - ChannelMap map = new ChannelMap( mName ); + ChannelMap map = new ChannelMap( new String( mName ) ); for( ChannelRange range: mRanges ) { @@ -58,29 +54,29 @@ public class ChannelMap return map; } - - public int getInvalidRange() + + @XmlTransient + public boolean isInvalid() { - for( int x = 0; x < mRanges.size(); x++ ) - { - if( !mRanges.get( x ).isValid() ) - { - return x; - } - } - - return -1; + return mInvalid; + } + + public String toString() + { + return mName + ( mInvalid ? " - Error" : "" ); } - public ArrayList getRanges() + @XmlElement( name = "range" ) + public List getRanges() { return mRanges; } - @XmlElement( name = "range" ) - public void setRanges( ArrayList ranges ) + public void setRanges( List ranges ) { mRanges = ranges; + + validate(); } @XmlAttribute @@ -106,15 +102,44 @@ public class ChannelMap return 0; } + + /** + * Validates each of the channel ranges for overlap + */ + private void validate() + { + mInvalid = false; + + for( int x = 0; x < mRanges.size(); x++ ) + { + if( !mRanges.get( x ).isValid() ) + { + mInvalid = true; + } + + for( int y = x + 1; y < mRanges.size(); y++ ) + { + if( mRanges.get( x ).overlaps( mRanges.get( y ) ) ) + { + mInvalid = true; + } + } + } + } + public void addRange( ChannelRange range ) { mRanges.add( range ); + + validate(); } - public void deleteRange( ChannelRange range ) + public void removeRange( ChannelRange range ) { mRanges.remove( range ); + + validate(); } } diff --git a/src/controller/channel/map/ChannelMapEditor.java b/src/controller/channel/map/ChannelMapEditor.java index 11ede472..ef7c4374 100644 --- a/src/controller/channel/map/ChannelMapEditor.java +++ b/src/controller/channel/map/ChannelMapEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,75 +17,112 @@ ******************************************************************************/ package controller.channel.map; +import gui.editor.DocumentListenerEditor; + import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; import javax.swing.AbstractCellEditor; import javax.swing.JButton; import javax.swing.JFormattedTextField; import javax.swing.JLabel; +import javax.swing.JList; import javax.swing.JOptionPane; -import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import javax.swing.table.TableCellEditor; import javax.swing.text.InternationalFormatter; import net.miginfocom.swing.MigLayout; +import controller.channel.map.ChannelMapEvent.Event; +import controller.channel.map.ChannelRangeModel.ChannelRangeEventListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ChannelMapEditor extends JPanel implements ActionListener +public class ChannelMapEditor extends DocumentListenerEditor + implements ListSelectionListener, ChannelRangeEventListener { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( ChannelMapEditor.class ); - private ChannelMapNode mChannelMapNode; - private JLabel mNameLabel; private JTextField mNameText; - private ChannelMapModel mRangeModel; + private ChannelRangeModel mRangeModel; private JTable mRangeTable; - private JButton mDelete; - private ChannelMap mChannelMapCopy; + private JButton mNewButton; + private JButton mDeleteButton; + private JButton mSaveButton; + private JLabel mRangeDescription; + private ChannelMapModel mChannelMapModel; - public ChannelMapEditor( ChannelMapNode ChannelMap ) + public ChannelMapEditor( ChannelMapModel channelMapModel ) { - mChannelMapNode = ChannelMap; - mChannelMapCopy = mChannelMapNode.getChannelMap().copyOf(); - initGUI(); + mChannelMapModel = channelMapModel; + init(); } - private void initGUI() + public ChannelMap getChannelMap() { - setLayout( new MigLayout( "fill,wrap 2", "[right][grow]", "[][][grow][][]" ) ); + if( hasItem() ) + { + return (ChannelMap)getItem(); + } - add( new JLabel( "Channel Map" ), "span, align center" ); + return null; + } + + @Override + public void setItem( ChannelMap item ) + { + super.setItem( item ); - mNameLabel = new JLabel( "Name:" ); + if( hasItem() ) + { + ChannelMap map = getChannelMap(); + + mNameText.setText( map.getName() ); + + mRangeModel.clear(); + mRangeModel.addRanges( map.getRanges() ); + + mNewButton.setEnabled( true ); + } + else + { + mNameText.setText( "" ); + + mRangeModel.clear(); + + mNewButton.setEnabled( false ); + } + + setModified( false ); + } + + @Override + public void setModified( boolean modified ) + { + super.setModified( modified ); + + mSaveButton.setEnabled( modified ); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 3", + "[grow,fill][grow,fill][grow,fill]", "[][grow][][]" ) ); + + mNameLabel = new JLabel( "Channel Map:" ); add( mNameLabel ); - mNameText = new JTextField( mChannelMapCopy.getName() ); - mNameText.addFocusListener( new FocusListener() - { - @Override public void focusGained( FocusEvent arg0 ) {} + mNameText = new JTextField(); + mNameText.getDocument().addDocumentListener( this ); + add( mNameText, "span" ); - @Override - public void focusLost( FocusEvent arg0 ) - { - mChannelMapCopy.setName( mNameText.getText() ); - } - } ); - add( mNameText, "growx, push" ); - - mRangeModel = new ChannelMapModel( mChannelMapCopy ); + mRangeModel = new ChannelRangeModel(); + mRangeModel.setListener( this ); mRangeTable = new JTable( mRangeModel ); mRangeTable.setPreferredScrollableViewportSize( new Dimension( 300, 150 ) ); @@ -96,129 +133,201 @@ public class ChannelMapEditor extends JPanel implements ActionListener mRangeTable.getColumnModel().getColumn( x ) .setCellEditor( new RangeTableEditor() ); } + + mRangeTable.getSelectionModel().addListSelectionListener( new ListSelectionListener() + { + @Override + public void valueChanged( ListSelectionEvent event ) + { + if( !event.getValueIsAdjusting() ) + { + int index = event.getFirstIndex(); + + mDeleteButton.setEnabled( index >= 0 ); + + if( index >= 0 ) + { + int modelIndex = mRangeTable.convertRowIndexToModel( index ); + + if( modelIndex >= 0 ) + { + ChannelRange selected = mRangeModel.getChannelRange( modelIndex ); + + if( selected != null ) + { + mRangeDescription.setText( selected.getDescription() ); + } + else + { + mRangeDescription.setText( "" ); + } + } + } + } + } + } ); JScrollPane scrollPane = new JScrollPane( mRangeTable ); add( scrollPane, "span,grow,push"); - JButton btnNew = new JButton( "New Range" ); - btnNew.addActionListener( ChannelMapEditor.this ); - add( btnNew ); - - mDelete = new JButton( "Delete" ); - mDelete.addActionListener( ChannelMapEditor.this ); - add( mDelete, "wrap" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( ChannelMapEditor.this ); - add( btnSave, "growx" ); - - JButton btnReset = new JButton( "Reset " ); - btnReset.addActionListener( ChannelMapEditor.this ); - add( btnReset, "wrap" ); - } - - private boolean checkForValidRanges() - { - int invalidRow = mChannelMapCopy.getInvalidRange(); - - if( invalidRow != -1 && invalidRow <= mRangeTable.getRowCount() ) - { - mRangeTable.setRowSelectionInterval( invalidRow, invalidRow ); - - JOptionPane.showMessageDialog( this, - "Starting channel number must be less than ending channel number", - "Channel number range error", - JOptionPane.WARNING_MESSAGE ); - - return false; - } + mRangeDescription = new JLabel( " " ); + add( mRangeDescription, "span" ); - return true; + mSaveButton = new JButton( "Save" ); + mSaveButton.setEnabled( false ); + mSaveButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + save(); + } + } ); + add( mSaveButton ); + + mNewButton = new JButton( "New Range" ); + mNewButton.setEnabled( false ); + mNewButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + mRangeModel.addRange( new ChannelRange() ); + } + } ); + add( mNewButton ); + + mDeleteButton = new JButton( "DeleteRange" ); + mDeleteButton.setEnabled( false ); + mDeleteButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + int selectedRow = mRangeTable.getSelectedRow(); + + if( selectedRow >= 0 ) + { + int modelRow = mRangeTable.convertRowIndexToModel( selectedRow ); + + ChannelRange selectedRange = mRangeModel.getChannelRange( modelRow ); + + if( selectedRange != null ) + { + int choice = JOptionPane.showConfirmDialog( mRangeTable, + "Do you want to delete this channel range?", + "Delete Channel Range?", JOptionPane.YES_NO_OPTION ); + + if( choice == JOptionPane.YES_OPTION ) + { + mRangeModel.removeRange( selectedRange ); + mDeleteButton.setEnabled( false ); + } + } + } + } + } ); + add( mDeleteButton, "wrap" ); } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - switch( command ) + public void save() + { + if( isModified() && hasItem() ) { - case "New Range": - mRangeModel.addRange( new ChannelRange() ); - break; - case "Save": - if( checkForValidRanges() ) + ChannelMap channelMap = getChannelMap(); + + channelMap.setName( mNameText.getText() ); + channelMap.setRanges( mRangeModel.getChannelRanges() ); + + mChannelMapModel.broadcast( new ChannelMapEvent( getChannelMap(), Event.CHANGE ) ); + setModified( false ); + } + } + + @Override + public void valueChanged( ListSelectionEvent e ) + { + if( !e.getValueIsAdjusting() ) + { + if( e.getSource() instanceof JList ) + { + JList list = (JList)e.getSource(); + + if( list.getSelectedValue() instanceof ChannelMap ) { - mChannelMapNode.getChannelMap() - .setName( mChannelMapCopy.getName() ); - mChannelMapNode.getChannelMap() - .setRanges( mChannelMapCopy.getRanges() ); - - mChannelMapNode.save(); - - mChannelMapNode.refresh(); - - mChannelMapNode.show(); - } - case "Reset": - mChannelMapCopy = mChannelMapNode.getChannelMap(); - mRangeModel.setChannelMap( mChannelMapCopy ); - mNameText.setText( mChannelMapCopy.getName() ); - break; - case "Delete": - int row = mRangeTable.getSelectedRow(); - - if( row < 0 ) - { - JOptionPane.showMessageDialog( this, - "Please select a channel range to delete", - "Select a range", - JOptionPane.WARNING_MESSAGE ); + setItem( (ChannelMap)list.getSelectedValue() ); } else { - mRangeModel.removeRange( - mRangeTable.convertRowIndexToModel( row ) ); + setItem( null ); } - break; + } } - } + } + + @Override + public void channelRangesChanged() + { + setModified( true ); + + //Update description + int index = mRangeTable.getSelectedRow(); + + if( index >= 0 ) + { + ChannelRange selected = mRangeModel.getChannelRange( + mRangeTable.convertRowIndexToModel( index ) ); + + if( selected != null ) + { + mRangeDescription.setText( selected.getDescription() ); + } + else + { + mRangeDescription.setText( "" ); + } + } + } public class RangeTableEditor extends AbstractCellEditor - implements TableCellEditor, ActionListener + implements TableCellEditor, ActionListener { + private static final long serialVersionUID = 1L; + JFormattedTextField mEditor; - + public RangeTableEditor() { InternationalFormatter nf = new InternationalFormatter(); nf.setMinimum( 1 ); - nf.setAllowsInvalid( false ); + nf.setAllowsInvalid( true ); mEditor = new JFormattedTextField( nf ); } @Override - public Object getCellEditorValue() - { - return mEditor.getText(); - } - + public Object getCellEditorValue() + { + return mEditor.getText(); + } + @Override - public void actionPerformed( ActionEvent e ) - { - } - + public void actionPerformed( ActionEvent e ) + { + } + @Override - public Component getTableCellEditorComponent( JTable table, - Object value, - boolean isSelected, - int row, - int column ) - { + public Component getTableCellEditorComponent( JTable table, + Object value, + boolean isSelected, + int row, + int column ) + { mEditor.setValue( value ); return mEditor; - } - } + } + } + } diff --git a/src/controller/channel/map/ChannelMapEvent.java b/src/controller/channel/map/ChannelMapEvent.java new file mode 100644 index 00000000..a3ca8821 --- /dev/null +++ b/src/controller/channel/map/ChannelMapEvent.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package controller.channel.map; + +public class ChannelMapEvent +{ + private ChannelMap mChannelMap; + private Event mEvent; + private String mPreviousChannelMapName; + + /** + * ChannelMapEvent - event describing any changes to a channel map + * + * @param channelMap that changed + * @param event describing the change + */ + public ChannelMapEvent( ChannelMap channelMap, Event event ) + { + mChannelMap = channelMap; + mEvent = event; + } + + /** + * ChannelMapEvent - channel map renaming event. + * + * @param channelMap that changed + * @param previousName of the channel map + */ + public ChannelMapEvent( ChannelMap channelMap, String previousName ) + { + this( channelMap, Event.RENAME ); + + mPreviousChannelMapName = previousName; + } + + public ChannelMap getChannelMap() + { + return mChannelMap; + } + + public String getPreviousChannelMapName() + { + return mPreviousChannelMapName; + } + + public Event getEvent() + { + return mEvent; + } + + /** + * Events + */ + public enum Event + { + ADD, + CHANGE, + RENAME, + DELETE; + } +} diff --git a/src/controller/channel/map/ChannelMapListNode.java b/src/controller/channel/map/ChannelMapListNode.java deleted file mode 100644 index 208e22a4..00000000 --- a/src/controller/channel/map/ChannelMapListNode.java +++ /dev/null @@ -1,87 +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 - ******************************************************************************/ -package controller.channel.map; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; - -import controller.ConfigurableNode; - -public class ChannelMapListNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - public ChannelMapListNode( ChannelMapList list ) - { - super( list ); - } - - public ChannelMapList getChannelMapList() - { - return (ChannelMapList)getUserObject(); - } - - public void init() - { - for( ChannelMap channelMap: getChannelMapList().getChannelMap() ) - { - ChannelMapNode node = new ChannelMapNode( channelMap ); - - getModel().addNode( node, ChannelMapListNode.this, getChildCount() ); - - node.init(); - } - - sort(); - } - - public String toString() - { - return "Channel Maps [" + this.getChildCount() + "]"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Add Channel Map" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - ChannelMap channelMap = new ChannelMap(); - - getChannelMapList().addChannelMap( channelMap ); - - getModel().addNode( new ChannelMapNode( channelMap ), - ChannelMapListNode.this, - ChannelMapListNode.this.getChildCount() ); - - save(); - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/controller/channel/map/ChannelMapManager.java b/src/controller/channel/map/ChannelMapManager.java new file mode 100644 index 00000000..78e0608e --- /dev/null +++ b/src/controller/channel/map/ChannelMapManager.java @@ -0,0 +1,135 @@ +package controller.channel.map; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import net.miginfocom.swing.MigLayout; + +import com.jidesoft.swing.JideSplitPane; + +public class ChannelMapManager extends JPanel +{ + private static final long serialVersionUID = 1L; + + private ChannelMapModel mChannelMapModel; + private JList mChannelMapList; + private ChannelMapEditor mChannelMapEditor; + private JButton mDeleteMapButton; + private JButton mCloneMapButton; + + public ChannelMapManager( ChannelMapModel channelMapModel ) + { + mChannelMapModel = channelMapModel; + mChannelMapList = new JList<>( mChannelMapModel ); + mChannelMapEditor = new ChannelMapEditor( mChannelMapModel ); + + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 1 1 1 1", "[fill,grow]", "[fill,grow]" ) ); + + JPanel listPanel = new JPanel(); + listPanel.setLayout( new MigLayout( "", + "[fill,grow][fill,grow][fill,grow]", "[fill,grow][]" ) ); + + mChannelMapList.addListSelectionListener( mChannelMapEditor ); + mChannelMapList.addListSelectionListener( new ListSelectionListener() + { + @Override + public void valueChanged( ListSelectionEvent e ) + { + if( mChannelMapList.getSelectedValue() != null ) + { + mCloneMapButton.setEnabled( true ); + mDeleteMapButton.setEnabled( true ); + } + else + { + mCloneMapButton.setEnabled( false ); + mDeleteMapButton.setEnabled( false ); + } + } + } ); + mChannelMapList.setLayoutOrientation( JList.VERTICAL ); + mChannelMapList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + + JScrollPane listScroller = new JScrollPane( mChannelMapList ); + + listPanel.add( listScroller, "span" ); + + JButton newMapButton = new JButton( "New" ); + newMapButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + ChannelMap channelMap = new ChannelMap( "New Channel Map" ); + mChannelMapModel.addChannelMap( channelMap ); + mChannelMapList.setSelectedValue( channelMap, true ); + } + } ); + + listPanel.add( newMapButton ); + + mCloneMapButton = new JButton( "Clone" ); + mCloneMapButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + ChannelMap selected = mChannelMapList.getSelectedValue(); + + if( selected != null ) + { + ChannelMap copy = selected.copyOf(); + mChannelMapModel.addChannelMap( copy ); + mChannelMapList.setSelectedValue( copy, true ); + } + } + } ); + listPanel.add( mCloneMapButton ); + + mDeleteMapButton = new JButton( "Delete" ); + mDeleteMapButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + ChannelMap channelMap = mChannelMapList.getSelectedValue(); + + if( channelMap != null ) + { + int choice = JOptionPane.showConfirmDialog( mChannelMapList, + "Do you want to delete this channel map?", + "Delete Channel Map?", JOptionPane.YES_NO_OPTION ); + + if( choice == JOptionPane.YES_OPTION ) + { + mChannelMapModel.removeChannelMap( channelMap ); + } + } + } + } ); + listPanel.add( mDeleteMapButton ); + + JScrollPane editorScroller = new JScrollPane( mChannelMapEditor ); + + JideSplitPane splitPane = new JideSplitPane(); + splitPane.setOrientation( JideSplitPane.HORIZONTAL_SPLIT ); + splitPane.add( listPanel ); + splitPane.add( editorScroller ); + + add( splitPane ); + } +} diff --git a/src/controller/channel/map/ChannelMapManagerFrame.java b/src/controller/channel/map/ChannelMapManagerFrame.java new file mode 100644 index 00000000..04812979 --- /dev/null +++ b/src/controller/channel/map/ChannelMapManagerFrame.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package controller.channel.map; + +import java.awt.EventQueue; + +import javax.swing.JFrame; + +import net.miginfocom.swing.MigLayout; + +public class ChannelMapManagerFrame extends JFrame +{ + private static final long serialVersionUID = 1L; + + public ChannelMapManagerFrame( ChannelMapModel channelMapModel ) + { + init( new ChannelMapManager( channelMapModel ) ); + } + + private void init( ChannelMapManager channelMapManager ) + { + setTitle( "Channel Map Manager" ); + setSize( 800, 400 ); + setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); + setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]" ) ); + add( channelMapManager ); + setLocationRelativeTo( null ); + } + + public static void main( String[] args ) + { + ChannelMapModel channelMapModel = new ChannelMapModel(); + + final ChannelMapManagerFrame frame = + new ChannelMapManagerFrame( channelMapModel ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + frame.setVisible( true ); + } + } ); + } +} diff --git a/src/controller/channel/map/ChannelMapModel.java b/src/controller/channel/map/ChannelMapModel.java index 50da7dfe..e0b486d4 100644 --- a/src/controller/channel/map/ChannelMapModel.java +++ b/src/controller/channel/map/ChannelMapModel.java @@ -1,171 +1,155 @@ -/******************************************************************************* - * 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 controller.channel.map; -import javax.swing.table.AbstractTableModel; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import javax.swing.AbstractListModel; -public class ChannelMapModel extends AbstractTableModel +import controller.channel.map.ChannelMapEvent.Event; +import sample.Broadcaster; +import sample.Listener; + +public class ChannelMapModel extends AbstractListModel { - private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( ChannelMapModel.class ); + private static final long serialVersionUID = 1L; + + private List mChannelMaps = new ArrayList<>(); + private Broadcaster mEventBroadcaster = new Broadcaster<>(); - private static final int sSTART = 0; - private static final int sSTOP = 1; - private static final int sBASE = 2; - private static final int sSIZE = 3; + public ChannelMapModel() + { + } - protected int[] mColumnWidths = { 110, 110, 110, -1 }; + /** + * Returns an unmodifiable list of channel maps currently in the model + */ + public List getChannelMaps() + { + return Collections.unmodifiableList( mChannelMaps ); + } - protected String[] mHeaders = new String[] { "Begin", - "End", - "Base", - "Size" }; - - private ChannelMap mChannelMap; - - public ChannelMapModel( ChannelMap map ) + /** + * Returns the channel map with a matching name or null + */ + public ChannelMap getChannelMap( String name ) { - mChannelMap = map; - } - - public ChannelMap getChannelMap() - { - return mChannelMap; - } - - public void setChannelMap( ChannelMap map ) - { - mChannelMap = map; - - fireTableDataChanged(); - } - - public void addRange( ChannelRange range ) - { - mChannelMap.addRange( range ); - - int index = mChannelMap.getRanges().size() - 1; - - fireTableRowsInserted( index, index ); - } - - public void removeRange( int rangeIndex ) - { - if( rangeIndex < mChannelMap.getRanges().size() ) + for( ChannelMap channelMap: mChannelMaps ) { - mChannelMap.getRanges().remove( rangeIndex ); - } - - fireTableRowsDeleted( rangeIndex, rangeIndex ); - } - - public int[] getColumnWidths() - { - return mColumnWidths; - } - - public void setColumnWidths( int[] widths ) - { - if( widths.length != 4 ) - { - throw new IllegalArgumentException( "ChannelMapRangeModel - " - + "column widths array should have 5 elements" ); - } - else - { - mColumnWidths = widths; - } - } - - @Override - public int getRowCount() - { - return mChannelMap.getRanges().size(); - } - - @Override - public int getColumnCount() - { - return mHeaders.length; - } - - public String getColumnName( int column ) - { - return mHeaders[ column ]; - } - - @Override - public Object getValueAt( int rowIndex, int columnIndex ) - { - ChannelRange range = mChannelMap.getRanges().get( rowIndex ); - - switch( columnIndex ) - { - case sSTART: - return range.getFirstChannelNumber(); - case sSTOP: - return range.getLastChannelNumber(); - case sBASE: - return range.getBase(); - case sSIZE: - return range.getSize(); + if( channelMap.getName().equalsIgnoreCase( name ) ) + { + return channelMap; + } } return null; - } - - public boolean isCellEditable( int row, int column ) - { - return true; } - - public void setValueAt( Object value, int row, int column ) + + /** + * Adds a listener to receive notifications when channel map updates occur + */ + public void addListener( Listener listener ) { - try + mEventBroadcaster.addListener( listener ); + } + + /** + * Removes the channel map update notification listener + */ + public void removeListener( Listener listener ) + { + mEventBroadcaster.removeListener( listener ); + } + + /** + * Broadcasts a channel map change. + * + * Note: use the add/remove methods to add or remove channel maps from this + * model. When using those methods, an add or delete event will automatically + * be generated. + */ + public void broadcast( ChannelMapEvent event ) + { + if( event.getEvent() == ChannelMapEvent.Event.CHANGE || + event.getEvent() == ChannelMapEvent.Event.RENAME ) { - int parsedValue = Integer.parseInt( (String)value ); + int index = mChannelMaps.indexOf( event.getChannelMap() ); - ChannelRange range = mChannelMap.getRanges().get( row ); - - switch( column ) + if( index >= 0 ) { - case sSTART: - range.setFirstChannelNumber( parsedValue ); - break; - case sSTOP: - range.setLastChannelNumber( parsedValue ); - break; - case sBASE: - range.setBase( parsedValue ); - break; - case sSIZE: - range.setSize( parsedValue ); - break; + fireContentsChanged( this, index, index ); } } - catch( Exception e ) + + mEventBroadcaster.broadcast( event ); + } + + /** + * Adds a list of channel maps to this model + */ + public void addChannelMaps( List channelMaps ) + { + for( ChannelMap channelMap: channelMaps ) { - mLog.error( "ChannelMapModel - couldn't parse integer value " - + "for [" + value + "]", e ); + addChannelMap( channelMap ); } } + + /** + * Adds the channel map to this model + */ + public void addChannelMap( ChannelMap channelMap ) + { + if( !mChannelMaps.contains( channelMap ) ) + { + mChannelMaps.add( channelMap ); + + int index = mChannelMaps.indexOf( channelMap ); + + fireIntervalAdded( this, index, index ); + + broadcast( new ChannelMapEvent( channelMap, Event.ADD ) ); + } + + } + + /** + * Removes the channel map from this model + */ + public void removeChannelMap( ChannelMap channelMap ) + { + if( mChannelMaps.contains( channelMap ) ) + { + int index = mChannelMaps.indexOf( channelMap ); + + mChannelMaps.remove( channelMap ); + + fireIntervalRemoved( this, index, index ); + + broadcast( new ChannelMapEvent( channelMap, Event.DELETE ) ); + } + } + + /** + * Size/Number of channel maps in this model + */ + @Override + public int getSize() + { + return mChannelMaps.size(); + } + + /** + * Returns the channel map at the specified index + */ + @Override + public ChannelMap getElementAt( int index ) + { + if( index <= mChannelMaps.size() ) + { + return mChannelMaps.get( index ); + } + + return null; + } } diff --git a/src/controller/channel/map/ChannelMapNode.java b/src/controller/channel/map/ChannelMapNode.java deleted file mode 100644 index 7a0712e0..00000000 --- a/src/controller/channel/map/ChannelMapNode.java +++ /dev/null @@ -1,102 +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 - ******************************************************************************/ -package controller.channel.map; - - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import controller.ConfigurableNode; - -public class ChannelMapNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - public ChannelMapNode( ChannelMap ChannelMap ) - { - super( ChannelMap ); - } - - public void init() - { - //For consistency, but not used - } - - @Override - public String getIconPath() - { - return "images/ChannelMap.png"; - } - - @Override - public JPanel getEditor() - { - return new ChannelMapEditor( this ); - } - - public ChannelMap getChannelMap() - { - return (ChannelMap)getUserObject(); - } - - public String toString() - { - return getChannelMap().getName(); - } - - public void delete() - { - ChannelMapListNode parent = (ChannelMapListNode)getParent(); - - parent.getChannelMapList().removeChannelMap( getChannelMap() ); - - save(); - - getModel().removeNodeFromParent( ChannelMapNode.this ); - - /* Show the first child, if one exists so that we don't have a - * collapsed list */ - if( parent.getChildCount() > 0 ) - { - ((ConfigurableNode)parent.getFirstChild()).show(); - } - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - delete(); - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/controller/channel/map/ChannelRange.java b/src/controller/channel/map/ChannelRange.java index a9814239..5e086b89 100644 --- a/src/controller/channel/map/ChannelRange.java +++ b/src/controller/channel/map/ChannelRange.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,21 +17,29 @@ ******************************************************************************/ package controller.channel.map; +import java.text.DecimalFormat; + import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlType; @XmlRootElement @XmlType( name = "range" ) public class ChannelRange { + private static final DecimalFormat FREQUENCY_FORMATTER = new DecimalFormat( "#.0000" ); + private int mFirst = 1; private int mLast = 1023; private int mBase = 450000000; private int mSize = 12500; + + private boolean mOverlapping = false; - /* JAXB requirement - do not remove */ - public ChannelRange() {} + public ChannelRange() + { + } public ChannelRange( int first, int last, int base, int size ) { @@ -46,11 +54,67 @@ public class ChannelRange return new ChannelRange( mFirst, mLast, mBase, mSize ); } + public String getDescription() + { + StringBuilder sb = new StringBuilder(); + + if( isValid() ) + { + sb.append( "First: " ); + sb.append( mFirst + "=" ); + + long frequency = getFrequency( mFirst ); + + sb.append( FREQUENCY_FORMATTER.format( (double)frequency / 1E6D ) ); + sb.append( " Last: " ); + sb.append( mLast + "=" ); + + frequency = getFrequency( mLast ); + sb.append( FREQUENCY_FORMATTER.format( (double)frequency / 1E6D ) ); + sb.append( " MHz" ); + } + else + { + sb.append( "First channel must be smaller than last channel" ); + } + + if( isOverlapping() ) + { + if( sb.length() > 0 ) + { + sb.append( ", " ); + } + + sb.append( "Overlap!" ); + } + + return sb.toString(); + } + public boolean isValid() { return mFirst < mLast; } + public boolean overlaps( ChannelRange other ) + { + return ( mFirst <= other.mFirst && other.mFirst <= mLast ) || + ( mFirst <= other.mLast && other.mLast <= mLast ) || + ( mFirst <= other.mFirst && other.mLast <= mLast ) || + ( other.mFirst <= mFirst && mLast <= other.mLast ); + } + + @XmlTransient + public boolean isOverlapping() + { + return mOverlapping; + } + + public void setOverlapping( boolean overlapping ) + { + mOverlapping = overlapping; + } + public boolean hasChannel( int channel ) { return mFirst <= channel && channel <= mLast; diff --git a/src/controller/channel/map/ChannelRangeModel.java b/src/controller/channel/map/ChannelRangeModel.java new file mode 100644 index 00000000..91d6c29f --- /dev/null +++ b/src/controller/channel/map/ChannelRangeModel.java @@ -0,0 +1,292 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package controller.channel.map; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import javax.swing.table.AbstractTableModel; + +public class ChannelRangeModel extends AbstractTableModel +{ + private static final long serialVersionUID = 1L; + + private static final int START = 0; + private static final int STOP = 1; + private static final int BASE = 2; + private static final int SIZE = 3; + private static final int ERROR = 4; + + protected int[] mColumnWidths = { 110, 110, 110, -1, 80 }; + + protected String[] mHeaders = new String[] { "Begin", + "End", + "Base", + "Size", + "Error" }; + + private List mRanges = new CopyOnWriteArrayList<>(); + private ChannelRangeEventListener mListener; + + public ChannelRangeModel() + { + } + + public void setListener( ChannelRangeEventListener listener ) + { + mListener = listener; + } + + private void broadcastChange() + { + if( mListener != null ) + { + mListener.channelRangesChanged(); + } + } + + public void clear() + { + for( ChannelRange range: mRanges ) + { + removeRange( range ); + } + + broadcastChange(); + } + + public List getChannelRanges() + { + return mRanges; + } + + public void addRanges( List ranges ) + { + for( ChannelRange range: ranges ) + { + addRange( range ); + } + + broadcastChange(); + } + + public void addRange( ChannelRange range ) + { + if( range != null && !mRanges.contains( range ) ) + { + mRanges.add( range ); + + int index = mRanges.indexOf( range ); + + fireTableRowsInserted( index, index ); + } + + validate(); + + broadcastChange(); + } + + public void removeRange( ChannelRange range ) + { + if( range != null && mRanges.contains( range ) ) + { + int index = mRanges.indexOf( range ); + + mRanges.remove( range ); + + fireTableRowsDeleted( index, index ); + } + + validate(); + + broadcastChange(); + } + + public int[] getColumnWidths() + { + return mColumnWidths; + } + + public void setColumnWidths( int[] widths ) + { + if( widths.length != mHeaders.length ) + { + throw new IllegalArgumentException( "ChannelMapRangeModel - " + + "column widths array should have " + + mHeaders.length + " elements" ); + } + else + { + mColumnWidths = widths; + } + } + + @Override + public int getRowCount() + { + return mRanges.size(); + } + + @Override + public int getColumnCount() + { + return mHeaders.length; + } + + public String getColumnName( int column ) + { + return mHeaders[ column ]; + } + + public ChannelRange getChannelRange( int index ) + { + if( index < mRanges.size() ) + { + return mRanges.get( index ); + } + + return null; + } + + @Override + public Object getValueAt( int rowIndex, int columnIndex ) + { + ChannelRange range = getChannelRange( rowIndex ); + + if( range != null ) + { + switch( columnIndex ) + { + case START: + return range.getFirstChannelNumber(); + case STOP: + return range.getLastChannelNumber(); + case BASE: + return range.getBase(); + case SIZE: + return range.getSize(); + case ERROR: + StringBuilder sb = new StringBuilder(); + + if( !range.isValid() ) + { + sb.append( "Sequence" ); + } + + if( range.isOverlapping() ) + { + if( sb.length() > 0 ) + { + sb.append( "," ); + } + + sb.append( "Overlap" ); + } + + return sb.toString(); + default: + break; + } + } + + return null; + } + + /** + * Validates each of the channel ranges for overlap + */ + private void validate() + { + for( int x = 0; x < mRanges.size(); x++ ) + { + mRanges.get( x ).setOverlapping( false ); + fireTableCellUpdated( x, ERROR ); + } + + for( int x = 0; x < mRanges.size(); x++ ) + { + for( int y = x + 1; y < mRanges.size(); y++ ) + { + if( mRanges.get( x ).overlaps( mRanges.get( y ) ) ) + { + mRanges.get( x ).setOverlapping( true ); + fireTableCellUpdated( x, ERROR ); + + mRanges.get( y ).setOverlapping( true ); + fireTableCellUpdated( y, ERROR ); + } + } + } + } + + public boolean isCellEditable( int row, int column ) + { + return row < mRanges.size() && column != ERROR; + } + + public void setValueAt( Object value, int row, int column ) + { + int parsedValue = -1; + + try + { + parsedValue = Integer.parseInt( (String)value ); + } + catch( Exception e ) + { + //Do nothing ... we'll use 0 + } + + if( parsedValue >= 0 ) + { + ChannelRange range = getChannelRange( row ); + + if( range != null ) + { + switch( column ) + { + case START: + range.setFirstChannelNumber( parsedValue ); + break; + case STOP: + range.setLastChannelNumber( parsedValue ); + break; + case BASE: + range.setBase( parsedValue ); + break; + case SIZE: + range.setSize( parsedValue ); + break; + } + + validate(); + + broadcastChange(); + } + else + { + throw new IllegalArgumentException( "Invalid channel range row index" ); + } + } + } + + public interface ChannelRangeEventListener + { + public void channelRangesChanged(); + } +} diff --git a/src/controller/site/SiteEditor.java b/src/controller/site/SiteEditor.java deleted file mode 100644 index 6008a78c..00000000 --- a/src/controller/site/SiteEditor.java +++ /dev/null @@ -1,101 +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 - ******************************************************************************/ -package controller.site; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; -import javax.swing.tree.TreePath; - -import net.miginfocom.swing.MigLayout; - -public class SiteEditor extends JPanel implements ActionListener -{ - private static final long serialVersionUID = 1L; - private SiteNode mSiteNode; - - private JLabel mLabelName; - private JTextField mTextSite; - - public SiteEditor( SiteNode site ) - { - mSiteNode = site; - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout() ); - - setBorder( BorderFactory.createTitledBorder( "Site" ) ); - - mLabelName = new JLabel( "Name:" ); - add( mLabelName, "align right" ); - - mTextSite = new JTextField( mSiteNode.getSite().getName() ); - add( mTextSite, "grow, wrap" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( SiteEditor.this ); - add( btnSave ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( SiteEditor.this ); - add( btnReset, "wrap" ); - } - - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) - { - boolean expanded = mSiteNode.getModel().getTree() - .isExpanded( new TreePath( mSiteNode ) ); - - String name = mTextSite.getText(); - - if( name != null ) - { - mSiteNode.getSite().setName( name ); - mSiteNode.save(); - mSiteNode.show(); - - if( expanded ) - { - mSiteNode.getModel().getTree().expandPath( new TreePath( mSiteNode ) ); - } - } - else - { - JOptionPane.showMessageDialog( SiteEditor.this, "Please enter a site name" ); - } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextSite.setText( mSiteNode.getSite().getName() ); - } - } -} diff --git a/src/controller/site/SiteNode.java b/src/controller/site/SiteNode.java deleted file mode 100644 index 21ec08d8..00000000 --- a/src/controller/site/SiteNode.java +++ /dev/null @@ -1,168 +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 - ******************************************************************************/ -package controller.site; - - -import java.awt.Color; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.Enumeration; - -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JSeparator; -import javax.swing.tree.DefaultMutableTreeNode; - -import controller.ConfigurableNode; -import controller.channel.Channel; -import controller.channel.ChannelNode; -import controller.system.System; -import controller.system.SystemNode; - -public class SiteNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - public SiteNode( Site site ) - { - super( site ); - } - - public void init() - { - for( Channel channel: getSite().getChannel() ) - { - ChannelNode node = new ChannelNode( channel ); - - getModel().insertNodeInto( node, SiteNode.this, getChildCount() ); - - node.init(); - } - - sort(); - } - - public String getIconPath() - { - return "images/site.png"; - } - - @Override - public Color getBackgroundColor() - { - @SuppressWarnings( "unchecked" ) - Enumeration nodes = children(); - - while( nodes.hasMoreElements() ) - { - ChannelNode child = (ChannelNode)nodes.nextElement(); - - if( child.getBackgroundColor() != null ) - { - return Color.CYAN; - } - } - - return null; - } - - @Override - public JPanel getEditor() - { - return new SiteEditor( this ); - } - - public Site getSite() - { - return (Site)getUserObject(); - } - - public String toString() - { - return getSite().getName(); - } - - public void delete() - { - for( int x = 0; x < getChildCount(); x++ ) - { - ((ChannelNode)getChildAt( x )).delete(); - } - - ((SystemNode)getParent()).getSystem().removeSite( getSite() ); - - save(); - - getModel().removeNodeFromParent( SiteNode.this ); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addChannelItem = new JMenuItem( "Add Channel" ); - addChannelItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - System system = ((SystemNode)getParent()).getSystem(); - - Channel channel = new Channel(); - channel.setSystem( system, false ); - channel.setSite( getSite(), false ); - - getSite().addChannel( channel ); - - ChannelNode node = new ChannelNode( channel ); - - getModel().addNode( node, - SiteNode.this, - SiteNode.this.getChildCount() ); - - sort(); - save(); - node.show(); - - /* Give channel reference to the resource manager and the - * channel will register the listeners and send a channel - * add event */ - channel.setResourceManager( getModel().getResourceManager() ); - } - } ); - - retVal.add( addChannelItem ); - - retVal.add( new JSeparator() ); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - delete(); - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/controller/system/SystemEditor.java b/src/controller/system/SystemEditor.java deleted file mode 100644 index f9c4e8b4..00000000 --- a/src/controller/system/SystemEditor.java +++ /dev/null @@ -1,101 +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 - ******************************************************************************/ -package controller.system; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; -import javax.swing.tree.TreePath; - -import net.miginfocom.swing.MigLayout; - -public class SystemEditor extends JPanel implements ActionListener -{ - private static final long serialVersionUID = 1L; - private SystemNode mSystemNode; - - private JLabel mLabelSystem; - private JTextField mTextSystem; - - public SystemEditor( SystemNode system ) - { - mSystemNode = system; - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout() ); - - setBorder( BorderFactory.createTitledBorder( "System" ) ); - - mLabelSystem = new JLabel( "Name:" ); - add( mLabelSystem, "align right" ); - - mTextSystem = new JTextField( mSystemNode.getSystem().getName() ); - add( mTextSystem, "grow, wrap" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( SystemEditor.this ); - add( btnSave ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( SystemEditor.this ); - add( btnReset, "wrap" ); - } - - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) - { - boolean expanded = mSystemNode.getModel().getTree() - .isExpanded( new TreePath( mSystemNode ) ); - - String name = mTextSystem.getText(); - - if( name != null ) - { - mSystemNode.getSystem().setName( name ); - mSystemNode.save(); - mSystemNode.show(); - - if( expanded ) - { - mSystemNode.getModel().getTree().expandPath( new TreePath( mSystemNode ) ); - } - } - else - { - JOptionPane.showMessageDialog( SystemEditor.this, "Please enter a system name" ); - } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextSystem.setText( mSystemNode.getSystem().getName() ); - } - } -} diff --git a/src/controller/system/SystemListNode.java b/src/controller/system/SystemListNode.java deleted file mode 100644 index 4629133d..00000000 --- a/src/controller/system/SystemListNode.java +++ /dev/null @@ -1,85 +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 - ******************************************************************************/ -package controller.system; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; - -import controller.ConfigurableNode; - -public class SystemListNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - public SystemListNode( SystemList list ) - { - super( list ); - } - - public SystemList getSystemList() - { - return (SystemList)getUserObject(); - } - - public void init() - { - for( System system: getSystemList().getSystem() ) - { - SystemNode node = new SystemNode( system ); - - getModel().addNode( node, SystemListNode.this, getChildCount() ); - - node.init(); - } - - sort(); - } - - public String toString() - { - return "Systems [" + this.getChildCount() + "]"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Add System" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - System system = new System(); - - getSystemList().addSystem( system ); - - getModel().addNode( new SystemNode( system ), - SystemListNode.this, - SystemListNode.this.getChildCount() ); - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/controller/system/SystemNode.java b/src/controller/system/SystemNode.java deleted file mode 100644 index a9c4e3b9..00000000 --- a/src/controller/system/SystemNode.java +++ /dev/null @@ -1,166 +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 - ******************************************************************************/ -package controller.system; - - -import java.awt.Color; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.Enumeration; - -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JSeparator; -import javax.swing.tree.DefaultMutableTreeNode; - -import controller.ConfigurableNode; -import controller.site.Site; -import controller.site.SiteNode; - -public class SystemNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - public SystemNode( System system ) - { - super( system ); - } - - public void init() - { - for( Site site: getSystem().getSite() ) - { - SiteNode node = new SiteNode( site ); - - getModel().insertNodeInto( node, SystemNode.this, getChildCount() ); - - node.init(); - } - - sort(); - } - - @Override - public String getIconPath() - { - return "images/system.png"; - } - - @Override - public Color getBackgroundColor() - { - @SuppressWarnings( "unchecked" ) - Enumeration nodes = children(); - - while( nodes.hasMoreElements() ) - { - SiteNode child = (SiteNode)nodes.nextElement(); - - if( child.getBackgroundColor() != null ) - { - return Color.CYAN; - } - } - - return null; - } - - @Override - public JPanel getEditor() - { - return new SystemEditor( this ); - } - - public System getSystem() - { - return (System)getUserObject(); - } - - public String toString() - { - return getSystem().getName(); - } - - public void delete() - { - for( int x = 0; x < getChildCount(); x++ ) - { - ((SiteNode)getChildAt( x )).delete(); - } - - SystemListNode parent = (SystemListNode)getParent(); - - parent.getSystemList().removeSystem( getSystem() ); - - save(); - - getModel().removeNodeFromParent( SystemNode.this ); - - /* Show the first child, if one exists so that we don't have a - * collapsed list */ - if( parent.getChildCount() > 0 ) - { - ((ConfigurableNode)parent.getFirstChild()).show(); - } - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSiteItem = new JMenuItem( "Add Site" ); - addSiteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - Site site = new Site(); - getSystem().addSite( site ); - - SiteNode node = new SiteNode( site ); - - getModel().addNode( node, - SystemNode.this, - SystemNode.this.getChildCount() ); - - sort(); - - node.show(); - } - } ); - - retVal.add( addSiteItem ); - - retVal.add( new JSeparator() ); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - delete(); - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/dsp/filter/ComplexCICDecimate.java b/src/dsp/filter/ComplexCICDecimate.java deleted file mode 100644 index f82fde74..00000000 --- a/src/dsp/filter/ComplexCICDecimate.java +++ /dev/null @@ -1,342 +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 - ******************************************************************************/ -package dsp.filter; - -import sample.Listener; -import sample.Provider; -import sample.complex.Complex; -import dsp.filter.Window.WindowType; - -/** - * Cascaded Integrator Comb (CIC) filter - anti-aliasing decimating filter for - * reducing a high rate complex sample stream to a lower rate by an integral - * decimation (ie reduction) value. - * - * Note: comb delay is fixed at 1 for this filter. - */ -public class ComplexCICDecimate extends ComplexFilter - implements Provider -{ - private Integrator[] mIntegrator; - private Comb[] mComb; - private Decimator mDecimator; - private Output mOutput; - - /** - * Constructs the complex sample CIC filter - * - * @param decimation - decimation factor - * - * @param stageCount - number of integrator/comb sections to use. The - * higher the number of stages, the sharper dropoff of the filter response - * - * @param outputSampleRate - output sample rate is used to calculate the - * coefficients for the final cleanup filter to adjust for frequency - * response droop induced by this CIC filter - */ - public ComplexCICDecimate( int decimation, - int stageCount, - int outputSampleRate ) - { - /* Construct integrator(s) and comb(s) */ - mIntegrator = new Integrator[ stageCount ]; - mComb = new Comb[ stageCount ]; - - for( int x = 0; x < stageCount; x++ ) - { - mIntegrator[ x ] = new Integrator(); - mComb[ x ] = new Comb(); - } - - /* Connect each stage */ - int y = 1; - - while( y < stageCount ) - { - mIntegrator[ y - 1 ].setListener( mIntegrator[ y ] ); - mComb[ y - 1 ].setListener( mComb[ y ] ); - - y++; - } - - /* Connect decimator to integrator output and comb input */ - mDecimator = new Decimator( decimation ); - - mIntegrator[ stageCount - 1 ].setListener( mDecimator ); - mDecimator.setListener( mComb[ 0 ] ); - - /* Connect output adapter to the comb output */ - mOutput = new Output( outputSampleRate, stageCount, decimation ); - mComb[ stageCount - 1 ].setListener( mOutput ); - } - - /** - * Sets a new decimation rate for this filter. This method can be invoked - * after filter construction to change the decimation rate dynamically. - */ - public void setDecimationRate( int rate ) - { - mDecimator.setDecimationRate( rate ); - mOutput.setDecimationRate( rate ); - } - - /** - * Input method for this filter to receive complex samples. - */ - @Override - public void receive( Complex sample ) - { - mIntegrator[ 0 ].receive( (double)sample.left(), (double)sample.right() ); - } - - /** - * Sets the listener to receive the output of this CIC filter - */ - @Override - public void setListener( Listener listener ) - { - mOutput.setListener( listener ); - } - - /** - * Removes the listener from receiving the output of this CIC filter - */ - @Override - public void removeListener( Listener listener ) - { - mOutput.removeListener( listener ); - } - - @Override - public Listener getListener() - { - return mOutput.getListener(); - } - - /** - * CIC Comb filter section - */ - public class Comb implements InternalListener - { - private double mPreviousLeft; - private double mPreviousRight; - private InternalListener mCombListener; - - /** - * Performs comb filtering on the received values, subtracting the - * previous received samples from these samples. - */ - @Override - public void receive( double left, double right ) - { - mCombListener.receive( ( left - mPreviousLeft ), - ( right - mPreviousRight ) ); - - mPreviousLeft = left; - mPreviousRight = right; - } - - /** - * Sets the comb output listener. - */ - public void setListener( InternalListener listener ) - { - mCombListener = listener; - } - } - - /** - * CIC Integrator section - */ - public class Integrator implements InternalListener - { - private double mRunningSumLeft = 0.0d; - private double mRunningSumRight = 0.0d; - private InternalListener mIntegratorListener; - - /** - * Integrates the received values with the previous running sum and - * sends the new running sum to the registered listener - */ - @Override - public void receive( double left, double right ) - { - mRunningSumLeft += left; - mRunningSumRight += right; - - mIntegratorListener.receive( mRunningSumLeft, mRunningSumRight ); - } - - /** - * Sets the integrator output listener - */ - public void setListener( InternalListener listener ) - { - mIntegratorListener = listener; - } - } - - /** - * Decimator - passes only 1 of xx samples to a registered listener and - * discards all other samples. - */ - public class Decimator implements InternalListener - { - private InternalListener mDecimatorListener; - private int mPointer = 0; - private int mDecimationRate; - - /** - * Constructs a new Decimator object with the specified decimation rate. - */ - public Decimator( int rate ) - { - mDecimationRate = rate; - } - - /** - * Sets a new decimation rate. Rate can be changed after this object - * is constructed. - */ - public synchronized void setDecimationRate( int rate ) - { - mDecimationRate = rate; - } - - /** - * Receives samples allowing only 1 of every (rate) sample to go on - * to the registered listener - */ - @Override - public void receive( double left, double right ) - { - mPointer++; - - if( mPointer >= mDecimationRate ) - { - mDecimatorListener.receive( left, right ); - - mPointer = 0; - } - } - - /** - * Sets the decimated output listener - */ - public void setListener( InternalListener listener ) - { - mDecimatorListener = listener; - } - } - - /** - * Output adapter - applies gain correction, applies cleanup filter, casts - * double-valued samples into float-valued complex sample and sends the - * result to the registered listener. - */ - public class Output implements InternalListener - { - private int mStageCount; - private int mDecimation; - private double mGain = 1.0d; - private ComplexFilter mCleanupFilter; - - public Output( int outputSampleRate, int stageCount, int decimation ) - { - mStageCount = stageCount; - mDecimation = decimation; - mCleanupFilter = new ComplexFIRFilterOld( FilterFactory - .getCICCleanupFilter( outputSampleRate, - 12000, - 60, - WindowType.BLACKMAN ), 1.0f ); - - setGain(); - } - - /** - * Receiver method for the output adapter to receive a filtered, - * decimated sample, apply gain correction, apply cleanup filtering - * and output the sample values as a complex sample. - */ - @Override - public void receive( double left, double right ) - { - mCleanupFilter.receive( - new Complex( (float)( left * mGain ), - (float)( right * mGain ) ) ); - } - - /** - * Sets a new decimation rate - */ - public void setDecimationRate( int rate ) - { - mDecimation = rate; - setGain(); - } - - /** - * Adds a listener to receive output samples - */ - public void setListener( Listener listener ) - { - mCleanupFilter.setListener( listener ); - } - - /** - * Removes the listener from receiving output samples - */ - public void removeListener( Listener listener ) - { - mCleanupFilter.setListener( null ); - } - - public Listener getListener() - { - return mCleanupFilter.getListener(); - } - - /** - * Adjusts the gain correction factor for output samples. - * - * The CIC decimation filter induces significant gain as a factor of - * delay, decimation rate and stage count. So, we apply a gain reduction - * to offset the added gain estimate: - * - * gain = ( decimation * delay ) to power of stageCount - * - * Delay is ONE for the Comb used in this filter. - */ - private void setGain() - { - /* decimation is NOT multiplied by delay since delay is 1 */ - double gain = Math.pow( (double)mDecimation, (double)mStageCount ); - - mGain = 10.0d / gain; - } - } - - /** - * Internal interface for each of the CIC components to pass samples - */ - private interface InternalListener - { - public void receive( double left, double right ); - } -} diff --git a/src/dsp/filter/ComplexFIRFilterOld.java b/src/dsp/filter/ComplexFIRFilterOld.java deleted file mode 100644 index 84e9fc02..00000000 --- a/src/dsp/filter/ComplexFIRFilterOld.java +++ /dev/null @@ -1,160 +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 - ******************************************************************************/ -package dsp.filter; - -import java.util.ArrayList; - -import sample.complex.Complex; - -public class ComplexFIRFilterOld extends ComplexFilter -{ - private ArrayList mBuffer; - private int mBufferSize = 1; //Temporary initial value - private int mBufferPointer = 0; - private float[] mCoefficients; - private int[][] mIndexMap; - private int mCenterCoefficient; - private int mCenterCoefficientMapIndex; - private float mGain; - - public ComplexFIRFilterOld( float[] coefficients, float gain ) - { - mCoefficients = coefficients; - mBuffer = new ArrayList(); - mBufferSize = mCoefficients.length; - mGain = gain; - - //Fill the buffer with zero valued samples, so we don't have to check for null - for( int x = 0; x < mCoefficients.length; x++ ) - { - mBuffer.add( new Complex( (float)0.0, (float)0.0 ) ); - } - - generateIndexMap( mCoefficients.length ); - } - - public void dispose() - { - mBuffer.clear(); - mBuffer = null; - - mCoefficients = null; - mIndexMap = null; - } - - public void receive( Complex newSample ) - { - //Add the new sample to the buffer - mBuffer.set( mBufferPointer, newSample ); - - //Increment & Adjust the buffer pointer for circular wrap around - mBufferPointer++; - - if( mBufferPointer >= mBufferSize ) - { - mBufferPointer = 0; - } - - //Convolution - multiply filter coefficients by the circular buffer - //samples to calculate a new filtered value - double leftAccumulator = 0; - double rightAccumulator = 0; - - //Start with the center tap value - leftAccumulator += mCoefficients[ mCenterCoefficient ] * - mBuffer.get( mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] ).left(); - rightAccumulator += mCoefficients[ mCenterCoefficient ] * - mBuffer.get( mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] ).right(); - - //For the remaining coefficients, add the symmetric samples, oldest and newest - //first, then multiply by the single coefficient - for( int x = 0; x < mCenterCoefficient; x++ ) - { - leftAccumulator += mCoefficients[ x ] * - ( mBuffer.get( mIndexMap[ mBufferPointer ][ x ] ).left() + - mBuffer.get( mIndexMap[ mBufferPointer ][ x + mCenterCoefficient ] ).left() ); - - rightAccumulator += mCoefficients[ x ] * - ( mBuffer.get( mIndexMap[ mBufferPointer ][ x ] ).right() + - mBuffer.get( mIndexMap[ mBufferPointer ][ x + mCenterCoefficient ] ).right() ); - } - - //We're almost finished ... apply gain, cast the doubles to floats and - //send it on it's merry way - send( new Complex( (float)( leftAccumulator * mGain ), - (float)( rightAccumulator * mGain ) ) ); - } - - /** - * @param odd-sized number of filter taps (ie coefficients) and buffer - */ - private void generateIndexMap( int size ) - { - //Ensure we have an odd size - assert( size % 2 == 1 ); - - mIndexMap = new int[ size ][ size ]; - - //Last column will be the center coefficient index value for each row - mCenterCoefficientMapIndex = size - 1; - mCenterCoefficient = (int)( size / 2 ); - - //Setup the first row. Offset is the first row's center coefficient value - //and will become the index value we place in the last column - mIndexMap[ 0 ][ mCenterCoefficientMapIndex ] = mCenterCoefficient; - - //Indexes 0 to 1/2 are their index value, and indexes 1/2 to end are - //an offset of the first half of the values - for( int x = 0; x < mCenterCoefficient; x++ ) - { - mIndexMap[ 0 ][ x ] = x; - mIndexMap[ 0 ][ x + mCenterCoefficient ] = size - 1 - x; - } - - //For each subsequent map row, increment the value of the preceding row - //same column by 1, wrapping to zero when we exceed the size value - for( int x = 1; x < size; x++ ) - { - for( int y = 0; y < size; y++ ) - { - mIndexMap[ x ][ y ] = mIndexMap[ x - 1 ][ y ] + 1; - - if( mIndexMap[ x ][ y ] >= size ) - { - mIndexMap[ x ][ y ] = 0; - } - } - } - } - - public String printIndexMap() - { - StringBuilder sb = new StringBuilder(); - - for( int x = 0; x < mCoefficients.length; x++ ) - { - for( int y = 0; y < mCoefficients.length; y++ ) - { - sb.append( mIndexMap[ x ][ y ] + " " ); - } - sb.append( "\n" ); - } - - return sb.toString(); - } -} diff --git a/src/dsp/filter/ComplexHalfBandFilter.java b/src/dsp/filter/ComplexHalfBandFilter.java deleted file mode 100644 index a8dc266d..00000000 --- a/src/dsp/filter/ComplexHalfBandFilter.java +++ /dev/null @@ -1,186 +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 - ******************************************************************************/ -package dsp.filter; - -import java.util.ArrayList; - -import sample.complex.Complex; - -public class ComplexHalfBandFilter extends ComplexFilter -{ - private ArrayList mBuffer; - private int mBufferSize = 1; //Temporary initial value - private int mBufferPointer = 0; - private float mGain; - private float[] mCoefficients; - private int[][] mIndexMap; - private int mCenterCoefficient; - private int mCenterCoefficientMapIndex; - private boolean mDispatchFlag = false; - - /** - * Half-Band filter with decimation by 2 against complex samples composed of - * real valued floats. - * - * Takes advantage of the symmetrical nature of FIR filter coefficients by - * adding oldest and newest sample first, then multiplying once by the - * corresponding coefficient - * - * Also, takes advantage of the 0-valued FIR half-band coefficents inherent - * in the half-band filter, and does not calculate those coefficients. - * - * This reduces the workload to (tap-size - 1) / 4 + 1 calculations per sample. - * - * @param filter - filter coefficients - * @param gain - gain multiplier. Use 1.0 for unity/no gain - */ - public ComplexHalfBandFilter( Filters filter, float gain ) - { - mCoefficients = filter.getCoefficients(); - mBuffer = new ArrayList(); - mBufferSize = mCoefficients.length; - - //Fill the buffer with zero valued samples - for( int x = 0; x < mCoefficients.length; x++ ) - { - mBuffer.add( new Complex( (float)0.0, (float)0.0 ) ); - } - - generateIndexMap( mCoefficients.length ); - mGain = gain; - } - - /** - * Calculate the filtered value by applying the coefficients against - * the complex samples in mBuffer - */ - public void receive( Complex newSample ) - { - //Add the new sample to the buffer - mBuffer.set( mBufferPointer, newSample ); - - //Increment & Adjust the buffer pointer for circular wrap around - mBufferPointer++; - - if( mBufferPointer >= mBufferSize ) - { - mBufferPointer = 0; - } - - //Toggle the flag every time, so that we can calculate and dispatch a - //sample every other time, when the flag is true (ie decimate by 2) - mDispatchFlag = !mDispatchFlag; - - //Calculate a filtered sample when the flag is true - if( mDispatchFlag ) - { - //Convolution - multiply filter coefficients by the circular buffer - //samples to calculate a new filtered value - double leftAccumulator = 0; - double rightAccumulator = 0; - - //Start with the center tap value - leftAccumulator += mCoefficients[ mCenterCoefficient ] * - mBuffer.get( mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] ).left(); - rightAccumulator += mCoefficients[ mCenterCoefficient ] * - mBuffer.get( mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] ).right(); - - //For the remaining coefficients, add the symmetric samples, oldest and newest - //first, then multiply by the single coefficient - for( int x = 0; x < mCenterCoefficientMapIndex; x += 2 ) - { - leftAccumulator += mCoefficients[ x ] * - ( mBuffer.get( mIndexMap[ mBufferPointer ][ x ] ).left() + - mBuffer.get( mIndexMap[ mBufferPointer ][ x + 1 ] ).left() ); - - rightAccumulator += mCoefficients[ x ] * - ( mBuffer.get( mIndexMap[ mBufferPointer ][ x ] ).right() + - mBuffer.get( mIndexMap[ mBufferPointer ][ x + 1 ] ).right() ); - } - - //We're almost finished ... apply gain, cast the doubles to floats and - //send it on it's merry way - send( new Complex( (float)( leftAccumulator * mGain ), - (float)( rightAccumulator * mGain ) ) ); - } - } - - /** - * Creates an n X (n + 1 / 2) index map enabling quick access to the - * circular buffer samples. - * - * As the buffer shifts right with each subsequent sample, we have to move - * the index pointers with it, for efficient access of the samples. - * - * The first array index value in the index map corresponds to the current - * buffer pointer location. - * - * The second array index value points to the samples that should be - * multiplied by the coefficients as follows: - * - * 0 = center tap sample, to be multiplied by center coefficient - * - * 0 = sample( 1 ) - * 1 = sample( size - 1 ) - * - * Indexes 0 and 1 will be multiplied by coefficient( 0 ). - * - * Subsequent indexes 3, 4, etc, point to the oldest and newest samples that - * correspond to the matching ( 3 ) coefficient index. - * - * @param odd-sized number of filter taps (ie coefficients) and buffer - */ - private void generateIndexMap( int size ) - { - //Ensure we have an odd size - assert( size % 2 == 1 ); - - int mapWidth = ( ( size + 1 ) / 2 ) + 1; - - //Set center tap index for coefficients array - mCenterCoefficient = ( size - 1 ) / 2; - mCenterCoefficientMapIndex = mCenterCoefficient + 1; - - mIndexMap = new int[ size ][ mapWidth ]; - - //Setup the first row, buffer pointer index 0, as a starting point - for( int x = 0; x < mapWidth - 2; x += 2 ) - { - mIndexMap[ 0 ][ x ] = x; - mIndexMap[ 0 ][ x + 1 ] = size - 1 - x; - } - - //Place center tap index in last element - mIndexMap[ 0 ][ mCenterCoefficientMapIndex ] = mCenterCoefficient; - - //For each subsequent row, increment the previous row's value by 1, - //subtracting size as needed, to keep the values between 0 and size - 1 - for( int x = 1; x < size; x++ ) - { - for( int y = 0; y < mapWidth; y++ ) - { - mIndexMap[ x ][ y ] = mIndexMap[ x - 1 ][ y ] + 1; - - if( mIndexMap[ x ][ y ] >= size ) - { - mIndexMap[ x ][ y ] -= size; - } - } - } - } -} diff --git a/src/dsp/filter/ComplexHalfBandNoDecimateFilter.java b/src/dsp/filter/ComplexHalfBandNoDecimateFilter.java deleted file mode 100644 index 72e40f06..00000000 --- a/src/dsp/filter/ComplexHalfBandNoDecimateFilter.java +++ /dev/null @@ -1,194 +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 - ******************************************************************************/ -package dsp.filter; - -import java.util.ArrayList; - -import sample.Listener; -import sample.complex.Complex; - -public class ComplexHalfBandNoDecimateFilter extends ComplexFilter -{ - private Listener mListener; - private ArrayList mBuffer; - private int mBufferSize = 1; //Temporary initial value - private int mBufferPointer = 0; - private float mGain; - private float[] mCoefficients; - private int[][] mIndexMap; - private int mCenterCoefficient; - private int mCenterCoefficientMapIndex; - - - /** - * Half-Band filter with no decimation against complex samples composed of - * real valued floats. - * - * Takes advantage of the symmetrical nature of FIR filter coefficients by - * adding oldest and newest sample first, then multiplying once by the - * corresponding coefficient - * - * Also, takes advantage of the 0-valued FIR half-band coefficents inherent - * in the half-band filter, and does not calculate those coefficients. - * - * This reduces the workload to (tap-size - 1) / 4 + 1 calculations per sample. - * - * @param filter - filter coefficients - * @param gain - gain multiplier. Use 1.0 for unity/no gain - */ - public ComplexHalfBandNoDecimateFilter( Filters filter, float gain ) - { - mCoefficients = filter.getCoefficients(); - mBuffer = new ArrayList(); - mBufferSize = mCoefficients.length; - - //Fill the buffer with zero valued samples - for( int x = 0; x < mCoefficients.length; x++ ) - { - mBuffer.add( new Complex( (float)0.0, (float)0.0 ) ); - } - - generateIndexMap( mCoefficients.length ); - mGain = gain; - } - - /** - * Calculate the filtered value by applying the coefficients against - * the complex samples in mBuffer - */ - public void receive( Complex newSample ) - { - //Add the new sample to the buffer - mBuffer.set( mBufferPointer, newSample ); - - //Increment & Adjust the buffer pointer for circular wrap around - mBufferPointer++; - - if( mBufferPointer >= mBufferSize ) - { - mBufferPointer = 0; - } - - //Convolution - multiply filter coefficients by the circular buffer - //samples to calculate a new filtered value - float leftAccumulator = 0; - float rightAccumulator = 0; - - //Start with the center tap value - leftAccumulator += mCoefficients[ mCenterCoefficient ] * - mBuffer.get( mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] ).left(); - rightAccumulator += mCoefficients[ mCenterCoefficient ] * - mBuffer.get( mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] ).right(); - - //For the remaining coefficients, add the symmetric samples, oldest and newest - //first, then multiply by the single coefficient - for( int x = 0; x < mCenterCoefficientMapIndex; x += 2 ) - { - leftAccumulator += mCoefficients[ x ] * - ( mBuffer.get( mIndexMap[ mBufferPointer ][ x ] ).left() + - mBuffer.get( mIndexMap[ mBufferPointer ][ x + 1 ] ).left() ); - - rightAccumulator += mCoefficients[ x ] * - ( mBuffer.get( mIndexMap[ mBufferPointer ][ x ] ).right() + - mBuffer.get( mIndexMap[ mBufferPointer ][ x + 1 ] ).right() ); - } - - //We're almost finished ... apply gain, cast the floats to floats and - //send it on it's merry way - send( new Complex( ( leftAccumulator * mGain ), - ( rightAccumulator * mGain ) ) ); - } - - /** - * Creates an n X (n + 1 / 2) index map enabling quick access to the - * circular buffer samples. - * - * As the buffer shifts right with each subsequent sample, we have to move - * the index pointers with it, for efficient access of the samples. - * - * The first array index value in the index map corresponds to the current - * buffer pointer location. - * - * The second array index value points to the samples that should be - * multiplied by the coefficients as follows: - * - * 0 = center tap sample, to be multiplied by center coefficient - * - * 0 = sample( 1 ) - * 1 = sample( size - 1 ) - * - * Indexes 0 and 1 will be multiplied by coefficient( 0 ). - * - * Subsequent indexes 3, 4, etc, point to the oldest and newest samples that - * correspond to the matching ( 3 ) coefficient index. - * - * @param odd-sized number of filter taps (ie coefficients) and buffer - */ - private void generateIndexMap( int size ) - { - //Ensure we have an odd size - assert( size % 2 == 1 ); - - int mapWidth = ( ( size + 1 ) / 2 ) + 1; - - //Set center tap index for coefficients array - mCenterCoefficient = ( size - 1 ) / 2; - mCenterCoefficientMapIndex = mCenterCoefficient + 1; - - mIndexMap = new int[ size ][ mapWidth ]; - - //Setup the first row, buffer pointer index 0, as a starting point - for( int x = 0; x < mapWidth - 2; x += 2 ) - { - mIndexMap[ 0 ][ x ] = x; - mIndexMap[ 0 ][ x + 1 ] = size - 1 - x; - } - - //Place center tap index in last element - mIndexMap[ 0 ][ mCenterCoefficientMapIndex ] = mCenterCoefficient; - - //For each subsequent row, increment the previous row's value by 1, - //subtracting size as needed, to keep the values between 0 and size - 1 - for( int x = 1; x < size; x++ ) - { - for( int y = 0; y < mapWidth; y++ ) - { - mIndexMap[ x ][ y ] = mIndexMap[ x - 1 ][ y ] + 1; - - if( mIndexMap[ x ][ y ] >= size ) - { - mIndexMap[ x ][ y ] -= size; - } - } - } - } - - public static void main( String[] args ) - { - float[] c = Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.mCoefficients; - - float sum = 0; - - for( int x = 0; x < c.length; x++ ) - { - sum += c[ x ]; - } - - System.out.println( sum ); - } -} diff --git a/src/dsp/filter/ComplexPolyphaseDecimationFilter.java b/src/dsp/filter/ComplexPolyphaseDecimationFilter.java deleted file mode 100644 index c2383050..00000000 --- a/src/dsp/filter/ComplexPolyphaseDecimationFilter.java +++ /dev/null @@ -1,177 +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 - ******************************************************************************/ -package dsp.filter; - -import sample.Listener; -import sample.complex.Complex; -import buffer.ComplexTappedCircularBuffer; - -public class ComplexPolyphaseDecimationFilter extends ComplexFilter -{ - private FilterPhase[] mFilterPhases; - private int mPhasePointer = 0; - private int mPhaseCount; - - /** - * Implements a decimating poly-phase filter bank for complex samples, as - * described in Understanding Digital Signal Processing, 3e, Lyons, in - * section 10.7, figure 10-14. - * - * Constraints: - * - Low-pass filter coefficient length must be an integer multiple - * of the decimation rate: - * - * Consideration: - * - Low pass filter cutoff frequency must be less than: - * ( input sample rate / 2 / decimation ) - * - * @param coefficients - low pass filter coefficients. - * @param decimationRate - integer decimation rate - */ - public ComplexPolyphaseDecimationFilter( double[] coefficients, - int decimationRate ) - { - /* Verify that the filter length is an integer multiple of the - * decimation rate */ - assert( coefficients.length % decimationRate == 0 ); - - mPhaseCount = decimationRate; - - /* Construct a number of filter phases equal to the decimation rate */ - mFilterPhases = new FilterPhase[ mPhaseCount ]; - - for( int phaseNumber = 0; phaseNumber < mPhaseCount; phaseNumber++ ) - { - double[] phaseCoefficients = - getPhaseCoefficients( coefficients, decimationRate, phaseNumber ); - - mFilterPhases[ phaseNumber ] = new FilterPhase( phaseCoefficients ); - } - } - - /** - * Inputs a new sample into the filter at the non-decimated rate - */ - @Override - public void receive( Complex sample ) - { - mFilterPhases[ mPhasePointer++ ].receive( sample ); - - if( mPhasePointer >= mPhaseCount ) - { - /* Broadcast a new output sample */ - calculate(); - - /* Reset the pointer */ - mPhasePointer = 0; - } - } - - /** - * Outputs a complex sample at the decimated rate, comprised of the sum of - * the outputs of the individual filter stages, trigger by a decimation rate - * counter on the input side. - */ - private void calculate() - { - if( hasListener() ) - { - /* Accumulators */ - float left = 0.0f; - float right = 0.0f; - - for( int x = 0; x < mPhaseCount; x++ ) - { - Complex phaseSample = mFilterPhases[ x ].calculate(); - - left += phaseSample.left(); - right += phaseSample.right(); - } - - send( new Complex( left, right ) ); - } - } - - /** - * Returns the subset of filter coefficients that belong to the filter bank - * phase. - */ - private double[] getPhaseCoefficients( double[] coefficients, - int phaseCount, - int phaseNumber ) - { - int phaseFilterLength = (int)( coefficients.length / phaseCount ); - - double[] phaseCoefficients = new double[ phaseFilterLength ]; - - for( int x = 0; x < phaseFilterLength; x++ ) - { - phaseCoefficients[ x ] = coefficients[ x * phaseCount + phaseNumber ]; - } - - return phaseCoefficients; - } - - /** - * Implements a single phase of a poly-phase filter bank - */ - public class FilterPhase implements Listener - { - private int mLength; - private double[] mCoefficients; - private ComplexTappedCircularBuffer mBuffer; - - /** - * @param coefficients - subset of the filter coefficients for this - * phase of the poly-phase filter bank - */ - public FilterPhase( double[] coefficients ) - { - mCoefficients = coefficients; - mLength = coefficients.length; - - /* The constructor preloads the buffer with 0-valued samples */ - mBuffer = new ComplexTappedCircularBuffer( coefficients.length ); - } - - @Override - public void receive( Complex sample ) - { - mBuffer.add( sample ); - } - - /** - * Performs convolution by multiplying the buffer samples by their - * corresponding filter coefficients and returns the sum - */ - public Complex calculate() - { - /* Accumulators */ - double left = 0.0d; - double right = 0.0d; - - for( int x = 0; x < mLength; x++ ) - { - left += ( mBuffer.get( x ).left() * mCoefficients[ x ] ); - right += ( mBuffer.get( x ).right() * mCoefficients[ x ] ); - } - - return new Complex( (float)left, (float)right ); - } - } -} diff --git a/src/dsp/filter/dc/DCRemovalFilter_RealBuffer.java b/src/dsp/filter/dc/DCRemovalFilter_RealBuffer.java new file mode 100644 index 00000000..308b4ed7 --- /dev/null +++ b/src/dsp/filter/dc/DCRemovalFilter_RealBuffer.java @@ -0,0 +1,60 @@ +package dsp.filter.dc; + +import sample.Listener; +import sample.real.RealBuffer; + +public class DCRemovalFilter_RealBuffer implements Listener +{ + private Listener mListener; + /** + * Removes DC bias present across the samples in the buffer by calculating + * the mean DC component and subtracting that component from each of the + * samples. + */ + public DCRemovalFilter_RealBuffer() + { + } + + public void dispose() + { + mListener = null; + } + + public RealBuffer filter( RealBuffer buffer ) + { + double accumulator = 0.0d; + + for( float sample: buffer.getSamples() ) + { + accumulator += sample; + } + + float mean = (float)( accumulator / (double)buffer.getSamples().length ); + + for( float sample: buffer.getSamples() ) + { + sample -= mean; + } + + return buffer; + } + + @Override + public void receive( RealBuffer buffer ) + { + if( mListener != null ) + { + mListener.receive( filter( buffer ) ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } +} diff --git a/src/dsp/fm/FilteringNBFMDemodulator.java b/src/dsp/fm/FilteringNBFMDemodulator.java deleted file mode 100644 index 391d4938..00000000 --- a/src/dsp/fm/FilteringNBFMDemodulator.java +++ /dev/null @@ -1,96 +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 - ******************************************************************************/ -package dsp.fm; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.Listener; -import sample.complex.Complex; -import sample.real.RealSampleBroadcaster; -import sample.real.RealSampleListener; -import dsp.filter.ComplexFIRFilterOld; -import dsp.filter.FilterFactory; -import dsp.filter.FloatFIRFilter; -import dsp.filter.Window.WindowType; - -@Deprecated -public class FilteringNBFMDemodulator implements Listener -{ - private final static Logger mLog = - LoggerFactory.getLogger( FilteringNBFMDemodulator.class ); - - private RealSampleBroadcaster mBroadcaster = new RealSampleBroadcaster(); - private ComplexFIRFilterOld mIQFilter; - private FloatFIRFilter mAudioFilter; - private FMDiscriminator mDiscriminator; - - public FilteringNBFMDemodulator( float[] iqFilter, float iqGain, - float[] audioFilter, float audioGain ) - { - mIQFilter = new ComplexFIRFilterOld( iqFilter, iqGain ); - - mAudioFilter = new FloatFIRFilter( audioFilter, audioGain ); - - mDiscriminator = new FMDiscriminator( 1 ); - - mIQFilter.setListener( mDiscriminator ); - - mDiscriminator.setListener( mAudioFilter ); - - mAudioFilter.setListener( mBroadcaster ); - } - - /** - * Implements a quadrature narrow-band demodulator that produces float - * valued demodulated, audio filtered output samples. - */ - public FilteringNBFMDemodulator() - { - this( FilterFactory.getLowPass( 48000, 5000, 7000, 48, WindowType.HAMMING, true ), - 1.0002f, - FilterFactory.getLowPass( 48000, 3000, 6000, 48, WindowType.HAMMING, true ), - 2 ); - } - - /** - * Receive method for complex samples that are fed into this class to be - * processed - */ - @Override - public void receive( Complex quadratureSample ) - { - mIQFilter.receive( quadratureSample ); - } - - /** - * Adds a listener to receive demodulated samples - */ - public void addListener( RealSampleListener listener ) - { - mBroadcaster.addListener( listener ); - } - - /** - * Removes a listener from receiving demodulated samples - */ - public void removeListener( RealSampleListener listener ) - { - mBroadcaster.removeListener( listener ); - } -} diff --git a/src/dsp/fm/NBFMDemodulator.java b/src/dsp/fm/NBFMDemodulator.java deleted file mode 100644 index ef0e33e9..00000000 --- a/src/dsp/fm/NBFMDemodulator.java +++ /dev/null @@ -1,90 +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 - ******************************************************************************/ -package dsp.fm; - -import sample.Listener; -import sample.complex.Complex; -import sample.real.RealSampleBroadcaster; -import sample.real.RealSampleListener; -import dsp.filter.ComplexFIRFilterOld; -import dsp.filter.FilterFactory; -import dsp.filter.Window.WindowType; - -@Deprecated -public class NBFMDemodulator implements Listener -{ - private RealSampleBroadcaster mBroadcaster = new RealSampleBroadcaster(); - private ComplexFIRFilterOld mIQFilter; - private FMDiscriminator mDiscriminator; - - /** - * Implements a quadrature narrow-band demodulator that produces float - * valued demodulated, audio filtered output samples. - */ - public NBFMDemodulator() - { - this( FilterFactory.getLowPass( 48000, 6250, 73, WindowType.HAMMING ), - 1.0002f, true ); - } - - public NBFMDemodulator( float[] iqFilter, float iqGain, boolean afc ) - { - mIQFilter = new ComplexFIRFilterOld( iqFilter, iqGain ); - - mDiscriminator = new FMDiscriminator( 1 ); - - mIQFilter.setListener( mDiscriminator ); - - mDiscriminator.setListener( mBroadcaster ); - } - - public void dispose() - { - mDiscriminator.dispose(); - - mBroadcaster.dispose(); - - mIQFilter.dispose(); - } - - /** - * Receive method for complex samples that are fed into this class to be - * processed - */ - @Override - public void receive( Complex sample ) - { - mIQFilter.receive( sample ); - } - - /** - * Adds a listener to receive demodulated samples - */ - public void addListener( RealSampleListener listener ) - { - mBroadcaster.addListener( listener ); - } - - /** - * Removes a listener from receiving demodulated samples - */ - public void removeListener( RealSampleListener listener ) - { - mBroadcaster.removeListener( listener ); - } -} diff --git a/src/dsp/gain/AutomaticGainControl.java b/src/dsp/gain/AutomaticGainControl.java index f7afe8da..8f397ebd 100644 --- a/src/dsp/gain/AutomaticGainControl.java +++ b/src/dsp/gain/AutomaticGainControl.java @@ -93,6 +93,7 @@ public class AutomaticGainControl Math.exp( -1.0 / ( SAMPLE_RATE * DECAY * .001 ) ); /* Hang timer release decay time constant in seconds */ + @SuppressWarnings( "unused" ) private static final double RELEASE_TIME_CONSTANT = 0.05; /* Specifies the AGC Knee in dB if AGC is active (nominal range -160 to 0 dB) */ diff --git a/src/dsp/gain/ComplexAutomaticGainControl.java b/src/dsp/gain/ComplexAutomaticGainControl.java index c5896016..3fc0d563 100644 --- a/src/dsp/gain/ComplexAutomaticGainControl.java +++ b/src/dsp/gain/ComplexAutomaticGainControl.java @@ -96,6 +96,7 @@ public class ComplexAutomaticGainControl implements Listener, Math.exp( -1.0 / ( SAMPLE_RATE * DECAY * .001 ) ); /* Hang timer release decay time constant in seconds */ + @SuppressWarnings( "unused" ) private static final double RELEASE_TIME_CONSTANT = 0.05; /* Specifies the AGC Knee in dB if AGC is active (nominal range -160 to 0 dB) */ diff --git a/src/dsp/gain/ComplexFeedForwardGainControl.java b/src/dsp/gain/ComplexFeedForwardGainControl.java index b5a8c033..6abcd62c 100644 --- a/src/dsp/gain/ComplexFeedForwardGainControl.java +++ b/src/dsp/gain/ComplexFeedForwardGainControl.java @@ -1,8 +1,5 @@ package dsp.gain; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sample.complex.Complex; import sample.complex.ComplexSampleListener; import buffer.FloatCircularBuffer; @@ -32,9 +29,6 @@ import buffer.FloatCircularBuffer; ******************************************************************************/ public class ComplexFeedForwardGainControl implements ComplexSampleListener { - private final static Logger mLog = - LoggerFactory.getLogger( ComplexFeedForwardGainControl.class ); - public static final float OBJECTIVE_ENVELOPE = 1.0f; public static final float MINIMUM_ENVELOPE = 0.0001f; diff --git a/src/dsp/mixer/Oscillator.java b/src/dsp/mixer/Oscillator.java index b3e300c7..19736800 100644 --- a/src/dsp/mixer/Oscillator.java +++ b/src/dsp/mixer/Oscillator.java @@ -17,20 +17,14 @@ ******************************************************************************/ package dsp.mixer; -import java.util.Arrays; - -import org.jtransforms.fft.FloatFFT_1D; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import dsp.filter.Filters; -import dsp.filter.hilbert.HilbertTransform; import sample.complex.Complex; public class Oscillator { - private final static Logger mLog = - LoggerFactory.getLogger( Oscillator.class ); + private final static Logger mLog = LoggerFactory.getLogger( Oscillator.class ); private double mFrequency; private double mSampleRate; diff --git a/src/dsp/psk/LSMDemodulator.java b/src/dsp/psk/LSMDemodulator.java index dd44a5a1..5142d0d9 100644 --- a/src/dsp/psk/LSMDemodulator.java +++ b/src/dsp/psk/LSMDemodulator.java @@ -40,13 +40,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sample.Listener; import sample.complex.Complex; import sample.complex.ComplexSampleListener; -import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeListener; import buffer.FloatAveragingBuffer; import dsp.filter.interpolator.RealInterpolator; import dsp.symbol.Dibit; @@ -61,9 +58,6 @@ import dsp.symbol.Dibit; */ public class LSMDemodulator implements Instrumentable, ComplexSampleListener { - private final static Logger mLog = - LoggerFactory.getLogger( LSMDemodulator.class ); - private List mAvailableTaps; /* 45 degree rotation angle */ @@ -79,7 +73,7 @@ public class LSMDemodulator implements Instrumentable, ComplexSampleListener private CostasLoop mCostasLoop = new CostasLoop(); - private FrequencyChangeListener mFrequencyChangeListener; + private IFrequencyChangeListener mFrequencyChangeListener; private EyeDiagramDataTap mEyeDiagramDataTap; @@ -153,12 +147,12 @@ public class LSMDemodulator implements Instrumentable, ComplexSampleListener } } - public void addListener( FrequencyChangeListener listener ) + public void addListener( IFrequencyChangeListener listener ) { mFrequencyChangeListener = listener; } - public void removeListener( FrequencyChangeListener listener ) + public void removeListener( IFrequencyChangeListener listener ) { mFrequencyChangeListener = null; } diff --git a/src/dsp/psk/LSMDemodulator2.java b/src/dsp/psk/LSMDemodulator2.java deleted file mode 100644 index 1df263f1..00000000 --- a/src/dsp/psk/LSMDemodulator2.java +++ /dev/null @@ -1,572 +0,0 @@ -package dsp.psk; - -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014, 2015 Dennis Sheirer - * - * Gardner detector, Costas loop and Interpolator derived from: - * - * OP25 - gardner_costas_cc_impl.cc - * Copyright 2010, 2011, 2012, 2013 KA1RBI (gardner_costas_cc_impl.cc) - * - * OP25 - scope.py - * Copyright 2008-2011 Steve Glass - * Copyright 2011, 2012, 2013 KA1RBI - * - * GNURadio - control_loop and mpsk_receiver_cc - * Copyright 2011,2013 Free Software Foundation, Inc. - * - * 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 - ******************************************************************************/ - -import instrument.Instrumentable; -import instrument.tap.Tap; -import instrument.tap.TapGroup; -import instrument.tap.stream.EyeDiagramData; -import instrument.tap.stream.EyeDiagramDataTap; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.Listener; -import sample.Provider; -import sample.complex.Complex; -import sample.complex.ComplexSampleListener; -import source.tuner.frequency.FrequencyChangeListener; -import buffer.FloatAveragingBuffer; -import dsp.filter.interpolator.RealInterpolator; -import dsp.symbol.Dibit; - -/** - * Implements an LSM (Pi/4) demodulator using an early/late symbol timing error - * detector and a Costas Loop as a phase locked loop synchronized with the - * incoming signal carrier frequency. - * - * Sample Rate: 48000 - * Symbol Rate: 4800 - */ -public class LSMDemodulator2 implements Instrumentable, ComplexSampleListener, - Provider -{ - private List mAvailableTaps; - - private final static Logger mLog = - LoggerFactory.getLogger( LSMDemodulator2.class ); - - /* 45 degree rotation angle */ - public static final float THETA = (float)( Math.PI / 4.0d ); - - /* 45 degree point */ - public static final Complex POINT_45_DEGREES = - new Complex( (float)Math.sin( THETA ), (float)Math.cos( THETA ) ); - - private DecimalFormat mDecimalFormat = new DecimalFormat( "0.000000" ); - - private Listener mListener; - - private EarlyLateSymbolTiming mGardnerDetector = new EarlyLateSymbolTiming(); - - private CostasLoop mCostasLoop = new CostasLoop(); - - private FrequencyChangeListener mFrequencyChangeListener; - - private EyeDiagramDataTap mEyeDiagramDataTap; - - public LSMDemodulator2() - { - } - - @Override - public void receive( float inphase, float quadrature ) - { - mGardnerDetector.receive( inphase, quadrature ); - } - - /** - * Applies a phase correction value to the costas loop to correct when a - * phase lock error is detected in the binary output stream. - * - * @param correction - value in radians - */ - public void correctPhaseError( double correction ) - { - mCostasLoop.correctPhaseError( correction ); - } - - /** - * Constrains value to the range of ( -maximum <> maximum ) - */ - public static float clip( float value, float maximum ) - { - if( value > maximum ) - { - return maximum; - } - else if( value < -maximum ) - { - return -maximum; - } - - return value; - } - - /** - * Constrains timing error to +/- the maximum value and corrects any - * floating point invalid numbers - */ - @SuppressWarnings("unused") - private float normalize( float error, float maximum ) - { - if( Float.isNaN( error ) ) - { - return 0.0f; - } - else - { - return clip( error, maximum ); - } - } - - public void addListener( FrequencyChangeListener listener ) - { - mFrequencyChangeListener = listener; - } - - public void removeListener( FrequencyChangeListener listener ) - { - mFrequencyChangeListener = null; - } - - /** - * Processes the current loop frequency of the costas loop and broadcasts - * tuner frequency offset adjustments 10 times a second as needed to keep - * the frequency offset below half of the maximum frequency - */ - public class FrequencyControl - { - /* Set the trigger threshold at half of the maximum costas loop control - * frequency */ - private static final float CORRECTION_THRESHOLD_FREQUENCY = - ( 2.0f * (float)Math.PI * 600.0f ) / 48000.0f; - - private FloatAveragingBuffer mBuffer = new FloatAveragingBuffer( 40 ); - - private long mFrequencyError = 0; - - private int mCounter = 0; - - public void receive( float frequency ) - { -// mCounter++; -// -// if( mCounter >= 480 ) -// { -// float average = mBuffer.get( frequency ); -// mLog.debug( "Avg:" + average + " Threshold:" + CORRECTION_THRESHOLD_FREQUENCY); -// -// boolean correctionNeeded = false; -// -// if( average > CORRECTION_THRESHOLD_FREQUENCY ) -// { -// mFrequencyError--; -// -// correctionNeeded = true; -// } -// else if( average < -CORRECTION_THRESHOLD_FREQUENCY ) -// { -// mFrequencyError++; -// -// correctionNeeded = true; -// } -// -// if( correctionNeeded && mFrequencyChangeListener != null ) -// { -// mLog.debug( "Issuing Frequency Correction: " + mFrequencyError ); -// mFrequencyChangeListener.frequencyChanged( -// new FrequencyChangeEvent( Attribute.FREQUENCY_ERROR, -// mFrequencyError ) ); -// } -// -// mCounter = 0; -// } -// else -// { -// mBuffer.get( frequency ); -// } - } - } - - /** - * Early/Late symbol timing error detector. Linear Simulcast Modulation (LSM) - * uses a Raised Cosine Filter at the transmitter which causes the - * signal magnitude to rise to unity at the optimal sampling point and drop - * to zero or a midpoint between unity and zero between each symbol period. - * - * This error detector continually adjusts symbol timing by continuously - * attempting to find the largest magnitude value between two interpolated - * sample points during each symbol period. - */ - public class EarlyLateSymbolTiming implements ComplexSampleListener - { - public static final int SAMPLES_PER_SYMBOL = 10; - public static final int TWICE_SAMPLES_PER_SYMBOL = 20; - - private float[] mDelayLineInphase = new float[ 2 * TWICE_SAMPLES_PER_SYMBOL ]; - private float[] mDelayLineQuadrature = new float[ 2 * TWICE_SAMPLES_PER_SYMBOL ]; - - private int mDelayLinePointer = 0; - - /* Sampling point */ - private float mMu = 10.0f; - private float mGainMu = 0.5f; - - /* Samples per symbol */ - private float mOmega = 10.0f; - private float mGainOmega = 0.1f * mGainMu * mGainMu; - private float mOmegaRel = 0.005f; - private float mOmegaMid = 10.0f; - - private RealInterpolator mInterpolator = new RealInterpolator( 1.0f ); - - private Complex mPreviousSample = new Complex( 0.0f, 0.0f ); - - /** - * Provides symbol sampling timing control - */ - public EarlyLateSymbolTiming() - { - } - - @Override - public void receive( float inphase, float quadrature ) - { - /* Count down samples per symbol until we calculate the symbol */ - mMu--; - - mCostasLoop.increment(); - - /* Mix incoming sample with costas loop to remove any rotation - * that is present from a mis-tuned carrier frequency */ - Complex derotatedSample = Complex.multiply( - mCostasLoop.getCurrentVector(), inphase, quadrature ); - - /* Fill up the delay line to use with the interpolator */ - mDelayLineInphase[ mDelayLinePointer ] = derotatedSample.inphase(); - mDelayLineInphase[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedSample.inphase(); - mDelayLineQuadrature[ mDelayLinePointer ] = derotatedSample.quadrature(); - mDelayLineQuadrature[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedSample.quadrature(); - - /* Imcrement pointer and keep pointer in bounds */ - mDelayLinePointer = ( mDelayLinePointer + 1 ) % TWICE_SAMPLES_PER_SYMBOL; - - /* Calculate the symbol once we've stored enough samples */ - if( mMu <= 1.0f ) - { - Complex earlySample = mInterpolator.filter( mDelayLineInphase, - mDelayLineQuadrature, mDelayLinePointer, mMu ); - - Complex middleSample = mInterpolator.filter( mDelayLineInphase, - mDelayLineQuadrature, mDelayLinePointer + 1, mMu ); - - Complex lateSample = mInterpolator.filter( mDelayLineInphase, - mDelayLineQuadrature, mDelayLinePointer + 2, mMu ); - - Complex currentSymbol = Complex.multiply( - middleSample, mPreviousSample.conjugate() ); - - /* Set symbol gain to unity */ - currentSymbol.normalize(); - - float earlyLateError = - lateSample.magnitude() - earlySample.magnitude(); - - if( mEyeDiagramDataTap != null ) - { - mEyeDiagramDataTap.receive( new EyeDiagramData( - Arrays.copyOfRange( mDelayLineInphase, 0, 20 ), - Arrays.copyOfRange( mDelayLineQuadrature, 0, 20 ), - (float)mDelayLinePointer + 4.0f + mMu, - (float)mDelayLinePointer + 4.0f + mMu + 0.25f, - earlyLateError ) ); - } - - /* mOmega is samples per symbol and is constrained to floating - * between +/- .005 of the nominal 10.0 samples per symbol */ - mOmega = mOmega + mGainOmega * earlyLateError; - mOmega = mOmegaMid + clip( mOmega - mOmegaMid, mOmegaRel ); - - - /* Adjust sample timing based on error of current sample */ - mMu += mOmega + ( mGainMu * earlyLateError ); - - - /* Store current samples/symbols to use for the next period */ - mPreviousSample = middleSample; - - /* Update costas loop using phase error present in current - * symbol. The symbol is rotated from star orientation to polar - * orientation to simplify error calculation */ - mCostasLoop.receive( currentSymbol ); -// mCostasLoop.receive( -// ComplexSample.multiply( currentSymbol, POINT_45_DEGREES ) ); - - /* Dispatch the differentiated symbol to the registered listener */ - if( mListener != null ) - { - mListener.receive( currentSymbol ); - } - } - } - } - - /** - * Costas Loop - phase locked loop synchronized to the frequency offset of - * the incoming signal to enable the signal to be mixed down to zero offset - * for proper synchronization. The mFrequency value indicates the detected - * frequency offset. We attempt to keep that value close to zero by issuing - * frequency adjustments to the tuner channel source. - * - * Most of the costas loop code was ported from gnuradio/control_loop and - * gnuradio/mpsk_receiver_cc using initialization values from KA1RBI's - * OP25/cqpsk.py - */ - public class CostasLoop implements Listener - { - public static final double TWO_PI = 2.0 * Math.PI; - - private static final float MAXIMUM_FREQUENCY = - ( 2400.0f * (float)TWO_PI ) / 48000.0f; - - /* http://www.trondeau.com/blog/2011/8/13/control-loop-gain-values.html */ - private float mDamping = (float)Math.sqrt( 2.0 ) / 2.0f; - - /* Use denominator between 100 (most) and 400 (least) to adjust costas - * loop control level */ - private float mLoopBandwidth = (float)( TWO_PI / 400.0d ); - - private float mAlphaGain = ( 4.0f * mDamping * mLoopBandwidth ) / - ( 1.0f + ( 2.0f * mDamping * mLoopBandwidth ) + - ( mLoopBandwidth * mLoopBandwidth ) ); - - private float mBetaGain = ( 4.0f * mLoopBandwidth * mLoopBandwidth ) / - ( 1.0f + ( 2.0f * mDamping * mLoopBandwidth ) + - ( mLoopBandwidth * mLoopBandwidth ) ); - - private float mLoopPhase = 0.0f; - - private float mLoopFrequency = 0.0f; - - private FrequencyControl mFrequencyControl = new FrequencyControl(); - - public CostasLoop() - { - } - - /** - * Applies a phase correction value to the current phasor. Use this - * method to apply correction when a +/- 90, or 180 degree phase error - * is detected in the binary output stream. - * - * If the supplied correction value places the loop frequency outside - * of the max frequency, then the frequency will be corrected 360 - * degrees in the opposite direction to maintain within the max - * frequency bounds. - * - * @param correction - correction value in radians - */ - public void correctPhaseError( double correction ) - { - mLoopFrequency += correction; - - if( mLoopFrequency > MAXIMUM_FREQUENCY ) - { - mLoopFrequency -= 2.0d * MAXIMUM_FREQUENCY; - } - - if( mLoopFrequency < -MAXIMUM_FREQUENCY ) - { - mLoopFrequency += 2.0d * MAXIMUM_FREQUENCY; - } - } - - /** - * Increments the phase of the loop - */ - public void increment() - { - mLoopPhase += mLoopFrequency; - - /* Keep the loop phase in bounds */ - unwrapPhase(); - } - - private void unwrapPhase() - { - while( mLoopPhase > TWO_PI ) - { - mLoopPhase -= TWO_PI; - } - - while( mLoopPhase < -TWO_PI ) - { - mLoopPhase += TWO_PI; - } - } - - /** - * Current vector of the loop rotated by THETA degrees. - */ - public Complex getCurrentVector() - { -// return ComplexSample.fromAngle( mLoopPhase + THETA ); - return Complex.fromAngle( mLoopPhase ); - } - - @Override - public void receive( Complex sample ) - { - /* Calculate phase error */ - float phaseError = getPhaseError( sample ); - - Dibit d = QPSKPolarSlicer.decide( sample ); -// mLog.debug( "Dibit: " + d.name() + " Error: " + phaseError ); - - /* Adjust for phase error */ - adjust( phaseError ); - } - - /** - * Updates the costas loop frequency and phase to adjust for the phase - * error value - * - * @param phase_error - (-)= late and (+)= early - */ - private void adjust( float phase_error ) - { - mLoopFrequency += mBetaGain * phase_error; - mLoopPhase += mLoopFrequency + mAlphaGain * phase_error; - - /* Maintain phase between +/- 2 * PI */ - unwrapPhase(); - - /* Limit frequency to +/- maximum loop frequency */ - limitFrequency(); - } - - /** - * Constrains the frequency within the bounds of +/- loop frequency - */ - private void limitFrequency() - { - /* Check for and issue tuner offset correction */ - mFrequencyControl.receive( mLoopFrequency ); - - if( mLoopFrequency > MAXIMUM_FREQUENCY ) - { - mLoopFrequency = MAXIMUM_FREQUENCY; - } - - if( mLoopFrequency < -MAXIMUM_FREQUENCY ) - { - mLoopFrequency = -MAXIMUM_FREQUENCY; - } - } - - /** - * Detects rotational error present when the costas loop is not aligned - * with the carrier frequency. Provides error feedback to adjust - * mixer frequency. - */ - public float getPhaseError( Complex sample ) - { - float phase_error = 0; - - if( Math.abs( sample.real() ) > Math.abs( sample.imaginary() ) ) - { - if( sample.real() > 0 ) - { - phase_error = -sample.imaginary(); - } - else - { - phase_error = sample.imaginary(); - } - } - else - { - if( sample.imaginary() > 0 ) - { - phase_error = sample.real(); - } - else - { - phase_error = -sample.real(); - } - } - - return phase_error; - } - } - - @Override - public void setListener( Listener listener ) - { - mListener = listener; - } - - @Override - public void removeListener( Listener listener ) - { - mListener = null; - } - - @Override - public List getTapGroups() - { - if( mAvailableTaps == null ) - { - mAvailableTaps = new ArrayList<>(); - - TapGroup group = new TapGroup( "LSM Demodulator 2" ); - group.add( new EyeDiagramDataTap( "Eye Diagram", 0, 4800 ) ); - - mAvailableTaps.add( group ); - } - - return mAvailableTaps; - } - - @Override - public void registerTap( Tap tap ) - { - if( tap instanceof EyeDiagramDataTap ) - { - mEyeDiagramDataTap = (EyeDiagramDataTap)tap; - } - } - - @Override - public void unregisterTap( Tap tap ) - { - if( tap instanceof EyeDiagramDataTap ) - { - mEyeDiagramDataTap = null; - } - } -} diff --git a/src/edac/CRCFleetsync.java b/src/edac/CRCFleetsync.java index 1533e2ca..eb936dc2 100644 --- a/src/edac/CRCFleetsync.java +++ b/src/edac/CRCFleetsync.java @@ -19,9 +19,6 @@ package edac; import java.util.BitSet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * Fleetsync CRC checksum utility * @@ -37,9 +34,6 @@ import org.slf4j.LoggerFactory; */ public class CRCFleetsync { - private final static Logger mLog = - LoggerFactory.getLogger( CRCFleetsync.class ); - private static short[] sCHECKSUMS = new short[] { 0x740A, // 111010000001010 Bit 0 0x3A05, // 011101000000101 Bit 1 diff --git a/src/gui/ChannelMemoryLogger.java b/src/gui/ChannelMemoryLogger.java index 9cf11951..39b1085b 100644 --- a/src/gui/ChannelMemoryLogger.java +++ b/src/gui/ChannelMemoryLogger.java @@ -5,6 +5,7 @@ import java.text.NumberFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent; import controller.channel.ChannelEventListener; @@ -46,33 +47,40 @@ public class ChannelMemoryLogger implements Runnable, ChannelEventListener { switch( event.getEvent() ) { - case CHANNEL_ADDED: - mChannelCount++; + case NOTIFICATION_ADD: + if( event.getChannel().getChannelType() == ChannelType.TRAFFIC ) + { + mTrafficChannelCount++; + } + else + { + mChannelCount++; + } break; - case CHANNEL_DELETED: - mChannelCount--; + case NOTIFICATION_DELETE: + if( event.getChannel().getChannelType() == ChannelType.TRAFFIC ) + { + mTrafficChannelCount--; + } + else + { + mChannelCount--; + } break; - case CHANNEL_DISABLED: + case REQUEST_DISABLE: break; - case CHANNEL_ENABLED: + case REQUEST_ENABLE: break; - case CHANNEL_PROCESSING_STARTED: + case NOTIFICATION_PROCESSING_START: mChannelProcessingCount++; break; - case CHANNEL_PROCESSING_STOPPED: + case NOTIFICATION_PROCESSING_STOP: mChannelProcessingCount--; break; - case CHANNEL_STATE_RESET: - break; - case TRAFFIC_CHANNEL_ADDED: - mTrafficChannelCount++; - break; - case TRAFFIC_CHANNEL_DELETED: - mTrafficChannelCount--; + case NOTIFICATION_STATE_RESET: break; default: break; - } } } diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index 80d3c8fb..40bfb51a 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,7 +43,9 @@ import javax.swing.JOptionPane; import javax.swing.JSeparator; import javax.swing.KeyStroke; +import map.IconManager; import map.MapService; +import module.log.EventLogManager; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; @@ -51,27 +53,37 @@ import org.slf4j.LoggerFactory; import playlist.PlaylistManager; import properties.SystemProperties; +import record.RecorderManager; +import sample.Listener; import settings.SettingsManager; -import source.tuner.Tuner; -import source.tuner.TunerSelectionListener; +import source.SourceManager; +import source.tuner.TunerEvent; +import source.tuner.TunerModel; +import source.tuner.TunerSpectralDisplayManager; +import source.tuner.configuration.TunerConfigurationModel; import spectrum.SpectralDisplayPanel; import util.TimeStamp; +import alias.AliasModel; +import alias.action.AliasActionManager; import audio.AudioManager; import com.jidesoft.swing.JideSplitPane; -import controller.ConfigurationControllerModel; import controller.ControllerPanel; -import controller.ResourceManager; +import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; import controller.channel.ChannelEventListener; -import controller.channel.ChannelManager; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; +import controller.channel.ChannelSelectionManager; +import controller.channel.map.ChannelMapModel; -public class SDRTrunk +public class SDRTrunk implements Listener { private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class ); private ControllerPanel mControllerPanel; + private SettingsManager mSettingsManager; private SpectralDisplayPanel mSpectralPanel; private JFrame mMainGui = new JFrame(); @@ -103,44 +115,79 @@ public class SDRTrunk //Log current properties setting SystemProperties.getInstance().logCurrentSettings(); - - /** - * Construct the resource manager now, so that it can use the system - * properties that were just loaded - */ - ResourceManager resource = new ResourceManager(); - //TODO: get rid of resource manager ... below is start of breaking out - //the encapsulated pieces from the resource manager + TunerConfigurationModel tunerConfigurationModel = new TunerConfigurationModel(); + TunerModel tunerModel = new TunerModel( tunerConfigurationModel ); - AudioManager audio = resource.getAudioManager(); - ChannelManager channel = resource.getChannelManager(); - ConfigurationControllerModel controller = resource.getController(); - MapService map = resource.getMapService(); - PlaylistManager playlist = resource.getPlaylistManager(); - SettingsManager settings = resource.getSettingsManager(); - - mControllerPanel = new ControllerPanel( audio, controller, channel, - map, playlist, settings ); - - mSpectralPanel = new SpectralDisplayPanel( channel, controller, - playlist, settings ); - - mTitle = getTitle(); + ThreadPoolManager threadPoolManager = new ThreadPoolManager(); - //Initialize the GUI - initGUI(); + mSettingsManager = new SettingsManager( threadPoolManager, tunerConfigurationModel ); + + AliasModel aliasModel = new AliasModel(); + + ChannelModel channelModel = new ChannelModel(); + + ChannelMapModel channelMapModel = new ChannelMapModel(); + + EventLogManager eventLogManager = new EventLogManager(); + + RecorderManager recorderManager = new RecorderManager( threadPoolManager ); + + SourceManager sourceManager = new SourceManager( tunerModel, + mSettingsManager, threadPoolManager ); + + ChannelProcessingManager channelProcessingManager = new ChannelProcessingManager( + channelModel, channelMapModel, aliasModel, eventLogManager, recorderManager, sourceManager ); + channelProcessingManager.addAudioPacketListener( recorderManager ); + + channelModel.addListener( channelProcessingManager ); + + ChannelSelectionManager channelSelectionManager = + new ChannelSelectionManager( channelModel ); + channelModel.addListener( channelSelectionManager ); + + AliasActionManager aliasActionManager = new AliasActionManager( threadPoolManager ); + channelProcessingManager.addMessageListener( aliasActionManager ); + + AudioManager audioManager = new AudioManager( threadPoolManager, sourceManager.getMixerManager() ); + channelProcessingManager.addAudioPacketListener( audioManager ); + + MapService mapService = new MapService( mSettingsManager ); + channelProcessingManager.addMessageListener( mapService ); + + mControllerPanel = new ControllerPanel( audioManager, aliasModel, + channelModel, channelMapModel, channelProcessingManager, + mapService, mSettingsManager, sourceManager, tunerModel ); + + mSpectralPanel = new SpectralDisplayPanel( channelModel, + channelProcessingManager, mSettingsManager ); + + TunerSpectralDisplayManager tunerSpectralDisplayManager = + new TunerSpectralDisplayManager( mSpectralPanel, + channelModel, channelProcessingManager, mSettingsManager ); + tunerModel.addListener( tunerSpectralDisplayManager ); + tunerModel.addListener( this ); + + PlaylistManager playlistManager = new PlaylistManager( threadPoolManager, + aliasModel, channelModel, channelMapModel ); + + playlistManager.init(); - mLog.info( "starting main application gui" ); - if( mLogChannelAndMemoryUsage ) { Runnable cml = new ChannelMemoryLogger(); - resource.getChannelManager().addListener( (ChannelEventListener)cml ); - resource.getThreadPoolManager().scheduleFixedRate( + channelModel.addListener( (ChannelEventListener)cml ); + threadPoolManager.scheduleFixedRate( ThreadType.DECODER, cml, 5, TimeUnit.SECONDS ); } + mLog.info( "starting main application gui" ); + + //Initialize the GUI + initGUI(); + + tunerModel.requestFirstTunerDisplay(); + //Start the gui EventQueue.invokeLater( new Runnable() { @@ -175,36 +222,17 @@ public class SDRTrunk "[grow,fill]", "[grow,fill]") ); - //init() the controller to load tuners and playlists - mControllerPanel.getController().init(); - /** * Setup main JFrame window */ + mTitle = getTitle(); mMainGui.setTitle( mTitle ); mMainGui.setBounds( 100, 100, 1280, 800 ); mMainGui.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); - - mControllerPanel.getController().addListener( new TunerSelectionListener() - { - @Override - public void tunerSelected( Tuner tuner ) - { - if( tuner != null ) - { - mMainGui.setTitle( mTitle + " - " + tuner.getName() ); - } - else - { - mMainGui.setTitle( mTitle ); - } - } - } ); - //Fire first tuner selection so it is displayed in the spectrum/waterfall - mControllerPanel.getController().fireFirstTunerSelected(); - - mSpectralPanel.setPreferredSize( new Dimension( 1280, 400 ) ); + //Set preferred sizes to influence the split + mSpectralPanel.setPreferredSize( new Dimension( 1280, 300 ) ); + mControllerPanel.setPreferredSize( new Dimension( 1280, 500 ) ); JideSplitPane splitPane = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT ); splitPane.setDividerSize( 5 ); @@ -252,7 +280,16 @@ public class SDRTrunk @Override public void actionPerformed( ActionEvent arg0 ) { - mControllerPanel.getController().showIconManager(); + final IconManager iconManager = new IconManager( mSettingsManager, mMainGui ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + iconManager.setVisible( true ); + } + } ); } } ); fileMenu.add( settingsMenu ); @@ -421,7 +458,7 @@ public class SDRTrunk if( version != null ) { - sb.append( " v" ); + sb.append( " V" ); sb.append( version ); } } @@ -432,5 +469,13 @@ public class SDRTrunk return sb.toString(); } - + + @Override + public void receive( TunerEvent event ) + { + if( event.getEvent() == TunerEvent.Event.REQUEST_MAIN_SPECTRAL_DISPLAY ) + { + mMainGui.setTitle( mTitle + " - " + event.getTuner().getName() ); + } + } } diff --git a/src/gui/control/ConstellationViewer.java b/src/gui/control/ConstellationViewer.java index 124b7d47..fe9b0d08 100644 --- a/src/gui/control/ConstellationViewer.java +++ b/src/gui/control/ConstellationViewer.java @@ -16,21 +16,14 @@ import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sample.Listener; import sample.complex.Complex; import buffer.CircularBuffer; -public class ConstellationViewer extends JPanel - implements Listener +public class ConstellationViewer extends JPanel implements Listener { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( ConstellationViewer.class ); - private int mSampleRate; private int mSymbolRate; private float mSamplesPerSymbol; diff --git a/src/gui/control/JFrequencyControl.java b/src/gui/control/JFrequencyControl.java index a413e11f..3f4a5102 100644 --- a/src/gui/control/JFrequencyControl.java +++ b/src/gui/control/JFrequencyControl.java @@ -33,6 +33,7 @@ import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import javax.swing.JLabel; import javax.swing.JPanel; @@ -44,17 +45,16 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; import source.tuner.frequency.FrequencyChangeEvent.Event; +import source.tuner.frequency.IFrequencyChangeProcessor; -public class JFrequencyControl extends JPanel implements FrequencyChangeListener +public class JFrequencyControl extends JPanel implements IFrequencyChangeProcessor { private static final long serialVersionUID = 1L; private final static Logger mLog = LoggerFactory.getLogger( JFrequencyControl.class ); - private ArrayList mListeners = - new ArrayList(); + private List mProcessors = new ArrayList<>(); private Color mHighlightColor = Color.YELLOW; @@ -77,7 +77,7 @@ public class JFrequencyControl extends JPanel implements FrequencyChangeListener private void init() { - setLayout( new MigLayout( "", "[]0[]", "" ) ); + setLayout( new MigLayout( "insets 0 0 0 0", "[]0[]", "" ) ); Font font = new Font( Font.MONOSPACED, Font.BOLD, 30 ); @@ -128,6 +128,17 @@ public class JFrequencyControl extends JPanel implements FrequencyChangeListener revalidate(); } + + @Override + public void setEnabled( boolean enabled ) + { + super.setEnabled( enabled ); + + for( Digit digit: mDigits.values() ) + { + digit.setEnabled( enabled ); + } + } /** * Receives a frequency change event invoked by another control. We don't @@ -136,7 +147,7 @@ public class JFrequencyControl extends JPanel implements FrequencyChangeListener @Override public void frequencyChanged( FrequencyChangeEvent event ) { - if( event.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) + if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) { setFrequency( event.getValue().longValue(), false ); } @@ -177,10 +188,10 @@ public class JFrequencyControl extends JPanel implements FrequencyChangeListener { updateFrequency(); - Iterator it = mListeners.iterator(); + Iterator it = mProcessors.iterator(); - FrequencyChangeEvent event = - new FrequencyChangeEvent( Event.FREQUENCY_CHANGE_NOTIFICATION, mFrequency ); + FrequencyChangeEvent event = new FrequencyChangeEvent( + Event.REQUEST_FREQUENCY_CHANGE, mFrequency ); while( it.hasNext() ) { @@ -188,14 +199,14 @@ public class JFrequencyControl extends JPanel implements FrequencyChangeListener } } - public void addListener( FrequencyChangeListener listener ) + public void addListener( IFrequencyChangeProcessor processor ) { - mListeners.add( listener ); + mProcessors.add( processor ); } - public void removeListener( FrequencyChangeListener listener ) + public void removeListener( IFrequencyChangeProcessor processor ) { - mListeners.remove( listener ); + mProcessors.remove( processor ); } public class Digit extends JTextField @@ -245,7 +256,10 @@ public class JFrequencyControl extends JPanel implements FrequencyChangeListener public void increment( boolean fireChangeEvent ) { - set( mValue + 1, fireChangeEvent ); + if( isEnabled() ) + { + set( mValue + 1, fireChangeEvent ); + } } public void decrement() @@ -255,7 +269,10 @@ public class JFrequencyControl extends JPanel implements FrequencyChangeListener public void decrement( boolean fireChangeEvent ) { - set( mValue - 1, fireChangeEvent ); + if( isEnabled() ) + { + set( mValue - 1, fireChangeEvent ); + } } /** diff --git a/src/gui/editor/DocumentListenerEditor.java b/src/gui/editor/DocumentListenerEditor.java new file mode 100644 index 00000000..2524de87 --- /dev/null +++ b/src/gui/editor/DocumentListenerEditor.java @@ -0,0 +1,29 @@ +package gui.editor; + +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +public abstract class DocumentListenerEditor extends Editor + implements DocumentListener +{ + private static final long serialVersionUID = 1L; + + public DocumentListenerEditor() + { + } + + @Override + public void insertUpdate( DocumentEvent e ) + { + setModified( true ); + } + + @Override + public void removeUpdate( DocumentEvent e ) + { + setModified( true ); + } + + @Override + public void changedUpdate( DocumentEvent e ) { } +} diff --git a/src/gui/editor/Editor.java b/src/gui/editor/Editor.java new file mode 100644 index 00000000..f8261d81 --- /dev/null +++ b/src/gui/editor/Editor.java @@ -0,0 +1,131 @@ +package gui.editor; + +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +/** + * Generic editor with change detection, save and reset functions. + * + * This editor supports hierarchical nesting of sub-component editors where changes are detected + * at the lowest editor in the tree and percolated up to the top-level editor for managing the + * configuration set as a whole. + * + * To configure this editor as a collective, register each parent editor as a saveRequestListener + * on each child editor. If no save request listener is registered, the editor will simply invoke + * save when a change is detected and the user opts to save the contents. + * + * The top-level editor can override the saveRequest() method to perform custom save operations + * and validation. + */ +public abstract class Editor extends JPanel +{ + private static final long serialVersionUID = 1L; + + protected T mItem; + protected boolean mModified = false; + protected Editor mSaveRequestListener; + + public Editor() + { + } + + /** + * Requests to save the contents of this editor. + * + * If a save request listener is registered, the listener is notified that a save is requested, + * otherwise the save is request is processed by the instance. + * + * This allows for a save event to be percolated up the hierarchical set of editors so that + * a top-level editor can invoke custom operations such as configuration validation. + */ + protected void saveRequest() + { + if( mSaveRequestListener != null ) + { + mSaveRequestListener.saveRequest(); + } + else + { + save(); + } + } + + /** + * Registers the editor to be notified when this editor contents have been saved. + */ + public void setSaveRequestListener( Editor listener ) + { + mSaveRequestListener = listener; + } + + + /** + * Sets the object to be edited. If the contents from the previous object have changed, this + * prompts the user to save these changes. A saveRequest() is issued and either this editor + * or the parent editor will respond to the save request and invoke the save() method. + */ + public void setItem( T item ) + { + if( isModified() ) + { + int option = JOptionPane.showConfirmDialog( + Editor.this, + "This item has changed. Do you want to save these changes?", + "Save Changes?", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE ); + + if( option == JOptionPane.YES_OPTION ) + { + saveRequest(); + } + + setModified( false ); + } + + mItem = item; + } + + /** + * Current editing item + */ + public T getItem() + { + return mItem; + } + + public boolean hasItem() + { + return mItem != null; + } + + /** + * Sets the contents modified flag + */ + public void setModified( boolean modified ) + { + mModified = modified; + } + + /** + * Indicates if this editor contents have been modified + */ + public boolean isModified() + { + return mModified; + } + + /** + * Save the contents of the editor + */ + public abstract void save(); + + /** + * Reset the contents of the editor without saving any changes + */ + public void reset() + { + mModified = false; + setItem( mItem ); + } +} diff --git a/src/gui/editor/EditorValidationException.java b/src/gui/editor/EditorValidationException.java new file mode 100644 index 00000000..00ccf251 --- /dev/null +++ b/src/gui/editor/EditorValidationException.java @@ -0,0 +1,25 @@ +package gui.editor; + +public class EditorValidationException extends Exception +{ + private static final long serialVersionUID = 1L; + + private Editor mEditor; + private String mReason; + + public EditorValidationException( Editor editor, String reason ) + { + mEditor = editor; + mReason = reason; + } + + public Editor getEditor() + { + return mEditor; + } + + public String getReason() + { + return mReason; + } +} diff --git a/src/gui/editor/EmptyEditor.java b/src/gui/editor/EmptyEditor.java new file mode 100644 index 00000000..7b16ee35 --- /dev/null +++ b/src/gui/editor/EmptyEditor.java @@ -0,0 +1,31 @@ +package gui.editor; + +import javax.swing.JLabel; + +import net.miginfocom.swing.MigLayout; + +public class EmptyEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + public EmptyEditor( String descriptor ) + { + setLayout( new MigLayout( "insets 0 0 0 0", "[center]", "[grow]" ) ); + add( new JLabel( "Please select " + descriptor ), "wrap" ); + } + + public EmptyEditor() + { + this( "an item" ); + } + + @Override + public void save() + { + } + + @Override + public void reset() + { + } +} diff --git a/src/gui/editor/EmptyValidatingEditor.java b/src/gui/editor/EmptyValidatingEditor.java new file mode 100644 index 00000000..cdcd438c --- /dev/null +++ b/src/gui/editor/EmptyValidatingEditor.java @@ -0,0 +1,36 @@ +package gui.editor; + +import javax.swing.JLabel; + +import net.miginfocom.swing.MigLayout; + +public class EmptyValidatingEditor extends ValidatingEditor +{ + private static final long serialVersionUID = 1L; + + public EmptyValidatingEditor( String descriptor ) + { + setLayout( new MigLayout( "insets 0 0 0 0", "", "" ) ); + add( new JLabel( "Please select " + descriptor ), "wrap" ); + } + + public EmptyValidatingEditor() + { + this( "an item" ); + } + + @Override + public void save() + { + } + + @Override + public void reset() + { + } + + @Override + public void validate( Editor editor ) throws EditorValidationException + { + } +} diff --git a/src/gui/editor/ValidatingEditor.java b/src/gui/editor/ValidatingEditor.java new file mode 100644 index 00000000..149a8c4f --- /dev/null +++ b/src/gui/editor/ValidatingEditor.java @@ -0,0 +1,15 @@ +package gui.editor; + + +/** + * Editor class that allows other editors to be validated for configuration with this editor + */ +public abstract class ValidatingEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + /** + * Indicates if the editor argument is valid according to the settings of this editor + */ + public abstract void validate( Editor editor ) throws EditorValidationException; +} diff --git a/src/instrument/InstrumentableProcessingChain.java b/src/instrument/InstrumentableProcessingChain.java index 43ca9942..882eb891 100644 --- a/src/instrument/InstrumentableProcessingChain.java +++ b/src/instrument/InstrumentableProcessingChain.java @@ -9,12 +9,13 @@ import java.util.List; import module.Module; import module.ProcessingChain; import source.Source; +import controller.channel.Channel.ChannelType; public class InstrumentableProcessingChain extends ProcessingChain implements Instrumentable { - public InstrumentableProcessingChain( String name ) + public InstrumentableProcessingChain() { - super( name ); + super( "instrumented", ChannelType.STANDARD ); } public void setSource( Source source ) throws IllegalStateException diff --git a/src/instrument/gui/ConstellationPanel.java b/src/instrument/gui/ConstellationPanel.java index 3ddeea54..b6cfd14b 100644 --- a/src/instrument/gui/ConstellationPanel.java +++ b/src/instrument/gui/ConstellationPanel.java @@ -24,10 +24,6 @@ import java.awt.Dimension; import javax.swing.JInternalFrame; import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sample.Listener; import sample.complex.Complex; import source.IFrameLocationListener; @@ -36,11 +32,8 @@ public class ConstellationPanel extends JInternalFrame implements IFrameLocationListener, Listener { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( ConstellationPanel.class ); - private ConstellationViewer mConstellation = - new ConstellationViewer( 48000, 4800 ); + private ConstellationViewer mConstellation = new ConstellationViewer( 48000, 4800 ); public ConstellationPanel() { diff --git a/src/instrument/gui/DecoderSelectionFrame.java b/src/instrument/gui/DecoderSelectionFrame.java index 3faccf90..ccd310c8 100644 --- a/src/instrument/gui/DecoderSelectionFrame.java +++ b/src/instrument/gui/DecoderSelectionFrame.java @@ -8,18 +8,21 @@ import javax.swing.JButton; import javax.swing.JDesktopPane; import javax.swing.JInternalFrame; -import module.decode.DecodeComponentEditor; -import module.decode.config.DecodeConfiguration; +import module.decode.DecodeConfigurationEditor; import net.miginfocom.swing.MigLayout; +import playlist.PlaylistManager; import source.IControllableFileSource; -import controller.ResourceManager; +import alias.AliasModel; +import controller.ThreadPoolManager; +import controller.channel.ChannelModel; +import controller.channel.map.ChannelMapModel; public class DecoderSelectionFrame extends JInternalFrame { private static final long serialVersionUID = 1L; - private DecodeComponentEditor mDecodeEditor = new DecodeComponentEditor( null ); - private ResourceManager mResourceManager = new ResourceManager(); + private DecodeConfigurationEditor mDecodeEditor = new DecodeConfigurationEditor( null ); + private PlaylistManager mPlaylistManager; private IControllableFileSource mSource; private JDesktopPane mDesktop; @@ -27,6 +30,14 @@ public class DecoderSelectionFrame extends JInternalFrame public DecoderSelectionFrame( JDesktopPane desktop, IControllableFileSource source ) { + AliasModel aliasModel = new AliasModel(); + ChannelModel channelModel = new ChannelModel(); + ChannelMapModel channelMapModel = new ChannelMapModel(); + + ThreadPoolManager tpm = new ThreadPoolManager(); + + mPlaylistManager = new PlaylistManager( tpm, aliasModel, channelModel, channelMapModel ); + mDesktop = desktop; mSource = source; @@ -65,22 +76,17 @@ public class DecoderSelectionFrame extends JInternalFrame @Override public void actionPerformed( ActionEvent arg0 ) { - DecodeConfiguration config = mDecodeEditor.getDecodeConfig(); - - if( config != null ) - { - DecoderViewFrame decoderFrame = new DecoderViewFrame( - mResourceManager, - config, - null, - null, - null, - mSource ); - - decoderFrame.setVisible( true ); - - mDesktop.add( decoderFrame ); - } +// DecodeConfiguration config = mDecodeEditor.getDecodeConfig(); +// +// if( config != null ) +// { +// DecoderViewFrame decoderFrame = new DecoderViewFrame( +// mPlaylistManager, null, mSource ); +// +// decoderFrame.setVisible( true ); +// +// mDesktop.add( decoderFrame ); +// } } } ); } diff --git a/src/instrument/gui/DecoderViewFrame.java b/src/instrument/gui/DecoderViewFrame.java index 7ac54e4f..57d03809 100644 --- a/src/instrument/gui/DecoderViewFrame.java +++ b/src/instrument/gui/DecoderViewFrame.java @@ -57,22 +57,22 @@ import javax.swing.JSeparator; import message.Message; import module.Module; import module.decode.DecoderFactory; -import module.decode.config.AuxDecodeConfiguration; import module.decode.config.DecodeConfiguration; +import module.log.EventLogManager; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import record.config.RecordConfiguration; +import playlist.PlaylistManager; import sample.Listener; import source.IControllableFileSource; import source.Source; -import alias.AliasList; -import controller.ResourceManager; -import controller.channel.Channel.ChannelType; -import controller.site.Site; -import controller.system.System; +import alias.AliasModel; +import controller.channel.Channel; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; +import controller.channel.map.ChannelMapModel; public class DecoderViewFrame extends JInternalFrame implements Listener @@ -88,29 +88,25 @@ public class DecoderViewFrame extends JInternalFrame private HashMap mPanelMap = new HashMap(); - public DecoderViewFrame( ResourceManager resourceManager, - DecodeConfiguration decodeConfig, - RecordConfiguration recordConfig, - AuxDecodeConfiguration auxConfig, - AliasList aliasList, + public DecoderViewFrame( PlaylistManager playlistManager, + Channel channel, IControllableFileSource source ) { mSource = source; - mDecodeConfig = decodeConfig; + mDecodeConfig = channel.getDecodeConfiguration(); - mProcessingChain = new InstrumentableProcessingChain( "instrumented" ); + mProcessingChain = new InstrumentableProcessingChain(); + + ChannelModel channelModel = new ChannelModel(); + ChannelMapModel channelMapModel = new ChannelMapModel(); - List modules = DecoderFactory - .getModules( ChannelType.STANDARD, - resourceManager, - decodeConfig, - recordConfig, - auxConfig, - aliasList, - new System( "Instrumented" ), - new Site( "Instrumented" ), - "Channel Name" ); + ChannelProcessingManager channelProcessingManager = + new ChannelProcessingManager( channelModel, channelMapModel, + new AliasModel(), new EventLogManager(), null, null ); + + List modules = DecoderFactory.getModules( channelModel, + channelMapModel, channelProcessingManager, new AliasModel(), channel ); mProcessingChain.addModules( modules ); diff --git a/src/instrument/gui/Viewer.java b/src/instrument/gui/Viewer.java index a7fbfc03..3ef30096 100644 --- a/src/instrument/gui/Viewer.java +++ b/src/instrument/gui/Viewer.java @@ -114,10 +114,8 @@ public class Viewer /* Attempt to open file as a 1-channel float source */ try { - RealWaveSource floatSource = new RealWaveSource( file ); - floatSource.open(); - - source = floatSource; + source = new RealWaveSource( file ); + source.open(); mLog.info( "File opened as float wave file" ); diff --git a/src/instrument/tap/TapViewPanel.java b/src/instrument/tap/TapViewPanel.java index 8020cebf..4124c71f 100644 --- a/src/instrument/tap/TapViewPanel.java +++ b/src/instrument/tap/TapViewPanel.java @@ -31,12 +31,14 @@ import javax.swing.JPanel; public abstract class TapViewPanel extends JPanel implements Observer { - protected SampleModel mModel; + private static final long serialVersionUID = 1L; + + protected SampleModel mModel; protected String mLabel; protected float mVerticalZoom = 1.0f; - public TapViewPanel( SampleModel model, String label ) + public TapViewPanel( SampleModel model, String label ) { mModel = model; mModel.addObserver( this ); @@ -76,7 +78,7 @@ public abstract class TapViewPanel extends JPanel implements Observer return menu; } - public SampleModel getModel() + public SampleModel getModel() { return mModel; } @@ -104,6 +106,8 @@ public abstract class TapViewPanel extends JPanel implements Observer public class VerticalZoomItem extends JCheckBoxMenuItem { + private static final long serialVersionUID = 1L; + private TapViewPanel mPanel; private float mZoom; diff --git a/src/instrument/tap/stream/BinaryTapViewPanel.java b/src/instrument/tap/stream/BinaryTapViewPanel.java index 95b2c3e1..07b72667 100644 --- a/src/instrument/tap/stream/BinaryTapViewPanel.java +++ b/src/instrument/tap/stream/BinaryTapViewPanel.java @@ -37,7 +37,6 @@ public class BinaryTapViewPanel extends TapViewPanel private Tap mTap; private List mSamples; private int mSampleCount; - private int mOffset = 0; public BinaryTapViewPanel( BinaryTap tap ) { diff --git a/src/map/IconManager.java b/src/map/IconManager.java index 220ca034..9cc07f3b 100644 --- a/src/map/IconManager.java +++ b/src/map/IconManager.java @@ -50,7 +50,7 @@ import org.slf4j.LoggerFactory; import settings.Setting; import settings.SettingChangeListener; -import controller.ResourceManager; +import settings.SettingsManager; public class IconManager extends JFrame implements ActionListener, SettingChangeListener @@ -62,8 +62,6 @@ public class IconManager extends JFrame private final static Logger mLog = LoggerFactory.getLogger( IconManager.class ); - private ResourceManager mResourceManager; - private static final String sMSG_NEW = "Type a name for the new icon, " + "select the file, and click Save,\nor Cancel to reset"; private static final String sMSG_EDIT = "Change the name for the selected \n" @@ -81,9 +79,11 @@ public class IconManager extends JFrame private JFileChooser mFileChooser; private Mode mMode = Mode.NORMAL; - public IconManager( ResourceManager resourceManager, Component displayOver ) + private SettingsManager mSettingsManager; + + public IconManager( SettingsManager settingsManager, Component displayOver ) { - mResourceManager = resourceManager; + mSettingsManager = settingsManager; init( displayOver ); } @@ -128,7 +128,7 @@ public class IconManager extends JFrame /** * MapIcon list */ - MapIcon[] icons = mResourceManager.getSettingsManager().getMapIcons(); + MapIcon[] icons = mSettingsManager.getMapIcons(); Arrays.sort( icons ); mIconList = new JList( icons ); @@ -176,8 +176,7 @@ public class IconManager extends JFrame @Override public void actionPerformed( ActionEvent e ) { - mResourceManager.getSettingsManager() - .setDefaultIcon( icon ); + mSettingsManager.setDefaultIcon( icon ); refreshList(); } @@ -193,8 +192,7 @@ public class IconManager extends JFrame @Override public void actionPerformed( ActionEvent arg0 ) { - mResourceManager.getSettingsManager() - .deleteMapIcon( icon ); + mSettingsManager.deleteMapIcon( icon ); } } ); @@ -350,7 +348,7 @@ public class IconManager extends JFrame private void refreshList() { - MapIcon[] icons = mResourceManager.getSettingsManager().getMapIcons(); + MapIcon[] icons = mSettingsManager.getMapIcons(); Arrays.sort( icons ); @@ -371,8 +369,7 @@ public class IconManager extends JFrame { if( path != null && !path.isEmpty() ) { - mResourceManager.getSettingsManager() - .addMapIcon( new MapIcon( name, path, true ) ); + mSettingsManager.addMapIcon( new MapIcon( name, path, true ) ); } else { @@ -400,8 +397,7 @@ public class IconManager extends JFrame { selected.setName( name ); selected.setPath( path ); - mResourceManager.getSettingsManager() - .updatMapIcon( selected, name, path ); + mSettingsManager.updatMapIcon( selected, name, path ); } else { diff --git a/src/map/MapPanel.java b/src/map/MapPanel.java index 9a0a1e5c..61e3b452 100644 --- a/src/map/MapPanel.java +++ b/src/map/MapPanel.java @@ -21,7 +21,6 @@ import java.awt.EventQueue; import javax.swing.JPanel; -import message.Message; import net.miginfocom.swing.MigLayout; import org.jdesktop.swingx.JXMapViewer; @@ -32,10 +31,8 @@ import org.jdesktop.swingx.mapviewer.DefaultTileFactory; import org.jdesktop.swingx.mapviewer.GeoPosition; import org.jdesktop.swingx.mapviewer.TileFactoryInfo; -import sample.Listener; import settings.MapViewSetting; import settings.SettingsManager; -import controller.channel.ChannelManager; public class MapPanel extends JPanel implements PlottableUpdateListener { @@ -46,17 +43,12 @@ public class MapPanel extends JPanel implements PlottableUpdateListener private JXMapViewer mMapViewer = new JXMapViewer(); private PlottableEntityPainter mMapPainter; - public MapPanel( MapService mapService, - SettingsManager settingsManager, - ChannelManager channelManager ) + public MapPanel( MapService mapService, SettingsManager settingsManager ) { mSettingsManager = settingsManager; mMapService = mapService; mMapPainter = new PlottableEntityPainter( mSettingsManager ); - /* Add Map Service as message listener to receive all messages */ - channelManager.addListener( (Listener)mMapService ); - init(); } diff --git a/src/map/MapService.java b/src/map/MapService.java index 0b6c9f87..0c251277 100644 --- a/src/map/MapService.java +++ b/src/map/MapService.java @@ -30,7 +30,6 @@ import sample.Listener; import settings.SettingsManager; import alias.Alias; import controller.NamingThreadFactory; -import controller.ResourceManager; public class MapService implements Listener { @@ -49,11 +48,11 @@ public class MapService implements Listener private HashMap mEntities = new HashMap(); - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; - public MapService( ResourceManager resourceManager ) + public MapService( SettingsManager resourceManager ) { - mResourceManager = resourceManager; + mSettingsManager = resourceManager; // scheduler.scheduleAtFixedRate( new CullThread(), 5, 5, TimeUnit.MINUTES ); } @@ -111,8 +110,7 @@ public class MapService implements Listener iconName = SettingsManager.DEFAULT_ICON; } - icon = mResourceManager.getSettingsManager() - .getMapIcon( iconName ); + icon = mSettingsManager.getMapIcon( iconName ); } if( color == null ) diff --git a/src/message/IMessageProvider.java b/src/message/IMessageProvider.java index 4618020a..41b29ac3 100644 --- a/src/message/IMessageProvider.java +++ b/src/message/IMessageProvider.java @@ -4,6 +4,6 @@ import sample.Listener; public interface IMessageProvider { - public void addMessageListener( Listener listener ); - public void removeMessageListener( Listener listener ); + public void setMessageListener( Listener listener ); + public void removeMessageListener(); } diff --git a/src/message/Message.java b/src/message/Message.java index 9b6b7a17..33fcef25 100644 --- a/src/message/Message.java +++ b/src/message/Message.java @@ -112,4 +112,4 @@ public abstract class Message { return Collections.EMPTY_LIST; } -} +} \ No newline at end of file diff --git a/src/module/Module.java b/src/module/Module.java index 87295327..a46b3e9c 100644 --- a/src/module/Module.java +++ b/src/module/Module.java @@ -18,6 +18,8 @@ package module; +import java.util.concurrent.ScheduledExecutorService; + public abstract class Module { /** @@ -57,9 +59,9 @@ public abstract class Module public abstract void reset(); /** - * Start processing. + * Start processing. Provides an executor to use for scheduling tasks */ - public abstract void start(); + public abstract void start( ScheduledExecutorService executor ); /** * Stop processing diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java index 75fe805e..bcc564fc 100644 --- a/src/module/ProcessingChain.java +++ b/src/module/ProcessingChain.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2015 Dennis Sheirer + * 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 @@ -27,8 +27,11 @@ import message.IMessageListener; import message.IMessageProvider; import message.Message; import module.decode.event.CallEvent; +import module.decode.event.CallEventModel; import module.decode.event.ICallEventListener; import module.decode.event.ICallEventProvider; +import module.decode.event.MessageActivityModel; +import module.decode.state.ChannelState; import module.decode.state.DecoderState; import module.decode.state.DecoderStateEvent; import module.decode.state.DecoderStateEvent.Event; @@ -43,16 +46,19 @@ import sample.Broadcaster; import sample.Listener; import sample.complex.ComplexBuffer; import sample.complex.IComplexBufferListener; -import sample.real.IRealBufferListener; -import sample.real.IRealBufferProvider; +import sample.real.IFilteredRealBufferListener; +import sample.real.IFilteredRealBufferProvider; +import sample.real.IUnFilteredRealBufferListener; +import sample.real.IUnFilteredRealBufferProvider; import sample.real.RealBuffer; import source.ComplexSource; import source.RealSource; import source.Source; import source.SourceException; import source.tuner.TunerChannelSource; -import source.tuner.frequency.FrequencyCorrectionControl; -import source.tuner.frequency.IFrequencyCorrectionController; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.IFrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeProvider; import audio.AudioPacket; import audio.IAudioPacketListener; import audio.IAudioPacketProvider; @@ -63,6 +69,7 @@ import audio.squelch.ISquelchStateListener; import audio.squelch.ISquelchStateProvider; import audio.squelch.SquelchState; import controller.NamingThreadFactory; +import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent; import controller.channel.IChannelEventListener; import controller.channel.IChannelEventProvider; @@ -87,8 +94,7 @@ import controller.channel.IChannelEventProvider; */ public class ProcessingChain implements IChannelEventListener { - private final static Logger mLog = - LoggerFactory.getLogger( ProcessingChain.class ); + private final static Logger mLog = LoggerFactory.getLogger( ProcessingChain.class ); private Broadcaster mAudioPacketBroadcaster = new Broadcaster<>(); private Broadcaster mMetadataBroadcaster = new Broadcaster<>(); @@ -96,23 +102,61 @@ public class ProcessingChain implements IChannelEventListener private Broadcaster mChannelEventBroadcaster = new Broadcaster<>(); private Broadcaster mComplexBufferBroadcaster = new Broadcaster<>(); private Broadcaster mDecoderStateEventBroadcaster = new Broadcaster<>(); + private Broadcaster mFrequencyChangeEventBroadcaster = new Broadcaster<>(); private Broadcaster mMessageBroadcaster = new Broadcaster<>(); - private Broadcaster mRealBufferBroadcaster = new Broadcaster<>(); + private Broadcaster mFilteredRealBufferBroadcaster = new Broadcaster<>(); + private Broadcaster mUnFilteredRealBufferBroadcaster = new Broadcaster<>(); private Broadcaster mSquelchStateBroadcaster = new Broadcaster<>(); private ScheduledExecutorService mScheduledExecutorService; + private String mName; private AtomicBoolean mRunning = new AtomicBoolean(); protected Source mSource; private List mModules = new ArrayList<>(); - private IFrequencyCorrectionController mFrequencyCorrectionController; - - public ProcessingChain( String name ) - { - mScheduledExecutorService = Executors.newScheduledThreadPool( 1, - new NamingThreadFactory( "channel " + name ) ); - } + private CallEventModel mCallEventModel; + private ChannelState mChannelState; + private MessageActivityModel mMessageActivityModel; + /** + * Creates a processing chain for managing a set of modules + * + * @param name for thread pool naming each thread + * @param channelType + */ + public ProcessingChain( String name, ChannelType channelType ) + { + mName = name; + + mChannelState = new ChannelState( channelType ); + addModule( mChannelState ); + + mCallEventModel = new CallEventModel(); + addCallEventListener( mCallEventModel ); + } + + public CallEventModel getCallEventModel() + { + return mCallEventModel; + } + + public ChannelState getChannelState() + { + return mChannelState; + } + + public MessageActivityModel getMessageActivityModel() + { + return mMessageActivityModel; + } + + public void setMessageActivityModel( MessageActivityModel model ) + { + mMessageActivityModel = model; + + addMessageListener( mMessageActivityModel ); + } + public void dispose() { stop(); @@ -127,15 +171,18 @@ public class ProcessingChain implements IChannelEventListener mScheduledExecutorService.shutdownNow(); mScheduledExecutorService = null; - mFrequencyCorrectionController = null; - mAudioPacketBroadcaster.dispose(); mCallEventBroadcaster.dispose(); mChannelEventBroadcaster.dispose(); mComplexBufferBroadcaster.dispose(); mMessageBroadcaster.dispose(); - mRealBufferBroadcaster.dispose(); + mFilteredRealBufferBroadcaster.dispose(); mSquelchStateBroadcaster.dispose(); + + if( mScheduledExecutorService != null ) + { + mScheduledExecutorService.shutdownNow(); + } } /** @@ -153,7 +200,7 @@ public class ProcessingChain implements IChannelEventListener * Indicates if this processing chain is currently receiving samples from * a source and sending those samples to the decoders. */ - public boolean processing() + public boolean isProcessing() { return mRunning.get(); } @@ -166,28 +213,6 @@ public class ProcessingChain implements IChannelEventListener return mSource != null; } - /** - * Indicates if one of the modules provides frequency error control. - */ - public boolean hasFrequencyCorrectionControl() - { - return mFrequencyCorrectionController != null && - mFrequencyCorrectionController.hasFrequencyCorrectionControl(); - } - - /** - * Frequency correction controller. Can be null. - */ - public FrequencyCorrectionControl getFrequencyCorrectionControl() - { - if( mFrequencyCorrectionController != null ) - { - return mFrequencyCorrectionController.getFrequencyCorrectionControl(); - } - - return null; - } - /** * Applies a sample source to this processing chain. Processing won't * start until the start() method is invoked. @@ -200,7 +225,7 @@ public class ProcessingChain implements IChannelEventListener */ public void setSource( Source source ) throws IllegalStateException { - if( processing() ) + if( isProcessing() ) { throw new IllegalStateException( "Processing chain is currently " + "processing. Invoke stop() on the processing chain before " @@ -208,23 +233,6 @@ public class ProcessingChain implements IChannelEventListener } mSource = source; - - /* Establish two-way communication between the tuner channel source and - * the decoder for fine tuning frequency error correction feedback from - * the decoder. The tuner channel source will also notify the frequency - * controller during frequency changes to allow the controller to reset - * frequency error tracking */ - if( mSource != null && - mSource instanceof TunerChannelSource && - hasFrequencyCorrectionControl() ) - { - TunerChannelSource channel = (TunerChannelSource)mSource; - - FrequencyCorrectionControl control = getFrequencyCorrectionControl(); - - channel.setFrequencyChangeListener( control ); - control.setFrequencyChangeListener( channel ); - } } /** @@ -253,24 +261,45 @@ public class ProcessingChain implements IChannelEventListener return decoderStates; } + /** + * Adds the list of modules to this processing chain + */ + public void addModules( List modules ) + { + for( Module module: modules ) + { + addModule( module ); + } + } + /** * Adds a module to the processing chain. Each module is tested for the * interfaces that it supports and is registered or receives a listener * to consume or produce the supported interface data type. All elements - * that are produced by any component are automatically routed to all other - * components that support the corresponding listener interface. + * and events that are produced by any module are automatically routed to + * all other components that support the corresponding listener interface. * * At least one module should consume complex samples and either produce * decoded messages and/or audio, or produce decoded real sample buffers * for all other modules to consume. * - * @param module - processing module, demodulator or decoder + * @param module - processing module, demodulator, decoder, source, state + * machine, etc. */ public void addModule( Module module ) { mModules.add( module ); + + registerListeners( module ); + registerProviders( module ); + } - /* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Listeners <<<<<<<<<<<<<<<<<<<<<<<<<<< */ + /** + * Registers the module as a listener to each of the broadcasters that + * provide the data interface(s) supported by the module. + */ + private void registerListeners( Module module ) + { if( module instanceof IAudioPacketListener ) { mAudioPacketBroadcaster.addListener( @@ -301,6 +330,12 @@ public class ProcessingChain implements IChannelEventListener ((IDecoderStateEventListener)module).getDecoderStateListener() ); } + if( module instanceof IFrequencyChangeListener ) + { + mFrequencyChangeEventBroadcaster.addListener( + ((IFrequencyChangeListener)module).getFrequencyChangeListener()); + } + if( module instanceof IMessageListener ) { mMessageBroadcaster.addListener( @@ -313,10 +348,10 @@ public class ProcessingChain implements IChannelEventListener ((IMetadataListener)module).getMetadataListener() ); } - if( module instanceof IRealBufferListener ) + if( module instanceof IFilteredRealBufferListener ) { - mRealBufferBroadcaster.addListener( - ((IRealBufferListener)module).getRealBufferListener() ); + mFilteredRealBufferBroadcaster.addListener( + ((IFilteredRealBufferListener)module).getFilteredRealBufferListener() ); } if( module instanceof ISquelchStateListener ) @@ -324,8 +359,20 @@ public class ProcessingChain implements IChannelEventListener mSquelchStateBroadcaster.addListener( ((ISquelchStateListener)module).getSquelchStateListener() ); } - - /* >>>>>>>>>>>>>>>>>>> Providers <<<<<<<<<<<<<<<<<<<<<<<<< */ + + if( module instanceof IUnFilteredRealBufferListener ) + { + mUnFilteredRealBufferBroadcaster.addListener( + ((IUnFilteredRealBufferListener)module).getUnFilteredRealBufferListener() ); + } + } + + /** + * Registers the broadcaster(s) as listeners to the module for each + * provider interface that is supported by the module. + */ + private void registerProviders( Module module ) + { if( module instanceof IAudioPacketProvider ) { ((IAudioPacketProvider)module).setAudioPacketListener( @@ -348,15 +395,16 @@ public class ProcessingChain implements IChannelEventListener ((IDecoderStateEventProvider)module).setDecoderStateListener( mDecoderStateEventBroadcaster ); } - - if( module instanceof IFrequencyCorrectionController ) - { - mFrequencyCorrectionController = (IFrequencyCorrectionController)module; - } + if( module instanceof IFrequencyChangeProvider ) + { + ((IFrequencyChangeProvider)module).setFrequencyChangeListener( + mFrequencyChangeEventBroadcaster ); + } + if( module instanceof IMessageProvider ) { - ((IMessageProvider)module).addMessageListener( mMessageBroadcaster ); + ((IMessageProvider)module).setMessageListener( mMessageBroadcaster ); } if( module instanceof IMetadataProvider ) @@ -365,9 +413,10 @@ public class ProcessingChain implements IChannelEventListener mMetadataBroadcaster ); } - if( module instanceof IRealBufferProvider ) + if( module instanceof IFilteredRealBufferProvider ) { - ((IRealBufferProvider)module).setRealBufferListener( mRealBufferBroadcaster ); + ((IFilteredRealBufferProvider)module) + .setFilteredRealBufferListener( mFilteredRealBufferBroadcaster ); } if( module instanceof ISquelchStateProvider ) @@ -375,19 +424,13 @@ public class ProcessingChain implements IChannelEventListener ((ISquelchStateProvider)module).setSquelchStateListener( mSquelchStateBroadcaster ); } - } - - /** - * Adds the list of modules to this processing chain - */ - public void addModules( List modules ) - { - for( Module module: modules ) + + if( module instanceof IUnFilteredRealBufferProvider ) { - addModule( module ); + ((IUnFilteredRealBufferProvider)module) + .setUnFilteredRealBufferListener( mUnFilteredRealBufferBroadcaster ); } } - /** * Starts processing if the chain has a valid source. Invocations on an @@ -405,19 +448,6 @@ public class ProcessingChain implements IChannelEventListener module.reset(); } - /* Start each of the modules */ - for( Module module: mModules ) - { - try - { - module.start(); - } - catch( Exception e ) - { - mLog.error( "Error starting module", e ); - } - } - /* Register with the source to receive sample data. Setup a * timer task to process the buffer queues 50 times a second * (every 20 ms) */ @@ -427,7 +457,7 @@ public class ProcessingChain implements IChannelEventListener ((ComplexSource)mSource).setListener( mComplexBufferBroadcaster ); break; case REAL: - ((RealSource)mSource).setListener( mRealBufferBroadcaster ); + ((RealSource)mSource).setListener( mFilteredRealBufferBroadcaster ); break; default: throw new IllegalArgumentException( "Unrecognized source " @@ -435,15 +465,15 @@ public class ProcessingChain implements IChannelEventListener } /* If this is a tuner source, broadcast the frequency to all - * of the decoder state's */ + * of the decoder state's and start the samples flowing */ if( mSource instanceof TunerChannelSource ) { - TunerChannelSource tunerChannelSource = (TunerChannelSource)mSource; - + TunerChannelSource tcs = (TunerChannelSource)mSource; + try { - long frequency = tunerChannelSource.getFrequency(); - + long frequency = tcs.getFrequency(); + mDecoderStateEventBroadcaster.broadcast( new DecoderStateEvent( this, Event.SOURCE_FREQUENCY, State.IDLE, frequency ) ); @@ -452,13 +482,32 @@ public class ProcessingChain implements IChannelEventListener { mLog.error( "Error getting frequency from tuner channel source", e ); } - - tunerChannelSource.start( mScheduledExecutorService ); } + + if( mScheduledExecutorService == null ) + { + mScheduledExecutorService = Executors.newScheduledThreadPool( + 1, new NamingThreadFactory( "channel " + mName ) ); + } + + /* Start each of the modules */ + for( Module module: mModules ) + { + try + { + module.start( mScheduledExecutorService ); + } + catch( Exception e ) + { + mLog.error( "Error starting module", e ); + } + } + + mSource.start( mScheduledExecutorService ); } else { - mLog.debug( "Source is null on start()" ); + mLog.error( "Source is null on start()" ); } } } @@ -473,13 +522,15 @@ public class ProcessingChain implements IChannelEventListener { if( mSource != null ) { + mSource.stop(); + switch( mSource.getSampleType() ) { case COMPLEX: ((ComplexSource)mSource).removeListener( mComplexBufferBroadcaster ); break; case REAL: - ((RealSource)mSource).removeListener( mRealBufferBroadcaster ); + ((RealSource)mSource).removeListener( mFilteredRealBufferBroadcaster ); break; default: throw new IllegalArgumentException( "Unrecognized source " @@ -595,12 +646,12 @@ public class ProcessingChain implements IChannelEventListener public void addRealBufferListener( Listener listener ) { - mRealBufferBroadcaster.addListener( listener ); + mFilteredRealBufferBroadcaster.addListener( listener ); } public void removeRealBufferListener( Listener listener ) { - mRealBufferBroadcaster.removeListener( listener ); + mFilteredRealBufferBroadcaster.removeListener( listener ); } @Override diff --git a/src/module/decode/AuxDecodeComponentEditor.java b/src/module/decode/AuxDecodeComponentEditor.java deleted file mode 100644 index dd55ee59..00000000 --- a/src/module/decode/AuxDecodeComponentEditor.java +++ /dev/null @@ -1,110 +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 - ******************************************************************************/ -package module.decode; - -import java.util.Collections; -import java.util.HashMap; -import java.util.List; - -import javax.swing.JCheckBox; -import javax.swing.SwingUtilities; - -import module.decode.config.AuxDecodeConfiguration; -import controller.channel.AbstractChannelEditor; -import controller.channel.ChannelNode; - -public class AuxDecodeComponentEditor extends AbstractChannelEditor -{ - private static final long serialVersionUID = 1L; - - private HashMap mControls = - new HashMap(); - - public AuxDecodeComponentEditor( ChannelNode channelNode ) - { - super( channelNode ); - - List decoders = DecoderType.getAuxDecoders(); - - Collections.sort( decoders ); - - for( DecoderType decoder: decoders ) - { - AuxDecoderCheckBox control = new AuxDecoderCheckBox( decoder ); - - if( getConfig().getAuxDecoders().contains( decoder ) ) - { - control.setSelected( true ); - } - - add( control, "wrap" ); - - mControls.put( decoder, control ); - } - } - - public AuxDecodeConfiguration getConfig() - { - return getChannelNode().getChannel().getAuxDecodeConfiguration(); - } - - @Override - public void save() - { - getConfig().clearAuxDecoders(); - - for( DecoderType decoder: mControls.keySet() ) - { - if( mControls.get( decoder ) .isSelected() ) - { - getConfig().addAuxDecoder( decoder ); - } - } - } - - @Override - public void reset() - { - SwingUtilities.invokeLater(new Runnable() - { - @Override - public void run() - { - for( DecoderType decoder: mControls.keySet() ) - { - mControls.get( decoder ).setSelected( - getConfig().getAuxDecoders().contains( decoder ) ); - } - } - }); - } - - public class AuxDecoderCheckBox extends JCheckBox - { - private static final long serialVersionUID = 1L; - - private DecoderType mDecoderType; - - public AuxDecoderCheckBox( DecoderType decoder ) - { - super( decoder.getDisplayString() ); - - mDecoderType = decoder; - } - } -} diff --git a/src/module/decode/AuxDecodeConfigurationEditor.java b/src/module/decode/AuxDecodeConfigurationEditor.java new file mode 100644 index 00000000..0e40834a --- /dev/null +++ b/src/module/decode/AuxDecodeConfigurationEditor.java @@ -0,0 +1,153 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode; + +import gui.editor.Editor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.swing.JCheckBox; + +import module.decode.config.AuxDecodeConfiguration; +import net.miginfocom.swing.MigLayout; +import controller.channel.Channel; + +public class AuxDecodeConfigurationEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + private List mControls = new ArrayList<>(); + + public AuxDecodeConfigurationEditor() + { + init(); + } + + public AuxDecodeConfiguration getConfiguration() + { + return hasItem() ? getItem().getAuxDecodeConfiguration() : null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "", "[][grow]" ) ); + + List decoders = DecoderType.getAuxDecoders(); + + Collections.sort( decoders ); + + for( DecoderType decoder: decoders ) + { + AuxDecoderCheckBox control = new AuxDecoderCheckBox( decoder ); + control.setEnabled( false ); + control.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( control ); + mControls.add( control ); + } + } + + @Override + public void save() + { + if( hasItem() ) + { + AuxDecodeConfiguration config = getItem().getAuxDecodeConfiguration(); + + config.clearAuxDecoders(); + + for( AuxDecoderCheckBox checkBox: mControls ) + { + if( checkBox.isSelected() ) + { + config.addAuxDecoder( checkBox.getDecoderType() ); + } + } + } + + setModified( false ); + } + + private void setControlsEnabled( boolean enabled ) + { + for( AuxDecoderCheckBox box: mControls ) + { + if( box.isEnabled() != enabled ) + { + box.setEnabled( enabled ); + } + } + } + + + public class AuxDecoderCheckBox extends JCheckBox + { + private static final long serialVersionUID = 1L; + + private DecoderType mDecoderType; + + public AuxDecoderCheckBox( DecoderType decoder ) + { + super( decoder.getDisplayString() ); + + mDecoderType = decoder; + } + + public DecoderType getDecoderType() + { + return mDecoderType; + } + } + + @Override + public void setItem( Channel channel ) + { + super.setItem( channel ); + + if( hasItem() ) + { + setControlsEnabled( true ); + + for( AuxDecoderCheckBox cb: mControls ) + { + if( getItem().getAuxDecodeConfiguration().getAuxDecoders().contains( cb.getDecoderType() ) ) + { + cb.setSelected( true ); + } + else + { + cb.setSelected( false ); + } + } + } + else + { + setControlsEnabled( false ); + } + } +} diff --git a/src/module/decode/DecodeComponentEditor.java b/src/module/decode/DecodeComponentEditor.java deleted file mode 100644 index 46092365..00000000 --- a/src/module/decode/DecodeComponentEditor.java +++ /dev/null @@ -1,172 +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 - ******************************************************************************/ -package module.decode; - - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.SwingUtilities; - -import module.decode.config.DecodeConfigFactory; -import module.decode.config.DecodeConfiguration; -import net.miginfocom.swing.MigLayout; -import playlist.PlaylistManager; -import controller.Editor; -import controller.channel.AbstractChannelEditor; -import controller.channel.Channel; -import controller.channel.ChannelNode; -import controller.channel.ChannelValidationException; - -public class DecodeComponentEditor extends AbstractChannelEditor -{ - private static final long serialVersionUID = 1L; - - private JComboBox mComboDecoders; - - private DecodeEditor mEditor; - - private PlaylistManager mPlaylistManager; - - public DecodeComponentEditor( PlaylistManager playlistManager ) - { - this( new ChannelNode( new Channel() ), playlistManager ); - } - - public DecodeComponentEditor( ChannelNode channelNode, - PlaylistManager playlistMangager ) - { - super( channelNode ); - - mPlaylistManager = playlistMangager; - - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][grow]" ) ); - - /** - * ComboBox: Decoders - */ - mComboDecoders = new JComboBox(); - - DefaultComboBoxModel model = - new DefaultComboBoxModel(); - - for( DecoderType type: DecoderType.getPrimaryDecoders() ) - { - model.addElement( type ); - } - - mComboDecoders.setModel( model ); - - mComboDecoders.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - DecoderType selected = mComboDecoders - .getItemAt( mComboDecoders.getSelectedIndex() ); - - if( selected != null ) - { - DecodeConfiguration config; - - if( mChannelNode.getChannel() - .getDecodeConfiguration().getDecoderType() == - selected ) - { - config = mChannelNode.getChannel() - .getDecodeConfiguration(); - } - else - { - config = DecodeConfigFactory - .getDecodeConfiguration( selected ); - } - - //Remove the existing one - if( mEditor != null ) - { - remove( mEditor ); - } - - //Change to the new one - mEditor = DecoderFactory.getEditorPanel( config, - mChannelNode, mPlaylistManager ); - - add( mEditor, "span 2,center" ); - - revalidate(); - repaint(); - } - } - }); - add( new JLabel( "Decoder:" ) ); - add( mComboDecoders, "wrap" ); - - reset(); - } - - public void reset() - { - final DecoderType decoder = mChannelNode.getChannel() - .getDecodeConfiguration().getDecoderType(); - - SwingUtilities.invokeLater(new Runnable() - { - @Override - public void run() { - mComboDecoders.setSelectedItem( decoder ); - - mComboDecoders.requestFocus(); - - mComboDecoders.requestFocusInWindow(); - } - }); - } - - public DecodeConfiguration getDecodeConfig() - { - return mEditor.getConfig(); - } - - @Override - public void save() - { - if( mEditor != null ) - { - mEditor.save(); - } - - //The component that calls save will invoke the change broadcast - mChannelNode.getChannel() - .setDecodeConfiguration( mEditor.getConfig(), false ); - } - - /** - * Validates the editor against the current decode editor - */ - @Override - public void validate( Editor editor ) throws ChannelValidationException - { - super.validate( editor ); - - mEditor.validate( editor ); - } -} diff --git a/src/module/decode/DecodeConfigurationEditor.java b/src/module/decode/DecodeConfigurationEditor.java new file mode 100644 index 00000000..d8a9f784 --- /dev/null +++ b/src/module/decode/DecodeConfigurationEditor.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode; + +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.EmptyValidatingEditor; +import gui.editor.ValidatingEditor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; + +import net.miginfocom.swing.MigLayout; +import controller.channel.Channel; +import controller.channel.map.ChannelMapModel; + +public class DecodeConfigurationEditor extends ValidatingEditor +{ + private static final long serialVersionUID = 1L; + + private JComboBox mComboDecoders; + private ValidatingEditor mCurrentEditor = new EmptyValidatingEditor<>( "a decoder" ); + private ChannelMapModel mChannelMapModel; + + public DecodeConfigurationEditor( final ChannelMapModel channelMapModel ) + { + mChannelMapModel = channelMapModel; + init(); + } + + private void init() + { + setLayout( new MigLayout( "wrap 2", "[][grow,fill]", "[align top][grow,fill]" ) ); + + mComboDecoders = new JComboBox(); + mComboDecoders.setEnabled( false ); + + DefaultComboBoxModel model = new DefaultComboBoxModel(); + + for( DecoderType type: DecoderType.getPrimaryDecoders() ) + { + model.addElement( type ); + } + + mComboDecoders.setModel( model ); + mComboDecoders.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + DecoderType selected = mComboDecoders.getItemAt( mComboDecoders.getSelectedIndex() ); + ValidatingEditor editor = DecoderFactory.getEditor( selected, mChannelMapModel ); + setEditor( editor ); + } + }); + + add( mComboDecoders ); + add( mCurrentEditor ); + } + + private void setEditor( ValidatingEditor editor ) + { + if( mCurrentEditor != editor ) + { + //Set channel to null in current editor to force a save prompt as required + if( mCurrentEditor.isModified() ) + { + mCurrentEditor.setItem( null ); + } + + remove( mCurrentEditor ); + + mCurrentEditor = editor; + mCurrentEditor.setSaveRequestListener( this ); + mCurrentEditor.setItem( getItem() ); + + add( mCurrentEditor ); + + revalidate(); + repaint(); + } + } + + @Override + public void save() + { + if( hasItem() ) + { + mCurrentEditor.save(); + } + + setModified( false ); + } + + @Override + public void setItem( Channel channel ) + { + super.setItem( channel ); + + if( hasItem() ) + { + mComboDecoders.setEnabled( true ); + mComboDecoders.setSelectedItem( channel.getDecodeConfiguration().getDecoderType() ); + mCurrentEditor.setItem( channel ); + } + else + { + mComboDecoders.setEnabled( false ); + } + + setModified( false ); + } + + @Override + public void validate( Editor editor ) throws EditorValidationException + { + mCurrentEditor.validate( editor ); + } +} diff --git a/src/module/decode/DecodeEditor.java b/src/module/decode/DecodeEditor.java deleted file mode 100644 index dcebf156..00000000 --- a/src/module/decode/DecodeEditor.java +++ /dev/null @@ -1,116 +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 - ******************************************************************************/ -package module.decode; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JSpinner; -import javax.swing.SpinnerModel; -import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import module.decode.config.DecodeConfiguration; -import net.miginfocom.swing.MigLayout; -import controller.Editor; - -public class DecodeEditor extends Editor -{ - private static final long serialVersionUID = 1L; - - protected DecodeConfiguration mConfig; - - protected JCheckBox mAFC; - protected JSpinner mAFCMaximumCorrection; - - public DecodeEditor( DecodeConfiguration config ) - { - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][grow]" ) ); - - mConfig = config; - - if( config.supportsAFC() ) - { - mAFC = new JCheckBox( "Automatic Frequency Control" ); - mAFC.setSelected( mConfig.isAFCEnabled() ); - mAFC.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - mConfig.setAFC( mAFC.isSelected() ); - } - } ); - - mAFC.setToolTipText( "AFC automatically adjusts the center " - + "frequency of the channel to correct/compensate for " - + "inaccuracies and frequency drift in the tuner" ); - - add( mAFC, "span" ); - - int initial = mConfig.getAFCMaximumCorrection(); - - SpinnerModel model = new SpinnerNumberModel( initial, 0, 15000, 1 ); - - mAFCMaximumCorrection = new JSpinner( model ); - - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mAFCMaximumCorrection.getEditor(); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - mAFCMaximumCorrection.setToolTipText( "Sets the maximum frequency " - + "correction value in hertz, 0 - 15kHz" ); - - mAFCMaximumCorrection.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - int value = ((SpinnerNumberModel)mAFCMaximumCorrection - .getModel()).getNumber().intValue(); - - mConfig.setAFCMaximumCorrection( value ); - - save(); - } - } ); - - add( new JLabel( "Correction Limit Hz:" ) ); - add( mAFCMaximumCorrection, "wrap" ); - } - } - - public DecodeConfiguration getConfig() - { - return mConfig; - } - - @Override - public void save() - { - } - - @Override - public void reset() - { - } -} diff --git a/src/module/decode/Decoder.java b/src/module/decode/Decoder.java index 80cc19b2..cc6880a7 100644 --- a/src/module/decode/Decoder.java +++ b/src/module/decode/Decoder.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,13 +20,13 @@ package module.decode; import message.IMessageProvider; import message.Message; import module.Module; -import sample.Broadcaster; import sample.Listener; -public abstract class Decoder extends Module implements IMessageProvider +public abstract class Decoder extends Module + implements IMessageProvider, Listener { /* This has to be a broadcaster in order for references to persist */ - protected Broadcaster mMessageBroadcaster = new Broadcaster<>(); + protected Listener mMessageListener; /** * Decoder - parent class for all decoders, demodulators and components. @@ -38,8 +38,7 @@ public abstract class Decoder extends Module implements IMessageProvider @Override public void dispose() { - mMessageBroadcaster.dispose(); - mMessageBroadcaster = null; + mMessageListener = null; } /** @@ -52,9 +51,9 @@ public abstract class Decoder extends Module implements IMessageProvider * Adds a listener for receiving decoded messages from this decoder */ @Override - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mMessageBroadcaster.addListener( listener ); + mMessageListener = listener; } /** @@ -62,8 +61,25 @@ public abstract class Decoder extends Module implements IMessageProvider * decoders */ @Override - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mMessageBroadcaster.removeListener( listener ); + mMessageListener = null; } + + /** + * Broadcasts the message to the registered message listener + */ + public void broadcast( Message message ) + { + if( mMessageListener != null ) + { + mMessageListener.receive( message ); + } + } + + @Override + public void receive( Message message ) + { + broadcast( message ); + } } diff --git a/src/module/decode/DecoderFactory.java b/src/module/decode/DecoderFactory.java index 2f40e741..94e4bcde 100644 --- a/src/module/decode/DecoderFactory.java +++ b/src/module/decode/DecoderFactory.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,7 +25,9 @@ import message.Message; import message.MessageDirection; import module.Module; import module.decode.am.AMDecoder; +import module.decode.am.AMDecoderEditor; import module.decode.am.AMDecoderPanel; +import module.decode.am.DecodeConfigAM; import module.decode.config.AuxDecodeConfiguration; import module.decode.config.DecodeConfiguration; import module.decode.fleetsync2.Fleetsync2Decoder; @@ -38,43 +40,45 @@ import module.decode.lj1200.LJ1200DecoderState; import module.decode.lj1200.LJ1200MessageFilter; import module.decode.ltrnet.DecodeConfigLTRNet; import module.decode.ltrnet.LTRNetDecoder; +import module.decode.ltrnet.LTRNetDecoderEditor; import module.decode.ltrnet.LTRNetDecoderPanel; import module.decode.ltrnet.LTRNetDecoderState; -import module.decode.ltrnet.LTRNetEditor; import module.decode.ltrnet.LTRNetMessageFilter; import module.decode.ltrstandard.DecodeConfigLTRStandard; import module.decode.ltrstandard.LTRStandardDecoder; +import module.decode.ltrstandard.LTRStandardDecoderEditor; import module.decode.ltrstandard.LTRStandardDecoderPanel; import module.decode.ltrstandard.LTRStandardDecoderState; -import module.decode.ltrstandard.LTRStandardEditor; import module.decode.ltrstandard.LTRStandardMessageFilter; import module.decode.mdc1200.MDCDecoder; import module.decode.mdc1200.MDCDecoderPanel; import module.decode.mdc1200.MDCDecoderState; import module.decode.mdc1200.MDCMessageFilter; import module.decode.mpt1327.DecodeConfigMPT1327; -import module.decode.mpt1327.MPT1327ConfigEditor; import module.decode.mpt1327.MPT1327Decoder; import module.decode.mpt1327.MPT1327Decoder.Sync; +import module.decode.mpt1327.MPT1327DecoderEditor; import module.decode.mpt1327.MPT1327DecoderPanel; import module.decode.mpt1327.MPT1327DecoderState; import module.decode.mpt1327.MPT1327MessageFilter; +import module.decode.nbfm.DecodeConfigNBFM; import module.decode.nbfm.NBFMDecoder; +import module.decode.nbfm.NBFMDecoderEditor; import module.decode.nbfm.NBFMDecoderPanel; -import module.decode.nbfm.NBFMEditor; import module.decode.p25.DecodeConfigP25Phase1; import module.decode.p25.P25Decoder.Modulation; +import module.decode.p25.P25DecoderEditor; import module.decode.p25.P25DecoderPanel; import module.decode.p25.P25DecoderState; -import module.decode.p25.P25DecodeEditor; import module.decode.p25.P25_C4FMDecoder; import module.decode.p25.P25_LSMDecoder; import module.decode.p25.audio.P25AudioModule; import module.decode.p25.message.filter.P25MessageFilterSet; +import module.decode.passport.DecodeConfigPassport; import module.decode.passport.PassportDecoder; +import module.decode.passport.PassportDecoderEditor; import module.decode.passport.PassportDecoderPanel; import module.decode.passport.PassportDecoderState; -import module.decode.passport.PassportEditor; import module.decode.passport.PassportMessageFilter; import module.decode.state.AlwaysUnsquelchedDecoderState; import module.decode.state.DecoderPanel; @@ -84,33 +88,30 @@ import module.decode.tait.Tait1200Decoder; import module.decode.tait.Tait1200DecoderPanel; import module.decode.tait.Tait1200DecoderState; import module.demodulate.am.AMDemodulatorModule; +import module.demodulate.audio.DemodulatedAudioFilterModule; import module.demodulate.fm.FMDemodulatorModule; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import playlist.PlaylistManager; import properties.SystemProperties; import record.RecorderType; import record.config.RecordConfiguration; import settings.SettingsManager; import util.TimeStamp; import alias.AliasList; +import alias.AliasModel; import audio.AudioModule; -import controller.ResourceManager; +import controller.channel.Channel; import controller.channel.Channel.ChannelType; -import controller.channel.ChannelNode; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; import controller.channel.map.ChannelMap; -import controller.site.Site; -import controller.system.System; +import controller.channel.map.ChannelMapModel; import filter.AllPassFilter; import filter.FilterSet; import filter.IFilter; +import gui.editor.EmptyValidatingEditor; +import gui.editor.ValidatingEditor; public class DecoderFactory { - private final static Logger mLog = LoggerFactory.getLogger( DecoderFactory.class ); - public static final boolean NO_REMOVE_DC = false; public static final boolean REMOVE_DC = true; @@ -120,72 +121,46 @@ public class DecoderFactory * Returns a list of one primary decoder and any auxiliary decoders, as * specified in the configurations. * - * @param channelType - standard or traffic channel type - * @param resourceManager - * @param decodeConfig - primary decoder configuration - * @param recordConfig - recording configuration used by traffic channel manager - * @param auxConfig - auxiliary decoder(s) configuration - * @param optional alias list - (null is ok) - * * @return list of configured decoders */ - public static List getModules( ChannelType channelType, - ResourceManager resourceManager, - DecodeConfiguration decodeConfig, - RecordConfiguration recordConfig, - AuxDecodeConfiguration auxConfig, - AliasList aliasList, - System system, - Site site, - String channelName ) + public static List getModules( ChannelModel channelModel, + ChannelMapModel channelMapModel, + ChannelProcessingManager channelProcessingManager, + AliasModel aliasModel, + Channel channel ) { - List modules = getPrimaryModules( channelType, decodeConfig, - recordConfig, aliasList, system, site, channelName, resourceManager ); - modules.addAll( getAuxiliaryDecoders( auxConfig, aliasList ) ); + /* Get the optional alias list for the decode modules to use */ + AliasList aliasList = aliasModel.getAliasList( channel.getAliasListName() ); + + List modules = getPrimaryModules( channelModel, channelMapModel, + channelProcessingManager, aliasList, channel ); + + modules.addAll( getAuxiliaryDecoders( channel.getAuxDecodeConfiguration(), aliasList ) ); return modules; } - public static FMDemodulatorModule getFMDemodulator( - DecodeConfiguration config, int pass, int stop, boolean removeDC ) - { - if( config.isAFCEnabled() ) - { - return new FMDemodulatorModule( pass, stop, - config.getAFCMaximumCorrection(), removeDC ); - } - else - { - return new FMDemodulatorModule( pass, stop, - NO_FREQUENCY_CORRECTION, removeDC ); - } - } - /** * Constructs a primary decoder as specified in the decode configuration - * - * @param channelType - traffic or standard decode channel - * @param decodeConfig - primary decoder configuration - * @param recordConfig - recording options - * @param aliasList - optional alias list - * @param resourceManager - shared resource manager - * @return configured decoder or null */ - public static List getPrimaryModules( ChannelType channelType, - DecodeConfiguration decodeConfig, - RecordConfiguration recordConfig, + public static List getPrimaryModules( ChannelModel channelModel, + ChannelMapModel channelMapModel, + ChannelProcessingManager channelProcessingManager, AliasList aliasList, - System system, - Site site, - String channelName, - ResourceManager resourceManager ) + Channel channel ) { List modules = new ArrayList(); + ChannelType channelType = channel.getChannelType(); + /* Baseband low-pass filter pass and stop frequencies */ - int pass = decodeConfig.getDecoderType().getChannelBandwidth() / 2; - int stop = pass + 1250; + DecodeConfiguration decodeConfig = channel.getDecodeConfiguration(); + + int iqPass = decodeConfig.getDecoderType().getChannelBandwidth() / 2; + int iqStop = iqPass + 1250; + + RecordConfiguration recordConfig = channel.getRecordConfiguration(); boolean recordAudio = recordConfig == null ? false : recordConfig.getRecorders().contains( RecorderType.AUDIO ); @@ -193,41 +168,45 @@ public class DecoderFactory switch( decodeConfig.getDecoderType() ) { case AM: - modules.add( new AMDemodulatorModule() ); modules.add( new AMDecoder( decodeConfig ) ); - modules.add( new AlwaysUnsquelchedDecoderState( DecoderType.AM, channelName ) ); - modules.add( new AudioModule( recordAudio, REMOVE_DC ) ); + modules.add( new AlwaysUnsquelchedDecoderState( DecoderType.AM, channel.getName() ) ); + modules.add( new AMDemodulatorModule() ); + modules.add( new DemodulatedAudioFilterModule( 4000, 6000 )); + modules.add( new AudioModule( recordAudio ) ); break; case NBFM: - modules.add( new AudioModule( recordAudio, NO_REMOVE_DC ) ); modules.add( new NBFMDecoder( decodeConfig ) ); - modules.add( new AlwaysUnsquelchedDecoderState( DecoderType.NBFM, channelName ) ); - modules.add( getFMDemodulator( decodeConfig, pass, stop, REMOVE_DC ) ); + modules.add( new AlwaysUnsquelchedDecoderState( DecoderType.NBFM, channel.getName() ) ); + modules.add( new FMDemodulatorModule( iqPass, iqStop ) ); + modules.add( new DemodulatedAudioFilterModule( 4000, 6000 )); + modules.add( new AudioModule( recordAudio ) ); break; case LTR_STANDARD: - modules.add( new AudioModule( recordAudio, REMOVE_DC ) ); MessageDirection direction = ((DecodeConfigLTRStandard)decodeConfig).getMessageDirection(); modules.add( new LTRStandardDecoder( aliasList, direction ) ); modules.add( new LTRStandardDecoderState( aliasList ) ); - modules.add( getFMDemodulator( decodeConfig, pass, stop, NO_REMOVE_DC ) ); + modules.add( new FMDemodulatorModule( iqPass, iqStop ) ); + modules.add( new DemodulatedAudioFilterModule( 4000, 6000 )); + modules.add( new AudioModule( recordAudio ) ); break; case LTR_NET: - modules.add( new AudioModule( recordAudio, REMOVE_DC ) ); modules.add( new LTRNetDecoder( (DecodeConfigLTRNet)decodeConfig, aliasList ) ); modules.add( new LTRNetDecoderState( aliasList ) ); - modules.add( getFMDemodulator( decodeConfig, pass, stop, NO_REMOVE_DC ) ); + modules.add( new FMDemodulatorModule( iqPass, iqStop ) ); + modules.add( new DemodulatedAudioFilterModule( 4000, 6000 )); + modules.add( new AudioModule( recordAudio ) ); break; case MPT1327: DecodeConfigMPT1327 mptConfig = (DecodeConfigMPT1327)decodeConfig; - modules.add( new AudioModule( recordAudio, NO_REMOVE_DC ) ); - - ChannelMap channelMap = resourceManager.getPlaylistManager().getPlayist() - .getChannelMapList().getChannelMap( mptConfig.getChannelMapName() ); + ChannelMap channelMap = channelMapModel + .getChannelMap( mptConfig.getChannelMapName() ); Sync sync = mptConfig.getSync(); modules.add( new MPT1327Decoder( aliasList, sync ) ); + + modules.add( new MPT1327DecoderState( aliasList, channelMap, channelType, mptConfig.getCallTimeout() * 1000 ) ); @@ -235,30 +214,34 @@ public class DecoderFactory { long timeout = mptConfig.getCallTimeout() * 1000; //convert to milliseconds - modules.add( new TrafficChannelManager( resourceManager, - decodeConfig, recordConfig, system, site, - ( aliasList != null ? aliasList.getName() : null ), timeout, - mptConfig.getTrafficChannelPoolSize() ) ); + modules.add( new TrafficChannelManager( channelModel, channelProcessingManager, + decodeConfig, recordConfig, channel.getSystem(), + channel.getSite(), ( aliasList != null ? aliasList.getName() : null ), timeout, + mptConfig.getTrafficChannelPoolSize() ) ); } - modules.add( getFMDemodulator( decodeConfig, pass, stop, REMOVE_DC ) ); + modules.add( new FMDemodulatorModule( iqPass, iqStop ) ); + modules.add( new DemodulatedAudioFilterModule( 4000, 6000 )); + modules.add( new AudioModule( recordAudio ) ); break; case PASSPORT: - modules.add( new AudioModule( recordAudio, REMOVE_DC ) ); modules.add( new PassportDecoder( decodeConfig, aliasList ) ); modules.add( new PassportDecoderState( aliasList ) ); - modules.add( getFMDemodulator( decodeConfig, pass, stop, NO_REMOVE_DC ) ); + modules.add( new FMDemodulatorModule( iqPass, iqStop ) ); + modules.add( new DemodulatedAudioFilterModule( 4000, 6000 )); + modules.add( new AudioModule( recordAudio ) ); break; case P25_PHASE1: DecodeConfigP25Phase1 p25Config = (DecodeConfigP25Phase1)decodeConfig; Modulation modulation = p25Config.getModulation(); + switch( modulation ) { case C4FM: - modules.add( getFMDemodulator( decodeConfig, 6750, 7500, NO_REMOVE_DC ) ); - modules.add( new P25_C4FMDecoder( aliasList ) ); + modules.add( new FMDemodulatorModule( 6750, 7500 ) ); + modules.add( new P25_C4FMDecoder( aliasList, decodeConfig.getAFCMaximumCorrection() ) ); modules.add( new P25DecoderState( aliasList, channelType, Modulation.C4FM, p25Config.getIgnoreDataCalls() ) ); break; @@ -277,8 +260,9 @@ public class DecoderFactory //Set call timeout to 3 seconds for P25 traffic channels long timeout = 3000; - modules.add( new TrafficChannelManager( resourceManager, - decodeConfig, recordConfig, system, site, + modules.add( new TrafficChannelManager( channelModel, + channelProcessingManager, decodeConfig, + recordConfig, channel.getSystem(), channel.getSite(), ( aliasList != null ? aliasList.getName() : null ), timeout, p25Config.getTrafficChannelPoolSize() ) ); } @@ -447,41 +431,132 @@ public class DecoderFactory return filters; } - - public static DecodeEditor getEditorPanel( DecodeConfiguration config, - ChannelNode channelNode, PlaylistManager playlistManager ) + + public static DecodeConfiguration getDefaultDecodeConfiguration() { - DecodeEditor configuredPanel; - - switch( config.getDecoderType() ) + return getDecodeConfiguration( DecoderType.NBFM ); + } + + public static DecodeConfiguration getDecodeConfiguration( DecoderType decoder ) + { + DecodeConfiguration retVal; + + switch( decoder ) { - case NBFM: - configuredPanel = new NBFMEditor( config ); - break; - case LTR_STANDARD: - configuredPanel = new LTRStandardEditor( config ); - break; + case AM: + return new DecodeConfigAM(); case LTR_NET: - configuredPanel = new LTRNetEditor( config ); - break; + return new DecodeConfigLTRNet(); + case LTR_STANDARD: + return new DecodeConfigLTRStandard(); case MPT1327: - configuredPanel = new MPT1327ConfigEditor( config, - channelNode, playlistManager ); - break; + return new DecodeConfigMPT1327(); + case NBFM: + return new DecodeConfigNBFM(); case PASSPORT: - configuredPanel = new PassportEditor( config ); - break; + return new DecodeConfigPassport(); case P25_PHASE1: - configuredPanel = new P25DecodeEditor( config ); - break; + return new DecodeConfigP25Phase1(); + default: + throw new IllegalArgumentException( "DecodeConfigFactory - unknown decoder type [" + + decoder.toString() + "]" ); + } + } + + public static ValidatingEditor getEditor( DecoderType type, ChannelMapModel model ) + { + switch( type ) + { + case AM: + return new AMDecoderEditor(); + case LTR_NET: + return new LTRNetDecoderEditor(); + case LTR_STANDARD: + return new LTRStandardDecoderEditor(); + case MPT1327: + return new MPT1327DecoderEditor( model ); + case NBFM: + return new NBFMDecoderEditor(); + case P25_PHASE1: + return new P25DecoderEditor(); + case PASSPORT: + return new PassportDecoderEditor(); default: - configuredPanel = new DecodeEditor( config ); break; } - return configuredPanel; + return new EmptyValidatingEditor( "a decoder" ); } - + + /** + * Creates a copy of the configuration + */ + public static DecodeConfiguration copy( DecodeConfiguration config ) + { + if( config != null ) + { + switch( config.getDecoderType() ) + { + case AM: + DecodeConfigAM originalAM = (DecodeConfigAM)config; + DecodeConfigAM copyAM = new DecodeConfigAM(); + copyAM.setAFC( originalAM.getAFC() ); + copyAM.setAFCMaximumCorrection( originalAM.getAFCMaximumCorrection() ); + return copyAM; + case LTR_NET: + DecodeConfigLTRNet originalLTRNet = (DecodeConfigLTRNet)config; + DecodeConfigLTRNet copyLTRNet = new DecodeConfigLTRNet(); + copyLTRNet.setAFC( originalLTRNet.getAFC() ); + copyLTRNet.setAFCMaximumCorrection( originalLTRNet.getAFCMaximumCorrection() ); + copyLTRNet.setMessageDirection( originalLTRNet.getMessageDirection() ); + return copyLTRNet; + case LTR_STANDARD: + DecodeConfigLTRStandard originalLTRStandard = (DecodeConfigLTRStandard)config; + DecodeConfigLTRStandard copyLTRStandard = new DecodeConfigLTRStandard(); + copyLTRStandard.setAFC( originalLTRStandard.getAFC() ); + copyLTRStandard.setAFCMaximumCorrection( originalLTRStandard.getAFCMaximumCorrection() ); + copyLTRStandard.setMessageDirection( originalLTRStandard.getMessageDirection() ); + return copyLTRStandard; + case MPT1327: + DecodeConfigMPT1327 originalMPT = (DecodeConfigMPT1327)config; + DecodeConfigMPT1327 copyMPT = new DecodeConfigMPT1327(); + copyMPT.setAFC( originalMPT.getAFC() ); + copyMPT.setAFCMaximumCorrection( originalMPT.getAFCMaximumCorrection() ); + copyMPT.setCallTimeout( originalMPT.getCallTimeout() ); + copyMPT.setChannelMapName( originalMPT.getChannelMapName() ); + copyMPT.setSync( originalMPT.getSync() ); + copyMPT.setTrafficChannelPoolSize( originalMPT.getTrafficChannelPoolSize() ); + return copyMPT; + case NBFM: + DecodeConfigNBFM originalNBFM = (DecodeConfigNBFM)config; + DecodeConfigNBFM copyNBFM = new DecodeConfigNBFM(); + copyNBFM.setAFC( originalNBFM.getAFC() ); + copyNBFM.setAFCMaximumCorrection( originalNBFM.getAFCMaximumCorrection() ); + return copyNBFM; + case P25_PHASE1: + DecodeConfigP25Phase1 originalP25 = (DecodeConfigP25Phase1)config; + DecodeConfigP25Phase1 copyP25 = new DecodeConfigP25Phase1(); + copyP25.setAFC( originalP25.getAFC() ); + copyP25.setAFCMaximumCorrection( originalP25.getAFCMaximumCorrection() ); + copyP25.setIgnoreDataCalls( originalP25.getIgnoreDataCalls() ); + copyP25.setModulation( originalP25.getModulation() ); + copyP25.setTrafficChannelPoolSize( originalP25.getTrafficChannelPoolSize() ); + return copyP25; + case PASSPORT: + DecodeConfigPassport originalPass = (DecodeConfigPassport)config; + DecodeConfigPassport copyPass = new DecodeConfigPassport(); + copyPass.setAFC( originalPass.getAFC() ); + copyPass.setAFCMaximumCorrection( originalPass.getAFCMaximumCorrection() ); + return copyPass; + default: + throw new IllegalArgumentException( "Unrecognized decoder " + + "configuration type:" + config.getDecoderType() ); + } + } + + return null; + } + public static String getRecordingFilename( String token ) { //Get the base recording filename diff --git a/src/module/decode/am/AMDecoder.java b/src/module/decode/am/AMDecoder.java index 8b407b67..c5b371e3 100644 --- a/src/module/decode/am/AMDecoder.java +++ b/src/module/decode/am/AMDecoder.java @@ -18,6 +18,8 @@ package module.decode.am; +import java.util.concurrent.ScheduledExecutorService; + import module.decode.DecoderType; import module.decode.PrimaryDecoder; import module.decode.config.DecodeConfiguration; @@ -41,7 +43,7 @@ public class AMDecoder extends PrimaryDecoder } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/source/tuner/fcd/FCDTunerDetailsPanel.java b/src/module/decode/am/AMDecoderEditor.java similarity index 50% rename from src/source/tuner/fcd/FCDTunerDetailsPanel.java rename to src/module/decode/am/AMDecoderEditor.java index 05e3b51f..697f7ebe 100644 --- a/src/source/tuner/fcd/FCDTunerDetailsPanel.java +++ b/src/module/decode/am/AMDecoderEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,35 +15,62 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package source.tuner.fcd; +package module.decode.am; + +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.ValidatingEditor; import javax.swing.JLabel; -import javax.swing.JPanel; import net.miginfocom.swing.MigLayout; +import controller.channel.Channel; -public class FCDTunerDetailsPanel extends JPanel +public class AMDecoderEditor extends ValidatingEditor { private static final long serialVersionUID = 1L; + + public AMDecoderEditor() + { + setLayout( new MigLayout( "insets 0 0 0 0", "", "" ) ); + add( new JLabel( "AM Decoder" ) ); + } - public FCDTunerDetailsPanel( FCDTunerController controller ) - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][grow]" ) ); + @Override + public void validate( Editor editor ) throws EditorValidationException + { + } - add( new JLabel( "USB ID:" ) ); - add( new JLabel( controller.getUSBID() ) ); - - add( new JLabel( "USB Address:" ) ); - add( new JLabel( controller.getUSBAddress() ) ); - - add( new JLabel( "USB Speed:" ) ); - add( new JLabel( controller.getUSBSpeed() ) ); - - add( new JLabel( "Cellular Band:" ) ); - add( new JLabel( controller.getConfiguration().getBandBlocking() - .getLabel() ) ); + @Override + public void setItem( Channel item ) + { + super.setItem( item ); - add( new JLabel( "Firmware:" ) ); - add( new JLabel( controller.getConfiguration().getFirmware() ) ); - } + if( hasItem() ) + { + if( getItem().getDecodeConfiguration() instanceof DecodeConfigAM ) + { + setModified( false ); + } + else + { + setModified( true ); + } + } + else + { + setModified( false ); + } + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + getItem().setDecodeConfiguration( new DecodeConfigAM() ); + } + + setModified( false ); + } } diff --git a/src/module/decode/am/AMEditor.java b/src/module/decode/am/AMEditor.java deleted file mode 100644 index 69561043..00000000 --- a/src/module/decode/am/AMEditor.java +++ /dev/null @@ -1,31 +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 - ******************************************************************************/ -package module.decode.am; - -import module.decode.DecodeEditor; -import module.decode.config.DecodeConfiguration; - -public class AMEditor extends DecodeEditor -{ - private static final long serialVersionUID = 1L; - - public AMEditor( DecodeConfiguration config ) - { - super( config ); - } -} diff --git a/src/module/decode/config/DecodeConfigFactory.java b/src/module/decode/config/DecodeConfigFactory.java deleted file mode 100644 index a4a88398..00000000 --- a/src/module/decode/config/DecodeConfigFactory.java +++ /dev/null @@ -1,71 +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 - ******************************************************************************/ -package module.decode.config; - -import module.decode.DecoderType; -import module.decode.am.DecodeConfigAM; -import module.decode.ltrnet.DecodeConfigLTRNet; -import module.decode.ltrstandard.DecodeConfigLTRStandard; -import module.decode.mpt1327.DecodeConfigMPT1327; -import module.decode.nbfm.DecodeConfigNBFM; -import module.decode.p25.DecodeConfigP25Phase1; -import module.decode.passport.DecodeConfigPassport; - -public class DecodeConfigFactory -{ - public static DecodeConfiguration getDefaultDecodeConfiguration() - { - return getDecodeConfiguration( DecoderType.NBFM ); - } - - public static DecodeConfiguration - getDecodeConfiguration( DecoderType decoder ) - { - DecodeConfiguration retVal; - - switch( decoder ) - { - case AM: - retVal = new DecodeConfigAM(); - break; - case NBFM: - retVal = new DecodeConfigNBFM(); - break; - case LTR_STANDARD: - retVal = new DecodeConfigLTRStandard(); - break; - case LTR_NET: - retVal = new DecodeConfigLTRNet(); - break; - case MPT1327: - retVal = new DecodeConfigMPT1327(); - break; - case PASSPORT: - retVal = new DecodeConfigPassport(); - break; - case P25_PHASE1: - retVal = new DecodeConfigP25Phase1(); - break; - default: - throw new IllegalArgumentException( "DecodeConfigFactory - " - + "unknown decoder type [" + decoder.toString() + "]" ); - } - - return retVal; - } -} diff --git a/src/module/decode/config/DecodeConfiguration.java b/src/module/decode/config/DecodeConfiguration.java index 95070a08..8390d608 100644 --- a/src/module/decode/config/DecodeConfiguration.java +++ b/src/module/decode/config/DecodeConfiguration.java @@ -17,7 +17,7 @@ ******************************************************************************/ package module.decode.config; -import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; @@ -48,10 +48,12 @@ public abstract class DecodeConfiguration extends Configuration public static final int TRAFFIC_CHANNEL_LIMIT_DEFAULT = 3; public static final int TRAFFIC_CHANNEL_LIMIT_MINIMUM = 0; public static final int TRAFFIC_CHANNEL_LIMIT_MAXIMUM = 30; + + public static final int DEFAULT_AFC_MAX_CORRECTION = 1000; private DecoderType mDecoderType = DecoderType.NBFM; private boolean mAFCEnabled = true; - private int mAFCMaxCorrection = 3000; + private int mAFCMaxCorrection = DEFAULT_AFC_MAX_CORRECTION; public DecodeConfiguration() { @@ -68,7 +70,7 @@ public abstract class DecodeConfiguration extends Configuration return mDecoderType; } - @XmlElement( name = "afc" ) + @XmlAttribute( name = "afc" ) public boolean getAFC() { return mAFCEnabled; @@ -84,6 +86,7 @@ public abstract class DecodeConfiguration extends Configuration mAFCEnabled = enabled; } + @XmlAttribute( name = "AFCMaximumCorrection" ) public int getAFCMaximumCorrection() { return mAFCMaxCorrection; diff --git a/src/module/decode/event/CallEventAliasCellRenderer.java b/src/module/decode/event/CallEventAliasCellRenderer.java index 17460e16..5f43b135 100644 --- a/src/module/decode/event/CallEventAliasCellRenderer.java +++ b/src/module/decode/event/CallEventAliasCellRenderer.java @@ -20,9 +20,6 @@ package module.decode.event; import javax.swing.ImageIcon; import javax.swing.table.DefaultTableCellRenderer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import settings.SettingsManager; import alias.Alias; @@ -35,9 +32,6 @@ public class CallEventAliasCellRenderer extends DefaultTableCellRenderer { private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory - .getLogger( CallEventAliasCellRenderer.class ); - private SettingsManager mSettingsManager; public CallEventAliasCellRenderer( SettingsManager settingsManager ) diff --git a/src/module/decode/event/CallEventModel.java b/src/module/decode/event/CallEventModel.java index eee813cf..c39d3dfc 100644 --- a/src/module/decode/event/CallEventModel.java +++ b/src/module/decode/event/CallEventModel.java @@ -25,20 +25,13 @@ import java.util.List; import javax.swing.table.AbstractTableModel; import module.decode.event.CallEvent.CallEventType; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sample.Listener; public class CallEventModel extends AbstractTableModel implements Listener { private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger( CallEventModel.class ); - - private static DecimalFormat mFrequencyFormatter = - new DecimalFormat( "0.000000" ); + private static DecimalFormat mFrequencyFormatter = new DecimalFormat( "0.000000" ); public static final int TIME = 0; public static final int EVENT = 1; diff --git a/src/module/decode/event/CallEventPanel.java b/src/module/decode/event/CallEventPanel.java index c56f53bf..48d8ce34 100644 --- a/src/module/decode/event/CallEventPanel.java +++ b/src/module/decode/event/CallEventPanel.java @@ -23,12 +23,14 @@ import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; +import module.ProcessingChain; import net.miginfocom.swing.MigLayout; import settings.SettingsManager; import controller.channel.Channel; import controller.channel.ChannelEvent; import controller.channel.ChannelEvent.Event; import controller.channel.ChannelEventListener; +import controller.channel.ChannelProcessingManager; public class CallEventPanel extends JPanel implements ChannelEventListener { @@ -37,9 +39,13 @@ public class CallEventPanel extends JPanel implements ChannelEventListener private JScrollPane mEmptyScroller; private Channel mDisplayedChannel; private CallEventAliasCellRenderer mRenderer; + private ChannelProcessingManager mChannelProcessingManager; - public CallEventPanel( SettingsManager settingsManager ) + public CallEventPanel( SettingsManager settingsManager, + ChannelProcessingManager channelProcessingManager ) { + mChannelProcessingManager = channelProcessingManager; + setLayout( new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]") ); JTable table = new JTable( new CallEventModel() ); @@ -62,45 +68,54 @@ public class CallEventPanel extends JPanel implements ChannelEventListener @Override public void channelChanged( final ChannelEvent event ) { - if( event.getEvent() == Event.CHANGE_SELECTED && + if( event.getEvent() == Event.NOTIFICATION_SELECTION_CHANGE && event.getChannel().isSelected() ) { if( mDisplayedChannel == null || ( mDisplayedChannel != null && mDisplayedChannel != event.getChannel() ) ) { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - removeAll(); - - JTable table = new JTable( - event.getChannel().getCallEventModel() ); - - table.setAutoCreateRowSorter( true ); - table.setAutoResizeMode( JTable.AUTO_RESIZE_LAST_COLUMN ); - - table.getColumnModel().getColumn( CallEventModel.FROM_ALIAS ) - .setCellRenderer( mRenderer ); - - table.getColumnModel().getColumn( CallEventModel.TO_ALIAS ) - .setCellRenderer( mRenderer ); - - add( new JScrollPane( table ) ); - - mDisplayedChannel = event.getChannel(); - - revalidate(); - repaint(); - } - } ); + ProcessingChain chain = mChannelProcessingManager + .getProcessingChain( event.getChannel() ); + if( chain != null ) + { + final CallEventModel model = chain.getCallEventModel(); + + if( model != null ) + { + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + removeAll(); + + JTable table = new JTable( model ); + + table.setAutoCreateRowSorter( true ); + table.setAutoResizeMode( JTable.AUTO_RESIZE_LAST_COLUMN ); + + table.getColumnModel().getColumn( CallEventModel.FROM_ALIAS ) + .setCellRenderer( mRenderer ); + + table.getColumnModel().getColumn( CallEventModel.TO_ALIAS ) + .setCellRenderer( mRenderer ); + + add( new JScrollPane( table ) ); + + mDisplayedChannel = event.getChannel(); + + revalidate(); + repaint(); + } + } ); + } + } } } - else if( event.getEvent() == Event.CHANNEL_PROCESSING_STOPPED || - event.getEvent() == Event.CHANNEL_DISABLED ) + else if( event.getEvent() == Event.NOTIFICATION_PROCESSING_STOP || + event.getEvent() == Event.REQUEST_DISABLE ) { if( mDisplayedChannel != null && mDisplayedChannel == event.getChannel() ) diff --git a/src/module/decode/event/MessageActivityModel.java b/src/module/decode/event/MessageActivityModel.java index 390c4c59..50a57b1b 100644 --- a/src/module/decode/event/MessageActivityModel.java +++ b/src/module/decode/event/MessageActivityModel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,11 +32,11 @@ public class MessageActivityModel extends AbstractTableModel implements Listener { private static final long serialVersionUID = 1L; - private static final int sTIME = 0; - private static final int sPROTOCOL = 1; - private static final int sERROR_STATUS = 2; - private static final int sMESSAGE = 3; - private static final int sMESSAGE_BITS = 4; + private static final int TIME = 0; + private static final int PROTOCOL = 1; + private static final int ERROR_STATUS = 2; + private static final int MESSAGE = 3; + private static final int MESSAGE_BITS = 4; protected int mMaxMessages = 500; protected LinkedList mMessages = new LinkedList(); @@ -174,18 +174,18 @@ public class MessageActivityModel extends AbstractTableModel switch( columnIndex ) { - case sTIME: + case TIME: return mSDFTime.format( new Date( message.getTimeReceived() ) ); - case sPROTOCOL: + case PROTOCOL: return message.getProtocol(); - case sERROR_STATUS: + case ERROR_STATUS: return message.getErrorStatus(); - case sMESSAGE: + case MESSAGE: return message.getMessage(); - case sMESSAGE_BITS: + case MESSAGE_BITS: return message.getBinaryMessage(); } return null; } -} +} \ No newline at end of file diff --git a/src/module/decode/event/MessageActivityPanel.java b/src/module/decode/event/MessageActivityPanel.java index ae026b14..0ef6a5ee 100644 --- a/src/module/decode/event/MessageActivityPanel.java +++ b/src/module/decode/event/MessageActivityPanel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,6 +36,7 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import message.Message; +import module.ProcessingChain; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; @@ -48,6 +49,7 @@ import controller.channel.Channel; import controller.channel.ChannelEvent; import controller.channel.ChannelEvent.Event; import controller.channel.ChannelEventListener; +import controller.channel.ChannelProcessingManager; import filter.FilterEditorPanel; import filter.FilterSet; @@ -63,16 +65,21 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener private JTable mMessageTable; private Channel mDisplayedChannel; + private MessageActivityModel mDisplayedModel = EMPTY_MODEL; private MessageManagementPanel mManagementPanel = new MessageManagementPanel(); + + private ChannelProcessingManager mChannelProcessingManager; - public MessageActivityPanel() + public MessageActivityPanel( ChannelProcessingManager channelProcessingManager ) { + mChannelProcessingManager = channelProcessingManager; + setLayout( new MigLayout("insets 0 0 0 0", "[][grow,fill]", "[]0[grow,fill]") ); add( mManagementPanel, "span,growx" ); - mMessageTable = new JTable( EMPTY_MODEL ); + mMessageTable = new JTable( mDisplayedModel ); add( new JScrollPane( mMessageTable ), "span,grow" ); } @@ -82,7 +89,7 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener { boolean changed = false; - if( event.getEvent() == Event.CHANGE_SELECTED && + if( event.getEvent() == Event.NOTIFICATION_SELECTION_CHANGE && event.getChannel().isSelected() ) { if( mDisplayedChannel == null || @@ -91,7 +98,19 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener { mDisplayedChannel = event.getChannel(); - mMessageTable.setModel( event.getChannel().getMessageActivityModel() ); + ProcessingChain chain = mChannelProcessingManager + .getProcessingChain( mDisplayedChannel ); + + if( chain != null ) + { + mDisplayedModel = chain.getMessageActivityModel(); + } + else + { + mDisplayedModel = EMPTY_MODEL; + } + + mMessageTable.setModel( mDisplayedModel ); if( mDisplayedChannel != null ) { @@ -105,8 +124,8 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener changed = true; } } - else if( event.getEvent() == Event.CHANNEL_PROCESSING_STOPPED || - event.getEvent() == Event.CHANNEL_DISABLED ) + else if( event.getEvent() == Event.NOTIFICATION_PROCESSING_STOP || + event.getEvent() == Event.REQUEST_DISABLE ) { if( mDisplayedChannel != null && mDisplayedChannel == event.getChannel() ) @@ -195,10 +214,9 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener public void mouseReleased( MouseEvent arg0 ) {} } ); - if( mDisplayedChannel != null ) + if( mDisplayedModel != null ) { - slider.setValue( mDisplayedChannel.getMessageActivityModel() - .getMaxMessageCount() ); + slider.setValue( mDisplayedModel.getMaxMessageCount() ); } else { @@ -210,10 +228,9 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener @Override public void stateChanged( ChangeEvent arg0 ) { - if( mDisplayedChannel != null ) + if( mDisplayedModel != null ) { - mDisplayedChannel.getMessageActivityModel() - .setMaxMessageCount( slider.getValue() ); + mDisplayedModel.setMaxMessageCount( slider.getValue() ); } } }); @@ -228,9 +245,9 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener @Override public void actionPerformed( ActionEvent e ) { - if( mDisplayedChannel != null ) + if( mDisplayedModel != null ) { - mDisplayedChannel.getMessageActivityModel().clear(); + mDisplayedModel.clear(); } } } ); @@ -258,50 +275,52 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener } else { - final JFrame editor = new JFrame(); - - editor.setTitle( "Message Filter Editor" ); - editor.setSize( 600, 400 ); - editor.setLocationRelativeTo( MessageFilterButton.this ); - editor.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); - editor.setLayout( new MigLayout( "", "[grow,fill]", - "[grow,fill][][]" ) ); - - @SuppressWarnings( "unchecked" ) - FilterSet filter = (FilterSet) - mDisplayedChannel.getMessageActivityModel() - .getMessageFilter(); - - FilterEditorPanel panel = - new FilterEditorPanel( filter ); - - JScrollPane scroller = new JScrollPane( panel ); - scroller.setViewportView( panel ); - - editor.add( scroller, "wrap" ); - - editor.add( new JLabel( "Right-click to select/deselect " - + "all nodes" ), "wrap" ); - - JButton close = new JButton( "Close" ); - close.addActionListener( new ActionListener() + if( mDisplayedModel != null ) { - @Override - public void actionPerformed( ActionEvent e ) - { - editor.dispose(); - } - } ); - - editor.add( close ); + final JFrame editor = new JFrame(); + + editor.setTitle( "Message Filter Editor" ); + editor.setSize( 600, 400 ); + editor.setLocationRelativeTo( MessageFilterButton.this ); + editor.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); + editor.setLayout( new MigLayout( "", "[grow,fill]", + "[grow,fill][][]" ) ); - EventQueue.invokeLater( new Runnable() - { - public void run() - { - editor.setVisible( true ); - } - } ); + @SuppressWarnings( "unchecked" ) + FilterSet filter = (FilterSet) + mDisplayedModel.getMessageFilter(); + + FilterEditorPanel panel = + new FilterEditorPanel( filter ); + + JScrollPane scroller = new JScrollPane( panel ); + scroller.setViewportView( panel ); + + editor.add( scroller, "wrap" ); + + editor.add( new JLabel( "Right-click to select/deselect " + + "all nodes" ), "wrap" ); + + JButton close = new JButton( "Close" ); + close.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + editor.dispose(); + } + } ); + + editor.add( close ); + + EventQueue.invokeLater( new Runnable() + { + public void run() + { + editor.setVisible( true ); + } + } ); + } } } } ); diff --git a/src/module/decode/fleetsync2/Fleetsync2Decoder.java b/src/module/decode/fleetsync2/Fleetsync2Decoder.java index 1425983a..5b18628b 100644 --- a/src/module/decode/fleetsync2/Fleetsync2Decoder.java +++ b/src/module/decode/fleetsync2/Fleetsync2Decoder.java @@ -25,11 +25,12 @@ import instrument.tap.stream.FloatBufferTap; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import module.decode.Decoder; import module.decode.DecoderType; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import alias.AliasList; import bits.MessageFramer; @@ -44,7 +45,7 @@ import dsp.fsk.FSK2Decoder.Output; * Fleetsync II Decoder - 1200 baud 2FSK decoder that can process 48k sample rate * floating point samples and output fully framed Fleetsync II messages */ -public class Fleetsync2Decoder extends Decoder implements IRealBufferListener, +public class Fleetsync2Decoder extends Decoder implements IFilteredRealBufferListener, Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ @@ -90,7 +91,7 @@ public class Fleetsync2Decoder extends Decoder implements IRealBufferListener, mMessageProcessor = new Fleetsync2MessageProcessor( aliasList ); mMessageFramer.addMessageListener( mMessageProcessor ); - mMessageProcessor.setMessageListener( mMessageBroadcaster ); + mMessageProcessor.setMessageListener( this ); } public void dispose() @@ -169,7 +170,7 @@ public class Fleetsync2Decoder extends Decoder implements IRealBufferListener, } @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { return mDecimationFilter; } @@ -180,7 +181,7 @@ public class Fleetsync2Decoder extends Decoder implements IRealBufferListener, } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/fleetsync2/Fleetsync2DecoderState.java b/src/module/decode/fleetsync2/Fleetsync2DecoderState.java index 8fb2cd00..bb5d614d 100644 --- a/src/module/decode/fleetsync2/Fleetsync2DecoderState.java +++ b/src/module/decode/fleetsync2/Fleetsync2DecoderState.java @@ -19,6 +19,7 @@ package module.decode.fleetsync2; import java.util.Iterator; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; @@ -58,7 +59,7 @@ public class Fleetsync2DecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/lj1200/LJ1200Decoder.java b/src/module/decode/lj1200/LJ1200Decoder.java index 57297d57..3e12b544 100644 --- a/src/module/decode/lj1200/LJ1200Decoder.java +++ b/src/module/decode/lj1200/LJ1200Decoder.java @@ -26,12 +26,13 @@ import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import module.decode.Decoder; import module.decode.DecoderType; import sample.Broadcaster; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import sample.real.RealSampleListener; import alias.AliasList; @@ -46,7 +47,7 @@ import dsp.fsk.FSK2Decoder.Output; /** * LJ1200 - 1200 baud 2FSK decoder */ -public class LJ1200Decoder extends Decoder implements IRealBufferListener, +public class LJ1200Decoder extends Decoder implements IFilteredRealBufferListener, Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ @@ -100,7 +101,7 @@ public class LJ1200Decoder extends Decoder implements IRealBufferListener, mMessageProcessor = new LJ1200MessageProcessor( aliasList ); mTowerMessageFramer.addMessageListener( mMessageProcessor ); mTransponderMessageFramer.addMessageListener( mMessageProcessor ); - mMessageProcessor.setMessageListener( mMessageBroadcaster ); + mMessageProcessor.setMessageListener( this ); } @Override @@ -187,7 +188,7 @@ public class LJ1200Decoder extends Decoder implements IRealBufferListener, } @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { return mDecimationFilter; } @@ -198,7 +199,7 @@ public class LJ1200Decoder extends Decoder implements IRealBufferListener, } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/lj1200/LJ1200DecoderState.java b/src/module/decode/lj1200/LJ1200DecoderState.java index 71b72f0f..5c438647 100644 --- a/src/module/decode/lj1200/LJ1200DecoderState.java +++ b/src/module/decode/lj1200/LJ1200DecoderState.java @@ -22,18 +22,19 @@ import java.util.Collections; import java.util.List; import java.util.Set; import java.util.TreeSet; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; -import module.decode.p25.P25DecoderState; import module.decode.state.ChangedAttribute; import module.decode.state.DecoderState; import module.decode.state.DecoderStateEvent; import module.decode.state.DecoderStateEvent.Event; import module.decode.state.State; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import alias.Alias; import alias.AliasList; @@ -58,7 +59,7 @@ public class LJ1200DecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/ltrnet/LTRCallEvent.java b/src/module/decode/ltrnet/LTRCallEvent.java index 3bb553ca..671d7c2a 100644 --- a/src/module/decode/ltrnet/LTRCallEvent.java +++ b/src/module/decode/ltrnet/LTRCallEvent.java @@ -51,16 +51,22 @@ public class LTRCallEvent extends CallEvent */ public boolean isMatchingTalkgroup( LTRNetMessage message ) { + String talkgroup = message.getTalkgroupID(); + + if( talkgroup != null && getToID() != null && + talkgroup.contentEquals( getToID() ) ) + { + return true; + } + return isMatchingTalkgroup( message.getTalkgroupID() ); } public boolean isMatchingTalkgroup( String talkgroup ) { - return talkgroup != null && - getToID() != null && - talkgroup.contentEquals( getToID() ); + return talkgroup != null && getToID() != null && talkgroup.contentEquals( getToID() ); } - + private Alias getAlias( String talkgroup ) { if( hasAliasList() ) diff --git a/src/module/decode/ltrnet/LTRNetDecoder.java b/src/module/decode/ltrnet/LTRNetDecoder.java index 095dffe4..5e1eecff 100644 --- a/src/module/decode/ltrnet/LTRNetDecoder.java +++ b/src/module/decode/ltrnet/LTRNetDecoder.java @@ -23,19 +23,21 @@ import instrument.tap.TapGroup; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import message.MessageDirection; import module.decode.Decoder; import module.decode.DecoderType; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IUnFilteredRealBufferListener; import sample.real.RealBuffer; import alias.AliasList; import bits.MessageFramer; import bits.SyncPattern; import dsp.fsk.LTRFSKDecoder; -public class LTRNetDecoder extends Decoder implements IRealBufferListener, Instrumentable +public class LTRNetDecoder extends Decoder + implements IUnFilteredRealBufferListener, Instrumentable { public static final int LTR_STANDARD_MESSAGE_LENGTH = 40; private LTRFSKDecoder mLTRFSKDecoder; @@ -44,6 +46,10 @@ public class LTRNetDecoder extends Decoder implements IRealBufferListener, Instr private List mAvailableTaps; + /** + * LTR-Net Decoder. Decodes unfiltered (e.g. demodulated but with no DC or + * audio filtering) samples and produces LTR-Net messages. + */ public LTRNetDecoder( DecodeConfigLTRNet config, AliasList aliasList ) { mLTRFSKDecoder = new LTRFSKDecoder(); @@ -69,7 +75,7 @@ public class LTRNetDecoder extends Decoder implements IRealBufferListener, Instr mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); - mLTRMessageProcessor.setMessageListener( mMessageBroadcaster ); + mLTRMessageProcessor.setMessageListener( this ); } @Override @@ -79,7 +85,7 @@ public class LTRNetDecoder extends Decoder implements IRealBufferListener, Instr } @Override - public Listener getRealBufferListener() + public Listener getUnFilteredRealBufferListener() { return mLTRFSKDecoder; } @@ -116,7 +122,7 @@ public class LTRNetDecoder extends Decoder implements IRealBufferListener, Instr } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/ltrnet/LTRNetDecoderEditor.java b/src/module/decode/ltrnet/LTRNetDecoderEditor.java new file mode 100644 index 00000000..1913412d --- /dev/null +++ b/src/module/decode/ltrnet/LTRNetDecoderEditor.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.ltrnet; + +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.ValidatingEditor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import message.MessageDirection; +import module.decode.config.DecodeConfiguration; +import net.miginfocom.swing.MigLayout; +import controller.channel.Channel; + +public class LTRNetDecoderEditor extends ValidatingEditor +{ + private static final long serialVersionUID = 1L; + + private JCheckBox mAFC; + private JSlider mAFCMaximumCorrection; + private JComboBox mComboDirection; + + public LTRNetDecoderEditor() + { + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0,wrap 4", "[right][grow,fill][right][grow,fill]", "" ) ); + + mAFC = new JCheckBox( "AFC: 3000 Hz" ); + mAFC.setEnabled( false ); + mAFC.setToolTipText( "AFC automatically adjusts the center frequency of the channel to " + + "correct/compensate for inaccuracies and frequency drift in the tuner" ); + mAFC.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + setModified( true ); + + if( mAFC.isSelected() && !mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( true ); + } + else if( !mAFC.isSelected() && mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( false ); + } + } + } ); + + add( mAFC ); + + mAFCMaximumCorrection = new JSlider( 0, 7000, 3000 ); + mAFCMaximumCorrection.setEnabled( false ); + mAFCMaximumCorrection.setToolTipText( "Maximum AFC frequency correction (0 - 15kHz)" ); + mAFCMaximumCorrection.setMajorTickSpacing( 2000 ); + mAFCMaximumCorrection.setMinorTickSpacing( 1000 ); + mAFCMaximumCorrection.setPaintTicks( true ); + + mAFCMaximumCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mAFC.setText( "AFC: " + mAFCMaximumCorrection.getValue() + " Hz" ); + setModified( true ); + } + } ); + add( mAFCMaximumCorrection ); + + mComboDirection = new JComboBox(); + mComboDirection.setModel( new DefaultComboBoxModel( MessageDirection.values() ) ); + mComboDirection.setSelectedItem( MessageDirection.OSW ); + mComboDirection.setEnabled( false ); + mComboDirection.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + + add( new JLabel( "Format:" ) ); + add( mComboDirection ); + } + + @Override + public void validate( Editor editor ) throws EditorValidationException + { + //No validation + } + + + @Override + public void setItem( Channel item ) + { + super.setItem( item ); + + if( hasItem() ) + { + mAFC.setEnabled( true ); + mComboDirection.setEnabled( true ); + + DecodeConfiguration config = getItem().getDecodeConfiguration(); + + if( config instanceof DecodeConfigLTRNet ) + { + DecodeConfigLTRNet ltr = (DecodeConfigLTRNet)config; + mComboDirection.setSelectedItem( ltr.getMessageDirection() ); + mAFC.setSelected( ltr.isAFCEnabled() ); + mAFCMaximumCorrection.setValue( ltr.getAFCMaximumCorrection() ); + mAFCMaximumCorrection.setEnabled( ltr.isAFCEnabled() ); + + setModified( false ); + } + else + { + mComboDirection.setSelectedItem( MessageDirection.OSW ); + mAFC.setSelected( false ); + mAFCMaximumCorrection.setValue( DecodeConfiguration.DEFAULT_AFC_MAX_CORRECTION ); + mAFCMaximumCorrection.setEnabled( false ); + + setModified( true ); + } + } + else + { + mAFC.setEnabled( false ); + mComboDirection.setEnabled( false ); + setModified( false ); + } + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + DecodeConfigLTRNet ltr = new DecodeConfigLTRNet(); + + ltr.setAFC( mAFC.isSelected() ); + ltr.setAFCMaximumCorrection( mAFCMaximumCorrection.getValue() ); + ltr.setMessageDirection( (MessageDirection)mComboDirection.getSelectedItem() ); + + getItem().setDecodeConfiguration( ltr ); + } + + setModified( false ); + } +} diff --git a/src/module/decode/ltrnet/LTRNetDecoderState.java b/src/module/decode/ltrnet/LTRNetDecoderState.java index 245f7ace..6f17d77a 100644 --- a/src/module/decode/ltrnet/LTRNetDecoderState.java +++ b/src/module/decode/ltrnet/LTRNetDecoderState.java @@ -21,6 +21,7 @@ import java.text.DecimalFormat; import java.util.HashMap; import java.util.Iterator; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; @@ -777,7 +778,7 @@ public class LTRNetDecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/ltrnet/LTRNetEditor.java b/src/module/decode/ltrnet/LTRNetEditor.java deleted file mode 100644 index 7d7d920e..00000000 --- a/src/module/decode/ltrnet/LTRNetEditor.java +++ /dev/null @@ -1,71 +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 - ******************************************************************************/ -package module.decode.ltrnet; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JLabel; - -import message.MessageDirection; -import module.decode.DecodeEditor; -import module.decode.config.DecodeConfiguration; - -public class LTRNetEditor extends DecodeEditor -{ - private static final long serialVersionUID = 1L; - - private JComboBox mComboDirection; - - public LTRNetEditor( DecodeConfiguration config ) - { - super( config ); - - initGUI(); - } - - private void initGUI() - { - mComboDirection = new JComboBox(); - - mComboDirection.setModel( - new DefaultComboBoxModel( MessageDirection.values() ) ); - - mComboDirection.setSelectedItem( ((DecodeConfigLTRNet)mConfig).getMessageDirection() ); - - mComboDirection.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - MessageDirection selected = mComboDirection - .getItemAt( mComboDirection.getSelectedIndex() ); - - if( selected != null ) - { - ((DecodeConfigLTRNet)mConfig).setMessageDirection( selected ); - } - } - } ); - - add( new JLabel( "Format:" ) ); - add( mComboDirection, "wrap" ); - } -} diff --git a/src/module/decode/ltrstandard/LTRStandardDecoder.java b/src/module/decode/ltrstandard/LTRStandardDecoder.java index a39d659c..06f0a927 100644 --- a/src/module/decode/ltrstandard/LTRStandardDecoder.java +++ b/src/module/decode/ltrstandard/LTRStandardDecoder.java @@ -17,18 +17,20 @@ ******************************************************************************/ package module.decode.ltrstandard; +import java.util.concurrent.ScheduledExecutorService; + import message.MessageDirection; import module.decode.Decoder; import module.decode.DecoderType; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IUnFilteredRealBufferListener; import sample.real.RealBuffer; import alias.AliasList; import bits.MessageFramer; import bits.SyncPattern; import dsp.fsk.LTRFSKDecoder; -public class LTRStandardDecoder extends Decoder implements IRealBufferListener +public class LTRStandardDecoder extends Decoder implements IUnFilteredRealBufferListener { public static final int LTR_STANDARD_MESSAGE_LENGTH = 40; @@ -36,6 +38,10 @@ public class LTRStandardDecoder extends Decoder implements IRealBufferListener private MessageFramer mLTRMessageFramer; private LTRStandardMessageProcessor mLTRMessageProcessor; + /** + * LTR Decoder. Decodes unfiltered (e.g. demodulated but with no DC or + * audio filtering) samples and produces LTR Standard messages. + */ public LTRStandardDecoder( AliasList aliasList, MessageDirection direction ) { @@ -58,7 +64,7 @@ public class LTRStandardDecoder extends Decoder implements IRealBufferListener mLTRMessageProcessor = new LTRStandardMessageProcessor( direction, aliasList ); mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); - mLTRMessageProcessor.setMessageListener( mMessageBroadcaster ); + mLTRMessageProcessor.setMessageListener( this ); } @Override @@ -68,7 +74,7 @@ public class LTRStandardDecoder extends Decoder implements IRealBufferListener } @Override - public Listener getRealBufferListener() + public Listener getUnFilteredRealBufferListener() { return mLTRFSKDecoder; } @@ -80,7 +86,7 @@ public class LTRStandardDecoder extends Decoder implements IRealBufferListener } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/ltrstandard/LTRStandardDecoderEditor.java b/src/module/decode/ltrstandard/LTRStandardDecoderEditor.java new file mode 100644 index 00000000..3e247d4f --- /dev/null +++ b/src/module/decode/ltrstandard/LTRStandardDecoderEditor.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.ltrstandard; + +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.ValidatingEditor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import message.MessageDirection; +import module.decode.config.DecodeConfiguration; +import net.miginfocom.swing.MigLayout; +import controller.channel.Channel; + +public class LTRStandardDecoderEditor extends ValidatingEditor +{ + private static final long serialVersionUID = 1L; + + private JCheckBox mAFC; + private JSlider mAFCMaximumCorrection; + private JComboBox mComboDirection; + + public LTRStandardDecoderEditor() + { + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0,wrap 4", "[right][grow,fill][right][grow,fill]", "" ) ); + + mAFC = new JCheckBox( "AFC: 3000 Hz" ); + mAFC.setEnabled( false ); + mAFC.setToolTipText( "AFC automatically adjusts the center frequency of the channel to " + + "correct/compensate for inaccuracies and frequency drift in the tuner" ); + mAFC.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + setModified( true ); + + if( mAFC.isSelected() && !mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( true ); + } + else if( !mAFC.isSelected() && mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( false ); + } + } + } ); + + add( mAFC ); + + mAFCMaximumCorrection = new JSlider( 0, 7000, 3000 ); + mAFCMaximumCorrection.setEnabled( false ); + mAFCMaximumCorrection.setToolTipText( "Maximum AFC frequency correction (0 - 15kHz)" ); + mAFCMaximumCorrection.setMajorTickSpacing( 2000 ); + mAFCMaximumCorrection.setMinorTickSpacing( 1000 ); + mAFCMaximumCorrection.setPaintTicks( true ); + + mAFCMaximumCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mAFC.setText( "AFC: " + mAFCMaximumCorrection.getValue() + " Hz" ); + setModified( true ); + } + } ); + add( mAFCMaximumCorrection ); + + mComboDirection = new JComboBox(); + mComboDirection.setModel( new DefaultComboBoxModel( MessageDirection.values() ) ); + mComboDirection.setSelectedItem( MessageDirection.OSW ); + mComboDirection.setEnabled( false ); + mComboDirection.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + + add( new JLabel( "Format:" ) ); + add( mComboDirection ); + } + + @Override + public void validate( Editor editor ) throws EditorValidationException + { + } + + @Override + public void setItem( Channel item ) + { + super.setItem( item ); + + if( hasItem() ) + { + mAFC.setEnabled( true ); + mComboDirection.setEnabled( true ); + + DecodeConfiguration config = getItem().getDecodeConfiguration(); + + if( config instanceof DecodeConfigLTRStandard ) + { + DecodeConfigLTRStandard ltr = (DecodeConfigLTRStandard)config; + mComboDirection.setSelectedItem( ltr.getMessageDirection() ); + mAFC.setSelected( ltr.isAFCEnabled() ); + mAFCMaximumCorrection.setValue( ltr.getAFCMaximumCorrection() ); + mAFCMaximumCorrection.setEnabled( ltr.isAFCEnabled() ); + setModified( false ); + } + else + { + mComboDirection.setSelectedItem( MessageDirection.OSW ); + mAFC.setSelected( false ); + mAFCMaximumCorrection.setValue( DecodeConfiguration.DEFAULT_AFC_MAX_CORRECTION ); + mAFCMaximumCorrection.setEnabled( false ); + + setModified( true ); + } + } + else + { + mAFC.setEnabled( false ); + mComboDirection.setEnabled( false ); + setModified( false ); + } + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + DecodeConfigLTRStandard ltr = new DecodeConfigLTRStandard(); + + ltr.setAFC( mAFC.isSelected() ); + ltr.setAFCMaximumCorrection( mAFCMaximumCorrection.getValue() ); + ltr.setMessageDirection( (MessageDirection)mComboDirection.getSelectedItem() ); + + getItem().setDecodeConfiguration( ltr ); + } + + setModified( false ); + } +} diff --git a/src/module/decode/ltrstandard/LTRStandardDecoderState.java b/src/module/decode/ltrstandard/LTRStandardDecoderState.java index 6dacaf17..e4af71e8 100644 --- a/src/module/decode/ltrstandard/LTRStandardDecoderState.java +++ b/src/module/decode/ltrstandard/LTRStandardDecoderState.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; @@ -73,8 +74,10 @@ public class LTRStandardDecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { + // TODO Auto-generated method stub + } @Override @@ -490,4 +493,4 @@ public class LTRStandardDecoderState extends DecoderState return active; } } -} +} \ No newline at end of file diff --git a/src/module/decode/ltrstandard/LTRStandardEditor.java b/src/module/decode/ltrstandard/LTRStandardEditor.java deleted file mode 100644 index 35f0e107..00000000 --- a/src/module/decode/ltrstandard/LTRStandardEditor.java +++ /dev/null @@ -1,71 +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 - ******************************************************************************/ -package module.decode.ltrstandard; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JLabel; - -import message.MessageDirection; -import module.decode.DecodeEditor; -import module.decode.config.DecodeConfiguration; - -public class LTRStandardEditor extends DecodeEditor -{ - private static final long serialVersionUID = 1L; - - private JComboBox mComboDirection; - - public LTRStandardEditor( DecodeConfiguration config ) - { - super( config ); - - initGUI(); - } - - private void initGUI() - { - mComboDirection = new JComboBox(); - - mComboDirection.setModel( - new DefaultComboBoxModel( MessageDirection.values() ) ); - - mComboDirection.setSelectedItem( ((DecodeConfigLTRStandard)mConfig).getMessageDirection() ); - - mComboDirection.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - MessageDirection selected = mComboDirection - .getItemAt( mComboDirection.getSelectedIndex() ); - - if( selected != null ) - { - ((DecodeConfigLTRStandard)mConfig).setMessageDirection( selected ); - } - } - } ); - - add( new JLabel( "Format:" ) ); - add( mComboDirection, "wrap" ); - } -} diff --git a/src/module/decode/ltrstandard/LTRStandardMessageProcessor.java b/src/module/decode/ltrstandard/LTRStandardMessageProcessor.java index ad1337a2..cb5c5c8e 100644 --- a/src/module/decode/ltrstandard/LTRStandardMessageProcessor.java +++ b/src/module/decode/ltrstandard/LTRStandardMessageProcessor.java @@ -118,4 +118,4 @@ public class LTRStandardMessageProcessor implements Listener { mMessageListener = null; } -} +} \ No newline at end of file diff --git a/src/module/decode/ltrstandard/message/CallEndMessage.java b/src/module/decode/ltrstandard/message/CallEndMessage.java deleted file mode 100644 index e5023139..00000000 --- a/src/module/decode/ltrstandard/message/CallEndMessage.java +++ /dev/null @@ -1,67 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package module.decode.ltrstandard.message; - -import message.MessageDirection; -import message.MessageType; -import alias.Alias; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRC; - -public class CallEndMessage extends LTRStandardMessage -{ - public CallEndMessage( BinaryMessage message, MessageDirection direction, - AliasList list, CRC crc ) - { - super( message, direction, list, crc ); - } - - @Override - public MessageType getMessageType() - { - return MessageType.CA_ENDD; - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "END* AREA:" ); - sb.append( getArea() ); - sb.append( " LCN:" ); - sb.append( getChannelFormatted() ); - sb.append( " TG [" ); - sb.append( getToID() ); - sb.append( "/" ); - Alias endAlias = getToIDAlias(); - if( endAlias != null ) - { - sb.append( getToIDAlias().getName() ); - } - else - { - sb.append( "UNKNOWN" ); - } - sb.append( "] FREE:" ); - sb.append( getFreeFormatted() ); - - return sb.toString(); - } -} diff --git a/src/module/decode/ltrstandard/message/CallMessage.java b/src/module/decode/ltrstandard/message/CallMessage.java deleted file mode 100644 index f7d44ca6..00000000 --- a/src/module/decode/ltrstandard/message/CallMessage.java +++ /dev/null @@ -1,68 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package module.decode.ltrstandard.message; - -import message.MessageDirection; -import message.MessageType; -import alias.Alias; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRC; - -public class CallMessage extends LTRStandardMessage -{ - public CallMessage( BinaryMessage message, MessageDirection direction, - AliasList list, CRC crc ) - { - super( message, direction, list, crc ); - } - - @Override - public MessageType getMessageType() - { - return MessageType.CA_STRT; - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "CALL LCN:" ); - sb.append( getChannelFormatted() ); - sb.append( " TG [ " ); - sb.append( getToID() ); - sb.append( "/" ); - - Alias alias = getToIDAlias(); - - if( alias != null ) - { - sb.append( getToIDAlias().getName() ); - } - else - { - sb.append( "UNKNOWN" ); - } - - sb.append( " ] FREE:" ); - sb.append( getFreeFormatted() ); - - return sb.toString(); - } -} diff --git a/src/module/decode/ltrstandard/message/IdleMessage.java b/src/module/decode/ltrstandard/message/IdleMessage.java deleted file mode 100644 index ba8e4d97..00000000 --- a/src/module/decode/ltrstandard/message/IdleMessage.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package module.decode.ltrstandard.message; - -import message.MessageDirection; -import message.MessageType; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRC; - -public class IdleMessage extends LTRStandardMessage -{ - public IdleMessage( BinaryMessage message, MessageDirection direction, - AliasList list, CRC crc ) - { - super( message, direction, list, crc ); - } - - @Override - public MessageType getMessageType() - { - return MessageType.SY_IDLE; - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "IDLE AREA:" ); - sb.append( getArea() ); - sb.append( " LCN:" ); - sb.append( getChannelFormatted() ); - sb.append( " HOME:" ); - sb.append( getHomeRepeaterFormatted() ); - sb.append( " GRP:" ); - sb.append( getGroupFormatted() ); - sb.append( " FREE:" ); - sb.append( getFreeFormatted() ); - - return sb.toString(); - } -} diff --git a/src/module/decode/ltrstandard/message/LTRStandardMessage.java b/src/module/decode/ltrstandard/message/LTRStandardMessage.java deleted file mode 100644 index 87f381b2..00000000 --- a/src/module/decode/ltrstandard/message/LTRStandardMessage.java +++ /dev/null @@ -1,213 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package module.decode.ltrstandard.message; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import message.Message; -import message.MessageDirection; -import message.MessageType; -import module.decode.DecoderType; - -import org.apache.commons.lang3.StringUtils; - -import alias.Alias; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRC; - -public abstract class LTRStandardMessage extends Message -{ - protected SimpleDateFormat mDatestampFormatter = - new SimpleDateFormat( "yyyyMMdd HHmmss" ); - - public static final int[] SYNC = { 0,1,2,3,4,5,6,7,8 }; - public static final int[] AREA = { 9 }; - public static final int[] CHANNEL = { 10,11,12,13,14 }; - public static final int[] HOME_REPEATER = { 15,16,17,18,19 }; - public static final int[] GROUP = { 20,21,22,23,24,25,26,27 }; - public static final int[] FREE = { 28,29,30,31,32 }; - public static final int[] CHECKSUM = { 33,34,35,36,37,38,39 }; - - protected BinaryMessage mMessage; - protected MessageDirection mMessageDirection; - protected AliasList mAliasList; - protected CRC mCRC; - - public LTRStandardMessage( BinaryMessage message, - MessageDirection direction, - AliasList list, - CRC crc ) - { - mMessage = message; - mMessageDirection = direction; - mAliasList = list; - mCRC = crc; - } - - public abstract MessageType getMessageType(); - - public boolean isValid() - { - return mCRC != CRC.FAILED_CRC && mCRC != CRC.FAILED_PARITY; - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(); - sb.append( mDatestampFormatter.format( - new Date( System.currentTimeMillis() ) ) ); - sb.append( " LTR " ); - sb.append( mMessageDirection.name() ); - sb.append( " [" ); - sb.append( mCRC.getAbbreviation() ); - sb.append( "] " ); - sb.append( getMessage() ); - - return sb.toString(); - } - - @Override - public String getBinaryMessage() - { - return mMessage.toString(); - } - - public CRC getCRC() - { - return mCRC; - } - - public int getArea() - { - return mMessage.getInt( AREA ); - } - - public int getChannel() - { - return mMessage.getInt( CHANNEL ); - } - - public String getChannelFormatted() - { - return StringUtils.leftPad( String.valueOf( getChannel() ), 2, "0" ); - } - - public int getHomeRepeater() - { - return mMessage.getInt( HOME_REPEATER ); - } - - public String getHomeRepeaterFormatted() - { - return StringUtils.leftPad( String.valueOf( getHomeRepeater() ), 2, "0" ); - } - - public int getGroup() - { - return mMessage.getInt( GROUP ); - } - - public String getGroupFormatted() - { - return StringUtils.leftPad( String.valueOf( getGroup() ), 3, "0" ); - } - - public int getFree() - { - return mMessage.getInt( FREE ); - } - - public String getFreeFormatted() - { - return StringUtils.leftPad( String.valueOf( getFree() ), 2, "0" ); - } - - @Override - public String getFromID() - { - return null; - } - - @Override - public Alias getFromIDAlias() - { - return null; - } - - @Override - public String getToID() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getArea() ); - sb.append( "-" ); - sb.append( getHomeRepeaterFormatted() ); - sb.append( "-" ); - sb.append( getGroupFormatted() ); - - return sb.toString(); - } - - @Override - public Alias getToIDAlias() - { - return mAliasList.getTalkgroupAlias( getToID() ); - } - - public List getAliases() - { - List aliases = new ArrayList<>(); - - Alias talkgroupAlias = getToIDAlias(); - - if( talkgroupAlias != null ) - { - aliases.add( talkgroupAlias ); - } - - return aliases; - } - - public int getCRCChecksum() - { - return mMessage.getInt( CHECKSUM ); - } - - @Override - public String getProtocol() - { - return DecoderType.LTR_STANDARD.getDisplayString(); - } - - @Override - public String getEventType() - { - return getMessageType().getDisplayText(); - } - - @Override - public String getErrorStatus() - { - return mCRC.getDisplayText(); - } -} diff --git a/src/module/decode/ltrstandard/message/UnknownMessage.java b/src/module/decode/ltrstandard/message/UnknownMessage.java deleted file mode 100644 index b975c667..00000000 --- a/src/module/decode/ltrstandard/message/UnknownMessage.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package module.decode.ltrstandard.message; - -import message.MessageDirection; -import message.MessageType; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRC; - -public class UnknownMessage extends LTRStandardMessage -{ - public UnknownMessage( BinaryMessage message, MessageDirection direction, - AliasList list, CRC crc ) - { - super( message, direction, list, crc ); - } - - @Override - public MessageType getMessageType() - { - return MessageType.UN_KNWN; - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "UNKNOWN AREA:" ); - sb.append( getArea() ); - sb.append( " LCN:" ); - sb.append( getChannelFormatted() ); - sb.append( " HOME:" ); - sb.append( getHomeRepeaterFormatted() ); - sb.append( " GRP:" ); - sb.append( getGroupFormatted() ); - sb.append( " FREE:" ); - sb.append( getFreeFormatted() ); - - return sb.toString(); - } -} diff --git a/src/module/decode/mdc1200/MDCDecoder.java b/src/module/decode/mdc1200/MDCDecoder.java index 86289146..89f04afc 100644 --- a/src/module/decode/mdc1200/MDCDecoder.java +++ b/src/module/decode/mdc1200/MDCDecoder.java @@ -26,11 +26,12 @@ import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import module.decode.Decoder; import module.decode.DecoderType; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import alias.AliasList; import bits.MessageFramer; @@ -46,7 +47,7 @@ import dsp.fsk.FSK2Decoder.Output; * MDC1200 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate * complex or floating point samples and output fully framed MDC1200 messages */ -public class MDCDecoder extends Decoder implements IRealBufferListener, +public class MDCDecoder extends Decoder implements IFilteredRealBufferListener, Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ @@ -102,7 +103,7 @@ public class MDCDecoder extends Decoder implements IRealBufferListener, /* Message Processor */ mMessageProcessor = new MDCMessageProcessor( aliasList ); mMessageFramer.addMessageListener( mMessageProcessor ); - mMessageProcessor.addMessageListener( mMessageBroadcaster ); + mMessageProcessor.addMessageListener( this ); } public void dispose() @@ -197,7 +198,7 @@ public class MDCDecoder extends Decoder implements IRealBufferListener, } @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { return mDecimationFilter; } @@ -210,7 +211,7 @@ public class MDCDecoder extends Decoder implements IRealBufferListener, } @Override - public void start() + public void start( ScheduledExecutorService executor ) { // TODO Auto-generated method stub diff --git a/src/module/decode/mdc1200/MDCDecoderState.java b/src/module/decode/mdc1200/MDCDecoderState.java index f896a429..0a9d3a7c 100644 --- a/src/module/decode/mdc1200/MDCDecoderState.java +++ b/src/module/decode/mdc1200/MDCDecoderState.java @@ -19,6 +19,7 @@ package module.decode.mdc1200; import java.util.Iterator; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; @@ -54,7 +55,7 @@ public class MDCDecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/mpt1327/MPT1327ConfigEditor.java b/src/module/decode/mpt1327/MPT1327ConfigEditor.java deleted file mode 100644 index 01c2af35..00000000 --- a/src/module/decode/mpt1327/MPT1327ConfigEditor.java +++ /dev/null @@ -1,202 +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 - ******************************************************************************/ -package module.decode.mpt1327; - -import java.util.ArrayList; -import java.util.List; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JSlider; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import module.decode.DecodeEditor; -import module.decode.config.DecodeConfiguration; -import module.decode.mpt1327.MPT1327Decoder.Sync; -import playlist.PlaylistManager; -import controller.channel.ChannelNode; -import controller.channel.map.ChannelMap; - -public class MPT1327ConfigEditor extends DecodeEditor -{ - private static final long serialVersionUID = 1L; - - private JComboBox mComboChannelMaps; - private JComboBox mComboSync; - private JLabel mCallTimeoutLabel; - private JSlider mCallTimeout; - private JLabel mTrafficChannelPoolSizeLabel; - private JSlider mTrafficChannelPoolSize; - private ChannelNode mChannelNode; - private PlaylistManager mPlaylistManager; - - public MPT1327ConfigEditor( DecodeConfiguration config, - ChannelNode channelNode, - PlaylistManager playlistManager ) - { - super( config ); - - mChannelNode = channelNode; - mPlaylistManager = playlistManager; - - initGUI(); - } - - private DecodeConfigMPT1327 getMPTConfig() - { - return (DecodeConfigMPT1327)mConfig; - } - - private void initGUI() - { - /** - * ComboBox: Alias Lists - */ - List maps = ( mPlaylistManager == null ? - new ArrayList() : mPlaylistManager.getPlayist() - .getChannelMapList().getChannelMap() ); - - ChannelMap[] mapArray = maps.toArray( new ChannelMap[ maps.size() ] ); - - mComboChannelMaps = new JComboBox(); - mComboChannelMaps.setModel( - new DefaultComboBoxModel( mapArray ) ); - - add( new JLabel( "Channel Map:" ) ); - add( mComboChannelMaps, "wrap" ); - - mComboSync = new JComboBox(); - mComboSync.setModel( new DefaultComboBoxModel( Sync.values() ) ); - - add( new JLabel( "Sync:" ) ); - add( mComboSync, "wrap" ); - - mCallTimeout = new JSlider( JSlider.HORIZONTAL, - DecodeConfiguration.CALL_TIMEOUT_MINIMUM, - DecodeConfiguration.CALL_TIMEOUT_MAXIMUM, - DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS ); - - mCallTimeout.setMajorTickSpacing( 90 ); - mCallTimeout.setMinorTickSpacing( 10 ); - mCallTimeout.setPaintTicks( true ); - - mCallTimeout.setLabelTable( mCallTimeout.createStandardLabels( 100, 100 ) ); - mCallTimeout.setPaintLabels( true ); - - mCallTimeoutLabel = new JLabel( "Call Timeout: " + mCallTimeout.getValue() + " " ); - - mCallTimeout.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - mCallTimeoutLabel.setText( "Call Timeout: " + mCallTimeout.getValue() ); - } - } ); - - add( mCallTimeoutLabel ); - add( mCallTimeout, "wrap,grow" ); - - mTrafficChannelPoolSize = new JSlider( JSlider.HORIZONTAL, - DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MINIMUM, - DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MAXIMUM, - DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT ); - - mTrafficChannelPoolSize.setMajorTickSpacing( 10 ); - mTrafficChannelPoolSize.setMinorTickSpacing( 1 ); - mTrafficChannelPoolSize.setPaintTicks( true ); - - mTrafficChannelPoolSize.setLabelTable( mTrafficChannelPoolSize.createStandardLabels( 10, 10 ) ); - mTrafficChannelPoolSize.setPaintLabels( true ); - - mTrafficChannelPoolSizeLabel = new JLabel( "Traffic Channel Pool: " + mTrafficChannelPoolSize.getValue() + " " ); - - mTrafficChannelPoolSize.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - mTrafficChannelPoolSizeLabel.setText( "Traffic Channel Pool: " + mTrafficChannelPoolSize.getValue() ); - } - } ); - - add( mTrafficChannelPoolSizeLabel ); - add( mTrafficChannelPoolSize, "wrap,grow" ); - - reset(); - } - - @Override - public void save() - { - DecodeConfigMPT1327 config = getMPTConfig(); - - ChannelMap selected = (ChannelMap)mComboChannelMaps.getSelectedItem(); - - if( selected != null ) - { - config.setChannelMapName( selected.getName() ); - } - - Sync selectedSync = (Sync)mComboSync.getSelectedItem(); - - if( selectedSync != null ) - { - config.setSync( selectedSync ); - } - - config.setCallTimeout( mCallTimeout.getValue() ); - - config.setTrafficChannelPoolSize( mTrafficChannelPoolSize.getValue() ); - } - - @Override - public void reset() - { - DecodeConfigMPT1327 config = (DecodeConfigMPT1327)mConfig; - - String name = config.getChannelMapName(); - - ChannelMap selected = null; - - if( name != null ) - { - for( ChannelMap map: mChannelNode.getModel().getResourceManager() - .getPlaylistManager().getPlayist().getChannelMapList() - .getChannelMap() ) - { - if( map.getName().equalsIgnoreCase( name ) ) - { - selected = map; - } - } - } - - mComboChannelMaps.setSelectedItem( selected ); - mComboChannelMaps.requestFocus(); - mComboChannelMaps.requestFocusInWindow(); - - mComboSync.setSelectedItem( config.getSync() ); - - mCallTimeout.setValue( config.getCallTimeout() ); - - mTrafficChannelPoolSize.setValue( config.getTrafficChannelPoolSize() ); - } -} diff --git a/src/module/decode/mpt1327/MPT1327Decoder.java b/src/module/decode/mpt1327/MPT1327Decoder.java index 679da857..cb495924 100644 --- a/src/module/decode/mpt1327/MPT1327Decoder.java +++ b/src/module/decode/mpt1327/MPT1327Decoder.java @@ -26,6 +26,7 @@ import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import module.decode.Decoder; import module.decode.DecoderType; @@ -35,7 +36,7 @@ import org.slf4j.LoggerFactory; import sample.Broadcaster; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import alias.AliasList; import bits.MessageFramer; @@ -55,7 +56,7 @@ import dsp.fsk.FSK2Decoder.Output; * traffic messages. */ public class MPT1327Decoder extends Decoder - implements IRealBufferListener, Instrumentable + implements IFilteredRealBufferListener, Instrumentable { private final static Logger mLog = LoggerFactory.getLogger( MPT1327Decoder.class ); @@ -140,13 +141,13 @@ public class MPT1327Decoder extends Decoder /* Fully decoded and framed messages processor */ mMessageProcessor = new MPT1327MessageProcessor( aliasList ); - mMessageProcessor.setMessageListener( mMessageBroadcaster ); + mMessageProcessor.setMessageListener( this ); mControlMessageFramer.addMessageListener( mMessageProcessor ); mTrafficMessageFramer.addMessageListener( mMessageProcessor ); } @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { return mDecimationFilter; } @@ -306,7 +307,7 @@ public class MPT1327Decoder extends Decoder } @Override - public void start() + public void start( ScheduledExecutorService executor ) { // TODO Auto-generated method stub diff --git a/src/module/decode/mpt1327/MPT1327DecoderEditor.java b/src/module/decode/mpt1327/MPT1327DecoderEditor.java new file mode 100644 index 00000000..c4af1b59 --- /dev/null +++ b/src/module/decode/mpt1327/MPT1327DecoderEditor.java @@ -0,0 +1,285 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.mpt1327; + +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.ValidatingEditor; + +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +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.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import module.decode.config.DecodeConfiguration; +import net.miginfocom.swing.MigLayout; +import controller.channel.Channel; +import controller.channel.map.ChannelMap; +import controller.channel.map.ChannelMapManagerFrame; +import controller.channel.map.ChannelMapModel; + +public class MPT1327DecoderEditor extends ValidatingEditor +{ + private static final long serialVersionUID = 1L; + + private JCheckBox mAFC; + private JSlider mAFCMaximumCorrection; + private JComboBox mComboChannelMaps; + private JLabel mCallTimeoutLabel; + private JSlider mCallTimeout; + private JLabel mTrafficChannelPoolSizeLabel; + private JSlider mTrafficChannelPoolSize; + private ChannelMapModel mChannelMapModel; + + public MPT1327DecoderEditor( ChannelMapModel channelMapModel ) + { + mChannelMapModel = channelMapModel; + + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0,wrap 4", "[right][grow,fill][right][grow,fill]", "" ) ); + + mAFC = new JCheckBox( "AFC: 3000 Hz" ); + mAFC.setEnabled( false ); + mAFC.setToolTipText( "AFC automatically adjusts the center frequency of the channel to " + + "correct/compensate for inaccuracies and frequency drift in the tuner" ); + mAFC.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + setModified( true ); + + if( mAFC.isSelected() && !mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( true ); + } + else if( !mAFC.isSelected() && mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( false ); + } + } + } ); + + add( mAFC ); + + mAFCMaximumCorrection = new JSlider( 0, 7000, 3000 ); + mAFCMaximumCorrection.setEnabled( false ); + mAFCMaximumCorrection.setToolTipText( "Maximum AFC frequency correction (0 - 15kHz)" ); + mAFCMaximumCorrection.setMajorTickSpacing( 2000 ); + mAFCMaximumCorrection.setMinorTickSpacing( 1000 ); + mAFCMaximumCorrection.setPaintTicks( true ); + + mAFCMaximumCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mAFC.setText( "AFC: " + mAFCMaximumCorrection.getValue() + " Hz" ); + setModified( true ); + } + } ); + add( mAFCMaximumCorrection ); + + JButton channelMaps = new JButton( "Channel Maps" ); + channelMaps.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + final ChannelMapManagerFrame manager = + new ChannelMapManagerFrame( mChannelMapModel ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + manager.setVisible( true ); + } + } ); + } + } ); + add( channelMaps, "span 2" ); + + List maps = mChannelMapModel.getChannelMaps(); + ChannelMap[] mapArray = maps.toArray( new ChannelMap[ maps.size() ] ); + + mComboChannelMaps = new JComboBox(); + mComboChannelMaps.setModel( new DefaultComboBoxModel( mapArray ) ); + mComboChannelMaps.setEnabled( false ); + add( new JLabel( "Channel Map:" ) ); + add( mComboChannelMaps, "span 3,grow" ); + + mTrafficChannelPoolSize = new JSlider( JSlider.HORIZONTAL, DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MINIMUM, + DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MAXIMUM, DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT ); + mTrafficChannelPoolSize.setEnabled( false ); + mTrafficChannelPoolSize.setMajorTickSpacing( 10 ); + mTrafficChannelPoolSize.setMinorTickSpacing( 5 ); + mTrafficChannelPoolSize.setPaintTicks( true ); + mTrafficChannelPoolSizeLabel = new JLabel( "Traffic Pool: " + + mTrafficChannelPoolSize.getValue() + " " ); + + mTrafficChannelPoolSize.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mTrafficChannelPoolSizeLabel.setText( "Traffic Pool: " + + mTrafficChannelPoolSize.getValue() ); + } + } ); + + add( mTrafficChannelPoolSizeLabel ); + add( mTrafficChannelPoolSize ); + + mCallTimeout = new JSlider( JSlider.HORIZONTAL, DecodeConfiguration.CALL_TIMEOUT_MINIMUM, + DecodeConfiguration.CALL_TIMEOUT_MAXIMUM, DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS ); + mCallTimeout.setEnabled( false ); + mCallTimeout.setMajorTickSpacing( 100 ); + mCallTimeout.setMinorTickSpacing( 50 ); + mCallTimeout.setPaintTicks( true ); + + mCallTimeoutLabel = new JLabel( "Timeout: " + mCallTimeout.getValue() + " " ); + + mCallTimeout.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mCallTimeoutLabel.setText( "Timeout: " + mCallTimeout.getValue() ); + } + } ); + + add( mCallTimeoutLabel ); + add( mCallTimeout ); + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + DecodeConfigMPT1327 mpt = new DecodeConfigMPT1327(); + mpt.setAFC( mAFC.isSelected() ); + mpt.setAFCMaximumCorrection( mAFCMaximumCorrection.getValue() ); + mpt.setTrafficChannelPoolSize( mTrafficChannelPoolSize.getValue() ); + mpt.setCallTimeout( mCallTimeout.getValue() ); + + if( mComboChannelMaps.getSelectedItem() != null ) + { + ChannelMap map = (ChannelMap)mComboChannelMaps.getSelectedItem(); + + mpt.setChannelMapName( map.getName() ); + } + + getItem().setDecodeConfiguration( mpt ); + } + + setModified( false ); + } + + @Override + public void validate( Editor editor ) throws EditorValidationException + { + //No validation required + } + + private void setControlsEnabled( boolean enabled ) + { + if( mAFC.isEnabled() != enabled ) + { + mAFC.setEnabled( enabled ); + } + + if( mComboChannelMaps.isEnabled() != enabled ) + { + mComboChannelMaps.setEnabled( enabled ); + } + + if( mTrafficChannelPoolSize.isEnabled() != enabled ) + { + mTrafficChannelPoolSize.setEnabled( enabled ); + } + + if( mCallTimeout.isEnabled() != enabled ) + { + mCallTimeout.setEnabled( enabled ); + } + } + + @Override + public void setItem( Channel item ) + { + super.setItem( item ); + + if( hasItem() ) + { + setControlsEnabled( true ); + + DecodeConfiguration config = getItem().getDecodeConfiguration(); + + if( config instanceof DecodeConfigMPT1327 ) + { + DecodeConfigMPT1327 mpt = (DecodeConfigMPT1327)config; + mAFC.setSelected( mpt.isAFCEnabled() ); + mAFCMaximumCorrection.setValue( mpt.getAFCMaximumCorrection() ); + mAFCMaximumCorrection.setEnabled( mpt.isAFCEnabled() ); + + ChannelMap map = mChannelMapModel.getChannelMap( mpt.getChannelMapName() ); + + if( map != null ) + { + mComboChannelMaps.setSelectedItem( map ); + } + + mTrafficChannelPoolSize.setValue( mpt.getTrafficChannelPoolSize() ); + mCallTimeout.setValue( mpt.getCallTimeout() ); + + setModified( false ); + } + else + { + mAFC.setSelected( false ); + mAFCMaximumCorrection.setValue( DecodeConfiguration.DEFAULT_AFC_MAX_CORRECTION ); + mAFCMaximumCorrection.setEnabled( false ); + mTrafficChannelPoolSize.setValue( DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT ); + mCallTimeout.setValue( DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS ); + + setModified( true ); + } + } + else + { + setControlsEnabled( false ); + setModified( false ); + } + } +} diff --git a/src/module/decode/mpt1327/MPT1327DecoderState.java b/src/module/decode/mpt1327/MPT1327DecoderState.java index cc22c847..20858552 100644 --- a/src/module/decode/mpt1327/MPT1327DecoderState.java +++ b/src/module/decode/mpt1327/MPT1327DecoderState.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; @@ -85,7 +86,7 @@ public class MPT1327DecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } @@ -612,4 +613,4 @@ public class MPT1327DecoderState extends DecoderState break; } } -} +} \ No newline at end of file diff --git a/src/module/decode/nbfm/NBFMDecoder.java b/src/module/decode/nbfm/NBFMDecoder.java index 1e715288..1b055a91 100644 --- a/src/module/decode/nbfm/NBFMDecoder.java +++ b/src/module/decode/nbfm/NBFMDecoder.java @@ -17,6 +17,8 @@ ******************************************************************************/ package module.decode.nbfm; +import java.util.concurrent.ScheduledExecutorService; + import module.decode.DecoderType; import module.decode.PrimaryDecoder; import module.decode.config.DecodeConfiguration; @@ -40,7 +42,7 @@ public class NBFMDecoder extends PrimaryDecoder } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/nbfm/NBFMDecoderEditor.java b/src/module/decode/nbfm/NBFMDecoderEditor.java new file mode 100644 index 00000000..9d6d025c --- /dev/null +++ b/src/module/decode/nbfm/NBFMDecoderEditor.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.nbfm; + +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.ValidatingEditor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JCheckBox; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import module.decode.config.DecodeConfiguration; +import net.miginfocom.swing.MigLayout; +import controller.channel.Channel; + +public class NBFMDecoderEditor extends ValidatingEditor +{ + private static final long serialVersionUID = 1L; + + private JCheckBox mAFC; + private JSlider mAFCMaximumCorrection; + + public NBFMDecoderEditor() + { + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0,wrap 2", "[right][grow,fill]", "" ) ); + + mAFC = new JCheckBox( "AFC: 3000 Hz" ); + mAFC.setEnabled( false ); + mAFC.setToolTipText( "AFC automatically adjusts the center frequency of the channel to " + + "correct/compensate for inaccuracies and frequency drift in the tuner" ); + mAFC.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + setModified( true ); + + if( mAFC.isSelected() && !mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( true ); + } + else if( !mAFC.isSelected() && mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( false ); + } + } + } ); + + add( mAFC ); + + mAFCMaximumCorrection = new JSlider( 0, 7000, 3000 ); + mAFCMaximumCorrection.setEnabled( false ); + mAFCMaximumCorrection.setToolTipText( "Maximum AFC frequency correction (0 - 15kHz)" ); + mAFCMaximumCorrection.setMajorTickSpacing( 2000 ); + mAFCMaximumCorrection.setMinorTickSpacing( 1000 ); + mAFCMaximumCorrection.setPaintTicks( true ); + + mAFCMaximumCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mAFC.setText( "AFC: " + mAFCMaximumCorrection.getValue() + " Hz" ); + setModified( true ); + } + } ); + add( mAFCMaximumCorrection ); + } + + @Override + public void validate( Editor editor ) throws EditorValidationException + { + //No validation + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + DecodeConfigNBFM nbfm = new DecodeConfigNBFM(); + + nbfm.setAFC( mAFC.isSelected() ); + nbfm.setAFCMaximumCorrection( mAFCMaximumCorrection.getValue() ); + + getItem().setDecodeConfiguration( nbfm ); + } + + setModified( false ); + } + + private void setControlsEnabled( boolean enabled ) + { + if( mAFC.isEnabled() != enabled ) + { + mAFC.setEnabled( enabled ); + } + } + + @Override + public void setItem( Channel item ) + { + super.setItem( item ); + + if( hasItem() ) + { + setControlsEnabled( true ); + + DecodeConfiguration config = getItem().getDecodeConfiguration(); + + if( config instanceof DecodeConfigNBFM ) + { + DecodeConfigNBFM nbfm = (DecodeConfigNBFM)config; + + mAFC.setSelected( nbfm.isAFCEnabled() ); + mAFCMaximumCorrection.setValue( nbfm.getAFCMaximumCorrection() ); + mAFCMaximumCorrection.setEnabled( nbfm.isAFCEnabled() ); + + setModified( false ); + } + else + { + mAFC.setSelected( false ); + mAFCMaximumCorrection.setValue( DecodeConfiguration.DEFAULT_AFC_MAX_CORRECTION ); + mAFCMaximumCorrection.setEnabled( false ); + + setModified( true ); + } + } + else + { + setControlsEnabled( false ); + setModified( false ); + } + } +} diff --git a/src/module/decode/nbfm/NBFMEditor.java b/src/module/decode/nbfm/NBFMEditor.java deleted file mode 100644 index d80e099a..00000000 --- a/src/module/decode/nbfm/NBFMEditor.java +++ /dev/null @@ -1,35 +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 - ******************************************************************************/ -package module.decode.nbfm; - -import javax.swing.JCheckBox; - -import module.decode.DecodeEditor; -import module.decode.config.DecodeConfiguration; - -public class NBFMEditor extends DecodeEditor -{ - private static final long serialVersionUID = 1L; - - private JCheckBox mAFC; - - public NBFMEditor( DecodeConfiguration config ) - { - super( config ); - } -} diff --git a/src/module/decode/p25/C4FMSymbolFilter.java b/src/module/decode/p25/C4FMSymbolFilter.java index 0c88b18a..9b91c6e0 100644 --- a/src/module/decode/p25/C4FMSymbolFilter.java +++ b/src/module/decode/p25/C4FMSymbolFilter.java @@ -9,25 +9,20 @@ import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sample.Listener; import sample.real.RealBuffer; import sample.real.RealSampleListener; import sample.real.RealSampleProvider; -import source.tuner.frequency.FrequencyCorrectionControl; -import source.tuner.frequency.FrequencyCorrectionControl.FrequencyCorrectionResetListener; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeEvent.Event; +import source.tuner.frequency.IFrequencyChangeListener; import dsp.gain.DirectGainControl; -public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, - Listener, +public class C4FMSymbolFilter implements Listener, + IFrequencyChangeListener, RealSampleProvider, Instrumentable { - private final static Logger mLog = - LoggerFactory.getLogger( C4FMSymbolFilter.class ); - private static final float TAPS[][] = { { 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f, 1.00000e+00f, 0.00000e+00f, 0.00000e+00f, 0.00000e+00f }, // 0/128 @@ -201,15 +196,17 @@ public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, private float mHistory[] = new float[ NUMBER_FILTER_TAPS ]; private int mHistoryLast = 0; - private boolean mResetFrequencyTracker = false; - private RealSampleListener mListener; private DirectGainControl mGainController = new DirectGainControl( 15.0f, 0.1f, 35.0f, 0.3f ); - private FrequencyCorrectionControl mFrequencyCorrectionControl; private int mFrequencyAdjustmentRequested = 0; + private int mFrequencyCorrection = 0; + private int mFrequencyCorrectionMaximum = 3000; + private boolean mResetFrequencyTracker = false; + private FrequencyCorrectionProcessor mFrequencyCorrectionProcessor; + private Listener mFrequencyChangeListener; /** * C4FM Symbol Filter @@ -224,18 +221,15 @@ public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, * setting for this filter is 15.4 and that yields a symbol spread that * is centered on 2.0, ranging 1.92 to 2.08. */ - public C4FMSymbolFilter( FrequencyCorrectionControl frequencyControl ) + public C4FMSymbolFilter( int frequencyCorrectionMaximum ) { - mFrequencyCorrectionControl = frequencyControl; - - /* Register for reset events, so we can reset internal frequency tracking */ - mFrequencyCorrectionControl.setListener( this ); + mFrequencyCorrectionMaximum = frequencyCorrectionMaximum; } public void dispose() { mGainController = null; - mFrequencyCorrectionControl = null; +// mFrequencyCorrectionControl = null; mListener = null; } @@ -253,13 +247,21 @@ public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, * the internal frequency correction tracker */ if( mFrequencyAdjustmentRequested != 0 ) { - mFrequencyCorrectionControl.adjust( mFrequencyAdjustmentRequested ); + int correction = mFrequencyCorrection + mFrequencyAdjustmentRequested; + + if( correction > mFrequencyCorrectionMaximum ) + { + correction = mFrequencyCorrectionMaximum; + } + else if( correction < -mFrequencyCorrectionMaximum ) + { + correction = -mFrequencyCorrectionMaximum; + } + + broadcast( new FrequencyChangeEvent( + Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, correction ) ); mFrequencyAdjustmentRequested = 0; - - //Reset internal frequency tracking - mCoarseFrequencyCorrection = 0.0f; - mFineFrequencyCorrection = 0.0f; } } @@ -272,7 +274,8 @@ public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, mCoarseFrequencyCorrection = 0.0f; mFineFrequencyCorrection = 0.0f; - mFrequencyCorrectionControl.setFrequencyCorrection( 0 ); + broadcast( new FrequencyChangeEvent( + Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, 0 )); mResetFrequencyTracker = false; } @@ -397,17 +400,6 @@ public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, } } - /** - * Listener interface for frequency correction reset events. We reset the - * coarse and fine frequency control values when the tuner channel source - * has changed frequency (PPM) or sample rate - */ - @Override - public void resetFrequencyCorrection() - { - mResetFrequencyTracker = true; - } - @Override public void setListener( RealSampleListener listener ) { @@ -459,4 +451,56 @@ public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, mSymbolSpreadTap = null; } } + + public void broadcast( FrequencyChangeEvent event ) + { + if( mFrequencyChangeListener != null ) + { + mFrequencyChangeListener.receive( event ); + } + } + + public void setFrequencyChangeListener( Listener listener ) + { + mFrequencyChangeListener = listener; + } + + @Override + public Listener getFrequencyChangeListener() + { + if( mFrequencyCorrectionProcessor == null ) + { + mFrequencyCorrectionProcessor = new FrequencyCorrectionProcessor(); + } + + return mFrequencyCorrectionProcessor; + } + + /** + * Receives notifications that the channel frequency correction has been + * applied and updates internal tracking value. + */ + public class FrequencyCorrectionProcessor implements Listener + { + @Override + public void receive( FrequencyChangeEvent event ) + { + switch( event.getEvent() ) + { + case NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE: + mFrequencyCorrection = event.getValue().intValue(); + + //Reset internal frequency tracking + mCoarseFrequencyCorrection = 0.0f; + mFineFrequencyCorrection = 0.0f; + break; + case NOTIFICATION_FREQUENCY_CORRECTION_CHANGE: + case NOTIFICATION_SAMPLE_RATE_CHANGE: + mResetFrequencyTracker = true; + break; + default: + break; + } + } + } } diff --git a/src/module/decode/p25/P25DecodeEditor.java b/src/module/decode/p25/P25DecodeEditor.java deleted file mode 100644 index e3c7052d..00000000 --- a/src/module/decode/p25/P25DecodeEditor.java +++ /dev/null @@ -1,147 +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 - ******************************************************************************/ -package module.decode.p25; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JSlider; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import module.decode.DecodeEditor; -import module.decode.config.DecodeConfiguration; -import module.decode.p25.P25Decoder.Modulation; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.SourceEditor; -import source.tuner.TunerEditor; -import controller.Editor; -import controller.channel.ChannelValidationException; - -public class P25DecodeEditor extends DecodeEditor -{ - private final static Logger mLog = LoggerFactory.getLogger( P25DecodeEditor.class ); - - private static final long serialVersionUID = 1L; - - private JComboBox mComboModulation; - private JCheckBox mIgnoreDataCalls; - private JLabel mTrafficChannelPoolSizeLabel; - private JSlider mTrafficChannelPoolSize; - - public P25DecodeEditor( DecodeConfiguration config ) - { - super( config ); - - initGUI(); - } - - private void initGUI() - { - final DecodeConfigP25Phase1 config = (DecodeConfigP25Phase1)mConfig; - - mComboModulation = new JComboBox(); - - mComboModulation.setModel( new DefaultComboBoxModel( - P25_LSMDecoder.Modulation.values() ) ); - - mComboModulation.setSelectedItem( config.getModulation() ); - - add( new JLabel( "Modulation:" ) ); - add( mComboModulation, "wrap" ); - - mTrafficChannelPoolSize = new JSlider( JSlider.HORIZONTAL, - DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MINIMUM, - DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MAXIMUM, - config.getTrafficChannelPoolSize() ); - - mTrafficChannelPoolSize.setMajorTickSpacing( 10 ); - mTrafficChannelPoolSize.setMinorTickSpacing( 1 ); - mTrafficChannelPoolSize.setPaintTicks( true ); - - mTrafficChannelPoolSize.setLabelTable( mTrafficChannelPoolSize.createStandardLabels( 10, 10 ) ); - mTrafficChannelPoolSize.setPaintLabels( true ); - - mTrafficChannelPoolSizeLabel = new JLabel( "Traffic Channel Pool: " + mTrafficChannelPoolSize.getValue() + " " ); - - mTrafficChannelPoolSize.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - mTrafficChannelPoolSizeLabel.setText( "Traffic Channel Pool: " + mTrafficChannelPoolSize.getValue() ); - } - } ); - - add( mTrafficChannelPoolSizeLabel ); - add( mTrafficChannelPoolSize, "wrap,grow" ); - - mIgnoreDataCalls = new JCheckBox(); - mIgnoreDataCalls.setSelected( config.getIgnoreDataCalls() ); - - add( new JLabel( "Ignore Data Calls" ) ); - add( mIgnoreDataCalls, "wrap,grow" ); - - reset(); - } - - /** - * Enforce source=tuner for CQPSK modulation - */ - @Override - public void validate( Editor editor ) throws ChannelValidationException - { - if( editor instanceof SourceEditor && - ((DecodeConfigP25Phase1)mConfig).getModulation() == Modulation.CQPSK ) - { - if( !( editor instanceof TunerEditor ) ) - { - throw new ChannelValidationException( - "

    LSM Simulcast

    " - + "

    P25 LSM Simulcast decoder can only be used with " - + "a tuner source. Please change the Source to use a tuner" - + " or change the P25 Decoder to C4FM modulation" ); - - } - } - } - - @Override - public void save() - { - DecodeConfigP25Phase1 config = (DecodeConfigP25Phase1)mConfig; - - config.setModulation( (Modulation)mComboModulation.getSelectedItem() ); - config.setIgnoreDataCalls( mIgnoreDataCalls.isSelected() ); - config.setTrafficChannelPoolSize( mTrafficChannelPoolSize.getValue() ); - } - - @Override - public void reset() - { - DecodeConfigP25Phase1 config = (DecodeConfigP25Phase1)mConfig; - - mComboModulation.setSelectedItem( config.getModulation() ); - mIgnoreDataCalls.setSelected( config.getIgnoreDataCalls() ); - mTrafficChannelPoolSize.setValue( config.getTrafficChannelPoolSize() ); - } -} diff --git a/src/module/decode/p25/P25Decoder.java b/src/module/decode/p25/P25Decoder.java index 674b00b9..184c6f88 100644 --- a/src/module/decode/p25/P25Decoder.java +++ b/src/module/decode/p25/P25Decoder.java @@ -1,20 +1,18 @@ package module.decode.p25; +import alias.AliasList; import instrument.Instrumentable; import module.decode.Decoder; import module.decode.DecoderType; -import source.tuner.frequency.IFrequencyCorrectionController; -import alias.AliasList; -public abstract class P25Decoder extends Decoder - implements IFrequencyCorrectionController, Instrumentable +public abstract class P25Decoder extends Decoder implements Instrumentable { private P25MessageProcessor mMessageProcessor; public P25Decoder( AliasList aliasList ) { mMessageProcessor = new P25MessageProcessor( aliasList ); - mMessageProcessor.setMessageListener( mMessageBroadcaster ); + mMessageProcessor.setMessageListener( this ); } public void dispose() diff --git a/src/module/decode/p25/P25DecoderEditor.java b/src/module/decode/p25/P25DecoderEditor.java new file mode 100644 index 00000000..e647e407 --- /dev/null +++ b/src/module/decode/p25/P25DecoderEditor.java @@ -0,0 +1,226 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.p25; + +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.ValidatingEditor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import module.decode.AuxDecodeConfigurationEditor; +import module.decode.config.AuxDecodeConfiguration; +import module.decode.config.DecodeConfiguration; +import module.decode.p25.P25Decoder.Modulation; +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import source.SourceConfigurationEditor; +import source.config.SourceConfigTuner; +import source.config.SourceConfiguration; +import controller.channel.Channel; + +public class P25DecoderEditor extends ValidatingEditor +{ + private final static Logger mLog = LoggerFactory.getLogger( P25DecoderEditor.class ); + + private static final long serialVersionUID = 1L; + + private JComboBox mComboModulation; + private JCheckBox mIgnoreDataCalls; + private JLabel mTrafficChannelPoolSizeLabel; + private JSlider mTrafficChannelPoolSize; + + public P25DecoderEditor() + { + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0,wrap 3", "[right][grow,fill][]", "" ) ); + + mComboModulation = new JComboBox(); + mComboModulation.setModel( new DefaultComboBoxModel( + P25_LSMDecoder.Modulation.values() ) ); + mComboModulation.setEnabled( false ); + mComboModulation.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + + add( new JLabel( "Modulation:" ) ); + add( mComboModulation ); + + mIgnoreDataCalls = new JCheckBox( "Ignore Data Calls" ); + mIgnoreDataCalls.setEnabled( false ); + mIgnoreDataCalls.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + + add( mIgnoreDataCalls ); + + mTrafficChannelPoolSize = new JSlider( JSlider.HORIZONTAL, DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MINIMUM, + DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MAXIMUM, DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT ); + mTrafficChannelPoolSize.setEnabled( false ); + mTrafficChannelPoolSize.setMajorTickSpacing( 10 ); + mTrafficChannelPoolSize.setMinorTickSpacing( 5 ); + mTrafficChannelPoolSize.setPaintTicks( true ); + mTrafficChannelPoolSize.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mTrafficChannelPoolSizeLabel.setText( "Traffic Pool: " + mTrafficChannelPoolSize.getValue() ); + setModified( true ); + } + } ); + + mTrafficChannelPoolSizeLabel = new JLabel( "Traffic Pool: " + mTrafficChannelPoolSize.getValue() + " " ); + + add( mTrafficChannelPoolSizeLabel ); + add( mTrafficChannelPoolSize ); + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + DecodeConfigP25Phase1 p25 = new DecodeConfigP25Phase1(); + + p25.setModulation( (Modulation)mComboModulation.getSelectedItem() ); + p25.setIgnoreDataCalls( mIgnoreDataCalls.isSelected() ); + p25.setTrafficChannelPoolSize( mTrafficChannelPoolSize.getValue() ); + + getItem().setDecodeConfiguration( p25 ); + } + + setModified( false ); + } + + @Override + public void validate( Editor editor ) throws EditorValidationException + { + mLog.debug( "Validating editor:" + editor.getClass() ); + /** + * Enforce CQPSK modulation uses a Tuner Source for I/Q sample data + */ + if( editor instanceof SourceConfigurationEditor && + ((Modulation)mComboModulation.getSelectedItem()) == Modulation.CQPSK ) + { + SourceConfiguration config = ((SourceConfigurationEditor)editor).getSourceConfiguration(); + + if( config != null && !( config instanceof SourceConfigTuner ) ) + { + throw new EditorValidationException( editor, + "

    LSM Simulcast

    " + + "

    P25 LSM Simulcast decoder can only be used with " + + "a tuner source. Please change the Source to use a tuner" + + " or change the P25 Decoder to C4FM modulation

    " ); + } + } + + if( editor instanceof AuxDecodeConfigurationEditor ) + { + AuxDecodeConfiguration config = ((AuxDecodeConfigurationEditor)editor).getConfiguration(); + + if( config != null && !config.getAuxDecoders().isEmpty() ) + { + throw new EditorValidationException( editor, + "

    Auxilary Decoders

    " + + "

    The auxiliary decoders work with analog audio and are not " + + "compatible with the P25 Decoder. Please uncheck any auxiliary " + + "decoders that you have selected for this channel

    " ); + } + } + } + + private void setControlsEnabled( boolean enabled ) + { + if( mComboModulation.isEnabled() != enabled ) + { + mComboModulation.setEnabled( enabled ); + } + + if( mTrafficChannelPoolSize.isEnabled() != enabled ) + { + mTrafficChannelPoolSize.setEnabled( enabled ); + } + + if( mIgnoreDataCalls.isEnabled() != enabled ) + { + mIgnoreDataCalls.setEnabled( enabled ); + } + } + + @Override + public void setItem( Channel item ) + { + super.setItem( item ); + + if( hasItem() ) + { + setControlsEnabled( true ); + + DecodeConfiguration config = getItem().getDecodeConfiguration(); + + if( config instanceof DecodeConfigP25Phase1 ) + { + DecodeConfigP25Phase1 p25 = (DecodeConfigP25Phase1)config; + + mComboModulation.setSelectedItem( p25.getModulation() ); + mIgnoreDataCalls.setSelected( p25.getIgnoreDataCalls() ); + mTrafficChannelPoolSize.setValue( p25.getTrafficChannelPoolSize() ); + setModified( false ); + } + else + { + mComboModulation.setSelectedItem( Modulation.C4FM ); + mIgnoreDataCalls.setSelected( false ); + mTrafficChannelPoolSize.setValue( DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT ); + setModified( true ); + } + } + else + { + setControlsEnabled( false ); + setModified( false ); + } + } +} diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java index 91ab112c..ef7b09e9 100644 --- a/src/module/decode/p25/P25DecoderState.java +++ b/src/module/decode/p25/P25DecoderState.java @@ -25,10 +25,10 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; -import module.decode.event.CallEvent; import module.decode.event.CallEvent.CallEventType; import module.decode.p25.P25Decoder.Modulation; import module.decode.p25.message.IAdjacentSite; @@ -121,6 +121,7 @@ import module.decode.p25.reference.IPProtocol; import module.decode.p25.reference.LinkControlOpcode; import module.decode.p25.reference.Response; import module.decode.p25.reference.Vendor; +import module.decode.state.ChangeChannelTimeoutEvent; import module.decode.state.ChangedAttribute; import module.decode.state.DecoderState; import module.decode.state.DecoderStateEvent; @@ -204,11 +205,6 @@ public class P25DecoderState extends DecoderState return DecoderType.P25_PHASE1; } - @Override - public void start() - { - } - @Override public void stop() { @@ -1668,7 +1664,7 @@ public class P25DecoderState extends DecoderState .to( satcr.getLogicalLinkID() ) .build() ); break; - case SN_ACTIVATE_TDS_CONTEXT_REQUEST: + case SNDCP_ACTIVATE_TDS_CONTEXT_REQUEST: SNDCPActivateTDSContextRequest satcreq = (SNDCPActivateTDSContextRequest)pduc; @@ -2292,6 +2288,8 @@ public class P25DecoderState extends DecoderState logAlternateVendorMessage( pdu ); } break; + default: + break; } } @@ -2437,8 +2435,6 @@ public class P25DecoderState extends DecoderState private void processTSBKResponse( TSBKMessage message ) { - CallEvent event = null; - switch( message.getOpcode() ) { case ACKNOWLEDGE_RESPONSE: @@ -3560,4 +3556,14 @@ public class P25DecoderState extends DecoderState break; } } + + @Override + public void start( ScheduledExecutorService executor ) + { + //Change the default (45-second) traffic channel timeout to 1 second + if( mChannelType == ChannelType.TRAFFIC ) + { + broadcast( new ChangeChannelTimeoutEvent( this, ChannelType.TRAFFIC, 1000 ) ); + } + } } diff --git a/src/module/decode/p25/P25MessageFramer.java b/src/module/decode/p25/P25MessageFramer.java index 61d03465..39d9bb80 100644 --- a/src/module/decode/p25/P25MessageFramer.java +++ b/src/module/decode/p25/P25MessageFramer.java @@ -17,6 +17,8 @@ import module.decode.p25.message.tdu.lc.TDULCMessageFactory; import module.decode.p25.message.tdu.lc.TDULinkControlMessage; import module.decode.p25.message.tsbk.TSBKMessage; import module.decode.p25.message.tsbk.TSBKMessageFactory; +import module.decode.p25.message.vselp.VSELP1Message; +import module.decode.p25.message.vselp.VSELP2Message; import module.decode.p25.reference.DataUnitID; import org.slf4j.Logger; @@ -129,7 +131,7 @@ public class P25MessageFramer implements Listener if( demodulator != null ) { /* For CQPSK, we include 3 additional sync detectors to watch for and - * correction +/-90 and 180 degree costas loop phase lock errors */ + * correct +/-90 and 180 degree costas loop phase lock errors */ mMatcher.add( new CostasPhaseErrorDetector( FrameSync.P25_PHASE1_ERROR_90_CCW, demodulator, PHASE_CORRECTION_90_DEGREES ) ); @@ -289,7 +291,7 @@ public class P25MessageFramer implements Listener else { mComplete = true; - dispatch( new P25Message( mMessage.copy(), duid, mAliasList ) ); + dispatch( new P25Message( mMessage.copy(), mDUID, mAliasList ) ); } } else @@ -631,6 +633,14 @@ public class P25MessageFramer implements Listener mComplete = true; break; + case VSELP1: + mComplete = true; + dispatch( new VSELP1Message( mMessage.copy(), mDUID, mAliasList ) ); + break; + case VSELP2: + mComplete = true; + dispatch( new VSELP2Message( mMessage.copy(), mDUID, mAliasList ) ); + break; case UNKN: mComplete = true; dispatch( new P25Message( mMessage.copy(), mDUID, mAliasList ) ); diff --git a/src/module/decode/p25/P25MessageProcessor.java b/src/module/decode/p25/P25MessageProcessor.java index 0ef49de5..d34437bd 100644 --- a/src/module/decode/p25/P25MessageProcessor.java +++ b/src/module/decode/p25/P25MessageProcessor.java @@ -10,8 +10,8 @@ import module.decode.p25.message.ldu.LDUMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Listener; import alias.AliasList; +import sample.Listener; public class P25MessageProcessor implements Listener { diff --git a/src/module/decode/p25/P25_C4FMDecoder.java b/src/module/decode/p25/P25_C4FMDecoder.java index 4775a9a5..650c0cc2 100644 --- a/src/module/decode/p25/P25_C4FMDecoder.java +++ b/src/module/decode/p25/P25_C4FMDecoder.java @@ -24,22 +24,25 @@ import instrument.tap.stream.FloatBufferTap; import instrument.tap.stream.FloatTap; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Listener; -import sample.real.IRealBufferListener; -import sample.real.RealBuffer; -import source.tuner.frequency.FrequencyCorrectionControl; import alias.AliasList; +import sample.Listener; +import sample.real.IFilteredRealBufferListener; +import sample.real.RealBuffer; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.IFrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeProvider; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; import dsp.filter.fir.real.RealFIRFilter_RB_RB; -public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener +public class P25_C4FMDecoder extends P25Decoder + implements IFrequencyChangeListener, IFrequencyChangeProvider,IFilteredRealBufferListener { private final static Logger mLog = LoggerFactory.getLogger( P25_C4FMDecoder.class ); @@ -48,11 +51,8 @@ public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener private static final String INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT = "Tap Point: Symbol Filter Output"; private static final String INSTRUMENT_C4FM_SLICER_OUTPUT = "Tap Point: C4FM Slicer Output"; - private final static int MAXIMUM_FREQUENCY_CORRECTION = 3000; //Hertz, +/- - private List mAvailableTaps; private RealFIRFilter_RB_RB mC4FMPreFilter; - private FrequencyCorrectionControl mFrequencyCorrectionControl; private C4FMSymbolFilter mSymbolFilter; private C4FMSlicer mC4FMSlicer; private P25MessageFramer mMessageFramer; @@ -70,7 +70,7 @@ public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener * infrastructure and subscriber identities that will be included in each * decoded message */ - public P25_C4FMDecoder( AliasList aliasList ) + public P25_C4FMDecoder( AliasList aliasList, int frequencyCorrectionMaximum ) { super( aliasList ); @@ -79,13 +79,8 @@ public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener mC4FMPreFilter = new RealFIRFilter_RB_RB( filter, 1.0f ); - /* Issue tuned frequency correction commands, remotely controlled by the - * downstream symbol filter */ - mFrequencyCorrectionControl = new FrequencyCorrectionControl( - MAXIMUM_FREQUENCY_CORRECTION ); - /* Shape gain and frequency offsets to optimize sample stream */ - mSymbolFilter = new C4FMSymbolFilter( mFrequencyCorrectionControl ); + mSymbolFilter = new C4FMSymbolFilter( frequencyCorrectionMaximum ); mC4FMPreFilter.setListener( mSymbolFilter ); /* Convert samples to symbols */ @@ -108,9 +103,6 @@ public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener mC4FMPreFilter.dispose(); mC4FMPreFilter = null; - mFrequencyCorrectionControl.dispose(); - mFrequencyCorrectionControl = null; - mSymbolFilter.dispose(); mSymbolFilter = null; @@ -127,19 +119,7 @@ public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener } @Override - public boolean hasFrequencyCorrectionControl() - { - return mFrequencyCorrectionControl != null; - } - - @Override - public FrequencyCorrectionControl getFrequencyCorrectionControl() - { - return mFrequencyCorrectionControl; - } - - @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { return mC4FMPreFilter; } @@ -249,7 +229,7 @@ public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener } @Override - public void start() + public void start( ScheduledExecutorService executor ) { // TODO Auto-generated method stub @@ -261,4 +241,33 @@ public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener // TODO Auto-generated method stub } + + @Override + public void setFrequencyChangeListener( Listener listener ) + { + if( mSymbolFilter != null ) + { + mSymbolFilter.setFrequencyChangeListener( listener ); + } + } + + @Override + public void removeFrequencyChangeListener() + { + if( mSymbolFilter != null ) + { + mSymbolFilter.setFrequencyChangeListener( null ); + } + } + + @Override + public Listener getFrequencyChangeListener() + { + if( mSymbolFilter != null ) + { + return mSymbolFilter.getFrequencyChangeListener(); + } + + return null; + } } diff --git a/src/module/decode/p25/P25_LSMDecoder.java b/src/module/decode/p25/P25_LSMDecoder.java index 21555ae9..011ff83d 100644 --- a/src/module/decode/p25/P25_LSMDecoder.java +++ b/src/module/decode/p25/P25_LSMDecoder.java @@ -26,16 +26,16 @@ import instrument.tap.stream.QPSKTap; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import alias.AliasList; import sample.Listener; import sample.complex.ComplexBuffer; import sample.complex.ComplexBufferToStreamConverter; import sample.complex.IComplexBufferListener; -import source.tuner.frequency.FrequencyCorrectionControl; -import alias.AliasList; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; @@ -50,8 +50,8 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener /* Instrumentation Taps */ private static final String INSTRUMENT_BASEBAND_FILTER_OUTPUT = "Tap Point: Baseband Filter Output"; private static final String INSTRUMENT_AGC_OUTPUT = "Tap Point: AGC Output"; - private static final String INSTRUMENT_QPSK_DEMODULATOR_OUTPUT = "Tap Point: CQPSK Demodulator Output"; - private static final String INSTRUMENT_CQPSK_SLICER_OUTPUT = "Tap Point: CQPSK Slicer Output"; + private static final String INSTRUMENT_LSM_DEMODULATOR_OUTPUT = "Tap Point: LSM Demodulator Output"; + private static final String INSTRUMENT_QPSK_SLICER_OUTPUT = "Tap Point: QPSK Slicer Output"; private List mAvailableTaps; @@ -60,8 +60,8 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener new ComplexBufferToStreamConverter(); private ComplexFeedForwardGainControl mAGC = new ComplexFeedForwardGainControl( 32 ); - private LSMDemodulator mCQPSKDemodulator = new LSMDemodulator(); - private QPSKPolarSlicer mCQPSKSlicer = new QPSKPolarSlicer(); + private LSMDemodulator mLSMDemodulator = new LSMDemodulator(); + private QPSKPolarSlicer mQPSKSlicer = new QPSKPolarSlicer(); private P25MessageFramer mMessageFramer; public P25_LSMDecoder( AliasList aliasList ) @@ -75,12 +75,12 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener mStreamConverter.setListener( mAGC ); - mAGC.setListener( mCQPSKDemodulator ); + mAGC.setListener( mLSMDemodulator ); - mCQPSKDemodulator.setSymbolListener( mCQPSKSlicer ); + mLSMDemodulator.setSymbolListener( mQPSKSlicer ); - mMessageFramer = new P25MessageFramer( aliasList, mCQPSKDemodulator ); - mCQPSKSlicer.addListener( mMessageFramer ); + mMessageFramer = new P25MessageFramer( aliasList, mLSMDemodulator ); + mQPSKSlicer.addListener( mMessageFramer ); mMessageFramer.setListener( getMessageProcessor() ); } @@ -98,11 +98,11 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener mAGC.dispose(); mAGC = null; - mCQPSKDemodulator.dispose(); - mCQPSKDemodulator = null; + mLSMDemodulator.dispose(); + mLSMDemodulator = null; - mCQPSKSlicer.dispose(); - mCQPSKSlicer = null; + mQPSKSlicer.dispose(); + mQPSKSlicer = null; mMessageFramer.dispose(); mMessageFramer = null; @@ -119,18 +119,6 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener return Modulation.CQPSK; } - @Override - public boolean hasFrequencyCorrectionControl() - { - return false; - } - - @Override - public FrequencyCorrectionControl getFrequencyCorrectionControl() - { - return null; - } - /** * Provides a list of instrumentation taps for monitoring internal processing */ @@ -145,14 +133,14 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener group.add( new ComplexTap( INSTRUMENT_BASEBAND_FILTER_OUTPUT, 0, 1.0f ) ); group.add( new ComplexTap( INSTRUMENT_AGC_OUTPUT, 0, 1.0f ) ); - group.add( new QPSKTap( INSTRUMENT_QPSK_DEMODULATOR_OUTPUT, 0, 1.0f ) ); - group.add( new DibitTap( INSTRUMENT_CQPSK_SLICER_OUTPUT, 0, 0.1f ) ); + group.add( new QPSKTap( INSTRUMENT_LSM_DEMODULATOR_OUTPUT, 0, 1.0f ) ); + group.add( new DibitTap( INSTRUMENT_QPSK_SLICER_OUTPUT, 0, 0.1f ) ); mAvailableTaps.add( group ); - if( mCQPSKDemodulator != null ) + if( mLSMDemodulator != null ) { - mAvailableTaps.addAll( mCQPSKDemodulator.getTapGroups() ); + mAvailableTaps.addAll( mLSMDemodulator.getTapGroups() ); } } @@ -165,9 +153,9 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener @Override public void registerTap( Tap tap ) { - if( mCQPSKDemodulator != null ) + if( mLSMDemodulator != null ) { - mCQPSKDemodulator.registerTap( tap ); + mLSMDemodulator.registerTap( tap ); } switch( tap.getName() ) @@ -180,15 +168,15 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener case INSTRUMENT_AGC_OUTPUT: ComplexSampleTap agcSymbol = (ComplexSampleTap)tap; mAGC.setListener( agcSymbol ); - agcSymbol.setListener( mCQPSKDemodulator ); + agcSymbol.setListener( mLSMDemodulator ); break; - case INSTRUMENT_QPSK_DEMODULATOR_OUTPUT: + case INSTRUMENT_LSM_DEMODULATOR_OUTPUT: QPSKTap qpsk = (QPSKTap)tap; - mCQPSKDemodulator.setSymbolListener( qpsk ); - qpsk.setListener( mCQPSKSlicer ); + mLSMDemodulator.setSymbolListener( qpsk ); + qpsk.setListener( mQPSKSlicer ); break; - case INSTRUMENT_CQPSK_SLICER_OUTPUT: - mCQPSKSlicer.addListener( (DibitTap)tap ); + case INSTRUMENT_QPSK_SLICER_OUTPUT: + mQPSKSlicer.addListener( (DibitTap)tap ); break; default: throw new IllegalArgumentException( "Unrecognized tap: " + @@ -202,9 +190,9 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener @Override public void unregisterTap( Tap tap ) { - if( mCQPSKDemodulator != null ) + if( mLSMDemodulator != null ) { - mCQPSKDemodulator.unregisterTap( tap ); + mLSMDemodulator.unregisterTap( tap ); } switch( tap.getName() ) @@ -213,13 +201,13 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener mStreamConverter.setListener( mAGC ); break; case INSTRUMENT_AGC_OUTPUT: - mAGC.setListener( mCQPSKDemodulator ); + mAGC.setListener( mLSMDemodulator ); break; - case INSTRUMENT_QPSK_DEMODULATOR_OUTPUT: - mCQPSKDemodulator.setSymbolListener( mCQPSKSlicer ); + case INSTRUMENT_LSM_DEMODULATOR_OUTPUT: + mLSMDemodulator.setSymbolListener( mQPSKSlicer ); break; - case INSTRUMENT_CQPSK_SLICER_OUTPUT: - mCQPSKSlicer.removeListener( (DibitTap)tap ); + case INSTRUMENT_QPSK_SLICER_OUTPUT: + mQPSKSlicer.removeListener( (DibitTap)tap ); break; default: throw new IllegalArgumentException( "Unrecognized tap: " + @@ -235,7 +223,7 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener } @Override - public void start() + public void start( ScheduledExecutorService executor ) { // TODO Auto-generated method stub diff --git a/src/module/decode/p25/audio/P25AudioModule.java b/src/module/decode/p25/audio/P25AudioModule.java index 4fa08148..8245b981 100644 --- a/src/module/decode/p25/audio/P25AudioModule.java +++ b/src/module/decode/p25/audio/P25AudioModule.java @@ -1,5 +1,7 @@ package module.decode.p25.audio; +import java.util.concurrent.ScheduledExecutorService; + import jmbe.iface.AudioConversionLibrary; import jmbe.iface.AudioConverter; import message.IMessageListener; @@ -21,7 +23,6 @@ import audio.squelch.ISquelchStateListener; import audio.squelch.SquelchState; import controller.channel.ChannelEvent; import controller.channel.IChannelEventListener; -import dsp.filter.iir.DeemphasisFilter; import dsp.gain.NonClippingGain; public class P25AudioModule extends Module implements Listener, @@ -78,7 +79,7 @@ public class P25AudioModule extends Module implements Listener, } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } @@ -232,10 +233,10 @@ public class P25AudioModule extends Module implements Listener, { switch( event.getEvent() ) { - case CHANNEL_STATE_RESET: + case NOTIFICATION_STATE_RESET: mAudioMetadata.reset(); break; - case CHANGE_SELECTED: + case NOTIFICATION_SELECTION_CHANGE: mAudioMetadata.setSelected( event.getChannel().isSelected() ); break; default: diff --git a/src/module/decode/p25/message/P25Message.java b/src/module/decode/p25/message/P25Message.java index dc33108f..e1e6fe29 100644 --- a/src/module/decode/p25/message/P25Message.java +++ b/src/module/decode/p25/message/P25Message.java @@ -86,7 +86,6 @@ public class P25Message extends Message sb.append( getNAC() ); sb.append( " " ); sb.append( getDUID().getLabel() ); - sb.append( " DATA UNIT ID:" + mMessage.getInt( DUID ) ); return sb.toString(); diff --git a/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java index 1f45e760..089912b8 100644 --- a/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java @@ -7,15 +7,9 @@ import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.message.tsbk.osp.control.SystemService; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class AdjacentSiteStatusBroadcast extends LDU1Message implements IdentifierReceiver, IAdjacentSite { - private final static Logger mLog = - LoggerFactory.getLogger( AdjacentSiteStatusBroadcast.class ); - public static final int[] LRA = { 364,365,366,367,372,373,374,375 }; public static final int[] SYSTEM_ID = { 384,385,386,387,536,537,538,539,540, 541,546,547 }; diff --git a/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java index 5008728f..893c2e5c 100644 --- a/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java @@ -6,15 +6,9 @@ import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class AdjacentSiteStatusBroadcastExplicit extends LDU1Message implements IdentifierReceiver, IAdjacentSite { - private final static Logger mLog = - LoggerFactory.getLogger( AdjacentSiteStatusBroadcastExplicit.class ); - public static final int[] LRA = { 364,365,366,367,372,373,374,375 }; public static final int[] TRANSMIT_IDENTIFIER = { 376,377,382,383 }; public static final int[] TRANSMIT_CHANNEL = { 384,385,386,387,536,537,538, diff --git a/src/module/decode/p25/message/ldu/lc/CallAlert.java b/src/module/decode/p25/message/ldu/lc/CallAlert.java index 731fc4ba..becfe19f 100644 --- a/src/module/decode/p25/message/ldu/lc/CallAlert.java +++ b/src/module/decode/p25/message/ldu/lc/CallAlert.java @@ -3,13 +3,8 @@ package module.decode.p25.message.ldu.lc; import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class CallAlert extends LDU1Message { - private final static Logger mLog = - LoggerFactory.getLogger( CallAlert.class ); public static final int[] TARGET_ADDRESS = { 536,537,538,539,540,541,546, 547,548,549,550,551,556,557,558,559,560,561,566,567,568,569,570,571 }; public static final int[] SOURCE_ADDRESS = { 720,721,722,723,724,725,730, diff --git a/src/module/decode/p25/message/ldu/lc/CallTermination.java b/src/module/decode/p25/message/ldu/lc/CallTermination.java index 2d69f791..9d567b0b 100644 --- a/src/module/decode/p25/message/ldu/lc/CallTermination.java +++ b/src/module/decode/p25/message/ldu/lc/CallTermination.java @@ -3,13 +3,8 @@ package module.decode.p25.message.ldu.lc; import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class CallTermination extends LDU1Message { - private final static Logger mLog = LoggerFactory.getLogger( CallTermination.class ); - public static final int[] SOURCE_ADDRESS = { 720,721,722,723,724,725,730, 731,732,733,734,735,740,741,742,743,744,745,750,751,752,753,754,755 }; diff --git a/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java index 8fa255ec..42cf0e78 100644 --- a/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java +++ b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java @@ -4,15 +4,9 @@ import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class ChannelIdentifierUpdate extends LDU1Message implements IBandIdentifier { - private final static Logger mLog = - LoggerFactory.getLogger( ChannelIdentifierUpdate.class ); - public static final int[] IDENTIFIER = { 364,365,366,367 }; public static final int[] BANDWIDTH = { 372,373,374,375,376,377,382,383,384 }; public static final int[] TRANSMIT_OFFSET = { 385,386,387,536,537,538,539, diff --git a/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java index c924e693..c857754f 100644 --- a/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java @@ -4,15 +4,9 @@ import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.ldu.LDU1Message; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class ChannelIdentifierUpdateExplicit extends LDU1Message implements IBandIdentifier { - private final static Logger mLog = - LoggerFactory.getLogger( ChannelIdentifierUpdateExplicit.class ); - public static final int[] IDENTIFIER = { 364,365,366,367 }; public static final int[] BANDWIDTH = { 372,373,374,375 }; public static final int[] TRANSMIT_OFFSET = { 376,377,382,383,384,385,386, diff --git a/src/module/decode/p25/message/pdu/PDUMessageFactory.java b/src/module/decode/p25/message/pdu/PDUMessageFactory.java index 1e11796d..26b25b1c 100644 --- a/src/module/decode/p25/message/pdu/PDUMessageFactory.java +++ b/src/module/decode/p25/message/pdu/PDUMessageFactory.java @@ -1,6 +1,5 @@ package module.decode.p25.message.pdu; -import module.decode.p25.message.pdu.confirmed.PacketData; import module.decode.p25.message.pdu.osp.control.AdjacentStatusBroadcastExtended; import module.decode.p25.message.pdu.osp.control.CallAlertExtended; import module.decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended; @@ -122,8 +121,7 @@ public class PDUMessageFactory } return new PDUMessage( message, duid, aliasList ); - case PACKET_DATA: - return new PacketData( message, aliasList ); + case UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL: return new PDUMessage( message, duid, aliasList ); default: diff --git a/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java b/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java index e8c08bcb..84fbf3ec 100644 --- a/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java +++ b/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java @@ -3,7 +3,6 @@ package module.decode.p25.message.pdu.confirmed; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - public class PDUConfirmedMessageFactory { public final static Logger mLog = @@ -52,7 +51,7 @@ public class PDUConfirmedMessageFactory return new SNDCPActivateTDSContextAccept( message ); case SNDCP_ACTIVATE_TDS_CONTEXT_REJECT: return new SNDCPActivateTDSContextReject( message ); - case SN_ACTIVATE_TDS_CONTEXT_REQUEST: + case SNDCP_ACTIVATE_TDS_CONTEXT_REQUEST: return new SNDCPActivateTDSContextRequest( message ); case SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT: case SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST: @@ -61,14 +60,6 @@ public class PDUConfirmedMessageFactory return new SNDCPUserData( message ); case SNDCP_RF_UNCONFIRMED_DATA: break; - case PDU_TYPE_6: - case PDU_TYPE_7: - case PDU_TYPE_8: - case PDU_TYPE_9: - case PDU_TYPE_10: - case PDU_TYPE_11: - return new PDUTypeUnknown( message ); - case UNKNOWN: default: break; } diff --git a/src/module/decode/p25/message/tdu/TDUMessage.java b/src/module/decode/p25/message/tdu/TDUMessage.java index d9808a47..85a533ac 100644 --- a/src/module/decode/p25/message/tdu/TDUMessage.java +++ b/src/module/decode/p25/message/tdu/TDUMessage.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ + package module.decode.p25.message.tdu; import module.decode.p25.message.P25Message; @@ -18,7 +36,7 @@ public class TDUMessage extends P25Message mCRC = new CRC[ 1 ]; mCRC[ 0 ] = CRC.PASSED; } - + @Override public String getMessage() { diff --git a/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java index 58935d2f..d14e03d6 100644 --- a/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java @@ -5,17 +5,10 @@ import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.message.tsbk.osp.control.SystemService; import module.decode.p25.reference.LinkControlOpcode; -import module.decode.p25.reference.Service; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class AdjacentSiteStatusBroadcast extends TDULinkControlMessage implements IdentifierReceiver, IAdjacentSite { - private final static Logger mLog = - LoggerFactory.getLogger( AdjacentSiteStatusBroadcast.class ); - public static final int[] LRA = { 72,73,74,75,88,89,90,91 }; public static final int[] SYSTEM_ID = { 96,97,98,99,112,113,114,115, 116,117,118,119 }; diff --git a/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java index 630c0075..da0c09d0 100644 --- a/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java @@ -5,15 +5,9 @@ import module.decode.p25.message.IBandIdentifier; import module.decode.p25.message.IdentifierReceiver; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class AdjacentSiteStatusBroadcastExplicit extends TDULinkControlMessage implements IdentifierReceiver, IAdjacentSite { - private final static Logger mLog = - LoggerFactory.getLogger( AdjacentSiteStatusBroadcastExplicit.class ); - public static final int[] LRA = { 72,73,74,75,88,89,90,91 }; public static final int[] TRANSMIT_IDENTIFIER = { 92,93,94,95 }; public static final int[] TRANSMIT_CHANNEL = { 96,97,98,99,112,113,114,115, diff --git a/src/module/decode/p25/message/tdu/lc/CallAlert.java b/src/module/decode/p25/message/tdu/lc/CallAlert.java index 6f746519..3455e455 100644 --- a/src/module/decode/p25/message/tdu/lc/CallAlert.java +++ b/src/module/decode/p25/message/tdu/lc/CallAlert.java @@ -2,13 +2,8 @@ package module.decode.p25.message.tdu.lc; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class CallAlert extends TDULinkControlMessage { - private final static Logger mLog = - LoggerFactory.getLogger( CallAlert.class ); public static final int[] TARGET_ADDRESS = { 112,113,114,115,116,117,118, 119,120,121,122,123,136,137,138,139,140,141,142,143,144,145,146,147 }; public static final int[] SOURCE_ADDRESS = { 160,161,162,163,164,165,166, diff --git a/src/module/decode/p25/message/tdu/lc/CallTermination.java b/src/module/decode/p25/message/tdu/lc/CallTermination.java index 2a2baeeb..f8d08b59 100644 --- a/src/module/decode/p25/message/tdu/lc/CallTermination.java +++ b/src/module/decode/p25/message/tdu/lc/CallTermination.java @@ -2,13 +2,8 @@ package module.decode.p25.message.tdu.lc; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class CallTermination extends TDULinkControlMessage { - private final static Logger mLog = LoggerFactory.getLogger( CallTermination.class ); - public static final int[] SOURCE_ADDRESS = { 160,161,162,163,164,165,166, 167,168,169,170,171,184,185,186,187,188,189,190,191,192,193,194,195 }; diff --git a/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java index 2730cec4..75e725f8 100644 --- a/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java +++ b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java @@ -3,15 +3,9 @@ package module.decode.p25.message.tdu.lc; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class ChannelIdentifierUpdate extends TDULinkControlMessage implements IBandIdentifier { - private final static Logger mLog = - LoggerFactory.getLogger( ChannelIdentifierUpdate.class ); - public static final int[] IDENTIFIER = { 72,73,74,75 }; public static final int[] BANDWIDTH = { 88,89,90,91,92,93,94,95,96 }; public static final int[] TRANSMIT_OFFSET = { 97,98,99,112,113,114,115,116, diff --git a/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java index b70210d5..eb427fe3 100644 --- a/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java @@ -3,15 +3,9 @@ package module.decode.p25.message.tdu.lc; import module.decode.p25.message.IBandIdentifier; import module.decode.p25.reference.LinkControlOpcode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class ChannelIdentifierUpdateExplicit extends TDULinkControlMessage implements IBandIdentifier { - private final static Logger mLog = - LoggerFactory.getLogger( ChannelIdentifierUpdateExplicit.class ); - public static final int[] IDENTIFIER = { 72,73,74,75 }; public static final int[] BANDWIDTH = { 88,89,90,91 }; public static final int[] TRANSMIT_OFFSET = { 92,93,94,95,96,97,98,99,112, diff --git a/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java b/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java index 3e8ab315..5db07165 100644 --- a/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java +++ b/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java @@ -78,93 +78,67 @@ public class TSBKMessageFactory switch( opcode ) { case ACKNOWLEDGE_RESPONSE: - return new AcknowledgeResponse( message, duid, - aliasList ); + return new AcknowledgeResponse( message, duid, aliasList ); case ADJACENT_STATUS_BROADCAST: - return new AdjacentStatusBroadcast( message, duid, - aliasList ); + return new AdjacentStatusBroadcast( message, duid, aliasList ); case AUTHENTICATION_COMMAND: - return new AuthenticationCommand( message, duid, - aliasList ); + return new AuthenticationCommand( message, duid, aliasList ); case CALL_ALERT: return new CallAlert( message, duid, aliasList ); case DENY_RESPONSE: return new DenyResponse( message, duid, aliasList ); case EXTENDED_FUNCTION_COMMAND: - return new ExtendedFunctionCommand( message, duid, - aliasList ); + return new ExtendedFunctionCommand( message, duid, aliasList ); case GROUP_AFFILIATION_QUERY: - return new GroupAffiliationQuery( message, duid, - aliasList ); + return new GroupAffiliationQuery( message, duid, aliasList ); case GROUP_AFFILIATION_RESPONSE: - return new GroupAffiliationResponse( message, duid, - aliasList ); + return new GroupAffiliationResponse( message, duid, aliasList ); case GROUP_DATA_CHANNEL_ANNOUNCEMENT: - return new GroupDataChannelAnnouncement( message, duid, - aliasList ); + return new GroupDataChannelAnnouncement( message, duid, aliasList ); case GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: - return new GroupDataChannelAnnouncementExplicit( - message, duid, aliasList ); + return new GroupDataChannelAnnouncementExplicit( message, duid, aliasList ); case GROUP_DATA_CHANNEL_GRANT: - return new GroupDataChannelGrant( message, duid, - aliasList ); + return new GroupDataChannelGrant( message, duid, aliasList ); case GROUP_VOICE_CHANNEL_GRANT: - return new GroupVoiceChannelGrant( message, duid, - aliasList ); + return new GroupVoiceChannelGrant( message, duid, aliasList ); case GROUP_VOICE_CHANNEL_GRANT_UPDATE: - return new GroupVoiceChannelGrantUpdate( message, duid, - aliasList ); + return new GroupVoiceChannelGrantUpdate( message, duid, aliasList ); case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: - return new GroupVoiceChannelGrantUpdateExplicit( message, - duid, aliasList ); + return new GroupVoiceChannelGrantUpdateExplicit( message, duid, aliasList ); case IDENTIFIER_UPDATE_NON_VUHF: - return new IdentifierUpdateNonVUHF( message, duid, - aliasList ); + return new IdentifierUpdateNonVUHF( message, duid, aliasList ); case IDENTIFIER_UPDATE_TDMA: return new IdentifierUpdateTDMA( message, duid, aliasList ); case IDENTIFIER_UPDATE_VHF_UHF_BANDS: - return new IdentifierUpdateVUHF( message, duid, - aliasList ); + return new IdentifierUpdateVUHF( message, duid, aliasList ); case INDIVIDUAL_DATA_CHANNEL_GRANT: - return new IndividualDataChannelGrant( message, duid, - aliasList ); + return new IndividualDataChannelGrant( message, duid, aliasList ); case LOCATION_REGISTRATION_RESPONSE: - return new LocationRegistrationResponse( message, duid, - aliasList ); + return new LocationRegistrationResponse( message, duid, aliasList ); case MESSAGE_UPDATE: return new MessageUpdate( message, duid, aliasList ); case NETWORK_STATUS_BROADCAST: - return new NetworkStatusBroadcast( message, duid, - aliasList ); + return new NetworkStatusBroadcast( message, duid, aliasList ); case QUEUED_RESPONSE: return new QueuedResponse( message, duid, aliasList ); case PROTECTION_PARAMETER_UPDATE: - return new ProtectionParameterUpdate( message, duid, - aliasList ); + return new ProtectionParameterUpdate( message, duid, aliasList ); case RADIO_UNIT_MONITOR_COMMAND: - return new RadioUnitMonitorCommand( message, duid, - aliasList ); + return new RadioUnitMonitorCommand( message, duid, aliasList ); case RFSS_STATUS_BROADCAST: - return new RFSSStatusBroadcast( message, duid, - aliasList ); + return new RFSSStatusBroadcast( message, duid, aliasList ); case ROAMING_ADDRESS_COMMAND: - return new RoamingAddressCommand( message, duid, - aliasList ); + return new RoamingAddressCommand( message, duid, aliasList ); case SECONDARY_CONTROL_CHANNEL_BROADCAST: - return new SecondaryControlChannelBroadcast( message, - duid, aliasList ); + return new SecondaryControlChannelBroadcast( message, duid, aliasList ); case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: - return new SecondaryControlChannelBroadcastExplicit( - message, duid, aliasList ); + return new SecondaryControlChannelBroadcastExplicit( message, duid, aliasList ); case SNDCP_DATA_CHANNEL_GRANT: - return new SNDCPDataChannelGrant( message, duid, - aliasList ); + return new SNDCPDataChannelGrant( message, duid, aliasList ); case SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: - return new SNDCPDataChannelAnnouncementExplicit( - message, duid, aliasList ); + return new SNDCPDataChannelAnnouncementExplicit( message, duid, aliasList ); case SNDCP_DATA_PAGE_REQUEST: - return new SNDCPDataPageRequest( message, duid, - aliasList ); + return new SNDCPDataPageRequest( message, duid, aliasList ); case STATUS_QUERY: return new StatusQuery( message, duid, aliasList ); case STATUS_UPDATE: @@ -172,35 +146,25 @@ public class TSBKMessageFactory case TDMA_SYNC_BROADCAST: return new SyncBroadcast( message, duid, aliasList ); case SYSTEM_SERVICE_BROADCAST: - return new SystemServiceBroadcast( message, duid, - aliasList ); + return new SystemServiceBroadcast( message, duid, aliasList ); case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - return new TelephoneInterconnectAnswerRequest( - message, duid, aliasList ); + return new TelephoneInterconnectAnswerRequest( message, duid, aliasList ); case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - return new TelephoneInterconnectVoiceChannelGrant( - message, duid, aliasList ); + return new TelephoneInterconnectVoiceChannelGrant( message, duid, aliasList ); case TIME_DATE_ANNOUNCEMENT: - return new TimeAndDateAnnouncement( message, duid, - aliasList ); + return new TimeAndDateAnnouncement( message, duid, aliasList ); case UNIT_DEREGISTRATION_ACKNOWLEDGE: - return new UnitDeregistrationAcknowledge( message, - duid, aliasList ); + return new UnitDeregistrationAcknowledge( message, duid, aliasList ); case UNIT_REGISTRATION_COMMAND: - return new UnitRegistrationCommand( message, duid, - aliasList ); + return new UnitRegistrationCommand( message, duid, aliasList ); case UNIT_REGISTRATION_RESPONSE: - return new UnitRegistrationResponse( message, duid, - aliasList ); + return new UnitRegistrationResponse( message, duid, aliasList ); case UNIT_TO_UNIT_ANSWER_REQUEST: - return new UnitToUnitAnswerRequest( message, duid, - aliasList ); + return new UnitToUnitAnswerRequest( message, duid, aliasList ); case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - return new UnitToUnitVoiceChannelGrant( message, - duid, aliasList ); + return new UnitToUnitVoiceChannelGrant( message, duid, aliasList ); case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - return new UnitToUnitVoiceChannelGrantUpdate( message, - duid, aliasList ); + return new UnitToUnitVoiceChannelGrantUpdate( message, duid, aliasList ); default: return new TSBKMessage( message, duid, aliasList ); } @@ -235,4 +199,4 @@ public class TSBKMessageFactory return new TSBKMessage( message, duid, aliasList ); } } -} +} \ No newline at end of file diff --git a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java index ca31b7c6..4fb02ca0 100644 --- a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java +++ b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java @@ -156,4 +156,4 @@ public class IdentifierUpdateTDMA extends IdentifierUpdate { HALF_RATE, FULL_RATE; } -} +} \ No newline at end of file diff --git a/src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java index c59d8f12..ecb9d25e 100644 --- a/src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java @@ -271,4 +271,4 @@ public class SyncBroadcast extends TSBKMessage { return mMessage.getInt( MICRO_SLOTS ); } -} +} \ No newline at end of file diff --git a/src/module/decode/p25/message/vselp/VSELP1Message.java b/src/module/decode/p25/message/vselp/VSELP1Message.java new file mode 100644 index 00000000..8f0ca8c8 --- /dev/null +++ b/src/module/decode/p25/message/vselp/VSELP1Message.java @@ -0,0 +1,42 @@ +package module.decode.p25.message.vselp; + +import module.decode.p25.message.P25Message; +import module.decode.p25.reference.DataUnitID; +import alias.AliasList; +import bits.BinaryMessage; + +public class VSELP1Message extends P25Message +{ + /** + * Motorola VSELP audio message 1. + * + * @param message + * @param duid + * @param aliasList + */ + public VSELP1Message( BinaryMessage message, DataUnitID duid, AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( getMessageStub() ); + sb.append( " " ); + sb.append( mMessage.toString() ); + + return sb.toString(); + } + + public String getMessageStub() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "VSELP 1 VOICE FRAME" ); + + return sb.toString(); + } +} diff --git a/src/module/decode/p25/message/vselp/VSELP2Message.java b/src/module/decode/p25/message/vselp/VSELP2Message.java new file mode 100644 index 00000000..e6e1b1c6 --- /dev/null +++ b/src/module/decode/p25/message/vselp/VSELP2Message.java @@ -0,0 +1,42 @@ +package module.decode.p25.message.vselp; + +import alias.AliasList; +import bits.BinaryMessage; +import module.decode.p25.message.P25Message; +import module.decode.p25.reference.DataUnitID; + +public class VSELP2Message extends P25Message +{ + /** + * Motorola VSELP audio message 1. + * + * @param message + * @param duid + * @param aliasList + */ + public VSELP2Message( BinaryMessage message, DataUnitID duid, AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( getMessageStub() ); + sb.append( " " ); + sb.append( mMessage.toString() ); + + return sb.toString(); + } + + public String getMessageStub() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "VSELP 2 VOICE FRAME" ); + + return sb.toString(); + } +} diff --git a/src/module/decode/p25/reference/DataUnitID.java b/src/module/decode/p25/reference/DataUnitID.java index c52e9af3..eba7fa42 100644 --- a/src/module/decode/p25/reference/DataUnitID.java +++ b/src/module/decode/p25/reference/DataUnitID.java @@ -14,7 +14,7 @@ public enum DataUnitID TSBK1( 7, 260, false, "TSBK1", "Trunking Signaling Block" ), TSBK2( 7, 260, false, "TSBK2", "Trunking Signaling Block" ), TSBK3( 7, 260, false, "TSBK3", "Trunking Signaling Block" ), - VSELP2( 9, 1680, false, "VSEL1", "Motorola VSELP Unit 1" ), + VSELP2( 9, 1680, false, "VSEL2", "Motorola VSELP Unit 2" ), LDU2 ( 10, 1632, true, "LDU2 ", "Logical Link Data Unit 2" ), PDU0 ( 12, 260, false, "PDU0 ", "Packet Header Data Unit" ), PDU1 ( 12, 356, false, "PDU1 ", "Packet Data Unit-Unconfirmed" ), @@ -75,8 +75,12 @@ public enum DataUnitID return TDU; case 5: return LDU1; + case 6: + return VSELP1; case 7: return TSBK1; + case 9: + return VSELP2; case 10: return LDU2; case 12: diff --git a/src/module/decode/p25/reference/PDUType.java b/src/module/decode/p25/reference/PDUType.java index 20acb4ab..3ff65849 100644 --- a/src/module/decode/p25/reference/PDUType.java +++ b/src/module/decode/p25/reference/PDUType.java @@ -17,7 +17,7 @@ public enum PDUType PDU_TYPE_11( "PDU TYPE 11 UNKNOWN", 11 ), /* Inbound */ - SN_ACTIVATE_TDS_CONTEXT_REQUEST( "SN-ACTIVATE TDS CONTEXT REQUEST", 0 ), + SNDCP_ACTIVATE_TDS_CONTEXT_REQUEST( "SNDCP-ACTIVATE TDS CONTEXT REQUEST", 0 ), UNKNOWN( "UNKNOWN", -1 ); @@ -48,9 +48,9 @@ public enum PDUType } else if( value == 0 && !outbound ) { - return SN_ACTIVATE_TDS_CONTEXT_REQUEST; + return SNDCP_ACTIVATE_TDS_CONTEXT_REQUEST; } return UNKNOWN; } -} +} \ No newline at end of file diff --git a/src/module/decode/p25/reference/ServiceAccessPoint.java b/src/module/decode/p25/reference/ServiceAccessPoint.java index 1040aa7c..ea1c735d 100644 --- a/src/module/decode/p25/reference/ServiceAccessPoint.java +++ b/src/module/decode/p25/reference/ServiceAccessPoint.java @@ -2,7 +2,7 @@ package module.decode.p25.reference; public enum ServiceAccessPoint { - UNENCRYPTED_USER_DATA( "UNENCRYPTED USER DATA", 0 ), + UNENCRYPTED_USER_DATA( "USER DATA", 0 ), ENCRYPTED_USER_DATA( "ENCRYPTED USER DATA", 1 ), CIRCUIT_DATA( "CIRCUIT DATA", 2 ), CIRCUIT_DATA_CONTROL( "CIRCUIT DATA CONTROL", 3 ), diff --git a/src/module/decode/passport/PassportDecoder.java b/src/module/decode/passport/PassportDecoder.java index b6d46a2b..859e7c9d 100644 --- a/src/module/decode/passport/PassportDecoder.java +++ b/src/module/decode/passport/PassportDecoder.java @@ -23,12 +23,13 @@ import instrument.tap.TapGroup; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import module.decode.Decoder; import module.decode.DecoderType; import module.decode.config.DecodeConfiguration; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IUnFilteredRealBufferListener; import sample.real.RealBuffer; import alias.AliasList; import bits.MessageFramer; @@ -36,7 +37,7 @@ import bits.SyncPattern; import dsp.fsk.LTRFSKDecoder; public class PassportDecoder extends Decoder - implements IRealBufferListener, Instrumentable + implements IUnFilteredRealBufferListener, Instrumentable { public static final int PASSPORT_MESSAGE_LENGTH = 68; public static final int PASSPORT_SYNC_LENGTH = 9; @@ -47,8 +48,11 @@ public class PassportDecoder extends Decoder private List mAvailableTaps; - public PassportDecoder( DecodeConfiguration config, - AliasList aliasList ) + /** + * Passport Decoder. Decodes unfiltered (e.g. demodulated but with no DC or + * audio filtering) samples and produces Passport messages. + */ + public PassportDecoder( DecodeConfiguration config, AliasList aliasList ) { mPassportFSKDecoder = new LTRFSKDecoder(); @@ -60,11 +64,11 @@ public class PassportDecoder extends Decoder mPassportMessageProcessor = new PassportMessageProcessor( aliasList ); mPassportMessageFramer.addMessageListener( mPassportMessageProcessor ); - mPassportMessageProcessor.setMessageListener( mMessageBroadcaster ); + mPassportMessageProcessor.setMessageListener( this ); } @Override - public Listener getRealBufferListener() + public Listener getUnFilteredRealBufferListener() { return mPassportFSKDecoder; } @@ -107,7 +111,7 @@ public class PassportDecoder extends Decoder } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/passport/PassportDecoderEditor.java b/src/module/decode/passport/PassportDecoderEditor.java new file mode 100644 index 00000000..e40dd11a --- /dev/null +++ b/src/module/decode/passport/PassportDecoderEditor.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.passport; + +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.ValidatingEditor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JCheckBox; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import module.decode.config.DecodeConfiguration; +import net.miginfocom.swing.MigLayout; +import controller.channel.Channel; + +public class PassportDecoderEditor extends ValidatingEditor +{ + private static final long serialVersionUID = 1L; + + private JCheckBox mAFC; + private JSlider mAFCMaximumCorrection; + + public PassportDecoderEditor() + { + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0,wrap 2", "[right][grow,fill]", "" ) ); + + mAFC = new JCheckBox( "AFC: 3000 Hz" ); + mAFC.setEnabled( false ); + mAFC.setToolTipText( "AFC automatically adjusts the center frequency of the channel to " + + "correct/compensate for inaccuracies and frequency drift in the tuner" ); + mAFC.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + setModified( true ); + + if( mAFC.isSelected() && !mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( true ); + } + else if( !mAFC.isSelected() && mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( false ); + } + } + } ); + + add( mAFC ); + + mAFCMaximumCorrection = new JSlider( 0, 7000, 3000 ); + mAFCMaximumCorrection.setEnabled( false ); + mAFCMaximumCorrection.setToolTipText( "Maximum AFC frequency correction (0 - 15kHz)" ); + mAFCMaximumCorrection.setMajorTickSpacing( 2000 ); + mAFCMaximumCorrection.setMinorTickSpacing( 1000 ); + mAFCMaximumCorrection.setPaintTicks( true ); + + mAFCMaximumCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mAFC.setText( "AFC: " + mAFCMaximumCorrection.getValue() + " Hz" ); + setModified( true ); + } + } ); + add( mAFCMaximumCorrection ); + } + + @Override + public void validate( Editor editor ) throws EditorValidationException + { + //No validation + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + DecodeConfigPassport passport = new DecodeConfigPassport(); + + passport.setAFC( mAFC.isSelected() ); + passport.setAFCMaximumCorrection( mAFCMaximumCorrection.getValue() ); + + getItem().setDecodeConfiguration( passport ); + } + + setModified( false ); + } + + private void setControlsEnabled( boolean enabled ) + { + if( mAFC.isEnabled() != enabled ) + { + mAFC.setEnabled( enabled ); + } + } + + @Override + public void setItem( Channel item ) + { + super.setItem( item ); + + if( hasItem() ) + { + setControlsEnabled( true ); + + DecodeConfiguration config = getItem().getDecodeConfiguration(); + + if( config instanceof DecodeConfigPassport ) + { + DecodeConfigPassport passport = (DecodeConfigPassport)config; + + mAFC.setSelected( passport.isAFCEnabled() ); + mAFCMaximumCorrection.setValue( passport.getAFCMaximumCorrection() ); + mAFCMaximumCorrection.setEnabled( passport.isAFCEnabled() ); + setModified( false ); + } + else + { + mAFC.setSelected( false ); + mAFCMaximumCorrection.setValue( DecodeConfiguration.DEFAULT_AFC_MAX_CORRECTION ); + mAFCMaximumCorrection.setEnabled( false ); + setModified( true ); + } + } + else + { + setControlsEnabled( false ); + setModified( false ); + } + } +} diff --git a/src/module/decode/passport/PassportDecoderState.java b/src/module/decode/passport/PassportDecoderState.java index 003d2318..99004efd 100644 --- a/src/module/decode/passport/PassportDecoderState.java +++ b/src/module/decode/passport/PassportDecoderState.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; @@ -75,7 +76,7 @@ public class PassportDecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/passport/PassportEditor.java b/src/module/decode/passport/PassportEditor.java deleted file mode 100644 index 4b39e15d..00000000 --- a/src/module/decode/passport/PassportEditor.java +++ /dev/null @@ -1,31 +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 - ******************************************************************************/ -package module.decode.passport; - -import module.decode.DecodeEditor; -import module.decode.config.DecodeConfiguration; - -public class PassportEditor extends DecodeEditor -{ - private static final long serialVersionUID = 1L; - - public PassportEditor( DecodeConfiguration config ) - { - super( config ); - } -} diff --git a/src/module/decode/state/AlwaysUnsquelchedDecoderState.java b/src/module/decode/state/AlwaysUnsquelchedDecoderState.java index ad74e2f8..3b97c6bb 100644 --- a/src/module/decode/state/AlwaysUnsquelchedDecoderState.java +++ b/src/module/decode/state/AlwaysUnsquelchedDecoderState.java @@ -17,13 +17,11 @@ ******************************************************************************/ package module.decode.state; +import java.util.concurrent.ScheduledExecutorService; + import message.Message; import module.decode.DecoderType; import module.decode.state.DecoderStateEvent.Event; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import audio.metadata.Metadata; import audio.metadata.MetadataType; @@ -33,9 +31,6 @@ import audio.metadata.MetadataType; */ public class AlwaysUnsquelchedDecoderState extends DecoderState { - private final static Logger mLog = - LoggerFactory.getLogger( AlwaysUnsquelchedDecoderState.class ); - private DecoderType mDecoderType; private String mChannelName; @@ -90,7 +85,7 @@ public class AlwaysUnsquelchedDecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { broadcast( new Metadata( MetadataType.TO, mChannelName, false ) ); broadcast( new DecoderStateEvent( this, Event.ALWAYS_UNSQUELCH, State.IDLE ) ); diff --git a/src/module/decode/state/ChangeChannelTimeoutEvent.java b/src/module/decode/state/ChangeChannelTimeoutEvent.java index a2950171..9119e4c9 100644 --- a/src/module/decode/state/ChangeChannelTimeoutEvent.java +++ b/src/module/decode/state/ChangeChannelTimeoutEvent.java @@ -26,4 +26,4 @@ public class ChangeChannelTimeoutEvent extends DecoderStateEvent { return mCallTimeout; } -} +} \ No newline at end of file diff --git a/src/module/decode/state/ChannelCollectionPanel.java b/src/module/decode/state/ChannelCollectionPanel.java deleted file mode 100644 index 685511d1..00000000 --- a/src/module/decode/state/ChannelCollectionPanel.java +++ /dev/null @@ -1,146 +0,0 @@ -package module.decode.state; - -import java.awt.Component; -import java.awt.Point; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.JMenu; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import playlist.PlaylistManager; -import settings.SettingsManager; -import controller.channel.Channel; - -/** - * Channel Collection assembles a primary channel panel and any related decoder - * state panels, provides contextual access to the channel and supports channel - * selection. - */ -public class ChannelCollectionPanel extends JPanel -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = LoggerFactory.getLogger( ChannelCollectionPanel.class ); - - private PlaylistManager mPlaylistManager; - private SettingsManager mSettingsManager; - - private List mChannelPanels = new ArrayList(); - - public ChannelCollectionPanel( PlaylistManager playlistManager, - SettingsManager settingsManager, - Channel channel ) - { - mPlaylistManager = playlistManager; - mSettingsManager = settingsManager; - - init( channel ); - } - - protected void init( Channel channel ) - { - setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]0[]") ); - - setOpaque( false ); - - ChannelStatePanel csp = new ChannelStatePanel( mPlaylistManager, - mSettingsManager, channel ); - - mChannelPanels.add( csp ); - - add( csp, "span,grow" ); - - } - - public Channel getChannel() - { - if( mChannelPanels.size() >= 1 ) - { - return mChannelPanels.get( 0 ).getChannel(); - } - - return null; - } - - public void dispose() - { - for( ChannelStatePanel panel: mChannelPanels ) - { - panel.dispose(); - } - - mChannelPanels.clear(); - - mPlaylistManager = null; - mSettingsManager = null; - } - - /** - * Toggles the selection state of the channel if it matches the channel - * argument. Otherwise, sets the selection state to false. - */ - public void setSelectedChannel( Channel channel ) - { - for( ChannelStatePanel panel: mChannelPanels ) - { - if( panel.getChannel() == channel ) - { - /* Toggle the selection state */ - panel.getChannel().setSelected( !panel.getChannel().isSelected() ); - panel.repaint(); - } - else - { - if( panel.getChannel().isSelected() ) - { - panel.getChannel().setSelected( false ); - panel.repaint(); - } - } - } - } - - /** - * Returns the channel represented by the channel panel at the gui point - * location, to selecting the channel - * - * @param point - current location of the mouse - * - * @return - channel or null - */ - public Channel getChannelAt( Point point ) - { - Component c = this.getComponentAt( point ); - - if( c instanceof ChannelCollectionPanel ) - { - return ((ChannelCollectionPanel)c).getChannel(); - } - else - { - return getChannel(); - } - } - - protected SettingsManager getSettingsManager() - { - return mSettingsManager; - } - - - public JMenu getContextMenu() - { - if( !mChannelPanels.isEmpty() ) - { - return mChannelPanels.get( 0 ).getContextMenu(); - } - - return null; - } -} diff --git a/src/module/decode/state/ChannelList.java b/src/module/decode/state/ChannelList.java index d1fd1b54..24d89a67 100644 --- a/src/module/decode/state/ChannelList.java +++ b/src/module/decode/state/ChannelList.java @@ -19,7 +19,6 @@ package module.decode.state; import java.awt.Color; import java.awt.Component; -import java.awt.Point; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.Map; @@ -29,21 +28,19 @@ import javax.swing.JMenu; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JSeparator; -import javax.swing.SwingUtilities; import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import playlist.PlaylistManager; import settings.ColorSetting.ColorSettingName; import settings.ColorSettingMenuItem; import settings.ColorSettingResetMenuItem; import settings.SettingsManager; +import audio.AudioPanel; import controller.channel.Channel; import controller.channel.ChannelEvent; +import controller.channel.ChannelEvent.Event; import controller.channel.ChannelEventListener; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; /** * Gui wrapper for the list of currently processing primary channels @@ -52,29 +49,37 @@ public class ChannelList extends JPanel implements ChannelEventListener { private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger( ChannelList.class ); + private Map mDisplayedPanels = new ConcurrentHashMap<>(); - private Map mDisplayedPanels = - new ConcurrentHashMap(); - - private PlaylistManager mPlaylistManager; + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; private SettingsManager mSettingsManager; + private AudioPanel mAudioPanel; - public ChannelList( PlaylistManager playlistManager, - SettingsManager settingsManager ) + public ChannelList( ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + SettingsManager settingsManager, + AudioPanel audioPanel ) { - mPlaylistManager = playlistManager; + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; mSettingsManager = settingsManager; + mAudioPanel = audioPanel; init(); } private void init() { - setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]") ); + setLayout( new MigLayout( "insets 0 0 0 0,wrap 1", "[grow,fill]", "[]0[]") ); setBackground( Color.BLACK ); addMouseListener( new ListSelectionListener() ); + + add( mAudioPanel ); + + JSeparator separator = new JSeparator( JSeparator.HORIZONTAL ); + add( separator ); } @Override @@ -82,10 +87,10 @@ public class ChannelList extends JPanel implements ChannelEventListener { switch( event.getEvent() ) { - case CHANNEL_PROCESSING_STARTED: + case NOTIFICATION_PROCESSING_START: addChannelPanel( event.getChannel() ); break; - case CHANNEL_PROCESSING_STOPPED: + case NOTIFICATION_PROCESSING_STOP: removeChannelPanel( event.getChannel() ); break; default: @@ -97,12 +102,12 @@ public class ChannelList extends JPanel implements ChannelEventListener { if( !mDisplayedPanels.containsKey( channel ) ) { - ChannelCollectionPanel panel = new ChannelCollectionPanel( - mPlaylistManager, mSettingsManager, channel ); + ChannelPanel channelPanel = new ChannelPanel( mChannelModel, + mChannelProcessingManager, mSettingsManager, channel ); - add( panel, "wrap" ); + add( channelPanel ); - mDisplayedPanels.put( channel, panel ); + mDisplayedPanels.put( channel, channelPanel ); revalidate(); repaint(); @@ -113,7 +118,7 @@ public class ChannelList extends JPanel implements ChannelEventListener { if( mDisplayedPanels.containsKey( channel ) ) { - ChannelCollectionPanel panel = mDisplayedPanels.remove( channel ); + ChannelPanel panel = mDisplayedPanels.remove( channel ); if( panel != null ) { @@ -127,18 +132,6 @@ public class ChannelList extends JPanel implements ChannelEventListener } } - public void setSelectedChannel( Channel channel ) - { - synchronized( mDisplayedPanels ) - { - /* Send channel selection to each channel panel */ - for( ChannelCollectionPanel panel: mDisplayedPanels.values() ) - { - panel.setSelectedChannel( channel ); - } - } - } - public class ListSelectionListener implements MouseListener { @Override @@ -146,18 +139,22 @@ public class ChannelList extends JPanel implements ChannelEventListener { Component component = getComponentAt( e.getPoint() ); - if( component instanceof ChannelCollectionPanel ) + if( component instanceof ChannelPanel ) { - ChannelCollectionPanel panel = (ChannelCollectionPanel)component; + ChannelPanel panel = (ChannelPanel)component; - Point translatedPoint = SwingUtilities - .convertPoint( e.getComponent(), e.getPoint(), panel ); - - Channel channel = panel.getChannelAt( translatedPoint ); + Channel channel = panel.getChannel(); if( e.getButton() == MouseEvent.BUTTON1 ) { - setSelectedChannel( channel ); + if( channel.isSelected() ) + { + mChannelModel.broadcast( new ChannelEvent( channel, Event.REQUEST_DESELECT ) ); + } + else + { + mChannelModel.broadcast( new ChannelEvent( channel, Event.REQUEST_SELECT ) ); + } } else if( e.getButton() == MouseEvent.BUTTON3 ) { @@ -254,6 +251,20 @@ public class ChannelList extends JPanel implements ChannelEventListener idleMenu.add( new ColorSettingResetMenuItem( mSettingsManager, ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE ) ); + JMenu noTunerMenu = new JMenu( "No Tuner" ); + channelsMenu.add( noTunerMenu ); + + noTunerMenu.add( new ColorSettingMenuItem( mSettingsManager, + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ) ); + noTunerMenu.add( new ColorSettingResetMenuItem( mSettingsManager, + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ) ); + noTunerMenu.add( new JSeparator() ); + + noTunerMenu.add( new ColorSettingMenuItem( mSettingsManager, + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ) ); + noTunerMenu.add( new ColorSettingResetMenuItem( mSettingsManager, + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ) ); + JMenu labelMenu = new JMenu( "Labels" ); colorMenu.add( labelMenu ); diff --git a/src/module/decode/state/ChannelStatePanel.java b/src/module/decode/state/ChannelPanel.java similarity index 82% rename from src/module/decode/state/ChannelStatePanel.java rename to src/module/decode/state/ChannelPanel.java index b1f12839..335b2588 100644 --- a/src/module/decode/state/ChannelStatePanel.java +++ b/src/module/decode/state/ChannelPanel.java @@ -14,14 +14,13 @@ import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JPanel; +import module.ProcessingChain; +import module.decode.DecoderFactory; +import net.miginfocom.swing.MigLayout; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import controller.channel.Channel; -import controller.channel.ChannelUtils; -import module.decode.DecoderFactory; -import net.miginfocom.swing.MigLayout; -import playlist.PlaylistManager; import record.RecorderType; import record.config.RecordConfiguration; import sample.Listener; @@ -30,17 +29,22 @@ import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingChangeListener; import settings.SettingsManager; +import controller.channel.Channel; +import controller.channel.ChannelEvent; +import controller.channel.ChannelEvent.Event; +import controller.channel.ChannelEventListener; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; +import controller.channel.ChannelUtils; -public class ChannelStatePanel extends JPanel - implements Listener, SettingChangeListener +public class ChannelPanel extends JPanel + implements Listener, + SettingChangeListener, + ChannelEventListener { private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger( ChannelStatePanel.class ); - - private static final boolean ENABLED = true; - private static final boolean DISABLED = false; - private static final boolean BROADCAST_CHANGE = true; + private final static Logger mLog = LoggerFactory.getLogger( ChannelPanel.class ); private Font mFontDetails = new Font( Font.MONOSPACED, Font.PLAIN, 12 ); private Font mFontDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 12 ); @@ -71,11 +75,13 @@ public class ChannelStatePanel extends JPanel private JLabel mSiteLabel; private JLabel mChannelLabel; - private PlaylistManager mPlaylistManager; protected SettingsManager mSettingsManager; private Channel mChannel; + private ChannelState mChannelState; private List mDecoderPanels = new ArrayList<>(); + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; /** * Gui component to a channel state. Provides System, Site and channel @@ -83,26 +89,40 @@ public class ChannelStatePanel extends JPanel * * Updates color background according to current channel state. */ - public ChannelStatePanel( PlaylistManager playlistManager, - SettingsManager settingsManager, - Channel channel ) + public ChannelPanel( ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + SettingsManager settingsManager, + Channel channel ) { - mPlaylistManager = playlistManager; - + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; mSettingsManager = settingsManager; - mSettingsManager.addListener( this ); - mChannel = channel; - mChannel.getChannelState().setChangedAttributeListener( this ); - init(); + ProcessingChain processingChain = + channelProcessingManager.getProcessingChain( mChannel ); - for( DecoderState decoderState: getChannel().getProcessingChain().getDecoderStates() ) - { - DecoderPanel panel = DecoderFactory.getDecoderPanel( mSettingsManager, decoderState ); - mDecoderPanels.add( panel ); - add( panel, "grow,span" ); - } + if( processingChain != null ) + { + mChannelState = processingChain.getChannelState(); + + init(); + + for( DecoderState decoderState: processingChain.getDecoderStates() ) + { + DecoderPanel panel = DecoderFactory.getDecoderPanel( mSettingsManager, decoderState ); + mDecoderPanels.add( panel ); + add( panel, "grow,span" ); + } + + mChannelState.setChangedAttributeListener( this ); + mChannelModel.addListener( this ); + mSettingsManager.addListener( this ); + } + else + { + mLog.error( "Processing Chain was null!" ); + } } private void init() @@ -111,12 +131,12 @@ public class ChannelStatePanel extends JPanel setLayout( new MigLayout( "insets 1 1 1 1", "[grow,fill]", "[]0[]0[]") ); - mSystemLabel = new JLabel( mChannel.getSystem().getName() ); + mSystemLabel = new JLabel( mChannel.getSystem() ); mSystemLabel.setFont( mFontDetails ); mSystemLabel.setForeground( mColorLabelDetails ); add( mSystemLabel ); - mSiteLabel = new JLabel( mChannel.getSite().getName() ); + mSiteLabel = new JLabel( mChannel.getSite() ); mSiteLabel.setFont( mFontDetails ); mSiteLabel.setForeground( mColorLabelDetails ); add( mSiteLabel ); @@ -126,8 +146,7 @@ public class ChannelStatePanel extends JPanel mChannelLabel.setForeground( mColorLabelDetails ); add( mChannelLabel, "wrap" ); - mStateLabel = new JLabel( mChannel.getChannelState().getState() - .getDisplayValue() ); + mStateLabel = new JLabel( mChannelState.getState().getDisplayValue() ); mStateLabel.setFont( mFontDecoder ); mStateLabel.setForeground( mColorLabelDecoder ); add( mStateLabel ); @@ -161,8 +180,6 @@ public class ChannelStatePanel extends JPanel mOptionsLabel.setFont( mFontDetails ); mOptionsLabel.setForeground( mColorLabelAuxDecoder ); add( mOptionsLabel, "wrap" ); - -// add( new JSeparator( JSeparator.HORIZONTAL ), "span,growx" ); } public Channel getChannel() @@ -172,12 +189,16 @@ public class ChannelStatePanel extends JPanel public void dispose() { - mPlaylistManager = null; + mChannelModel.removeListener( this ); + mChannelModel = null; + mChannelProcessingManager = null; + mSettingsManager.removeListener( this ); mSettingsManager = null; - mChannel.getChannelState().removeChangedAttributeListener(); + mChannelState.removeChangedAttributeListener(); + mChannelState = null; mChannel = null; for( DecoderPanel panel: mDecoderPanels ) @@ -192,8 +213,6 @@ public class ChannelStatePanel extends JPanel @Override public void receive( final ChangedAttribute changedAttribute ) { - final ChannelState state = mChannel.getChannelState(); - EventQueue.invokeLater( new Runnable() { @Override @@ -202,9 +221,9 @@ public class ChannelStatePanel extends JPanel switch( changedAttribute ) { case CHANNEL_STATE: - if( state != null && state.getState() != null ) + if( mChannelState != null && mChannelState.getState() != null ) { - mStateLabel.setText( state.getState().getDisplayValue() ); + mStateLabel.setText( mChannelState.getState().getDisplayValue() ); } break; case SOURCE: @@ -214,7 +233,7 @@ public class ChannelStatePanel extends JPanel case CHANNEL_NAME: case SITE_NAME: case SYSTEM_NAME: - mChannelLabel.setText( mChannel.getChannelDisplayName() ); + mChannelLabel.setText( mChannel.toString() ); break; default: break; @@ -238,11 +257,9 @@ public class ChannelStatePanel extends JPanel final Channel channel = mChannel; - final ChannelState channelState = ( channel == null ? null : channel.getChannelState() ); - - if( channelState != null ) + if( mChannelState != null ) { - switch( channelState.getState() ) + switch( mChannelState.getState() ) { case CALL: p = getGradient( mColorTopCall, mColorMiddleCall ); @@ -260,7 +277,7 @@ public class ChannelStatePanel extends JPanel default: p = getGradient( mColorTopIdle, mColorMiddleIdle ); break; - } + } } if( p == null ) @@ -419,7 +436,17 @@ public class ChannelStatePanel extends JPanel public JMenu getContextMenu() { - return ChannelUtils.getContextMenu( mPlaylistManager, mChannel, - ChannelStatePanel.this ); + return ChannelUtils.getContextMenu( mChannelModel, mChannelProcessingManager, + mChannel, ChannelPanel.this ); + } + + @Override + public void channelChanged( ChannelEvent event ) + { + if( event.getEvent() == Event.NOTIFICATION_SELECTION_CHANGE && + event.getChannel() == mChannel ) + { + repaint(); + } } } diff --git a/src/module/decode/state/ChannelState.java b/src/module/decode/state/ChannelState.java index 7f00a42b..1b5f89d1 100644 --- a/src/module/decode/state/ChannelState.java +++ b/src/module/decode/state/ChannelState.java @@ -18,32 +18,30 @@ package module.decode.state; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; + +import module.Module; +import module.decode.config.DecodeConfiguration; +import module.decode.event.CallEvent; +import module.decode.event.ICallEventProvider; +import module.decode.state.DecoderStateEvent.Event; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sample.Listener; import audio.metadata.IMetadataProvider; import audio.metadata.Metadata; import audio.metadata.MetadataReset; import audio.metadata.MetadataType; import audio.squelch.ISquelchStateProvider; import audio.squelch.SquelchState; -import controller.ThreadPoolManager; -import controller.ThreadPoolManager.ThreadType; import controller.channel.Channel.ChannelType; -import module.Module; -import module.decode.config.DecodeConfiguration; -import module.decode.event.CallEvent; -import module.decode.event.ICallEventProvider; -import module.decode.state.DecoderStateEvent.Event; -import sample.Listener; -public class ChannelState extends Module implements ICallEventProvider, - IChangedAttributeProvider, IDecoderStateEventListener, - IDecoderStateEventProvider, IMetadataProvider, ISquelchStateProvider +public class ChannelState extends Module implements ICallEventProvider, IChangedAttributeProvider, + IDecoderStateEventListener, IDecoderStateEventProvider, IMetadataProvider, ISquelchStateProvider { private final static Logger mLog = LoggerFactory.getLogger( ChannelState.class ); @@ -65,19 +63,16 @@ public class ChannelState extends Module implements ICallEventProvider, private boolean mSelected = false; private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY; - private long mTrafficChannelFadeTimeout = - DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000; + private long mTrafficChannelFadeTimeout = DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000; private long mFadeTimeout; private long mEndTimeout; private StateMonitor mStateMonitor = new StateMonitor(); - private AtomicBoolean mMonitoring = new AtomicBoolean(); private ScheduledFuture mStateMonitorFuture; - private ThreadPoolManager mThreadPoolManager; private ChannelType mChannelType; - private TrafficChannelStatusListener mTrafficChannelStatusListener; + private TrafficChannelManager mTrafficChannelEndListener; private CallEvent mTrafficChannelCallEvent; /** @@ -106,9 +101,8 @@ public class ChannelState extends Module implements ICallEventProvider, * ended, while continuing to display the call details for the user * TEARDOWN Indicates a traffic channel that will be torn down for reuse. */ - public ChannelState( ThreadPoolManager manager, ChannelType channelType ) + public ChannelState( ChannelType channelType ) { - mThreadPoolManager = manager; mChannelType = channelType; } @@ -126,23 +120,22 @@ public class ChannelState extends Module implements ICallEventProvider, } @Override - public void start() + public void start( ScheduledExecutorService executor ) { - //Start a monitoring task to check for FADE and END event timeouts - if( mMonitoring.compareAndSet( false, true ) ) + if( mStateMonitorFuture == null && mStateMonitor != null ) { - if( mStateMonitorFuture == null && mStateMonitor != null ) + if( mTrafficChannelEndListener != null ) { - try - { - mStateMonitorFuture = mThreadPoolManager.scheduleFixedRate( - ThreadType.DECODER, mStateMonitor, 20, TimeUnit.MILLISECONDS ); - } - catch( RejectedExecutionException ree ) - { - mLog.error( "state monitor scheduled task rejected", ree ); - mMonitoring.set( false ); - } + setState( State.CALL ); + } + + try + { + mStateMonitorFuture = executor.scheduleAtFixedRate( mStateMonitor, 0, 20, TimeUnit.MILLISECONDS ); + } + catch( RejectedExecutionException ree ) + { + mLog.error( "state monitor scheduled task rejected", ree ); } } } @@ -150,23 +143,29 @@ public class ChannelState extends Module implements ICallEventProvider, @Override public void stop() { - mSquelchLocked = false; - - //Shutdown the state montoring task - if( mMonitoring.compareAndSet( true, false ) ) + if( mStateMonitorFuture != null ) { - if( mStateMonitorFuture != null ) - { - boolean success = mThreadPoolManager.cancel( mStateMonitorFuture ); - - if( !success ) - { - mLog.error("Couldn't stop monitoring scheduled future" ); - } - } + boolean success = mStateMonitorFuture.cancel( true ); - mStateMonitorFuture = null; + if( !success ) + { + mLog.error("Couldn't stop monitoring scheduled future" ); + } } + + mTrafficChannelEndListener = null; + + if( mTrafficChannelCallEvent != null ) + { + mTrafficChannelCallEvent.end(); + broadcast( mTrafficChannelCallEvent ); + } + + mTrafficChannelCallEvent = null; + + mStateMonitorFuture = null; + + mSquelchLocked = false; } public void dispose() @@ -175,7 +174,6 @@ public class ChannelState extends Module implements ICallEventProvider, mChangedAttributeListener = null; mDecoderStateListener = null; mSquelchStateListener = null; - mThreadPoolManager = null; mStateMonitor = null; } @@ -205,7 +203,7 @@ public class ChannelState extends Module implements ICallEventProvider, if( mChannelType == ChannelType.TRAFFIC ) { - mFadeTimeout = mTrafficChannelFadeTimeout; + mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout; } } @@ -237,6 +235,7 @@ public class ChannelState extends Module implements ICallEventProvider, { mFadeTimeout = System.currentTimeMillis() + mStandardChannelFadeTimeout; } + } /** @@ -389,23 +388,9 @@ public class ChannelState extends Module implements ICallEventProvider, broadcast( ChangedAttribute.CHANNEL_STATE ); - if( mTrafficChannelCallEvent != null ) + if( mTrafficChannelEndListener != null ) { - mTrafficChannelCallEvent.end(); - - broadcast( mTrafficChannelCallEvent ); - mTrafficChannelCallEvent = null; - } - - if( mTrafficChannelStatusListener != null ) - { - mTrafficChannelStatusListener.callEnd(); - mTrafficChannelStatusListener = null; - } - else - { - mLog.error( "Traffic channel status listener was null - couldn't " - + "teardown the channel"); + mTrafficChannelEndListener.callEnd( mTrafficChannelCallEvent.getChannel() ); } } @@ -530,11 +515,11 @@ public class ChannelState extends Module implements ICallEventProvider, * Registers a listener to be notified when a traffic channel call event is * completed, so that the listener can perform call tear-down */ - public void configureAsTrafficChannel( TrafficChannelStatusListener listener, - TrafficChannelAllocationEvent allocationEvent ) + public void configureAsTrafficChannel( TrafficChannelManager manager, CallEvent callEvent ) { - mTrafficChannelStatusListener = listener; - mTrafficChannelCallEvent = allocationEvent.getCallEvent(); + mTrafficChannelEndListener = manager; + + mTrafficChannelCallEvent = callEvent; /* Broadcast the call event details as metadata for the audio manager */ String channel = mTrafficChannelCallEvent.getChannel(); @@ -543,9 +528,9 @@ public class ChannelState extends Module implements ICallEventProvider, { broadcast( new Metadata( MetadataType.CHANNEL_NUMBER, channel, true ) ); } - + broadcast( new Metadata( MetadataType.PROTOCOL, - mTrafficChannelCallEvent.getDecoderType().getDisplayString(), true ) ); + mTrafficChannelCallEvent.getDecoderType().getDisplayString(), true ) ); String details = mTrafficChannelCallEvent.getDetails(); @@ -574,13 +559,12 @@ public class ChannelState extends Module implements ICallEventProvider, if( frequency > 0 ) { - broadcast( new Metadata( MetadataType.FREQUENCY, - String.valueOf( frequency ), true ) ); + broadcast( new Metadata( MetadataType.FREQUENCY, String.valueOf( frequency ), true ) ); } /* Rebroadcast the allocation event so that the internal decoder states * can self-configure with the call event details */ - broadcast( allocationEvent ); + broadcast( mTrafficChannelCallEvent ); } /** @@ -662,7 +646,6 @@ public class ChannelState extends Module implements ICallEventProvider, if( State.CALL_STATES.contains( mState ) && mFadeTimeout <= System.currentTimeMillis() ) { - mState = State.FADE; processFadeState(); } else if( mState == State.FADE && diff --git a/src/module/decode/state/DecoderState.java b/src/module/decode/state/DecoderState.java index 693986df..469f4be6 100644 --- a/src/module/decode/state/DecoderState.java +++ b/src/module/decode/state/DecoderState.java @@ -7,10 +7,6 @@ import module.decode.DecoderType; import module.decode.event.ActivitySummaryProvider; import module.decode.event.CallEvent; import module.decode.event.ICallEventProvider; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sample.Broadcaster; import sample.Listener; import alias.AliasList; @@ -33,8 +29,6 @@ public abstract class DecoderState extends Module IMessageListener, IMetadataProvider { - private final static Logger mLog = LoggerFactory.getLogger( DecoderState.class ); - /* This has to be a broadcaster in order for references to persist */ private Broadcaster mCallEventBroadcaster = new Broadcaster<>(); private Listener mChangedAttributeListener; diff --git a/src/module/decode/state/DecoderStateEvent.java b/src/module/decode/state/DecoderStateEvent.java index fa60b7d8..a4ebd68f 100644 --- a/src/module/decode/state/DecoderStateEvent.java +++ b/src/module/decode/state/DecoderStateEvent.java @@ -65,4 +65,4 @@ public class DecoderStateEvent START, TRAFFIC_CHANNEL_ALLOCATION; } -} +} \ No newline at end of file diff --git a/src/module/decode/state/State.java b/src/module/decode/state/State.java index 141c5ea6..3c9f5189 100644 --- a/src/module/decode/state/State.java +++ b/src/module/decode/state/State.java @@ -1,3 +1,20 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ package module.decode.state; import java.util.EnumSet; @@ -86,11 +103,8 @@ public enum State private String mDisplayValue; - public static final EnumSet CALL_STATES = - EnumSet.of( CALL, CONTROL, DATA, ENCRYPTED ); - - public static final EnumSet IDLE_STATES = - EnumSet.of( IDLE, FADE ); + public static final EnumSet CALL_STATES = EnumSet.of( CALL, CONTROL, DATA, ENCRYPTED ); + public static final EnumSet IDLE_STATES = EnumSet.of( IDLE, FADE ); private State( String displayValue ) { @@ -103,4 +117,4 @@ public enum State { return mDisplayValue; } -} +} \ No newline at end of file diff --git a/src/module/decode/state/TrafficChannelManager.java b/src/module/decode/state/TrafficChannelManager.java index 43a25991..452d5680 100644 --- a/src/module/decode/state/TrafficChannelManager.java +++ b/src/module/decode/state/TrafficChannelManager.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledExecutorService; import module.Module; import module.decode.config.DecodeConfiguration; @@ -15,18 +16,19 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import record.config.RecordConfiguration; -import sample.Broadcaster; import sample.Listener; import source.config.SourceConfigTuner; import source.tuner.TunerChannel; import source.tuner.TunerChannel.Type; import alias.Alias; -import alias.priority.Priority; -import controller.ResourceManager; +import alias.id.priority.Priority; import controller.channel.Channel; import controller.channel.Channel.ChannelType; -import controller.site.Site; -import controller.system.System; +import controller.channel.ChannelEvent; +import controller.channel.ChannelEvent.Event; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; +import controller.channel.TrafficChannelEvent; public class TrafficChannelManager extends Module implements ICallEventProvider, IDecoderStateEventListener @@ -41,15 +43,14 @@ public class TrafficChannelManager extends Module private DecoderStateEventListener mEventListener = new DecoderStateEventListener(); private Listener mCallEventListener; + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; private DecodeConfiguration mDecodeConfiguration; private RecordConfiguration mRecordConfiguration; - private ResourceManager mResourceManager; - private System mSystem; - private Site mSite; + private String mSystem; + private String mSite; private String mAliasListName; - private long mTrafficChannelTimeout; private CallEvent mPreviousDoNotMonitorCallEvent; - private boolean mShuttingDown = false; /** * Monitors call events and allocates traffic decoder channels in response @@ -69,22 +70,23 @@ public class TrafficChannelManager extends Module * @param trafficChannelPoolSize - maximum number of allocated traffic channels * in the pool */ - public TrafficChannelManager( ResourceManager resourceManager, + public TrafficChannelManager( ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, DecodeConfiguration decodeConfiguration, RecordConfiguration recordConfiguration, - System system, - Site site, + String system, + String site, String aliasListName, long trafficChannelTimeout, int trafficChannelPoolSize ) { - mResourceManager = resourceManager; + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; mDecodeConfiguration = decodeConfiguration; mRecordConfiguration = recordConfiguration; mSystem = system; mSite = site; mAliasListName = aliasListName; - mTrafficChannelTimeout = trafficChannelTimeout; mTrafficChannelPoolMaximumSize = trafficChannelPoolSize; } @@ -93,14 +95,14 @@ public class TrafficChannelManager extends Module { for( Channel trafficChannel: mTrafficChannelPool ) { - trafficChannel.setEnabled( false ); - trafficChannel.dispose(); + mChannelModel.broadcast( new ChannelEvent( trafficChannel, Event.REQUEST_DISABLE ) ); } - + mTrafficChannelPool.clear(); + mTrafficChannelsInUse.clear(); + mCallEventListener = null; - mResourceManager = null; mDecodeConfiguration = null; mPreviousDoNotMonitorCallEvent = null; } @@ -124,7 +126,7 @@ public class TrafficChannelManager extends Module { for( Channel configuredChannel: mTrafficChannelPool ) { - if( !configuredChannel.isProcessing() ) + if( !configuredChannel.getEnabled() ) { channel = configuredChannel; break; @@ -139,39 +141,22 @@ public class TrafficChannelManager extends Module channel.setRecordConfiguration( mRecordConfiguration ); - channel.setResourceManager( mResourceManager ); - - channel.setTrafficChannelTimeout( mTrafficChannelTimeout ); - channel.setAliasListName( mAliasListName ); + mChannelModel.addChannel( channel ); + mTrafficChannelPool.add( channel ); } - /* If we have a configured channel, start it and track it */ + /* If we have a configured channel, update metadata */ if( channel != null ) { channel.setSourceConfiguration( new SourceConfigTuner( tunerChannel ) ); - - channel.setSystem( mSystem, false ); - channel.setSite( mSite, false ); + channel.setSystem( mSystem ); + channel.setSite( mSite ); channel.setName( channelNumber ); - channel.setEnabled( true ); - - /* Check the channel obtained a source and is currently processing */ - if( channel.isProcessing() ) - { - mTrafficChannelsInUse.put( channelNumber, channel ); - } - else - { - channel.setEnabled( false ); - - channel.setSystem( mSystem, false ); - channel.setSite( mSite, false ); - channel.setName( "Traffic" ); - } + mChannelModel.broadcast( new ChannelEvent( channel, Event.NOTIFICATION_CONFIGURATION_CHANGE ) ); } } @@ -212,33 +197,26 @@ public class TrafficChannelManager extends Module } else if( frequency > 0 ) { - Channel channel = getChannel( callEvent.getChannel(), - new TunerChannel( Type.TRAFFIC, frequency, - mDecodeConfiguration.getDecoderType() - .getChannelBandwidth() ) ); + Channel channel = getChannel( callEvent.getChannel(), new TunerChannel( Type.TRAFFIC, + frequency, mDecodeConfiguration.getDecoderType().getChannelBandwidth() ) ); if( channel != null ) { - if( channel.isProcessing() ) + TrafficChannelEvent trafficChannelEvent = + new TrafficChannelEvent( this, channel, Event.REQUEST_ENABLE, callEvent ); + + //Request to enable the channel + mChannelModel.broadcast( trafficChannelEvent ); + + if( channel.getEnabled() ) { - callEvent.setDetails( "TRAFFIC CHANNEL ALLOCATED" ); - - ChannelState state = channel.getChannelState(); - - /* Register as a listener to be notified when the call - * is completed */ - state.configureAsTrafficChannel( - new TrafficChannelStatusListener( this, - callEvent.getChannel() ), event ); - - /* Set state to call so that audio squelch state is correct */ - state.setState( State.CALL ); + mTrafficChannelsInUse.put( callEvent.getChannel(), channel ); } else { callEvent.setCallEventType( CallEventType.CALL_DETECT ); - callEvent.setDetails( "CHANNEL PROCESSING ERROR" ); - } + callEvent.setDetails( "CHANNEL START REJECTED" ); + } } else { @@ -325,7 +303,7 @@ public class TrafficChannelManager extends Module Alias to = event.getToIDAlias(); if( to != null && - to.hasPriority() && + to.hasCallPriority() && to.getCallPriority() == Priority.DO_NOT_MONITOR ) { return true; @@ -334,7 +312,7 @@ public class TrafficChannelManager extends Module Alias from = event.getFromIDAlias(); if( from != null && - from.hasPriority() && + from.hasCallPriority() && from.getCallPriority() == Priority.DO_NOT_MONITOR ) { return true; @@ -367,7 +345,7 @@ public class TrafficChannelManager extends Module } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } @@ -399,15 +377,13 @@ public class TrafficChannelManager extends Module { synchronized( mTrafficChannelsInUse ) { - if( channelNumber != null && - mTrafficChannelsInUse.containsKey( channelNumber ) ) + if( channelNumber != null && mTrafficChannelsInUse.containsKey( channelNumber ) ) { Channel channel = mTrafficChannelsInUse.get( channelNumber ); - - mTrafficChannelsInUse.remove( channelNumber ); - /* Disable the channel and broadcast a notification */ - channel.setEnabled( false, true ); + mChannelModel.broadcast( new ChannelEvent( channel, Event.REQUEST_DISABLE ) ); + + mTrafficChannelsInUse.remove( channelNumber ); } } } diff --git a/src/module/decode/state/TrafficChannelStatusListener.java b/src/module/decode/state/TrafficChannelStatusListener.java deleted file mode 100644 index e1b31a58..00000000 --- a/src/module/decode/state/TrafficChannelStatusListener.java +++ /dev/null @@ -1,21 +0,0 @@ -package module.decode.state; - -public class TrafficChannelStatusListener -{ - private TrafficChannelManager mTrafficChannelManager; - private String mChannelNumber; - - public TrafficChannelStatusListener( TrafficChannelManager manager, String channelNumber ) - { - mTrafficChannelManager = manager; - mChannelNumber = channelNumber; - } - - public void callEnd() - { - if( mTrafficChannelManager != null ) - { - mTrafficChannelManager.callEnd( mChannelNumber ); - } - } -} diff --git a/src/module/decode/tait/Tait1200Decoder.java b/src/module/decode/tait/Tait1200Decoder.java index 83ccf2aa..97b9b788 100644 --- a/src/module/decode/tait/Tait1200Decoder.java +++ b/src/module/decode/tait/Tait1200Decoder.java @@ -26,12 +26,13 @@ import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import module.decode.Decoder; import module.decode.DecoderType; import sample.Broadcaster; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import alias.AliasList; import bits.MessageFramer; @@ -45,7 +46,7 @@ import dsp.fsk.FSK2Decoder.Output; /** * TAIT 1200 - 1200 baud 2FSK decoder */ -public class Tait1200Decoder extends Decoder implements IRealBufferListener, +public class Tait1200Decoder extends Decoder implements IFilteredRealBufferListener, Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ @@ -104,8 +105,8 @@ public class Tait1200Decoder extends Decoder implements IRealBufferListener, mMessageFramerGPS.addMessageListener( mMessageAProcessor ); mMessageFramerANI.addMessageListener( mMessageBProcessor ); - mMessageAProcessor.setMessageListener( mMessageBroadcaster ); - mMessageBProcessor.setMessageListener( mMessageBroadcaster ); + mMessageAProcessor.setMessageListener( this ); + mMessageBProcessor.setMessageListener( this ); } public void dispose() @@ -179,7 +180,7 @@ public class Tait1200Decoder extends Decoder implements IRealBufferListener, } @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { return mDecimationFilter; } @@ -198,7 +199,7 @@ public class Tait1200Decoder extends Decoder implements IRealBufferListener, } @Override - public void start() + public void start( ScheduledExecutorService executor ) { // TODO Auto-generated method stub diff --git a/src/module/decode/tait/Tait1200DecoderState.java b/src/module/decode/tait/Tait1200DecoderState.java index 589e9d5f..569c6a36 100644 --- a/src/module/decode/tait/Tait1200DecoderState.java +++ b/src/module/decode/tait/Tait1200DecoderState.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import org.jdesktop.swingx.mapviewer.GeoPosition; @@ -60,7 +61,7 @@ public class Tait1200DecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/demodulate/am/AMDemodulatorModule.java b/src/module/demodulate/am/AMDemodulatorModule.java index 716c6677..374491aa 100644 --- a/src/module/demodulate/am/AMDemodulatorModule.java +++ b/src/module/demodulate/am/AMDemodulatorModule.java @@ -17,11 +17,13 @@ ******************************************************************************/ package module.demodulate.am; +import java.util.concurrent.ScheduledExecutorService; + import module.Module; import sample.Listener; import sample.complex.ComplexBuffer; import sample.complex.IComplexBufferListener; -import sample.real.IRealBufferProvider; +import sample.real.IFilteredRealBufferProvider; import sample.real.RealBuffer; import dsp.am.AMDemodulator_CB; import dsp.filter.FilterFactory; @@ -31,7 +33,7 @@ import dsp.filter.fir.real.RealFIRFilter_RB_RB; import dsp.gain.AutomaticGainControl_RB; public class AMDemodulatorModule extends Module - implements IComplexBufferListener, IRealBufferProvider + implements IComplexBufferListener, IFilteredRealBufferProvider { private static final int SAMPLE_RATE = 48000; @@ -99,19 +101,19 @@ public class AMDemodulatorModule extends Module } @Override - public void setRealBufferListener( Listener listener ) + public void setFilteredRealBufferListener( Listener listener ) { mAGC.setListener( listener ); } @Override - public void removeRealBufferListener() + public void removeFilteredRealBufferListener() { mAGC.removeListener(); } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/demodulate/audio/DemodulatedAudioFilterModule.java b/src/module/demodulate/audio/DemodulatedAudioFilterModule.java new file mode 100644 index 00000000..63da95da --- /dev/null +++ b/src/module/demodulate/audio/DemodulatedAudioFilterModule.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.demodulate.audio; + +import java.util.concurrent.ScheduledExecutorService; + +import module.Module; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import sample.real.IFilteredRealBufferProvider; +import sample.real.IUnFilteredRealBufferListener; +import sample.real.RealBuffer; +import dsp.filter.FilterFactory; +import dsp.filter.Window.WindowType; +import dsp.filter.dc.DCRemovalFilter_RealBuffer; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; + +public class DemodulatedAudioFilterModule extends Module + implements IUnFilteredRealBufferListener, IFilteredRealBufferProvider +{ + private final static Logger mLog = LoggerFactory.getLogger( DemodulatedAudioFilterModule.class ); + + private DCRemovalFilter_RealBuffer mDCFilter = new DCRemovalFilter_RealBuffer(); + private RealFIRFilter_RB_RB mBandPassFilter; + /** + * Filters demodulated audio and removes any DC component from the buffer + * + * @param pass - audio pass band pass frequency + * @param stop - audio pass band stop frequency + */ + public DemodulatedAudioFilterModule( int pass, int stop ) + { + assert( stop > pass ); + +//TODO: change this to a band pass filter + mBandPassFilter = new RealFIRFilter_RB_RB( FilterFactory.getLowPass( 48000, pass, stop, 60, + WindowType.HANNING, true ), 1.0f ); + + mDCFilter.setListener( mBandPassFilter ); + } + + @Override + public void dispose() + { + mBandPassFilter.dispose(); + mBandPassFilter = null; + + mDCFilter.dispose(); + mDCFilter = null; + } + + @Override + public void reset() + { + } + + @Override + public void setFilteredRealBufferListener( Listener listener ) + { + mBandPassFilter.setListener( listener ); + } + + @Override + public void removeFilteredRealBufferListener() + { + mBandPassFilter.removeListener(); + } + + @Override + public void start( ScheduledExecutorService executor ) + { + } + + @Override + public void stop() + { + } + + @Override + public Listener getUnFilteredRealBufferListener() + { + return mDCFilter; + } +} diff --git a/src/module/demodulate/fm/FMDemodulatorModule.java b/src/module/demodulate/fm/FMDemodulatorModule.java index 2d2afe16..0b4033a0 100644 --- a/src/module/demodulate/fm/FMDemodulatorModule.java +++ b/src/module/demodulate/fm/FMDemodulatorModule.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,48 +17,36 @@ ******************************************************************************/ package module.demodulate.fm; +import java.util.concurrent.ScheduledExecutorService; + import module.Module; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Broadcaster; import sample.Listener; import sample.complex.ComplexBuffer; import sample.complex.IComplexBufferListener; -import sample.real.IRealBufferProvider; +import sample.real.IUnFilteredRealBufferProvider; import sample.real.RealBuffer; -import source.tuner.frequency.AutomaticFrequencyControl_RB; -import source.tuner.frequency.FrequencyCorrectionControl; -import source.tuner.frequency.IFrequencyCorrectionController; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; -import dsp.filter.dc.DCRemovalFilter_RB; import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; import dsp.fm.FMDemodulator_CB; -public class FMDemodulatorModule extends Module implements IComplexBufferListener, - IFrequencyCorrectionController, IRealBufferProvider +public class FMDemodulatorModule extends Module + implements IComplexBufferListener, IUnFilteredRealBufferProvider { private final static Logger mLog = LoggerFactory.getLogger( FMDemodulatorModule.class ); - /* Determines responsiveness of DC filter to frequency changes */ - private static final float DC_REMOVAL_RATIO = 0.000003f; - private static final int SAMPLE_RATE = 48000; private ComplexFIRFilter_CB_CB mIQFilter; private FMDemodulator_CB mDemodulator; - private Broadcaster mUnfilteredBufferBroadcaster = new Broadcaster(); - private DCRemovalFilter_RB mDCRemovalFilter; - private AutomaticFrequencyControl_RB mAutomaticFrequencyControl; - private boolean mRemoveDC = true; - - private Listener mListener; /** - * FM Demodulator with integrated DC removal and automatic frequency - * correction control. + * FM Demodulator with I/Q filter. Demodulated output is unfiltered and + * may contain a DC component. * * @param pass - pass frequency for IQ filtering prior to demodulation. This * frequency should be half of the signal bandwidth since the filter will @@ -66,56 +54,16 @@ public class FMDemodulatorModule extends Module implements IComplexBufferListene * combined pass bandwidth will be twice this value. * * @param stop - stop frequency for IQ filtering prior to demodulation. - * - * @param maxFrequencyCorrection - defines the maximum frequency +/- that - * the correction controller can adjust from the center tuned frequency. - * Set this value to 0 for no frequency correction control. */ - public FMDemodulatorModule( int pass, int stop, int maxFrequencyCorrection ) - { - this( pass, stop, maxFrequencyCorrection, true ); - } - - public FMDemodulatorModule( int pass, int stop, int maxFrequencyCorrection, boolean removeDC ) + public FMDemodulatorModule( int pass, int stop ) { assert( stop > pass ); - mRemoveDC = removeDC; - mIQFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass( SAMPLE_RATE, pass, stop, 60, WindowType.HAMMING, true ), 1.0f ); mDemodulator = new FMDemodulator_CB( 1.0f ); mIQFilter.setListener( mDemodulator ); - - mUnfilteredBufferBroadcaster = new Broadcaster(); - mDemodulator.setListener( mUnfilteredBufferBroadcaster ); - - if( maxFrequencyCorrection > 0 ) - { - /* Frequency correction requires unfiltered samples in order to detect - * the DC component present in a mistuned FM demodulated sample stream */ - mAutomaticFrequencyControl = new AutomaticFrequencyControl_RB( maxFrequencyCorrection ); - mUnfilteredBufferBroadcaster.addListener( mAutomaticFrequencyControl ); - } - - if( mRemoveDC ) - { - mDCRemovalFilter = new DCRemovalFilter_RB( DC_REMOVAL_RATIO ); - mUnfilteredBufferBroadcaster.addListener( mDCRemovalFilter ); - } - } - - @Override - public boolean hasFrequencyCorrectionControl() - { - return mAutomaticFrequencyControl != null; - } - - @Override - public FrequencyCorrectionControl getFrequencyCorrectionControl() - { - return mAutomaticFrequencyControl; } @Override @@ -132,72 +80,28 @@ public class FMDemodulatorModule extends Module implements IComplexBufferListene mDemodulator.dispose(); mDemodulator = null; - - if( mDCRemovalFilter != null ) - { - mDCRemovalFilter.dispose(); - } - mDCRemovalFilter = null; - - if( mAutomaticFrequencyControl != null ) - { - mAutomaticFrequencyControl.dispose(); - mAutomaticFrequencyControl = null; - } - - if( mUnfilteredBufferBroadcaster != null ) - { - mUnfilteredBufferBroadcaster.dispose(); - mUnfilteredBufferBroadcaster = null; - } } @Override public void reset() { mDemodulator.reset(); - - if( mDCRemovalFilter != null ) - { - mDCRemovalFilter.reset(); - } - - if( mAutomaticFrequencyControl != null ) - { - mAutomaticFrequencyControl.reset(); - } } @Override - public void setRealBufferListener( Listener listener ) + public void setUnFilteredRealBufferListener( Listener listener ) { - mListener = listener; - - if( mRemoveDC ) - { - mDCRemovalFilter.setListener( listener ); - } - else - { - mUnfilteredBufferBroadcaster.addListener( listener ); - } + mDemodulator.setListener( listener ); } @Override - public void removeRealBufferListener() + public void removeUnFilteredRealBufferListener() { - if( mRemoveDC ) - { - mDCRemovalFilter.removeListener(); - } - else - { - mUnfilteredBufferBroadcaster.removeListener( mListener ); - } + mDemodulator.removeListener(); } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/log/CallEventLogger.java b/src/module/log/CallEventLogger.java index 3d01e45b..a3e5ab14 100644 --- a/src/module/log/CallEventLogger.java +++ b/src/module/log/CallEventLogger.java @@ -58,6 +58,5 @@ public class CallEventLogger extends EventLogger implements ICallEventListener, @Override public void reset() { - super.start(); } } diff --git a/src/module/log/EventLogComponentEditor.java b/src/module/log/EventLogComponentEditor.java deleted file mode 100644 index e8fefbee..00000000 --- a/src/module/log/EventLogComponentEditor.java +++ /dev/null @@ -1,97 +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 - ******************************************************************************/ -package module.log; - -import java.util.List; - -import javax.swing.JCheckBox; -import javax.swing.SwingUtilities; - -import module.log.config.EventLogConfiguration; -import controller.channel.AbstractChannelEditor; -import controller.channel.ChannelNode; - -public class EventLogComponentEditor extends AbstractChannelEditor -{ - private static final long serialVersionUID = 1L; - - private JCheckBox mBinaryLogger = new JCheckBox( "Binary Messages" ); - private JCheckBox mDecodedLogger = new JCheckBox( "Decoded Messages" ); - private JCheckBox mCallEventLogger = new JCheckBox( "Call Events" ); - - public EventLogComponentEditor( ChannelNode channelNode ) - { - super( channelNode ); - - add( mBinaryLogger, "span" ); - add( mDecodedLogger, "span" ); - add( mCallEventLogger, "span" ); - - reset(); - } - - /** - * Sets the combo box to the type stored in the config object, causing the - * editor panel to reset with the stored config - */ - public void reset() - { - SwingUtilities.invokeLater(new Runnable() - { - final List mLoggers = mChannelNode.getChannel() - .getEventLogConfiguration().getLoggers(); - - @Override - public void run() - { - mBinaryLogger.setSelected( - mLoggers.contains( EventLogType.BINARY_MESSAGE ) ); - mDecodedLogger.setSelected( - mLoggers.contains( EventLogType.DECODED_MESSAGE ) ); - mCallEventLogger.setSelected( - mLoggers.contains( EventLogType.CALL_EVENT ) ); - } - }); - } - - /** - * Saves the current configuration as the stored configuration - */ - @Override - public void save() - { - EventLogConfiguration config = mChannelNode.getChannel() - .getEventLogConfiguration(); - - config.clear(); - - if( mBinaryLogger.isSelected() ) - { - config.addLogger( EventLogType.BINARY_MESSAGE ); - } - - if( mDecodedLogger.isSelected() ) - { - config.addLogger( EventLogType.DECODED_MESSAGE ); - } - if( mCallEventLogger.isSelected() ) - { - config.addLogger( EventLogType.CALL_EVENT ); - } - } -} diff --git a/src/module/log/EventLogConfigurationEditor.java b/src/module/log/EventLogConfigurationEditor.java new file mode 100644 index 00000000..9ecdf68e --- /dev/null +++ b/src/module/log/EventLogConfigurationEditor.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.log; + +import gui.editor.Editor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +import javax.swing.JCheckBox; + +import module.log.config.EventLogConfiguration; +import net.miginfocom.swing.MigLayout; +import controller.channel.Channel; + +public class EventLogConfigurationEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + private JCheckBox mBinaryLogger; + private JCheckBox mDecodedLogger; + private JCheckBox mCallEventLogger; + + public EventLogConfigurationEditor() + { + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "", "[][grow]" ) ); + + mBinaryLogger = new JCheckBox( "Binary Messages" ); + mBinaryLogger.setEnabled( false ); + mBinaryLogger.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mBinaryLogger ); + + mDecodedLogger = new JCheckBox( "Decoded Messages" ); + mDecodedLogger.setEnabled( false ); + mDecodedLogger.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mDecodedLogger ); + + mCallEventLogger = new JCheckBox( "Call Events" ); + mCallEventLogger.setEnabled( false ); + mCallEventLogger.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mCallEventLogger ); + } + + /** + * Saves the current configuration as the stored configuration + */ + @Override + public void save() + { + if( hasItem() ) + { + EventLogConfiguration config = getItem().getEventLogConfiguration(); + + config.clear(); + + if( mBinaryLogger.isSelected() ) + { + config.addLogger( EventLogType.BINARY_MESSAGE ); + } + if( mDecodedLogger.isSelected() ) + { + config.addLogger( EventLogType.DECODED_MESSAGE ); + } + if( mCallEventLogger.isSelected() ) + { + config.addLogger( EventLogType.CALL_EVENT ); + } + } + + setModified( false ); + } + + @Override + public void setItem( Channel channel ) + { + super.setItem( channel ); + + if( hasItem() ) + { + setControlsEnabled( true ); + + List loggers = getItem().getEventLogConfiguration().getLoggers(); + mBinaryLogger.setSelected( loggers.contains( EventLogType.BINARY_MESSAGE ) ); + mDecodedLogger.setSelected( loggers.contains( EventLogType.DECODED_MESSAGE ) ); + mCallEventLogger.setSelected( loggers.contains( EventLogType.CALL_EVENT ) ); + } + else + { + setControlsEnabled( false ); + } + } + + private void setControlsEnabled( boolean enabled ) + { + if( mBinaryLogger.isEnabled() != enabled ) + { + mBinaryLogger.setEnabled( enabled ); + } + + if( mDecodedLogger.isEnabled() != enabled ) + { + mDecodedLogger.setEnabled( enabled ); + } + + if( mCallEventLogger.isEnabled() != enabled ) + { + mCallEventLogger.setEnabled( enabled ); + } + } +} diff --git a/src/module/log/EventLogManager.java b/src/module/log/EventLogManager.java index ee0909ee..2b43522c 100644 --- a/src/module/log/EventLogManager.java +++ b/src/module/log/EventLogManager.java @@ -24,16 +24,10 @@ import java.util.List; import module.Module; import module.log.MessageEventLogger.Type; import module.log.config.EventLogConfiguration; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import properties.SystemProperties; public class EventLogManager { - private final static Logger mLog = LoggerFactory.getLogger( EventLogManager.class ); - private Path mDirectory; public EventLogManager() diff --git a/src/module/log/EventLogger.java b/src/module/log/EventLogger.java index b1d5e46f..120ee29a 100644 --- a/src/module/log/EventLogger.java +++ b/src/module/log/EventLogger.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.file.Path; +import java.util.concurrent.ScheduledExecutorService; import module.Module; @@ -67,7 +68,8 @@ public abstract class EventLogger extends Module public abstract String getHeader(); - public void start() + @Override + public void start( ScheduledExecutorService executor ) { if( mLogFile == null ) { diff --git a/src/module/log/MessageEventLogger.java b/src/module/log/MessageEventLogger.java index 65c1c918..de021df7 100644 --- a/src/module/log/MessageEventLogger.java +++ b/src/module/log/MessageEventLogger.java @@ -58,7 +58,6 @@ public class MessageEventLogger extends EventLogger implements @Override public void reset() { - super.start(); } @Override diff --git a/src/org/jdesktop/swingx/JXMapViewer.java b/src/org/jdesktop/swingx/JXMapViewer.java index e0bd81f9..8c999c07 100644 --- a/src/org/jdesktop/swingx/JXMapViewer.java +++ b/src/org/jdesktop/swingx/JXMapViewer.java @@ -131,7 +131,7 @@ public class JXMapViewer extends JPanel implements DesignMode catch (Throwable ex) { - mLog.error( "could not load 'loading.png'" ); + mLog.error( "JXMapViewer could not load 'loading.png'" ); BufferedImage img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = img.createGraphics(); diff --git a/src/playlist/PlaylistManager.java b/src/playlist/PlaylistManager.java index c3a973b4..df075a70 100644 --- a/src/playlist/PlaylistManager.java +++ b/src/playlist/PlaylistManager.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +25,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; @@ -34,62 +36,210 @@ import javax.xml.bind.Unmarshaller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import playlist.version1.PlaylistConverterV1ToV2; import properties.SystemProperties; +import sample.Listener; +import alias.AliasEvent; +import alias.AliasModel; +import controller.ThreadPoolManager; +import controller.channel.Channel.ChannelType; +import controller.channel.ChannelEvent; +import controller.channel.ChannelEventListener; +import controller.channel.ChannelModel; +import controller.channel.map.ChannelMapEvent; +import controller.channel.map.ChannelMapModel; -public class PlaylistManager +public class PlaylistManager implements ChannelEventListener { private final static Logger mLog = LoggerFactory.getLogger( PlaylistManager.class ); - private Playlist mPlaylist = new Playlist(); + private ThreadPoolManager mThreadPoolManager; + private AliasModel mAliasModel; + private ChannelModel mChannelModel; + private ChannelMapModel mChannelMapModel; + private Path mCurrentPlaylistPath; + private AtomicBoolean mPlaylistSavePending = new AtomicBoolean(); + private boolean mPlaylistLoading = false; - public PlaylistManager() + /** + * Playlist manager - manages all channel configurations, channel maps, and + * alias lists and handles loading or persisting to a playlist.xml file + * + * Monitors playlist changes to automatically save configuration changes + * after they occur. + * + * @param threadPoolManager + * @param channelModel + */ + public PlaylistManager( ThreadPoolManager threadPoolManager, + AliasModel aliasModel, + ChannelModel channelModel, + ChannelMapModel channelMapModel ) { - init(); - } + mThreadPoolManager = threadPoolManager; + mAliasModel = aliasModel; + mChannelModel = channelModel; + mChannelMapModel = channelMapModel; - public Playlist getPlayist() - { - return mPlaylist; + //Register for alias, channel and channel map events so that we can + //save the playlist when there are any changes + mChannelModel.addListener( this ); + + mAliasModel.addListener( new Listener() + { + @Override + public void receive( AliasEvent t ) + { + schedulePlaylistSave(); + } + } ); + + mChannelMapModel.addListener( new Listener() + { + @Override + public void receive( ChannelMapEvent t ) + { + schedulePlaylistSave(); + } + } ); } /** * Loads playlist from the current playlist file, or the default playlist file, * as specified in the current SDRTRunk system settings */ - private void init() + public void init() { SystemProperties props = SystemProperties.getInstance(); Path playlistFolder = props.getApplicationFolder( "playlist" ); String defaultPlaylistFile = - props.get( "playlist.defaultfilename", "playlist.xml" ); + props.get( "playlist.v2.defaultfilename", "playlist_v2.xml" ); String playlistFile = - props.get( "playlist.currentfilename", defaultPlaylistFile ); + props.get( "playlist.v2.currentfilename", defaultPlaylistFile ); + + mCurrentPlaylistPath = playlistFolder.resolve( playlistFile ); + + PlaylistV2 playlist = load( mCurrentPlaylistPath ); + + boolean saveRequired = false; - load( playlistFolder.resolve( playlistFile ) ); + if( playlist == null ) + { + mLog.info( "Couldn't find version 2 playlist - looking for " + + "version 1 playlist to convert" ); + + Path playlistV1Path = playlistFolder.resolve( "playlist.xml" ); + + PlaylistConverterV1ToV2 converter = + new PlaylistConverterV1ToV2( playlistV1Path ); + + if( converter.hasErrorMessages() ) + { + mLog.error( "Playlist version 1 conversion errors: " + + converter.getErrorMessages() ); + } + + playlist = converter.getConvertedPlaylist(); + + saveRequired = true; + } + + transferPlaylistToModels( playlist ); + + if( saveRequired ) + { + schedulePlaylistSave(); + } } + + /** + * Transfers data from persisted playlist into system models + */ + private void transferPlaylistToModels( PlaylistV2 playlist ) + { + if( playlist != null ) + { + mPlaylistLoading = true; + + mAliasModel.addAliases( playlist.getAliases() ); + mChannelModel.addChannels( playlist.getChannels() ); + mChannelMapModel.addChannelMaps( playlist.getChannelMaps() ); + + mPlaylistLoading = false; + } + } + + /** + * Channel event listener method. Monitors channel events for events that + * indicate that the playlist has changed and queues automatic playlist + * saving. + */ + @Override + public void channelChanged( ChannelEvent event ) + { + //Only save playlist for changes to standard channels (not traffic) + if( event.getChannel().getChannelType() == ChannelType.STANDARD ) + { + switch( event.getEvent() ) + { + case NOTIFICATION_ADD: + case NOTIFICATION_CONFIGURATION_CHANGE: + case NOTIFICATION_DELETE: + case NOTIFICATION_PROCESSING_START: + case NOTIFICATION_PROCESSING_STOP: + schedulePlaylistSave(); + break; + case NOTIFICATION_ENABLE_REJECTED: + case NOTIFICATION_SELECTION_CHANGE: + case NOTIFICATION_STATE_RESET: + case REQUEST_DELETE: + case REQUEST_DESELECT: + case REQUEST_DISABLE: + case REQUEST_ENABLE: + case REQUEST_SELECT: + //Do nothing for these event types + break; + default: + //When a new event enum entry is added and received, throw an + //exception here to ensure developer adds support for the + //use case + throw new IllegalArgumentException( "Unrecognized Channel " + + "Event [" + event.getEvent().name() + "]" ); + } + } + } public void save() { + PlaylistV2 playlist = new PlaylistV2(); + + playlist.setAliases( mAliasModel.getAliases() ); + playlist.setChannels( mChannelModel.getChannels() ); + playlist.setChannelMaps( mChannelMapModel.getChannelMaps() ); + JAXBContext context = null; - - SystemProperties props = SystemProperties.getInstance(); - Path playlistPath = props.getApplicationFolder( "playlist" ); - - String playlistDefault = props.get( "playlist.defaultfilename", - "playlist.xml" ); + if( mCurrentPlaylistPath == null ) + { + SystemProperties props = SystemProperties.getInstance(); - String playlistCurrent = props.get( "playlist.currentfilename", - playlistDefault ); + Path playlistPath = props.getApplicationFolder( "playlist" ); + + String playlistDefault = props.get( "playlist.defaultfilename", + "playlist_v2.xml" ); + + String playlistCurrent = props.get( "playlist.currentfilename", + playlistDefault ); + + mCurrentPlaylistPath = playlistPath.resolve( playlistCurrent ); + } - Path filePath = playlistPath.resolve( playlistCurrent ); - - File outputFile = new File( filePath.toString() ); + File outputFile = new File( mCurrentPlaylistPath.toString() ); try { @@ -101,7 +251,7 @@ public class PlaylistManager catch( Exception e ) { mLog.error( "couldn't create file to save " - + "playlist [" + filePath.toString() + "]" ); + + "playlist [" + mCurrentPlaylistPath.toString() + "]" ); } OutputStream out = null; @@ -112,13 +262,13 @@ public class PlaylistManager try { - context = JAXBContext.newInstance( Playlist.class ); + context = JAXBContext.newInstance( PlaylistV2.class ); Marshaller m = context.createMarshaller(); m.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, true ); - m.marshal( mPlaylist, out ); + m.marshal( playlist, out ); } catch ( JAXBException e ) { @@ -127,7 +277,8 @@ public class PlaylistManager } catch ( Exception e ) { - mLog.error( "coulcn't open outputstream to save playlist [" + filePath.toString() + "]", e ); + mLog.error( "coulcn't open outputstream to save playlist [" + + mCurrentPlaylistPath.toString() + "]", e ); } finally { @@ -146,15 +297,16 @@ public class PlaylistManager } /** - * Erases current playlist and loads playlist from the playlistPath filename, - * if it exists. + * Loads a version 2 playlist */ - public void load( Path playlistPath ) + public PlaylistV2 load( Path playlistPath ) { + mLog.info( "loading version 2 playlist file [" + playlistPath.toString() + "]" ); + + PlaylistV2 playlist = null; + if( Files.exists( playlistPath ) ) { - mLog.info( "loading playlist file [" + playlistPath.toString() + "]" ); - JAXBContext context = null; InputStream in = null; @@ -165,11 +317,11 @@ public class PlaylistManager try { - context = JAXBContext.newInstance( Playlist.class ); + context = JAXBContext.newInstance( PlaylistV2.class ); Unmarshaller m = context.createUnmarshaller(); - mPlaylist = (Playlist)m.unmarshal( in ); + playlist = (PlaylistV2)m.unmarshal( in ); } catch ( JAXBException e ) { @@ -198,12 +350,42 @@ public class PlaylistManager } else { - mLog.info( "playlist does not exist [" + playlistPath.toString() + "]" ); + mLog.info( "PlaylistManager - playlist not found at [" + + playlistPath.toString() + "]" ); } - - if( mPlaylist == null ) + + return playlist; + } + + /** + * Schedules a playlist save task. Subsequent calls to this method will be + * ignored until the save event occurs, thus limiting repetitive playlist + * saving to a minimum. + */ + private void schedulePlaylistSave() + { + if( !mPlaylistLoading ) { - mPlaylist = new Playlist(); + if( mPlaylistSavePending.compareAndSet( false, true ) ) + { + mThreadPoolManager.scheduleOnce( new PlaylistSaveTask(), + 2, TimeUnit.SECONDS ); + } + } + } + + /** + * Resets the playlist save pending flag to false and proceeds to save the + * playlist. + */ + public class PlaylistSaveTask implements Runnable + { + @Override + public void run() + { + mPlaylistSavePending.set( false ); + + save(); } } } diff --git a/src/playlist/PlaylistNode.java b/src/playlist/PlaylistNode.java deleted file mode 100644 index 93fdc2a8..00000000 --- a/src/playlist/PlaylistNode.java +++ /dev/null @@ -1,147 +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 - ******************************************************************************/ -package playlist; - -import javax.swing.JPanel; - -import alias.AliasDirectory; -import alias.AliasDirectoryNode; -import controller.BaseNode; -import controller.EmptyEditor; -import controller.channel.map.ChannelMapList; -import controller.channel.map.ChannelMapListNode; -import controller.system.SystemList; -import controller.system.SystemListNode; - -public class PlaylistNode extends BaseNode -{ - private static final long serialVersionUID = 1L; - private AliasDirectoryNode mAliasDirectoryNode; - private ChannelMapListNode mChannelMapListNode; - private SystemListNode mSystemListNode; - - public PlaylistNode() - { - super( null ); - } - - private void init() - { - /** - * Delete any existing children - */ - delete(); - - /** - * Load the AliasDirectory - */ - AliasDirectory directory = getPlaylist().getAliasDirectory(); - - if( directory == null ) - { - directory = new AliasDirectory(); - getPlaylist().setAliasDirectory( directory ); - } - - mAliasDirectoryNode = new AliasDirectoryNode( directory ); - - getModel().insertNodeInto( mAliasDirectoryNode, this, 0 ); - - mAliasDirectoryNode.init(); - - /** - * Load the Channel Map List - */ - ChannelMapList channelMapList = getPlaylist().getChannelMapList(); - - if( channelMapList == null ) - { - channelMapList = new ChannelMapList(); - } - - mChannelMapListNode = new ChannelMapListNode( channelMapList ); - - getModel().insertNodeInto( mChannelMapListNode, this, 1 ); - - mChannelMapListNode.init(); - - /** - * Load the SystemList - */ - SystemList list = getPlaylist().getSystemList(); - - if( list == null ) - { - list = new SystemList(); - getPlaylist().setSystemList( list ); - } - - mSystemListNode = new SystemListNode( list ); - - getModel().insertNodeInto( mSystemListNode, this, 2 ); - - mSystemListNode.init(); - - mSystemListNode.show(); - } - - public SystemListNode getSystemListNode() - { - return mSystemListNode; - } - - public AliasDirectoryNode getAliasDirectoryNode() - { - return mAliasDirectoryNode; - } - - public void loadPlaylist() - { - Playlist playlist = - getModel().getResourceManager().getPlaylistManager().getPlayist(); - - setUserObject( playlist ); - - init(); - } - - - public Playlist getPlaylist() - { - Playlist retVal = null; - - Object userObject = getUserObject(); - - if( userObject != null ) - { - retVal = (Playlist)userObject; - } - - return retVal; - } - - public String toString() - { - return "Playlist"; - } - - public JPanel getEditor() - { - return new EmptyEditor(); - } -} diff --git a/src/playlist/PlaylistV2.java b/src/playlist/PlaylistV2.java new file mode 100644 index 00000000..aa3fa2fe --- /dev/null +++ b/src/playlist/PlaylistV2.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package playlist; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSeeAlso; + +import alias.Alias; +import controller.channel.Channel; +import controller.channel.map.ChannelMap; + +@XmlSeeAlso( { Alias.class,Channel.class,ChannelMap.class } ) + +@XmlRootElement( name = "playlist" ) +public class PlaylistV2 +{ + //Version 2 member variables + private List mAliases = new ArrayList<>(); + private List mChannels = new ArrayList<>(); + private List mChannelMaps = new ArrayList<>(); + + public PlaylistV2() + { + } + + @XmlElement( name = "alias" ) + public List getAliases() + { + return mAliases; + } + + public void setAliases( List aliases ) + { + mAliases = aliases; + } + + @XmlElement( name = "channel" ) + public List getChannels() + { + return mChannels; + } + + public void setChannels( List channels ) + { + mChannels = channels; + } + + @XmlElement( name = "channel_map" ) + public List getChannelMaps() + { + return mChannelMaps; + } + + public void setChannelMaps( List channelMaps ) + { + mChannelMaps = channelMaps; + } +} diff --git a/src/alias/AliasDirectory.java b/src/playlist/version1/AliasDirectory.java similarity index 74% rename from src/alias/AliasDirectory.java rename to src/playlist/version1/AliasDirectory.java index 0e6003ec..49c64eb7 100644 --- a/src/alias/AliasDirectory.java +++ b/src/playlist/version1/AliasDirectory.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias; +package playlist.version1; import java.util.ArrayList; @@ -23,11 +23,14 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; +import alias.AliasList; + @XmlSeeAlso( { AliasList.class} ) @XmlRootElement( name = "alias_directory" ) +@Deprecated public class AliasDirectory { - private ArrayList mAliasList = new ArrayList(); + private ArrayList mAliasList = new ArrayList<>(); public AliasDirectory() { @@ -35,34 +38,35 @@ public class AliasDirectory public void refresh() { - for( AliasList list: mAliasList ) - { - list.update(); - } + } + + public void clearAliasLists() + { + mAliasList.clear(); } @XmlElement( name = "alias_list" ) - public ArrayList getAliasList() + public ArrayList getAliasList() { return mAliasList; } - public void setAliasList( ArrayList lists ) + public void setAliasList( ArrayList lists ) { mAliasList = lists; if( mAliasList == null ) { - mAliasList = new ArrayList(); + mAliasList = new ArrayList(); } } - public void addAliasList( AliasList list ) + public void addAliasList( AliasListOld list ) { mAliasList.add( list ); } - public void removeAliasList( AliasList list ) + public void removeAliasList( AliasListOld list ) { mAliasList.remove( list ); } @@ -70,11 +74,11 @@ public class AliasDirectory /** * Gets the named alias list, or returns an empty alias list */ - public AliasList getAliasList( String name ) + public AliasListOld getAliasList( String name ) { - AliasList retVal = new AliasList(); + AliasListOld retVal = new AliasListOld( name ); - for( AliasList list: mAliasList ) + for( AliasListOld list: mAliasList ) { if( list.getName().equalsIgnoreCase( name ) ) { diff --git a/src/playlist/version1/AliasListOld.java b/src/playlist/version1/AliasListOld.java new file mode 100644 index 00000000..7df3c6be --- /dev/null +++ b/src/playlist/version1/AliasListOld.java @@ -0,0 +1,583 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,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 + ******************************************************************************/ +package playlist.version1; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSeeAlso; + +import module.decode.lj1200.LJ1200Message.Function; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import alias.Alias; +import alias.action.AliasAction; +import alias.action.beep.BeepAction; +import alias.action.clip.ClipAction; +import alias.action.script.ScriptAction; +import alias.id.AliasID; +import alias.id.esn.Esn; +import alias.id.fleetsync.FleetsyncID; +import alias.id.lojack.LoJackFunctionAndID; +import alias.id.mdc.MDC1200ID; +import alias.id.mobileID.Min; +import alias.id.mpt1327.MPT1327ID; +import alias.id.nonrecordable.NonRecordable; +import alias.id.priority.Priority; +import alias.id.siteID.SiteID; +import alias.id.status.StatusID; +import alias.id.talkgroup.TalkgroupID; +import alias.id.uniqueID.UniqueID; +import audio.metadata.MetadataType; + +@XmlSeeAlso( { Alias.class, + + /* Alias ID's */ + AliasID.class, + FleetsyncID.class, + Esn.class, + Group.class, + LoJackFunctionAndID.class, + MDC1200ID.class, + Min.class, + MPT1327ID.class, + UniqueID.class, + SiteID.class, + StatusID.class, + TalkgroupID.class, + + /* Alias Attributes */ + NonRecordable.class, + Priority.class, + + /* Alias Actions */ + AliasAction.class, + BeepAction.class, + ClipAction.class, + ScriptAction.class } ) + +@Deprecated +@XmlRootElement( name = "alias_list" ) +public class AliasListOld implements Comparable +{ + private final static Logger mLog = LoggerFactory.getLogger( AliasListOld.class ); + + private String mName; + private ArrayList mGroups = new ArrayList(); + + private Map mESN = new HashMap<>(); + private Map mFleetsync = new HashMap<>(); + private Map mLoJack = new HashMap<>(); + private Map mMDC1200 = new HashMap<>(); + private Map mMobileID = new HashMap<>(); + private Map mMPT1327 = new HashMap<>(); + private Map mSiteID = new HashMap<>(); + private Map mStatus = new HashMap<>(); + private Map mTalkgroup = new HashMap<>(); + private Map mUniqueID = new HashMap<>(); + + private boolean mESNWildcard = false; + private boolean mMobileIDWildcard = false; + private boolean mFleetsyncWildcard = false; + private boolean mMDC1200Wildcard = false; + private boolean mMPT1327Wildcard = false; + private boolean mSiteWildcard = false; + private boolean mTalkgroupWildcard = false; + + public AliasListOld() + { + this( null ); + } + + public AliasListOld( String name ) + { + mName = name; + update(); + } + + /** + * Load/Reload all lookup hashmaps + */ + public void update() + { + //Clear hashmaps + mESN.clear(); + mFleetsync.clear(); + mLoJack.clear(); + mMDC1200.clear(); + mMobileID.clear(); + mMPT1327.clear(); + mSiteID.clear(); + mStatus.clear(); + mTalkgroup.clear(); + mUniqueID.clear(); + + for( Group group: mGroups ) + { + for( Alias alias: group.getAlias() ) + { + for( AliasID id: alias.getId() ) + { + switch( id.getType() ) + { + case ESN: + Esn esn = (Esn)id; + if( esn.getEsn().contains( "*" ) ) + { + mESNWildcard = true; + + mESN.put( fixWildcard( esn.getEsn() ), alias ); + } + else + { + mESN.put( esn.getEsn(), alias ); + } + break; + case Fleetsync: + FleetsyncID fs = (FleetsyncID)id; + + if( fs.getIdent().contains( "*" ) ) + { + mFleetsyncWildcard = true; + + mFleetsync.put( fixWildcard( fs.getIdent() ), alias ); + } + else + { + mFleetsync.put( fs.getIdent(), alias ); + } + break; + case LoJack: + mLoJack.put( (LoJackFunctionAndID)id, alias ); + break; + case MDC1200: + MDC1200ID mdc = (MDC1200ID)id; + + if( mdc.getIdent().contains( "*" ) ) + { + mMDC1200Wildcard = true; + + //Replace (*) wildcard with regex wildcard (.) + mMDC1200.put( fixWildcard( mdc.getIdent() ), alias ); + } + else + { + mMDC1200.put( mdc.getIdent(), alias ); + } + break; + case MPT1327: + MPT1327ID mpt = (MPT1327ID)id; + + String ident = mpt.getIdent(); + + if( ident != null ) + { + if( ident.contains( "*" ) ) + { + mMPT1327Wildcard = true; + + //Replace (*) wildcard with regex wildcard (.) + mMPT1327.put( fixWildcard( ident ), alias ); + } + else + { + mMPT1327.put( ident, alias ); + } + } + break; + case MIN: + Min min = (Min)id; + + if( min.getMin().contains( "*" ) ) + { + mMobileIDWildcard = true; + mMobileID.put( fixWildcard( min.getMin() ), alias ); + } + else + { + mMobileID.put( min.getMin(), alias ); + } + break; + case LTRNetUID: + UniqueID uid = (UniqueID)id; + + mUniqueID.put( uid.getUid(), alias ); + break; + case Site: + SiteID siteID = (SiteID)id; + + if( siteID.getSite().contains( "*" ) ) + { + mSiteWildcard = true; + mSiteID.put( fixWildcard( siteID.getSite() ), alias ); + } + else + { + mSiteID.put( siteID.getSite(), alias ); + } + + break; + case Status: + mStatus.put( ((StatusID)id).getStatus(), alias ); + break; + case Talkgroup: + TalkgroupID tgid = (TalkgroupID)id; + + if( tgid.getTalkgroup().contains( "*" ) ) + { + mTalkgroupWildcard = true; + + //Replace (*) wildcard with regex wildcard (.) + mTalkgroup.put( fixWildcard( tgid.getTalkgroup() ), alias ); + } + else + { + mTalkgroup.put( tgid.getTalkgroup(), alias ); + } + break; + case NonRecordable: + case Priority: + //We don't maintain lookups for these items + break; + default: + mLog.warn( "Unrecognized Alias ID Type:" + id.getType().name() ); + break; + } + } + } + } + } + + /** + * Converts user wildcard character (*) to regex single character wildcard (.) + * but ignores a regex multi-character wildcard (.*) + */ + private String fixWildcard( String value ) + { + if( value.contains( "*" ) && !value.contains( ".*" ) ) + { + return value.replace( "*", "." ); + } + + return value; + } + + public Alias getSiteID( String siteID ) + { + if( siteID != null ) + { + if( mSiteWildcard ) + { + for( String regex: mSiteID.keySet() ) + { + if( siteID.matches( regex ) ) + { + return mSiteID.get( regex ); + } + } + } + else + { + return mSiteID.get( siteID ); + } + } + + return null; + } + + public Alias getStatus( int status ) + { + return mStatus.get( status ); + } + + public Alias getUniqueID( int uniqueID ) + { + return mUniqueID.get( uniqueID ); + } + + public Alias getESNAlias( String esn ) + { + if( esn != null ) + { + if( mESNWildcard ) + { + for( String regex: mESN.keySet() ) + { + if( esn.matches( regex ) ) + { + return mESN.get( regex ); + } + } + } + else + { + return mESN.get( esn ); + } + } + + return null; + } + + + public Alias getFleetsyncAlias( String ident ) + { + if( ident != null ) + { + if( mFleetsyncWildcard ) + { + for( String regex: mFleetsync.keySet() ) + { + if( ident.matches( regex ) ) + { + return mFleetsync.get( regex ); + } + } + } + else + { + return mFleetsync.get( ident ); + } + } + + return null; + } + + public Alias getLoJackAlias( Function function, String id ) + { + if( id != null ) + { + for( LoJackFunctionAndID lojack: mLoJack.keySet() ) + { + if( lojack.matches( function, id ) ) + { + return mLoJack.get( lojack ); + } + } + } + + return null; + } + + public Alias getMDC1200Alias( String ident ) + { + if( ident != null ) + { + if( mMDC1200Wildcard ) + { + for( String regex: mMDC1200.keySet() ) + { + if( ident.matches( regex ) ) + { + return mMDC1200.get( regex ); + } + } + } + else + { + return mMDC1200.get( ident ); + } + } + + return null; + } + + public Alias getMPT1327Alias( String ident ) + { + if( ident != null ) + { + if( mMPT1327Wildcard ) + { + for( String regex: mMPT1327.keySet() ) + { + if( ident.matches( regex ) ) + { + return mMPT1327.get( regex ); + } + } + } + else + { + return mMPT1327.get( ident ); + } + } + + return null; + } + + public Group getGroup( Alias alias ) + { + if( alias != null ) + { + for( Group group: mGroups ) + { + if( group.contains( alias ) ) + { + return group; + } + } + } + + return null; + } + + public Alias getMobileIDNumberAlias( String ident ) + { + if( ident != null ) + { + if( mMobileIDWildcard ) + { + for( String regex: mMobileID.keySet() ) + { + if( ident.matches( regex ) ) + { + return mMobileID.get( regex ); + } + } + } + else + { + return mMobileID.get( ident ); + } + } + + return null; + } + + public Alias getTalkgroupAlias( String tgid ) + { + if( tgid != null ) + { + if( mTalkgroupWildcard ) + { + for( String regex: mTalkgroup.keySet() ) + { + if( tgid.matches( regex ) ) + { + return mTalkgroup.get( regex ); + } + } + } + else + { + return mTalkgroup.get( tgid ); + } + } + + return null; + } + + public String toString() + { + return mName; + } + + @XmlAttribute + public String getName() + { + return mName; + } + + public void setName( String name ) + { + mName = name; + } + + public ArrayList getGroup() + { + return mGroups; + } + + public void setGroup( ArrayList groups ) + { + mGroups = groups; + } + + public void addGroup( Group group ) + { + mGroups.add( group ); + } + + public void removeGroup( Group group ) + { + mGroups.remove( group ); + } + + @Override + public int compareTo( AliasListOld otherAliasList ) + { + return getName().compareTo( otherAliasList.getName() ); + } + + public Alias getAlias( String value, MetadataType type ) + { + switch( type ) + { + case ESN: + return getESNAlias( value ); + case FLEETSYNC: + return getFleetsyncAlias( value ); + case MDC1200: + return getMDC1200Alias( value ); + case MOBILE_ID: + return getMobileIDNumberAlias( value ); + case MPT1327: + return getMPT1327Alias( value ); + case SITE_ID: + return getSiteID( value ); + case STATUS: + if( value != null ) + { + try + { + return getStatus( Integer.valueOf( value ) ); + } + catch( Exception e ) + { + //do nothing, we couldn't parse the int value + } + } + break; + case FROM: + case TO: + return getTalkgroupAlias( value ); + case UNIQUE_ID: + if( value != null ) + { + try + { + return getUniqueID( Integer.valueOf( value ) ); + } + catch( Exception e ) + { + //do nothing, we couldn't parse the int value + } + } + break; + default: + break; + } + + return null; + } + +} diff --git a/src/controller/channel/map/ChannelMapList.java b/src/playlist/version1/ChannelMapList.java similarity index 96% rename from src/controller/channel/map/ChannelMapList.java rename to src/playlist/version1/ChannelMapList.java index 7a43b25b..5d0f6d61 100644 --- a/src/controller/channel/map/ChannelMapList.java +++ b/src/playlist/version1/ChannelMapList.java @@ -15,14 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel.map; +package playlist.version1; import java.util.ArrayList; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlSeeAlso; +import controller.channel.map.ChannelMap; + @XmlSeeAlso( ChannelMap.class ) +@Deprecated public class ChannelMapList { private ArrayList mChannelMap = new ArrayList(); diff --git a/src/alias/Group.java b/src/playlist/version1/Group.java similarity index 96% rename from src/alias/Group.java rename to src/playlist/version1/Group.java index d5e30228..d8a17d0f 100644 --- a/src/alias/Group.java +++ b/src/playlist/version1/Group.java @@ -15,12 +15,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias; +package playlist.version1; import java.util.ArrayList; import javax.xml.bind.annotation.XmlAttribute; +import alias.Alias; + +@Deprecated public class Group { private String mName; diff --git a/src/playlist/version1/PlaylistConverterV1ToV2.java b/src/playlist/version1/PlaylistConverterV1ToV2.java new file mode 100644 index 00000000..2ed14fd4 --- /dev/null +++ b/src/playlist/version1/PlaylistConverterV1ToV2.java @@ -0,0 +1,168 @@ +package playlist.version1; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import playlist.PlaylistV2; +import alias.Alias; +import controller.channel.Channel; +import controller.channel.map.ChannelMap; + +public class PlaylistConverterV1ToV2 +{ + private final static Logger mLog = + LoggerFactory.getLogger( PlaylistConverterV1ToV2.class ); + + private PlaylistV2 mConvertedPlaylist; + private List mErrors = new ArrayList<>(); + + @SuppressWarnings( "deprecation" ) + public PlaylistConverterV1ToV2( Path path ) + { + PlaylistV1 playlistVersion1 = null; + + if( path != null && Files.exists( path ) ) + { + mLog.info( "attempting playlist v1 to v2 conversion [" + + path.toString() + "]" ); + + JAXBContext context = null; + + InputStream in = null; + + try + { + in = new FileInputStream( path.toString() ); + + try + { + context = JAXBContext.newInstance( PlaylistV1.class ); + + Unmarshaller m = context.createUnmarshaller(); + + playlistVersion1 = (PlaylistV1)m.unmarshal( in ); + } + catch ( JAXBException e ) + { + mErrors.add( "Couldn't unmarshall version 1 playlist - " + + e.getLocalizedMessage() ); + } + } + catch ( Exception e ) + { + mErrors.add( "Couldn't open version 1 playlist - [" + + path.toString() + "] - " + e.getLocalizedMessage() ); + } + finally + { + if( in != null ) + { + try + { + in.close(); + } + catch ( IOException e ) + { + mLog.error( "exception while closing " + + "the playlist file inputstream reader", e ); + } + } + } + } + else + { + mErrors.add( "version 1 playlist is null or not found [" + + path.toString() + "]" ); + } + + if( playlistVersion1 != null ) + { + mConvertedPlaylist = new PlaylistV2(); + + if( playlistVersion1.hasSystemList() ) + { + SystemList systemList = playlistVersion1.getSystemList(); + + for( playlist.version1.System system: systemList.getSystem() ) + { + for( Site site: system.getSite() ) + { + for( Channel channel: site.getChannel() ) + { + channel.setSystem( system.getName() ); + channel.setSite( site.getName() ); + + mConvertedPlaylist.getChannels().add( channel ); + } + } + } + } + + if( playlistVersion1.hasAliasDirectory() ) + { + AliasDirectory aliasDirectory = playlistVersion1.getAliasDirectory(); + + for( AliasListOld list: aliasDirectory.getAliasList() ) + { + for( Group group: list.getGroup() ) + { + for( Alias alias: group.getAlias() ) + { + alias.setList( list.getName() ); + alias.setGroup( group.getName() ); + + mConvertedPlaylist.getAliases().add( alias ); + } + } + } + } + + if( playlistVersion1.hasChannelMapList() ) + { + //Move the channel maps out of the channel map list + ChannelMapList channelMapList = playlistVersion1.getChannelMapList(); + + List channelMaps = channelMapList.getChannelMap(); + + if( channelMaps != null && !channelMaps.isEmpty() ) + { + mConvertedPlaylist.getChannelMaps().addAll( channelMaps ); + } + } + + mLog.info( "Converted [" + mConvertedPlaylist.getChannels().size() + + "] channels to new playlist format" ); + mLog.info( "Converted [" + mConvertedPlaylist.getAliases().size() + + "] aliases to new playlist format" ); + mLog.info( "Converted [" + mConvertedPlaylist.getChannelMaps().size() + + "] channel maps to new playlist format" ); + } + } + + public PlaylistV2 getConvertedPlaylist() + { + return mConvertedPlaylist; + } + + public List getErrorMessages() + { + return mErrors; + } + + public boolean hasErrorMessages() + { + return !mErrors.isEmpty(); + } +} diff --git a/src/playlist/Playlist.java b/src/playlist/version1/PlaylistV1.java similarity index 72% rename from src/playlist/Playlist.java rename to src/playlist/version1/PlaylistV1.java index 859f3968..52e8dc4a 100644 --- a/src/playlist/Playlist.java +++ b/src/playlist/version1/PlaylistV1.java @@ -15,28 +15,31 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package playlist; +package playlist.version1; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; -import alias.AliasDirectory; -import controller.channel.map.ChannelMapList; -import controller.system.SystemList; +import alias.Alias; +import controller.channel.Channel; @XmlSeeAlso( { AliasDirectory.class, + Alias.class, + Channel.class, ChannelMapList.class, SystemList.class } ) +@Deprecated @XmlRootElement( name = "playlist" ) -public class Playlist +public class PlaylistV1 { + //Note: these are for legacy version 1 format playlists private AliasDirectory mAliasDirectory = new AliasDirectory(); private ChannelMapList mChannelMapList = new ChannelMapList(); private SystemList mSystemList = new SystemList(); - - public Playlist() + + public PlaylistV1() { } @@ -48,28 +51,54 @@ public class Playlist return mAliasDirectory; } + public void setAliasDirectory( AliasDirectory directory ) { mAliasDirectory = directory; } + public boolean hasAliasDirectory() + { + return mAliasDirectory != null && + mAliasDirectory.getAliasList() != null && + !mAliasDirectory.getAliasList().isEmpty(); + } + + @XmlElement( name = "system_list" ) public SystemList getSystemList() { return mSystemList; } + public void setSystemList( SystemList list ) { mSystemList = list; } + public boolean hasSystemList() + { + return mSystemList != null && + mSystemList.getSystem() != null && + !mSystemList.getSystem().isEmpty(); + } + + @XmlElement( name = "channel_maps" ) public ChannelMapList getChannelMapList() { return mChannelMapList; } + public boolean hasChannelMapList() + { + return mChannelMapList != null && + mChannelMapList.getChannelMap() != null && + !mChannelMapList.getChannelMap().isEmpty(); + } + + public void setChannelMapList( ChannelMapList list ) { mChannelMapList = list; diff --git a/src/controller/site/Site.java b/src/playlist/version1/Site.java similarity index 98% rename from src/controller/site/Site.java rename to src/playlist/version1/Site.java index 2cc24419..876fc30b 100644 --- a/src/controller/site/Site.java +++ b/src/playlist/version1/Site.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.site; +package playlist.version1; import java.util.ArrayList; @@ -27,6 +27,7 @@ import controller.channel.Channel; @XmlSeeAlso( { Channel.class } ) @XmlRootElement( name = "site" ) +@Deprecated public class Site { private String mName; diff --git a/src/controller/system/System.java b/src/playlist/version1/System.java similarity index 97% rename from src/controller/system/System.java rename to src/playlist/version1/System.java index e88432f9..548bff7d 100644 --- a/src/controller/system/System.java +++ b/src/playlist/version1/System.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.system; +package playlist.version1; import java.util.ArrayList; @@ -23,10 +23,9 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; -import controller.site.Site; - @XmlSeeAlso( { Site.class } ) @XmlRootElement( name = "system" ) +@Deprecated public class System { private String mName; diff --git a/src/controller/system/SystemList.java b/src/playlist/version1/SystemList.java similarity index 92% rename from src/controller/system/SystemList.java rename to src/playlist/version1/SystemList.java index 91fcacec..0491f50e 100644 --- a/src/controller/system/SystemList.java +++ b/src/playlist/version1/SystemList.java @@ -15,16 +15,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.system; +package playlist.version1; import java.util.ArrayList; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; -@XmlSeeAlso( { controller.system.System.class } ) +@SuppressWarnings( "deprecation" ) +@XmlSeeAlso( { playlist.version1.System.class } ) @XmlRootElement( name = "system_list" ) +@Deprecated public class SystemList { private ArrayList mSystem = new ArrayList(); diff --git a/src/properties/SystemProperties.java b/src/properties/SystemProperties.java index a36a171b..38837ed5 100644 --- a/src/properties/SystemProperties.java +++ b/src/properties/SystemProperties.java @@ -82,13 +82,18 @@ public class SystemProperties { out = new FileOutputStream( propsPath.toString() ); - String comments = "SDRTrunk - SDR Trunking Decoder Application Settings"; + String comments = + "SDRTrunk - SDR Trunking Decoder Application Settings"; mProperties.store( out, comments ); + +// mLog.info( "SystemProperties - saved current properties [" + +// propsPath.toString() + "]" ); } catch( Exception e ) { - mLog.error( "exception while saving application properties", e ); + mLog.error( "SystemProperties - exception while saving " + + "application properties", e ); } finally { @@ -141,7 +146,8 @@ public class SystemProperties } catch ( IOException e ) { - mLog.error( "exception while creating app folder [" + folder + "]", e ); + mLog.error( "SystemProperties - exception while creating " + + "app folder [" + folder + "]", e ); } } @@ -152,11 +158,11 @@ public class SystemProperties { if( mPropertiesPath == null ) { - mLog.info( "no properties file loaded - using defaults" ); + mLog.info( "SystemProperties - no properties file loaded - using defaults" ); } else { - mLog.info( "application properties loaded [" + mPropertiesPath.toString() + "]" ); + mLog.info( "SystemProperties - application properties loaded [" + mPropertiesPath.toString() + "]" ); } } @@ -178,7 +184,7 @@ public class SystemProperties } catch ( FileNotFoundException e ) { - mLog.error( "exception while opening inputstream on " + + mLog.error( "SDRTrunk - exception while opening inputstream on " + "application properties file", e ); } @@ -190,7 +196,7 @@ public class SystemProperties } catch ( IOException e ) { - mLog.error( "exception while loading properties " + + mLog.error( "SDRTrunk - exception while loading properties " + "inputstream into SystemProperties", e ); } finally @@ -206,7 +212,8 @@ public class SystemProperties } } - mLog.info( "loaded [" + propertiesPath.toString() + "]" ); + mLog.info( "SystemProperties - loaded [" + + propertiesPath.toString() + "]" ); } /** diff --git a/src/record/RecordComponentEditor.java b/src/record/RecordComponentEditor.java deleted file mode 100644 index 435a6ccb..00000000 --- a/src/record/RecordComponentEditor.java +++ /dev/null @@ -1,93 +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 - ******************************************************************************/ -package record; - - -import javax.swing.JCheckBox; - -import record.config.RecordConfiguration; -import controller.channel.AbstractChannelEditor; -import controller.channel.ChannelNode; - -public class RecordComponentEditor extends AbstractChannelEditor -{ - private static final long serialVersionUID = 1L; - - private JCheckBox mAudioRecorder = new JCheckBox( "Audio" ); - private JCheckBox mBasebandRecorder = new JCheckBox( "Baseband I/Q" ); - private JCheckBox mTrafficBasebandRecorder = new JCheckBox( "Traffic Channel Baseband I/Q" ); - - public RecordComponentEditor( ChannelNode channelNode ) - { - super( channelNode ); - - add( mAudioRecorder, "span" ); - add( mBasebandRecorder, "span" ); - add( mTrafficBasebandRecorder, "span" ); - - reset(); - } - - @Override - public void save() - { - RecordConfiguration config = - getChannelNode().getChannel().getRecordConfiguration(); - - config.clearRecorders(); - - if( mAudioRecorder.isSelected() ) - { - config.addRecorder( RecorderType.AUDIO ); - } - - if( mBasebandRecorder.isSelected() ) - { - config.addRecorder( RecorderType.BASEBAND ); - } - - if( mTrafficBasebandRecorder.isSelected() ) - { - config.addRecorder( RecorderType.TRAFFIC_BASEBAND ); - } - - } - - @Override - public void reset() - { - javax.swing.SwingUtilities.invokeLater(new Runnable() - { - @Override - public void run() - { - RecordConfiguration config = - getChannelNode().getChannel().getRecordConfiguration(); - - mAudioRecorder.setSelected( config.getRecorders() - .contains( RecorderType.AUDIO ) ); - - mBasebandRecorder.setSelected( config.getRecorders() - .contains( RecorderType.BASEBAND ) ); - - mTrafficBasebandRecorder.setSelected( config.getRecorders() - .contains( RecorderType.TRAFFIC_BASEBAND ) ); - } - }); - } -} diff --git a/src/record/RecordConfigurationEditor.java b/src/record/RecordConfigurationEditor.java new file mode 100644 index 00000000..832f242e --- /dev/null +++ b/src/record/RecordConfigurationEditor.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package record; + +import gui.editor.Editor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +import javax.swing.JCheckBox; + +import net.miginfocom.swing.MigLayout; +import record.config.RecordConfiguration; +import controller.channel.Channel; + +public class RecordConfigurationEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + private JCheckBox mAudioRecorder; + private JCheckBox mBasebandRecorder; + private JCheckBox mTrafficBasebandRecorder; + + public RecordConfigurationEditor() + { + + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "", "[][grow]" ) ); + + mAudioRecorder = new JCheckBox( "Audio" ); + mAudioRecorder.setEnabled( false ); + mAudioRecorder.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mAudioRecorder ); + + mBasebandRecorder = new JCheckBox( "Baseband I/Q" ); + mBasebandRecorder.setEnabled( false ); + mBasebandRecorder.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mBasebandRecorder ); + + mTrafficBasebandRecorder = new JCheckBox( "Traffic Channel Baseband I/Q" ); + mTrafficBasebandRecorder.setEnabled( false ); + mTrafficBasebandRecorder.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mTrafficBasebandRecorder ); + } + + private void setControlsEnabled( boolean enabled ) + { + if( mAudioRecorder.isEnabled() != enabled ) + { + mAudioRecorder.setEnabled( enabled ); + } + + if( mBasebandRecorder.isEnabled() != enabled ) + { + mBasebandRecorder.setEnabled( enabled ); + } + + if( mTrafficBasebandRecorder.isEnabled() != enabled ) + { + mTrafficBasebandRecorder.setEnabled( enabled ); + } + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + RecordConfiguration config = getItem().getRecordConfiguration(); + + config.clearRecorders(); + + if( mAudioRecorder.isSelected() ) + { + config.addRecorder( RecorderType.AUDIO ); + } + + if( mBasebandRecorder.isSelected() ) + { + config.addRecorder( RecorderType.BASEBAND ); + } + + if( mTrafficBasebandRecorder.isSelected() ) + { + config.addRecorder( RecorderType.TRAFFIC_BASEBAND ); + } + } + + setModified( false ); + } + + @Override + public void setItem( Channel channel ) + { + super.setItem( channel ); + + if( hasItem() ) + { + setControlsEnabled( true ); + + List recorders = getItem().getRecordConfiguration().getRecorders(); + mAudioRecorder.setSelected( recorders.contains( RecorderType.AUDIO ) ); + mBasebandRecorder.setSelected( recorders.contains( RecorderType.BASEBAND ) ); + mTrafficBasebandRecorder.setSelected( recorders.contains( RecorderType.TRAFFIC_BASEBAND ) ); + } + else + { + setControlsEnabled( false ); + } + } +} diff --git a/src/record/RecordEditor.java b/src/record/RecordEditor.java deleted file mode 100644 index 6d25832e..00000000 --- a/src/record/RecordEditor.java +++ /dev/null @@ -1,48 +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 - ******************************************************************************/ -package record; - -import record.config.RecordConfiguration; -import controller.Editor; - -public class RecordEditor extends Editor -{ - private static final long serialVersionUID = 1L; - - protected RecordConfiguration mConfig; - - public RecordEditor( RecordConfiguration config ) - { - mConfig = config; - } - - public RecordConfiguration getConfig() - { - return mConfig; - } - - @Override - public void save() - { - } - - @Override - public void reset() - { - } -} diff --git a/src/record/RecorderManager.java b/src/record/RecorderManager.java index 949ac241..4cc30ab2 100644 --- a/src/record/RecorderManager.java +++ b/src/record/RecorderManager.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,7 +20,6 @@ package record; import java.io.File; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,7 +32,6 @@ import audio.AudioPacket; import audio.metadata.Metadata; import audio.metadata.MetadataType; import controller.ThreadPoolManager; -import controller.ThreadPoolManager.ThreadType; public class RecorderManager implements Listener { @@ -88,10 +86,9 @@ public class RecorderManager implements Listener try { RealBufferWaveRecorder recorder = - new RealBufferWaveRecorder( mThreadPoolManager, - AUDIO_SAMPLE_RATE, filePrefix ); + new RealBufferWaveRecorder( AUDIO_SAMPLE_RATE, filePrefix ); - recorder.start(); + recorder.start( mThreadPoolManager.getScheduledExecutorService() ); recorder.receive( audioPacket.getAudioBuffer() ); mRecorders.put( identifier, recorder ); @@ -170,8 +167,7 @@ public class RecorderManager implements Listener /** * Constructs a baseband recorder for use in a processing chain. */ - public static ComplexBufferWaveRecorder getBasebandRecorder( - ThreadPoolManager threadPoolManager, String channelName ) + public ComplexBufferWaveRecorder getBasebandRecorder( String channelName ) { StringBuilder sb = new StringBuilder(); sb.append( SystemProperties.getInstance() @@ -180,7 +176,6 @@ public class RecorderManager implements Listener sb.append( channelName ); sb.append( "_baseband" ); - return new ComplexBufferWaveRecorder( threadPoolManager, - AUDIO_SAMPLE_RATE, sb.toString() ); + return new ComplexBufferWaveRecorder( AUDIO_SAMPLE_RATE, sb.toString() ); } } diff --git a/src/record/config/RecordConfiguration.java b/src/record/config/RecordConfiguration.java index 93c32cf9..5315e878 100644 --- a/src/record/config/RecordConfiguration.java +++ b/src/record/config/RecordConfiguration.java @@ -18,6 +18,7 @@ package record.config; import java.util.ArrayList; +import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @@ -28,19 +29,19 @@ import controller.config.Configuration; @XmlRootElement( name = "record_configuration" ) public class RecordConfiguration extends Configuration { - private ArrayList mRecorders = new ArrayList(); + private List mRecorders = new ArrayList<>(); public RecordConfiguration() { } @XmlElement( name = "recorder" ) - public ArrayList getRecorders() + public List getRecorders() { return mRecorders; } - public void setRecorders( ArrayList recorders ) + public void setRecorders( List recorders ) { mRecorders = recorders; } diff --git a/src/record/wave/ComplexBufferWaveRecorder.java b/src/record/wave/ComplexBufferWaveRecorder.java index 53eed8cb..f738bc7f 100644 --- a/src/record/wave/ComplexBufferWaveRecorder.java +++ b/src/record/wave/ComplexBufferWaveRecorder.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -38,8 +39,6 @@ import sample.Listener; import sample.complex.ComplexBuffer; import sample.complex.IComplexBufferListener; import util.TimeStamp; -import controller.ThreadPoolManager; -import controller.ThreadPoolManager.ThreadType; /** * WAVE audio recorder module for recording complex (I&Q) samples to a wave file @@ -55,18 +54,14 @@ public class ComplexBufferWaveRecorder extends Module private Path mFile; private AudioFormat mAudioFormat; - private ThreadPoolManager mThreadPoolManager; private BufferProcessor mBufferProcessor; private ScheduledFuture mProcessorHandle; private LinkedBlockingQueue mBuffers = new LinkedBlockingQueue<>( 500 ); private AtomicBoolean mRunning = new AtomicBoolean(); - public ComplexBufferWaveRecorder( ThreadPoolManager threadPoolManager, - int sampleRate, String filePrefix ) + public ComplexBufferWaveRecorder( int sampleRate, String filePrefix ) { - mThreadPoolManager = threadPoolManager; - mAudioFormat = new AudioFormat( sampleRate, //SampleRate 16, //Sample Size 2, //Channels @@ -81,7 +76,7 @@ public class ComplexBufferWaveRecorder extends Module return mFile; } - public void start() + public void start( ScheduledExecutorService executor ) { if( mRunning.compareAndSet( false, true ) ) { @@ -103,9 +98,8 @@ public class ComplexBufferWaveRecorder extends Module mWriter = new WaveWriter( mAudioFormat, mFile ); /* Schedule the processor to run every 500 milliseconds */ - mProcessorHandle = mThreadPoolManager.scheduleFixedRate( - ThreadType.BASEBAND_RECORDING, mBufferProcessor, 500, - TimeUnit.MILLISECONDS ); + mProcessorHandle = executor.scheduleAtFixedRate( + mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS ); } catch( IOException io ) { @@ -149,8 +143,6 @@ public class ComplexBufferWaveRecorder extends Module public void dispose() { stop(); - - mThreadPoolManager = null; } @Override @@ -190,7 +182,10 @@ public class ComplexBufferWaveRecorder extends Module mFile = null; - mThreadPoolManager.cancel( mProcessorHandle ); + if( mProcessorHandle != null ) + { + mProcessorHandle.cancel( true ); + } mProcessorHandle = null; } diff --git a/src/record/wave/RealBufferWaveRecorder.java b/src/record/wave/RealBufferWaveRecorder.java index 49b0f3b6..ac17988a 100644 --- a/src/record/wave/RealBufferWaveRecorder.java +++ b/src/record/wave/RealBufferWaveRecorder.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -34,17 +35,15 @@ import org.slf4j.LoggerFactory; import sample.ConversionUtils; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import util.TimeStamp; -import controller.ThreadPoolManager; -import controller.ThreadPoolManager.ThreadType; /** * WAVE audio recorder module for recording real sample buffers to a wave file */ public class RealBufferWaveRecorder extends Module - implements IRealBufferListener, Listener + implements IFilteredRealBufferListener, Listener { private final static Logger mLog = LoggerFactory.getLogger( RealBufferWaveRecorder.class ); @@ -54,7 +53,6 @@ public class RealBufferWaveRecorder extends Module private Path mFile; private AudioFormat mAudioFormat; - private ThreadPoolManager mThreadPoolManager; private BufferProcessor mBufferProcessor; private ScheduledFuture mProcessorHandle; private LinkedBlockingQueue mBuffers = @@ -63,11 +61,8 @@ public class RealBufferWaveRecorder extends Module private AtomicBoolean mRunning = new AtomicBoolean(); - public RealBufferWaveRecorder( ThreadPoolManager threadPoolManager, - int sampleRate, String filePrefix ) + public RealBufferWaveRecorder( int sampleRate, String filePrefix ) { - mThreadPoolManager = threadPoolManager; - mAudioFormat = new AudioFormat( sampleRate, //SampleRate 16, //Sample Size 1, //Channels @@ -90,7 +85,7 @@ public class RealBufferWaveRecorder extends Module return mFile; } - public void start() + public void start( ScheduledExecutorService executor ) { if( mRunning.compareAndSet( false, true ) ) { @@ -112,9 +107,8 @@ public class RealBufferWaveRecorder extends Module mWriter = new WaveWriter( mAudioFormat, mFile ); /* Schedule the processor to run every 500 milliseconds */ - mProcessorHandle = mThreadPoolManager.scheduleFixedRate( - ThreadType.AUDIO_RECORDING, mBufferProcessor, 500, - TimeUnit.MILLISECONDS ); + mProcessorHandle = executor.scheduleAtFixedRate( + mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS ); } catch( IOException io ) { @@ -151,7 +145,7 @@ public class RealBufferWaveRecorder extends Module } @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { return this; } @@ -160,8 +154,6 @@ public class RealBufferWaveRecorder extends Module public void dispose() { stop(); - - mThreadPoolManager = null; } @Override @@ -198,12 +190,14 @@ public class RealBufferWaveRecorder extends Module } } - mThreadPoolManager.cancel( mProcessorHandle ); + if( mProcessorHandle != null ) + { + mProcessorHandle.cancel( true ); + } + mProcessorHandle = null; mWriter = null; mFile = null; - mProcessorHandle = null; - mThreadPoolManager = null; } else { diff --git a/src/sample/Broadcaster.java b/src/sample/Broadcaster.java index 35fcefa4..65fa765b 100644 --- a/src/sample/Broadcaster.java +++ b/src/sample/Broadcaster.java @@ -17,23 +17,15 @@ ******************************************************************************/ package sample; +import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import module.decode.state.ChannelState; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** * Broadcasts a received float sample to multiple listeners */ public class Broadcaster implements Listener { - private final static Logger mLog = - LoggerFactory.getLogger( Broadcaster.class ); - - private CopyOnWriteArrayList> mListeners = - new CopyOnWriteArrayList>(); + private List> mListeners = new CopyOnWriteArrayList<>(); @Override public void receive( T t ) @@ -59,6 +51,11 @@ public class Broadcaster implements Listener return mListeners.size(); } + public List> getListeners() + { + return mListeners; + } + public void addListener( Listener listener ) { mListeners.add( listener ); diff --git a/src/sample/complex/ComplexBufferToStreamConverter.java b/src/sample/complex/ComplexBufferToStreamConverter.java index ecce6561..c8d1b464 100644 --- a/src/sample/complex/ComplexBufferToStreamConverter.java +++ b/src/sample/complex/ComplexBufferToStreamConverter.java @@ -9,7 +9,6 @@ public class ComplexBufferToStreamConverter implements Listener { private ComplexSampleListener mListener; - public void dispose() { mListener = null; diff --git a/src/sample/real/IFilteredRealBufferListener.java b/src/sample/real/IFilteredRealBufferListener.java new file mode 100644 index 00000000..06928fda --- /dev/null +++ b/src/sample/real/IFilteredRealBufferListener.java @@ -0,0 +1,8 @@ +package sample.real; + +import sample.Listener; + +public interface IFilteredRealBufferListener +{ + public Listener getFilteredRealBufferListener(); +} diff --git a/src/sample/real/IFilteredRealBufferProvider.java b/src/sample/real/IFilteredRealBufferProvider.java new file mode 100644 index 00000000..e600d368 --- /dev/null +++ b/src/sample/real/IFilteredRealBufferProvider.java @@ -0,0 +1,9 @@ +package sample.real; + +import sample.Listener; + +public interface IFilteredRealBufferProvider +{ + public void setFilteredRealBufferListener( Listener listener ); + public void removeFilteredRealBufferListener(); +} diff --git a/src/sample/real/IRealBufferListener.java b/src/sample/real/IRealBufferListener.java deleted file mode 100644 index 6ef33052..00000000 --- a/src/sample/real/IRealBufferListener.java +++ /dev/null @@ -1,8 +0,0 @@ -package sample.real; - -import sample.Listener; - -public interface IRealBufferListener -{ - public Listener getRealBufferListener(); -} diff --git a/src/sample/real/IRealBufferProvider.java b/src/sample/real/IRealBufferProvider.java deleted file mode 100644 index 0b11debd..00000000 --- a/src/sample/real/IRealBufferProvider.java +++ /dev/null @@ -1,9 +0,0 @@ -package sample.real; - -import sample.Listener; - -public interface IRealBufferProvider -{ - public void setRealBufferListener( Listener listener ); - public void removeRealBufferListener(); -} diff --git a/src/sample/real/IUnFilteredRealBufferListener.java b/src/sample/real/IUnFilteredRealBufferListener.java new file mode 100644 index 00000000..bce0f168 --- /dev/null +++ b/src/sample/real/IUnFilteredRealBufferListener.java @@ -0,0 +1,8 @@ +package sample.real; + +import sample.Listener; + +public interface IUnFilteredRealBufferListener +{ + public Listener getUnFilteredRealBufferListener(); +} diff --git a/src/sample/real/IUnFilteredRealBufferProvider.java b/src/sample/real/IUnFilteredRealBufferProvider.java new file mode 100644 index 00000000..764a5aeb --- /dev/null +++ b/src/sample/real/IUnFilteredRealBufferProvider.java @@ -0,0 +1,9 @@ +package sample.real; + +import sample.Listener; + +public interface IUnFilteredRealBufferProvider +{ + public void setUnFilteredRealBufferListener( Listener listener ); + public void removeUnFilteredRealBufferListener(); +} diff --git a/src/settings/ColorSetting.java b/src/settings/ColorSetting.java index d0cb3e1f..16e8f88a 100644 --- a/src/settings/ColorSetting.java +++ b/src/settings/ColorSetting.java @@ -24,6 +24,7 @@ import javax.xml.bind.annotation.XmlTransient; public class ColorSetting extends Setting { + @SuppressWarnings( "unused" ) private static final int NO_TRANSLUCENCY = 255; private static final int SPECTRUM_TRANSLUCENCY = 128; private static final int CONFIG_TRANSLUCENCY = 60; diff --git a/src/settings/ColorSettingResetAllMenuItem.java b/src/settings/ColorSettingResetAllMenuItem.java index 49b5e209..86595fd5 100644 --- a/src/settings/ColorSettingResetAllMenuItem.java +++ b/src/settings/ColorSettingResetAllMenuItem.java @@ -17,7 +17,6 @@ ******************************************************************************/ package settings; -import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -33,17 +32,14 @@ public class ColorSettingResetAllMenuItem extends JMenuItem { private static final long serialVersionUID = 1L; - private ColorSettingName mColorSettingName; private SettingsManager mSettingsManager; - private Color mCurrentColor; - public ColorSettingResetAllMenuItem( SettingsManager settingsManager, - ColorSettingName colorSettingName ) + public ColorSettingResetAllMenuItem( SettingsManager settingsManager, + ColorSettingName colorSettingName ) { super( "Reset - " + colorSettingName.getLabel() ); mSettingsManager = settingsManager; - mColorSettingName = colorSettingName; addActionListener( new ActionListener() { diff --git a/src/settings/ColorSettingResetMenuItem.java b/src/settings/ColorSettingResetMenuItem.java index f868d221..e54a0a4a 100644 --- a/src/settings/ColorSettingResetMenuItem.java +++ b/src/settings/ColorSettingResetMenuItem.java @@ -17,7 +17,6 @@ ******************************************************************************/ package settings; -import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -35,7 +34,6 @@ public class ColorSettingResetMenuItem extends JMenuItem private ColorSettingName mColorSettingName; private SettingsManager mSettingsManager; - private Color mCurrentColor; public ColorSettingResetMenuItem( SettingsManager settingsManager, ColorSettingName colorSettingName ) diff --git a/src/settings/Settings.java b/src/settings/Settings.java index 464e4d46..a7f2d261 100644 --- a/src/settings/Settings.java +++ b/src/settings/Settings.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,10 +28,8 @@ import map.DefaultIcon; import map.MapIcon; import settings.ColorSetting.ColorSettingName; import source.recording.RecordingConfiguration; -import source.tuner.TunerConfiguration; -import source.tuner.TunerConfigurationAssignment; -import source.tuner.TunerType; import source.tuner.airspy.AirspyTunerConfiguration; +import source.tuner.configuration.TunerConfiguration; import source.tuner.fcd.proV1.FCD1TunerConfiguration; import source.tuner.fcd.proplusV2.FCD2TunerConfiguration; import source.tuner.hackrf.HackRFTunerConfiguration; @@ -54,21 +52,17 @@ import source.tuner.rtl.r820t.R820TTunerConfiguration; R820TTunerConfiguration.class, RecordingConfiguration.class, Setting.class, - TunerConfiguration.class, - TunerConfigurationAssignment.class} ) + TunerConfiguration.class } ) @XmlRootElement( name = "SDRTrunk_settings" ) public class Settings { - private ArrayList mSettings = new ArrayList(); + private List mSettings = new ArrayList(); - private ArrayList mTunerConfiguration = + private List mTunerConfiguration = new ArrayList(); - private ArrayList mConfigurationAssignments = - new ArrayList(); - - private ArrayList mRecordingConfigurations = + private List mRecordingConfigurations = new ArrayList(); public Settings() @@ -76,7 +70,7 @@ public class Settings } @XmlElement( name="recording_configuration" ) - public ArrayList getRecordingConfigurations() + public List getRecordingConfigurations() { return mRecordingConfigurations; } @@ -89,7 +83,6 @@ public class Settings public void addRecordingConfiguration( RecordingConfiguration config ) { mRecordingConfigurations.add( config ); - } public void removeRecordingConfiguration( RecordingConfiguration config ) @@ -98,45 +91,18 @@ public class Settings } @XmlElement( name="tuner_configuration" ) - public ArrayList getTunerConfigurations() + public List getTunerConfigurations() { return mTunerConfiguration; } - public void setTunerConfigurations( ArrayList configs ) + public void setTunerConfigurations( List configs ) { mTunerConfiguration = configs; } - - public void addTunerConfiguration( TunerConfiguration config ) - { - mTunerConfiguration.add( config ); - - } - - public void removeTunerConfiguration( TunerConfiguration config ) - { - mTunerConfiguration.remove( config ); - } - - public ArrayList getTunerConfigurations( TunerType type ) - { - ArrayList configs = - new ArrayList(); - - for( TunerConfiguration config: mTunerConfiguration ) - { - if( config.getTunerType() == type ) - { - configs.add( config ); - } - } - - return configs; - } @XmlElement( name="setting" ) - public ArrayList getSettings() + public List getSettings() { return mSettings; } @@ -254,59 +220,6 @@ public class Settings return icons; } - @XmlElement( name="tuner_configuration_assignment" ) - public ArrayList getSelectedTunerConfiguration() - { - return mConfigurationAssignments; - } - - public void setSelectedTunerConfiguration( ArrayList configs ) - { - mConfigurationAssignments = configs; - } - - public void addSelectedTunerConfiguration( TunerConfigurationAssignment config ) - { - mConfigurationAssignments.add( config ); - - } - - public void removeSelectedTunerConfiguration( TunerConfigurationAssignment config ) - { - mConfigurationAssignments.remove( config ); - } - - public TunerConfigurationAssignment getConfigurationAssignment( TunerType type, String uniqueID ) - { - for( TunerConfigurationAssignment config: mConfigurationAssignments ) - { - if( config.getTunerType() == type && - uniqueID.contentEquals( config.getUniqueID() ) ) - { - return config; - } - } - - return null; - } - - public void setConfigurationAssignment( TunerType type, - String uniqueID, - String tunerConfigurationName ) - { - TunerConfigurationAssignment config = getConfigurationAssignment( type, uniqueID ); - - if( config == null ) - { - config = new TunerConfigurationAssignment(); - config.setTunerType( type ); - config.setUniqueID( uniqueID ); - addSelectedTunerConfiguration( config ); - } - - config.setTunerConfigurationName( tunerConfigurationName ); - } - public MapViewSetting getMapViewSetting( String name ) { for( Setting setting: mSettings ) diff --git a/src/settings/SettingsManager.java b/src/settings/SettingsManager.java index 2d40e0ad..a9bd4998 100644 --- a/src/settings/SettingsManager.java +++ b/src/settings/SettingsManager.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,6 +30,9 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.ImageIcon; import javax.xml.bind.JAXBContext; @@ -45,19 +48,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import properties.SystemProperties; +import sample.Listener; import settings.ColorSetting.ColorSettingName; import source.recording.RecordingConfiguration; -import source.tuner.TunerConfiguration; -import source.tuner.TunerConfigurationAssignment; -import source.tuner.TunerType; -import source.tuner.airspy.AirspyTunerConfiguration; -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; +import source.tuner.configuration.TunerConfigurationEvent; +import source.tuner.configuration.TunerConfigurationModel; +import controller.ThreadPoolManager; -public class SettingsManager +public class SettingsManager implements Listener { private final static Logger mLog = LoggerFactory.getLogger( SettingsManager.class ); @@ -76,8 +74,25 @@ public class SettingsManager private HashMap mResizedIcons = new HashMap(); - public SettingsManager() + private TunerConfigurationModel mTunerConfigurationModel; + private ThreadPoolManager mThreadPoolManager; + + private boolean mLoadingSettings = false; + private AtomicBoolean mSettingsSavePending = new AtomicBoolean(); + + public SettingsManager( ThreadPoolManager threadPoolManager, + TunerConfigurationModel tunerConfigurationModel ) { + //TODO: move settings and icons into a SettingsModel and an IconModel + //and update this class to only provide loading, saving, and model + //change detection producing a save. + + mThreadPoolManager = threadPoolManager; + mTunerConfigurationModel = tunerConfigurationModel; + + //Register for tuner config events so that we can save the settings + mTunerConfigurationModel.addListener( this ); + init(); } @@ -101,6 +116,15 @@ public class SettingsManager refreshIcons(); } + + @Override + public void receive( TunerConfigurationEvent t ) + { + if( !mLoadingSettings ) + { + scheduleSettingsSave(); + } + } /** * Reloads the icon hash map, and sets up the default iconology. If the @@ -180,7 +204,7 @@ public class SettingsManager broadcastSettingChange( setting ); - save(); + scheduleSettingsSave(); } public void resetColorSetting( ColorSettingName name ) @@ -226,7 +250,7 @@ public class SettingsManager broadcastSettingChange( setting ); - save(); + scheduleSettingsSave(); } public MapIcon[] getMapIcons() @@ -323,7 +347,7 @@ public class SettingsManager broadcastSettingDeleted( existing ); - save(); + scheduleSettingsSave(); } } @@ -346,7 +370,7 @@ public class SettingsManager existing.setName( icon.getName() ); existing.setPath( icon.getPath() ); broadcastSettingChange( existing ); - save(); + scheduleSettingsSave(); } } @@ -363,7 +387,7 @@ public class SettingsManager existing.setName( newName ); existing.setPath( newPath ); broadcastSettingChange( existing ); - save(); + scheduleSettingsSave(); } } @@ -386,6 +410,12 @@ public class SettingsManager } MapIcon mapIcon = mIcons.get( name ); + + if( mapIcon == null ) + { + mapIcon = new MapIcon( name, name ); + mIcons.put( name, mapIcon ); + } if( mapIcon != null ) { @@ -399,11 +429,12 @@ public class SettingsManager } else { - mResizedIcons.put( mergedName, mapIcon.getImageIcon() ); + return mapIcon.getImageIcon(); } } /* Use the default Icon */ + @SuppressWarnings( "unused" ) String mergedDefault = mCurrentDefaultIconName + height; if( mResizedIcons.containsKey( mergedDefault ) ) @@ -431,7 +462,8 @@ public class SettingsManager } /* Something happened ... the above should always return an icon */ - mLog.error( "couldn't return an icon named [" + name + "] of heigh [" + height + "]" ); + mLog.error( "SettingsManager - couldn't return an icon named [" + + name + "] of heigh [" + height + "]" ); } return null; @@ -516,7 +548,7 @@ public class SettingsManager defaultIconSetting.setName( icon.getName() ); - save(); + scheduleSettingsSave(); broadcastSettingChange( defaultIconSetting ); } @@ -530,12 +562,12 @@ public class SettingsManager { mSettings.addSetting( setting ); - save(); + scheduleSettingsSave(); broadcastSettingChange( setting ); } - public ArrayList getRecordingConfigurations() + public List getRecordingConfigurations() { return mSettings.getRecordingConfigurations(); } @@ -543,106 +575,15 @@ public class SettingsManager public void addRecordingConfiguration( RecordingConfiguration config ) { mSettings.addRecordingConfiguration( config ); - save(); + scheduleSettingsSave(); } public void removeRecordingConfiguration( RecordingConfiguration config ) { mSettings.removeRecordingConfiguration( config ); - save(); + scheduleSettingsSave(); } - public ArrayList getTunerConfigurations( TunerType type ) - { - ArrayList configs = getSettings() - .getTunerConfigurations( type ); - - return configs; - } - - public void deleteTunerConfiguration( TunerConfiguration config ) - { - getSettings().removeTunerConfiguration( config ); - save(); - } - - public TunerConfiguration addNewTunerConfiguration( TunerType type, - String name ) - { - switch( type ) - { - case AIRSPY_R820T: - AirspyTunerConfiguration airspyConfig = - new AirspyTunerConfiguration( name ); - - getSettings().addTunerConfiguration( airspyConfig ); - - save(); - return airspyConfig; - case ELONICS_E4000: - E4KTunerConfiguration e4KConfig = - new E4KTunerConfiguration( name ); - - getSettings().addTunerConfiguration( e4KConfig ); - - save(); - return e4KConfig; - case FUNCUBE_DONGLE_PRO: - FCD1TunerConfiguration config = - new FCD1TunerConfiguration( name ); - - getSettings().addTunerConfiguration( config ); - - save(); - - return config; - case FUNCUBE_DONGLE_PRO_PLUS: - FCD2TunerConfiguration configPlus = - new FCD2TunerConfiguration( name ); - - getSettings().addTunerConfiguration( configPlus ); - - save(); - - return configPlus; - case HACKRF: - HackRFTunerConfiguration hackConfig = - new HackRFTunerConfiguration( name ); - - getSettings().addTunerConfiguration( hackConfig ); - - save(); - - return hackConfig; - case RAFAELMICRO_R820T: - R820TTunerConfiguration r820TConfig = - new R820TTunerConfiguration( name ); - - getSettings().addTunerConfiguration( r820TConfig ); - - save(); - - return r820TConfig; - default: - throw new IllegalArgumentException( "tuner type is unrecognized [" + - type.toString() + "]" ); - } - } - - public TunerConfigurationAssignment getSelectedTunerConfiguration( - TunerType type, String address ) - { - return mSettings.getConfigurationAssignment( type, address ); - } - - public void setSelectedTunerConfiguration( TunerType type, - String address, TunerConfiguration config ) - { - mSettings.setConfigurationAssignment( type, address, config.getName() ); - - save(); - } - public MapViewSetting getMapViewSetting( String name, GeoPosition position, int zoom ) { MapViewSetting loc = mSettings.getMapViewSetting( name ); @@ -668,11 +609,13 @@ public class SettingsManager loc.setGeoPosition( position ); loc.setZoom( zoom ); - save(); + scheduleSettingsSave(); } - public void save() + private void save() { + saveTunerConfigurationModel(); + JAXBContext context = null; SystemProperties props = SystemProperties.getInstance(); @@ -698,7 +641,8 @@ public class SettingsManager } catch( Exception e ) { - mLog.error( "couldn't create file to save settings [" + filePath.toString() + "]", e ); + mLog.error( "SettingsManager - couldn't create file to save " + + "settings [" + filePath.toString() + "]", e ); } OutputStream out = null; @@ -719,12 +663,14 @@ public class SettingsManager } catch ( JAXBException e ) { - mLog.error( "jaxb exception while saving settings", e ); + mLog.error( "SettingsManager - jaxb exception while saving " + + "settings", e ); } } catch ( Exception e ) { - mLog.error( "couldn't open outputstream to save settings [" + filePath.toString() + "]" ); + mLog.error( "SettingsManager - coulcn't open outputstream to " + + "save settings [" + filePath.toString() + "]" ); } finally { @@ -748,9 +694,11 @@ public class SettingsManager */ public void load( Path settingsPath ) { + mLoadingSettings = true; + if( Files.exists( settingsPath ) ) { - mLog.info( "loading settings file [" + settingsPath.toString() + "]" ); + mLog.info( "SettingsManager - loading settings file [" + settingsPath.toString() + "]" ); JAXBContext context = null; @@ -770,13 +718,13 @@ public class SettingsManager } catch ( JAXBException e ) { - mLog.error( "jaxb exception while loading " + + mLog.error( "SettingsManager - jaxb exception while loading " + "settings", e ); } } catch ( Exception e ) { - mLog.error( "coulcn't open inputstream to " + + mLog.error( "SettingsManager - coulcn't open inputstream to " + "load settings [" + settingsPath.toString() + "]", e ); } finally @@ -789,7 +737,7 @@ public class SettingsManager } catch ( IOException e ) { - mLog.error( "exception while closing " + + mLog.error( "SettingsManager - exception while closing " + "the settings file inputstream reader", e ); } } @@ -797,13 +745,38 @@ public class SettingsManager } else { - mLog.info( "settings does not exist [" + settingsPath.toString() + "]" ); + mLog.info( "SettingsManager - settings does not exist [" + + settingsPath.toString() + "]" ); } if( mSettings == null ) { mSettings = new Settings(); } + + loadTunerConfigurationModel(); + + mLoadingSettings = false; + } + + private void loadTunerConfigurationModel() + { + if( mSettings != null ) + { + mTunerConfigurationModel.clear(); + + mTunerConfigurationModel.addTunerConfigurations( + mSettings.getTunerConfigurations() ); + } + } + + private void saveTunerConfigurationModel() + { + if( mSettings != null ) + { + mSettings.setTunerConfigurations( + mTunerConfigurationModel.getTunerConfigurations() ); + } } public void broadcastSettingChange( Setting setting ) @@ -853,4 +826,35 @@ public class SettingsManager { mListeners.remove( listener ); } + + /** + * Schedules a settings save task. Subsequent calls to this method will be ignored until the + * save event occurs, thus limiting repetitive saving to a minimum. + */ + private void scheduleSettingsSave() + { + if( !mLoadingSettings ) + { + if( mSettingsSavePending.compareAndSet( false, true ) ) + { + mThreadPoolManager.scheduleOnce( new SettingsSaveTask(), + 2, TimeUnit.SECONDS ); + } + } + } + + /** + * Resets the settings save pending flag to false and proceeds to save the + * settings. + */ + public class SettingsSaveTask implements Runnable + { + @Override + public void run() + { + mSettingsSavePending.set( false ); + + save(); + } + } } diff --git a/src/source/ComplexSource.java b/src/source/ComplexSource.java index eeb4084e..e7e1939c 100644 --- a/src/source/ComplexSource.java +++ b/src/source/ComplexSource.java @@ -6,8 +6,8 @@ import sample.complex.ComplexBuffer; public abstract class ComplexSource extends Source implements Provider { - public ComplexSource( String name ) + public ComplexSource() { - super( name, SampleType.COMPLEX ); + super( SampleType.COMPLEX ); } } diff --git a/src/source/EmptySourceEditor.java b/src/source/EmptySourceEditor.java deleted file mode 100644 index ad1c1669..00000000 --- a/src/source/EmptySourceEditor.java +++ /dev/null @@ -1,46 +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 - ******************************************************************************/ -package source; - -import javax.swing.JLabel; - -import source.config.SourceConfiguration; -import controller.ResourceManager; - -public class EmptySourceEditor extends SourceEditor -{ - private static final long serialVersionUID = 1L; - - public EmptySourceEditor( ResourceManager resourceManager, - SourceConfiguration config ) - { - super( resourceManager, config ); - - initGUI(); - } - - public void reset() {} - - public void save() {} - - private void initGUI() - { - JLabel selectLabel = new JLabel( "Please select a source" ); - add( selectLabel ); - } -} diff --git a/src/source/FloatSource.java b/src/source/FloatSource.java deleted file mode 100644 index c8161a9c..00000000 --- a/src/source/FloatSource.java +++ /dev/null @@ -1,12 +0,0 @@ -package source; - -import sample.Provider; -import sample.SampleType; - -public abstract class FloatSource extends Source implements Provider -{ - public FloatSource( String name ) - { - super( name, SampleType.REAL ); - } -} diff --git a/src/source/RealSource.java b/src/source/RealSource.java index 6b09ccd5..b512bfbc 100644 --- a/src/source/RealSource.java +++ b/src/source/RealSource.java @@ -7,8 +7,8 @@ import sample.real.RealBuffer; public abstract class RealSource extends Source implements Provider { - public RealSource( String name ) + public RealSource() { - super( name, SampleType.REAL ); + super( SampleType.REAL ); } } diff --git a/src/source/Source.java b/src/source/Source.java index 064cc6ce..217def00 100644 --- a/src/source/Source.java +++ b/src/source/Source.java @@ -17,27 +17,21 @@ ******************************************************************************/ package source; +import module.Module; import sample.SampleType; /** * Abstract class to define the minimum functionality of a sample data provider. */ -public abstract class Source +public abstract class Source extends Module { - protected String mName; protected SampleType mSampleType; - public Source( String name, SampleType sampleType ) + public Source( SampleType sampleType ) { - mName = name; mSampleType = sampleType; } - public String getName() - { - return mName; - } - public abstract int getSampleRate() throws SourceException; public abstract long getFrequency() throws SourceException; diff --git a/src/source/SourceComponentEditor.java b/src/source/SourceComponentEditor.java deleted file mode 100644 index 0cf4f186..00000000 --- a/src/source/SourceComponentEditor.java +++ /dev/null @@ -1,129 +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 - ******************************************************************************/ -package source; - - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JLabel; - -import net.miginfocom.swing.MigLayout; -import source.config.SourceConfigFactory; -import source.config.SourceConfiguration; -import controller.channel.AbstractChannelEditor; -import controller.channel.ChannelNode; - -public class SourceComponentEditor extends AbstractChannelEditor -{ - private static final long serialVersionUID = 1L; - - private JComboBox mComboSources; - private SourceEditor mEditor; - - public SourceComponentEditor( ChannelNode channelNode ) - { - super( channelNode ); - - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][grow]" ) ); - - mComboSources = new JComboBox(); - mComboSources.setModel( new DefaultComboBoxModel( SourceType.values() ) ); - mComboSources.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - SourceType selected = mComboSources.getItemAt( mComboSources.getSelectedIndex() ); - - if( selected != null ) - { - SourceConfiguration config; - - if( selected == mChannelNode.getChannel() - .getSourceConfiguration().getSourceType() ) - { - config = mChannelNode.getChannel() - .getSourceConfiguration(); - } - else - { - config = SourceConfigFactory - .getSourceConfiguration( selected ); - } - - //Remove the existing one - if( mEditor != null ) - { - remove( mEditor ); - } - - //Change to the new one - mEditor = SourceEditorFactory.getPanel( - mChannelNode.getModel().getResourceManager(), config ); - - //Add it to the jpanel - add( mEditor, "span 2" ); - - revalidate(); - repaint(); - } - } - }); - - add( new JLabel( "Source:" ) ); - add( mComboSources, "wrap" ); - - reset(); - } - - public SourceEditor getSourceEditor() - { - return mEditor; - } - - public void reset() - { - javax.swing.SwingUtilities.invokeLater(new Runnable() - { - - @Override - public void run() { - mComboSources.setSelectedItem( - mChannelNode.getChannel() - .getSourceConfiguration().getSourceType() ); - - mComboSources.requestFocus(); - - mComboSources.requestFocusInWindow(); - } - }); - } - - @Override - public void save() - { - mEditor.save(); - - //Let the calling save method send the config change event - mChannelNode.getChannel() - .setSourceConfiguration( mEditor.getConfig(), false ); - } -} diff --git a/src/source/SourceConfigurationEditor.java b/src/source/SourceConfigurationEditor.java new file mode 100644 index 00000000..2c16c299 --- /dev/null +++ b/src/source/SourceConfigurationEditor.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package source; + +import gui.editor.Editor; +import gui.editor.EmptyEditor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; + +import net.miginfocom.swing.MigLayout; +import source.config.SourceConfiguration; +import source.mixer.MixerSourceEditor; +import source.tuner.TunerSourceEditor; +import controller.channel.Channel; + +public class SourceConfigurationEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + private JComboBox mComboSources; + private MixerSourceEditor mMixerSourceEditor; + private TunerSourceEditor mTunerSourceEditor; + private Editor mCurrentEditor; + + public SourceConfigurationEditor( SourceManager sourceManager ) + { + mMixerSourceEditor = new MixerSourceEditor( sourceManager ); + mMixerSourceEditor.setSaveRequestListener( this ); + + mTunerSourceEditor = new TunerSourceEditor(); + mTunerSourceEditor.setSaveRequestListener( this ); + + init(); + } + + public SourceConfiguration getSourceConfiguration() + { + return hasItem() ? getItem().getSourceConfiguration() : null; + } + + private void init() + { + setLayout( new MigLayout( "wrap 2", "[][grow,fill]", "[align top][grow]" ) ); + + mComboSources = new JComboBox(); + mComboSources.setModel( new DefaultComboBoxModel( SourceType.getTypes() ) ); + mComboSources.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + SourceType selected = (SourceType)mComboSources.getSelectedItem(); + + switch( selected ) + { + case MIXER: + setEditor( mMixerSourceEditor ); + break; + case TUNER: + setEditor( mTunerSourceEditor ); + break; + default: + setEditor( new EmptyEditor() ); + break; + } + } + }); + + add( mComboSources ); + + mCurrentEditor = mTunerSourceEditor; + add( mCurrentEditor ); + } + + private void setEditor( Editor editor ) + { + if( mCurrentEditor != editor ) + { + //Set channel to null to force a save prompt as required + mCurrentEditor.setItem( null ); + + remove( mCurrentEditor ); + mCurrentEditor = editor; + mCurrentEditor.setSaveRequestListener( this ); + add( mCurrentEditor ); + + mCurrentEditor.setItem( getItem() ); + + revalidate(); + } + } + + @Override + public void save() + { + if( hasItem() ) + { + mCurrentEditor.save(); + } + } + + /** + * Sets the channel configuration. Note: this method must be invoked from + * the swing event dispatch thread. + */ + @Override + public void setItem( Channel channel ) + { + super.setItem( channel ); + + if( hasItem() ) + { + mComboSources.setSelectedItem( getItem().getSourceConfiguration().getSourceType() ); + repaint(); + mCurrentEditor.setItem( channel ); + } + } +} diff --git a/src/source/SourceEditor.java b/src/source/SourceEditor.java deleted file mode 100644 index dd5ad11e..00000000 --- a/src/source/SourceEditor.java +++ /dev/null @@ -1,46 +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 - ******************************************************************************/ -package source; - -import source.config.SourceConfiguration; -import controller.Editor; -import controller.ResourceManager; - -public abstract class SourceEditor extends Editor -{ - private static final long serialVersionUID = 1L; - - protected ResourceManager mResourceManager; - protected SourceConfiguration mConfig; - - public SourceEditor( ResourceManager resourceManager, SourceConfiguration config ) - { - mResourceManager = resourceManager; - mConfig = config; - } - - public SourceConfiguration getConfig() - { - return mConfig; - } - - public ResourceManager getResourceManager() - { - return mResourceManager; - } -} diff --git a/src/source/SourceEditorFactory.java b/src/source/SourceEditorFactory.java deleted file mode 100644 index b918f79f..00000000 --- a/src/source/SourceEditorFactory.java +++ /dev/null @@ -1,52 +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 - ******************************************************************************/ -package source; - -import source.config.SourceConfiguration; -import source.mixer.MixerEditor; -import source.recording.RecordingEditor; -import source.tuner.TunerEditor; -import controller.ResourceManager; - -public class SourceEditorFactory -{ - public static SourceEditor getPanel( ResourceManager resourceManager, - SourceConfiguration config ) - { - SourceEditor configuredPanel; - - switch( config.getSourceType() ) - { - case MIXER: - configuredPanel = new MixerEditor( resourceManager, config ); - break; - case TUNER: - configuredPanel = new TunerEditor( resourceManager, config ); - break; - case RECORDING: - configuredPanel = new RecordingEditor( resourceManager, config ); - break; - case NONE: - default: - configuredPanel = new EmptySourceEditor( resourceManager, config ); - break; - } - - return configuredPanel; - } -} diff --git a/src/source/SourceManager.java b/src/source/SourceManager.java index 8c783ab9..cc010d06 100644 --- a/src/source/SourceManager.java +++ b/src/source/SourceManager.java @@ -17,27 +17,48 @@ ******************************************************************************/ package source; +import settings.SettingsManager; +import source.config.SourceConfigTuner; import source.config.SourceConfiguration; import source.mixer.MixerManager; -import controller.ResourceManager; +import source.recording.RecordingSourceManager; +import source.tuner.TunerManager; +import source.tuner.TunerModel; +import controller.ThreadPoolManager; -public class SourceManager { - /** - * NOTE: each source will auto-start once a listener registers on it to - * receive samples and should auto-stop once a listener de-registers - * - * Each channel object will handle the starting and stopping of each channel - * processing chain. - * - * Source Manager will reach out to the MixerManager or the TunerManager - * to get source objects to pass off to the channel object, as requested. - */ - - private ResourceManager mResourceManager; +public class SourceManager +{ + private MixerManager mMixerManager; + private RecordingSourceManager mRecordingSourceManager; + private TunerManager mTunerManager; + private TunerModel mTunerModel; - public SourceManager( ResourceManager resourceManager ) + public SourceManager( TunerModel tunerModel, + SettingsManager settingsManager, + ThreadPoolManager threadPoolManager ) { - mResourceManager = resourceManager; + mTunerModel = tunerModel; + mMixerManager = new MixerManager(); + mRecordingSourceManager = new RecordingSourceManager( settingsManager ); + mTunerManager = new TunerManager( mMixerManager, tunerModel, threadPoolManager ); + + //TODO: change mixer & recording managers to be models and hand them + //in via the constructor. Perform loading outside of this class. + } + + public MixerManager getMixerManager() + { + return mMixerManager; + } + + public RecordingSourceManager getRecordingSourceManager() + { + return mRecordingSourceManager; + } + + public TunerManager getTunerManager() + { + return mTunerManager; } public Source getSource( SourceConfiguration config, int bandwidth ) @@ -48,14 +69,13 @@ public class SourceManager { switch( config.getSourceType() ) { case MIXER: - retVal = MixerManager.getInstance().getSource( config ); + retVal = mMixerManager.getSource( config ); break; case TUNER: - retVal = mResourceManager.getTunerManager().getSource( config, bandwidth ); + retVal = mTunerModel.getSource( (SourceConfigTuner)config, bandwidth ); break; case RECORDING: - retVal = mResourceManager.getRecordingSourceManager() - .getSource( config, bandwidth ); + retVal = mRecordingSourceManager.getSource( config, bandwidth ); case NONE: default: break; diff --git a/src/source/SourceType.java b/src/source/SourceType.java index be04d87c..421bdd91 100644 --- a/src/source/SourceType.java +++ b/src/source/SourceType.java @@ -17,10 +17,11 @@ ******************************************************************************/ package source; + public enum SourceType { NONE( "No Source" ), - MIXER( "Mixer/Sound Card" ), + MIXER( "Sound Card" ), TUNER( "Tuner" ), RECORDING( "IQ Recording" ); @@ -31,6 +32,15 @@ public enum SourceType mDisplayString = displayString; } + public static SourceType[] getTypes() + { + SourceType[] types = new SourceType[ 2 ]; + types[ 0 ] = SourceType.TUNER; + types[ 1 ] = SourceType.MIXER; + + return types; + } + public String getDisplayString() { return mDisplayString; diff --git a/src/source/config/SourceConfigFactory.java b/src/source/config/SourceConfigFactory.java index 2cedeaec..788518a1 100644 --- a/src/source/config/SourceConfigFactory.java +++ b/src/source/config/SourceConfigFactory.java @@ -23,24 +23,20 @@ public class SourceConfigFactory { public static SourceConfiguration getDefaultSourceConfiguration() { - return getSourceConfiguration( SourceType.NONE ); + return getSourceConfiguration( SourceType.TUNER ); } - public static SourceConfiguration - getSourceConfiguration( SourceType source ) + public static SourceConfiguration getSourceConfiguration( SourceType source ) { SourceConfiguration retVal; switch( source ) { - case MIXER: - retVal = new SourceConfigMixer(); - break; case TUNER: retVal = new SourceConfigTuner(); break; - case RECORDING: - retVal = new SourceConfigRecording(); + case MIXER: + retVal = new SourceConfigMixer(); break; case NONE: default: @@ -50,4 +46,39 @@ public class SourceConfigFactory return retVal; } + + /** + * Creates a copy of the configuration + */ + public static SourceConfiguration copy( SourceConfiguration config ) + { + if( config != null ) + { + switch( config.getSourceType() ) + { + case MIXER: + SourceConfigMixer originalMixer = (SourceConfigMixer)config; + SourceConfigMixer copyMixer = new SourceConfigMixer(); + copyMixer.setChannel( originalMixer.getChannel() ); + copyMixer.setMixer( originalMixer.getMixer() ); + return copyMixer; + case RECORDING: + SourceConfigRecording originalRec = (SourceConfigRecording)config; + SourceConfigRecording copyRec = new SourceConfigRecording(); + copyRec.setFrequency( originalRec.getFrequency() ); + copyRec.setRecordingAlias( originalRec.getRecordingAlias() ); + return copyRec; + case TUNER: + SourceConfigTuner originalTuner = (SourceConfigTuner)config; + SourceConfigTuner copyTuner = new SourceConfigTuner(); + copyTuner.setFrequency( originalTuner.getFrequency() ); + return copyTuner; + case NONE: + default: + return new SourceConfigNone(); + } + } + + return null; + } } diff --git a/src/source/config/SourceConfigMixer.java b/src/source/config/SourceConfigMixer.java index 1074b3cf..01a9e1af 100644 --- a/src/source/config/SourceConfigMixer.java +++ b/src/source/config/SourceConfigMixer.java @@ -42,6 +42,7 @@ public class SourceConfigMixer extends SourceConfiguration { StringBuilder sb = new StringBuilder(); + sb.append( "Mixer: " ); sb.append( mMixer ); sb.append( "-" ); sb.append( mChannel.toString() ); diff --git a/src/source/config/SourceConfigTuner.java b/src/source/config/SourceConfigTuner.java index 61637d12..d6d9a8d4 100644 --- a/src/source/config/SourceConfigTuner.java +++ b/src/source/config/SourceConfigTuner.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,7 +30,7 @@ public class SourceConfigTuner extends SourceConfiguration private static DecimalFormat sFORMAT = new DecimalFormat( "0.00000" ); private long mFrequency = 0; - private int mBandwidth = 15000; + private int mBandwidth = 12500; public SourceConfigTuner() { diff --git a/src/source/mixer/MixerEditor.java b/src/source/mixer/MixerEditor.java deleted file mode 100644 index 3782213b..00000000 --- a/src/source/mixer/MixerEditor.java +++ /dev/null @@ -1,158 +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 - ******************************************************************************/ -package source.mixer; - -import java.util.EnumSet; - -import javax.sound.sampled.Mixer; -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JLabel; - -import source.SourceEditor; -import source.config.SourceConfigMixer; -import source.config.SourceConfiguration; -import source.mixer.MixerManager.InputMixerConfiguration; -import controller.ResourceManager; - -public class MixerEditor extends SourceEditor -{ - private static final long serialVersionUID = 1L; - private JComboBox mComboMixers; - private JComboBoxmComboChannels; - protected Mixer.Info mSelectedMixer = null; - - - public MixerEditor( ResourceManager resourceManager, - SourceConfiguration config ) - { - super( resourceManager, config ); - - initGUI(); - } - - private void initGUI() - { - JLabel mixerLabel = new JLabel( "Mixer:" ); - add( mixerLabel, "align right" ); - - mComboMixers = new JComboBox(); - - add( mComboMixers, "wrap" ); - - resetMixers(); - - JLabel channelLabel = new JLabel( "Channel:" ); - add( channelLabel, "align right" ); - - mComboChannels = new JComboBox(); - - add( mComboChannels, "wrap" ); - - resetChannels(); - } - - public void reset() - { - resetMixers(); - resetChannels(); - } - - public void save() - { - SourceConfigMixer config = (SourceConfigMixer)mConfig; - - InputMixerConfiguration selectedMixer = - mComboMixers.getItemAt( mComboMixers.getSelectedIndex() ); - - if( selectedMixer != null ) - { - config.setMixer( selectedMixer.getMixerName() ); - } - - MixerChannel channel = mComboChannels.getItemAt( - mComboChannels.getSelectedIndex() ); - - if( channel != null ) - { - if( config.getChannel() != channel ) - { - config.setChannel( channel ); - } - } - } - - private void resetMixers() - { - - javax.swing.SwingUtilities.invokeLater(new Runnable() - { - @Override - public void run() - { - InputMixerConfiguration[] mixers = MixerManager.getInstance().getInputMixers(); - - mComboMixers.setModel( - new DefaultComboBoxModel( mixers ) ); - - SourceConfigMixer config = (SourceConfigMixer)mConfig; - - String mixer = config.getMixer(); - - if( mixer != null ) - { - mComboMixers.setSelectedItem( mixer ); - } - else - { - //Mixer either hasn't been set yet, or it was set to a - //mixer that is no longer available. Set the mixer to the - //first item, and then store the value - if( mixers.length > 0 ) - { - mComboMixers.setSelectedIndex( 0 ); - - InputMixerConfiguration selectedMixer = - (InputMixerConfiguration)mComboMixers.getSelectedItem(); - - config.setMixer( selectedMixer.getMixerName() ); - } - } - } - }); - } - - private void resetChannels() - { - javax.swing.SwingUtilities.invokeLater(new Runnable() - { - @Override - public void run() - { - EnumSet channels = MixerChannel.getTargetChannels(); - - mComboChannels.setModel( new DefaultComboBoxModel( - channels.toArray( new MixerChannel[ channels.size() ] ) ) ); - - MixerChannel channel = ((SourceConfigMixer)mConfig).getChannel(); - - mComboChannels.setSelectedItem( channel ); - } - }); - } -} diff --git a/src/source/mixer/MixerManager.java b/src/source/mixer/MixerManager.java index 298e760d..2026e6c6 100644 --- a/src/source/mixer/MixerManager.java +++ b/src/source/mixer/MixerManager.java @@ -19,6 +19,7 @@ package source.mixer; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -49,27 +50,15 @@ public class MixerManager private final static Logger mLog = LoggerFactory.getLogger( MixerManager.class ); - private static MixerManager sInstance = null; - private List mInputMixers = new ArrayList<>(); private List mOutputMixers = new ArrayList<>(); private HashMap mMixerTuners = new HashMap<>(); - private MixerManager() + public MixerManager() { loadMixers(); } - public static MixerManager getInstance() - { - if( sInstance == null ) - { - sInstance = new MixerManager(); - } - - return sInstance; - } - public RealMixerSource getSource( SourceConfiguration config ) { RealMixerSource retVal = null; @@ -108,7 +97,6 @@ public class MixerManager { return new RealMixerSource( dataLine, AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO, - mixerName, new ShortAdapter() ); } } @@ -137,7 +125,7 @@ public class MixerManager { return new RealMixerSource( dataLine, AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO, - mixerName, new ChannelShortAdapter( + new ChannelShortAdapter( mixerConfig.getChannel() ) ); } } @@ -181,7 +169,14 @@ public class MixerManager public Collection getMixerTunerDataLines() { - return mMixerTuners.values(); + if( mMixerTuners.isEmpty() ) + { + return Collections.emptyList(); + } + else + { + return mMixerTuners.values(); + } } private void loadMixers() diff --git a/src/source/mixer/MixerSourceEditor.java b/src/source/mixer/MixerSourceEditor.java new file mode 100644 index 00000000..11ad5516 --- /dev/null +++ b/src/source/mixer/MixerSourceEditor.java @@ -0,0 +1,177 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package source.mixer; + +import gui.editor.Editor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.EnumSet; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; +import javax.swing.JLabel; + +import net.miginfocom.swing.MigLayout; +import source.SourceManager; +import source.config.SourceConfigMixer; +import source.config.SourceConfiguration; +import source.mixer.MixerManager.InputMixerConfiguration; +import controller.channel.Channel; + +public class MixerSourceEditor extends Editor +{ + private static final long serialVersionUID = 1L; + private JComboBox mComboMixers; + private JComboBoxmComboChannels; + + private SourceManager mSourceManager; + + public MixerSourceEditor( SourceManager sourceManager ) + { + mSourceManager = sourceManager; + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0,wrap 4", "[right][grow,fill][right][grow,fill]", "" ) ); + + mComboMixers = new JComboBox(); + InputMixerConfiguration[] inputMixers = mSourceManager.getMixerManager().getInputMixers(); + mComboMixers.setModel( new DefaultComboBoxModel( inputMixers ) ); + mComboMixers.setEnabled( false ); + mComboMixers.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + InputMixerConfiguration selected = (InputMixerConfiguration)mComboMixers.getSelectedItem(); + + EnumSet channels = selected.getChannels(); + + mComboChannels.setModel( new DefaultComboBoxModel( + channels.toArray( new MixerChannel[ channels.size() ] ) ) ); + + repaint(); + + setModified( true ); + } + } ); + + add( new JLabel( "Mixer:" ) ); + add( mComboMixers ); + + mComboChannels = new JComboBox(); + + InputMixerConfiguration selected = (InputMixerConfiguration)mComboMixers.getSelectedItem(); + + if( selected != null ) + { + EnumSet channels = selected.getChannels(); + + mComboChannels.setModel( new DefaultComboBoxModel( + channels.toArray( new MixerChannel[ channels.size() ] ) ) ); + } + + mComboChannels.setEnabled( false ); + mComboChannels.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + + add( new JLabel( "Channel:" ) ); + add( mComboChannels ); + } + + @Override + public void setItem( Channel item ) + { + super.setItem( item ); + + if( hasItem() ) + { + mComboMixers.setEnabled( true ); + mComboChannels.setEnabled( true ); + + SourceConfiguration config = item.getSourceConfiguration(); + + if( config instanceof SourceConfigMixer ) + { + SourceConfigMixer mixer = (SourceConfigMixer)config; + + for( InputMixerConfiguration inputMixer: mSourceManager.getMixerManager().getInputMixers() ) + { + if( inputMixer.getMixerName().equalsIgnoreCase( mixer.getMixer() ) ) + { + mComboMixers.setSelectedItem( inputMixer ); + + for( MixerChannel channel: inputMixer.getChannels() ) + { + if( channel.getLabel() != null && mixer.getChannel() != null ) + { + if( channel.getLabel().equalsIgnoreCase( mixer.getChannel().getLabel() ) ) + { + mComboChannels.setSelectedItem( channel ); + } + } + } + + continue; + } + } + + setModified( false ); + } + else + { + setModified( true ); + } + } + else + { + mComboMixers.setEnabled( false ); + mComboChannels.setEnabled( false ); + + setModified( false ); + } + } + + public void save() + { + if( hasItem() && isModified() ) + { + SourceConfigMixer config = new SourceConfigMixer(); + + InputMixerConfiguration selected = + (InputMixerConfiguration)mComboMixers.getSelectedItem(); + config.setMixer( selected.getMixerName() ); + + MixerChannel channel = (MixerChannel)mComboChannels.getSelectedItem(); + config.setChannel( channel ); + + getItem().setSourceConfiguration( config ); + } + + setModified( false ); + } +} diff --git a/src/source/mixer/RealMixerSource.java b/src/source/mixer/RealMixerSource.java index 58b8d953..7ea8dd7b 100644 --- a/src/source/mixer/RealMixerSource.java +++ b/src/source/mixer/RealMixerSource.java @@ -19,6 +19,7 @@ package source.mixer; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.sampled.AudioFormat; @@ -29,12 +30,12 @@ import javax.sound.sampled.TargetDataLine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import dsp.gain.AutomaticGainControl; import sample.Listener; import sample.adapter.ISampleAdapter; import sample.real.RealBuffer; import source.RealSource; import source.SourceException; +import dsp.gain.AutomaticGainControl; public class RealMixerSource extends RealSource { @@ -72,18 +73,30 @@ public class RealMixerSource extends RealSource * the left/right stereo channels are inverted. */ public RealMixerSource( TargetDataLine targetDataLine, - AudioFormat format, - String name, - ISampleAdapter sampleAdapter ) + AudioFormat format, + ISampleAdapter sampleAdapter ) { - super( name ); - mTargetDataLine = targetDataLine; mAudioFormat = format; mSampleAdapter = sampleAdapter; } - public void setListener( Listener listener ) + @Override + public void reset() + { + } + + @Override + public void start( ScheduledExecutorService executor ) + { + } + + @Override + public void stop() + { + } + + public void setListener( Listener listener ) { mSampleListeners.add( listener ); @@ -92,7 +105,7 @@ public class RealMixerSource extends RealSource { Thread thread = new Thread( mBufferReader ); thread.setDaemon( true ); - thread.setName( getName() + " Sample Reader" ); + thread.setName( "Sample Reader" ); thread.start(); } } diff --git a/src/source/recording/Recording.java b/src/source/recording/Recording.java index 492efec7..c01d8566 100644 --- a/src/source/recording/Recording.java +++ b/src/source/recording/Recording.java @@ -4,32 +4,34 @@ import java.io.File; import java.util.ArrayList; import java.util.concurrent.RejectedExecutionException; +import settings.SettingsManager; import source.SourceException; +import source.tuner.ITunerChannelProvider; import source.tuner.TunerChannel; -import source.tuner.TunerChannelProvider; import source.tuner.TunerChannelSource; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; import source.tuner.frequency.FrequencyChangeEvent.Event; -import controller.ResourceManager; -import controller.ThreadPoolManager; +import source.tuner.frequency.IFrequencyChangeProcessor; public class Recording implements Comparable, - FrequencyChangeListener, - TunerChannelProvider + IFrequencyChangeProcessor, + ITunerChannelProvider { - private ResourceManager mResourceManager; private RecordingConfiguration mConfiguration; private ArrayList mTunerChannels = new ArrayList(); + @SuppressWarnings( "unused" ) private long mCenterFrequency; - public Recording( ResourceManager resourceManager, + @SuppressWarnings( "unused" ) + private SettingsManager mSettingsManager; + + public Recording( SettingsManager settingsManager, RecordingConfiguration configuration ) { - mResourceManager = resourceManager; + mSettingsManager = settingsManager; mConfiguration = configuration; mCenterFrequency = mConfiguration.getCenterFrequency(); } @@ -37,7 +39,7 @@ public class Recording implements Comparable, public void setAlias( String alias ) { mConfiguration.setAlias( alias ); - mResourceManager.getSettingsManager().save(); +// mSettingsManager.save(); } public void setRecordingFile( File file ) throws SourceException @@ -50,7 +52,7 @@ public class Recording implements Comparable, } mConfiguration.setFilePath( file.getAbsolutePath() ); - mResourceManager.getSettingsManager().save(); +// mSettingsManager.save(); } /** @@ -89,13 +91,13 @@ public class Recording implements Comparable, @Override public void frequencyChanged( FrequencyChangeEvent event ) { - if( event.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) + if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) { long frequency = event.getValue().longValue(); mConfiguration.setCenterFrequency( frequency ); - mResourceManager.getSettingsManager().save(); +// mSettingsManager.save(); mCenterFrequency = frequency; diff --git a/src/source/recording/RecordingEditor.java b/src/source/recording/RecordingEditor.java deleted file mode 100644 index aabec88f..00000000 --- a/src/source/recording/RecordingEditor.java +++ /dev/null @@ -1,120 +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 - ******************************************************************************/ -package source.recording; - -import gui.control.JFrequencyControl; - -import java.util.List; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.SwingUtilities; - -import source.SourceEditor; -import source.config.SourceConfigRecording; -import source.config.SourceConfigTuner; -import source.config.SourceConfiguration; -import controller.ResourceManager; - -public class RecordingEditor extends SourceEditor -{ - private static final long serialVersionUID = 1L; - private JComboBox mComboRecordings; - private JFrequencyControl mFrequencyControl; - - public RecordingEditor( ResourceManager resourceManager, - SourceConfiguration config ) - { - super( resourceManager, config ); - - initGUI(); - } - - public void reset() - { - mFrequencyControl.setFrequency( - ((SourceConfigTuner)mConfig).getFrequency(), false ); - } - - public void save() - { - SourceConfigRecording config = (SourceConfigRecording)mConfig; - - Recording selected = (Recording)mComboRecordings.getSelectedItem(); - - if( selected != null ) - { - config.setRecordingAlias( selected - .getRecordingConfiguration().getAlias() ); - } - - config.setFrequency( mFrequencyControl.getFrequency() ); - } - - private void initGUI() - { - add( new JLabel( "Recording" ) ); - - mComboRecordings = new JComboBox(); - - add( mComboRecordings, "wrap" ); - - mFrequencyControl = new JFrequencyControl(); - - mFrequencyControl.setFrequency( - ((SourceConfigRecording)mConfig).getFrequency(), false ); - - add( mFrequencyControl, "span" ); - - resetRecordings(); - } - - private void resetRecordings() - { - SwingUtilities.invokeLater( new Runnable() - { - @Override - public void run() - { - List recordings = getResourceManager() - .getRecordingSourceManager().getRecordings(); - - mComboRecordings.setModel( - new DefaultComboBoxModel( - recordings.toArray( new Recording[ recordings.size()] ) ) ); - - SourceConfigRecording config = (SourceConfigRecording)mConfig; - - String recordingAlias = config.getRecordingAlias(); - - if( recordingAlias != null ) - { - Recording selected = mResourceManager - .getRecordingSourceManager() - .getRecordingFromAlias( recordingAlias ); - - if( selected != null ) - { - mComboRecordings.setSelectedItem( selected ); - } - } - } - }); - } -} diff --git a/src/source/recording/RecordingEditorPanel.java b/src/source/recording/RecordingEditorPanel.java deleted file mode 100644 index 408538d5..00000000 --- a/src/source/recording/RecordingEditorPanel.java +++ /dev/null @@ -1,130 +0,0 @@ -package source.recording; - -import gui.control.JFrequencyControl; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.io.File; - -import javax.swing.JButton; -import javax.swing.JFileChooser; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; - -import net.miginfocom.swing.MigLayout; - -public class RecordingEditorPanel extends JPanel -{ - private static final long serialVersionUID = 1L; - - private RecordingNode mRecordingNode; - - private JTextField mRecordingAlias; - private JLabel mFilePathLabel; - - public RecordingEditorPanel( RecordingNode recordingNode ) - { - mRecordingNode = recordingNode; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][][grow]" ) ); - - add( new JLabel( "I/Q Recording" ), "span,align left" ); - - add( new JLabel( "Alias:" ) ); - - mRecordingAlias = new JTextField( mRecordingNode.getRecording() - .getRecordingConfiguration().getAlias() ); - - mRecordingAlias.addFocusListener( new FocusListener() - { - @Override - public void focusLost( FocusEvent arg0 ) - { - String alias = mRecordingAlias.getText(); - - if( alias != null ) - { - mRecordingNode.getRecording().setAlias( alias ); - } - } - - @Override - public void focusGained( FocusEvent arg0 ) {} - } ); - - add( mRecordingAlias, "wrap" ); - - JButton mFileSelectionButton = new JButton( "File ..." ); - mFileSelectionButton.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - if( mRecordingNode.getRecording().hasChannels() ) - { - JOptionPane.showMessageDialog( RecordingEditorPanel.this, - "This recording is currently in replay. Please " - + "disable any channels that are using this " - + "recording before changing the recording file." ); - } - else - { - final JFileChooser fc = new JFileChooser(); - - int returnVal = fc.showOpenDialog( RecordingEditorPanel.this ); - - if ( returnVal == JFileChooser.APPROVE_OPTION ) - { - File file = fc.getSelectedFile(); - - if( file != null && file.exists() ) - { - mRecordingNode.getRecording() - .getRecordingConfiguration() - .setFilePath( file.getAbsolutePath() ); - - if( mFilePathLabel != null ) - { - mFilePathLabel.setText( file.getName() ); - } - } - } - } - } - } ); - add( mFileSelectionButton ); - - String filePath = mRecordingNode.getRecording() - .getRecordingConfiguration().getFilePath(); - - if( filePath != null ) - { - mFilePathLabel = new JLabel( filePath ); - } - else - { - mFilePathLabel = new JLabel( "Please select a recording" ); - } - - add( mFilePathLabel, "wrap" ); - - add( new JLabel( "Center Frequency:" ), "span,align left" ); - - JFrequencyControl frequencyControl = new JFrequencyControl( - mRecordingNode.getRecording().getRecordingConfiguration() - .getCenterFrequency() ); - - frequencyControl.addListener( mRecordingNode.getRecording() ); - - add( frequencyControl, "span,align left" ); - } -} diff --git a/src/source/recording/RecordingGroupNode.java b/src/source/recording/RecordingGroupNode.java deleted file mode 100644 index 8eb2029a..00000000 --- a/src/source/recording/RecordingGroupNode.java +++ /dev/null @@ -1,91 +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 - ******************************************************************************/ -package source.recording; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.List; - -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; - -import controller.BaseNode; - -public class RecordingGroupNode extends BaseNode -{ - private static final long serialVersionUID = 1L; - - public RecordingGroupNode() - { - super( null ); - } - - public String toString() - { - return "Recordings [" + this.getChildCount() + "]"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu menu = new JPopupMenu(); - - JMenuItem addRecordingItem = new JMenuItem( "Add Recording" ); - - addRecordingItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - RecordingConfiguration config = new RecordingConfiguration(); - - getModel().getResourceManager().getSettingsManager() - .addRecordingConfiguration( config ); - - Recording recording = getModel().getResourceManager() - .getRecordingSourceManager().addRecording( config ); - - RecordingNode node = new RecordingNode( recording ); - - getModel().addNode( node, RecordingGroupNode.this, 0 ); - - sort(); - - node.show(); - } - } ); - - menu.add( addRecordingItem ); - - return menu; - } - - public void loadRecordings() - { - List recordings = getModel().getResourceManager() - .getRecordingSourceManager().getRecordings(); - - for( Recording recording: recordings ) - { - RecordingNode node = new RecordingNode( recording ); - - getModel().addNode( node, this, 0 ); - } - - sort(); - } -} diff --git a/src/source/recording/RecordingNode.java b/src/source/recording/RecordingNode.java deleted file mode 100644 index 54ec12fd..00000000 --- a/src/source/recording/RecordingNode.java +++ /dev/null @@ -1,79 +0,0 @@ -package source.recording; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import controller.BaseNode; - -public class RecordingNode extends BaseNode -{ - private static final long serialVersionUID = 1L; - - private JPanel mEditor; - - public RecordingNode( Recording recording ) - { - super( recording ); - } - - public Recording getRecording() - { - return (Recording)getUserObject(); - } - - public String toString() - { - return getRecording().toString(); - } - - @Override - public JPanel getEditor() - { - if( mEditor == null ) - { - mEditor = new RecordingEditorPanel( this ); - } - - return mEditor; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu menu = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - if( getRecording().hasChannels() ) - { - JOptionPane.showMessageDialog( getModel().getTree(), - "Can't delete recording - currently in use. Please" - + " disable any channel(s) that are using this " - + "recording before deleting it", - "Can't delete recording", - JOptionPane.ERROR_MESSAGE ); - } - else - { - getModel().getResourceManager().getRecordingSourceManager() - .removeRecording( getRecording() ); - - getModel().removeNodeFromParent( RecordingNode.this ); - } - } - } ); - - menu.add( deleteItem ); - - return menu; - } -} diff --git a/src/source/recording/RecordingSourceManager.java b/src/source/recording/RecordingSourceManager.java index 260c33b7..4a47c660 100644 --- a/src/source/recording/RecordingSourceManager.java +++ b/src/source/recording/RecordingSourceManager.java @@ -31,21 +31,19 @@ import source.config.SourceConfigRecording; import source.config.SourceConfiguration; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; -import controller.ResourceManager; public class RecordingSourceManager { private final static Logger mLog = LoggerFactory.getLogger( RecordingSourceManager.class ); - private ResourceManager mResourceManager; - private ArrayList mRecordings = new ArrayList(); - public RecordingSourceManager( ResourceManager resourceManager ) + private SettingsManager mSettingsManager; + + public RecordingSourceManager( SettingsManager settingsManager ) { - mResourceManager = resourceManager; - + mSettingsManager = settingsManager; loadRecordings(); } @@ -114,7 +112,7 @@ public class RecordingSourceManager public Recording addRecording( RecordingConfiguration config ) { - Recording recording = new Recording( mResourceManager, config ); + Recording recording = new Recording( mSettingsManager, config ); mRecordings.add( recording ); @@ -124,7 +122,7 @@ public class RecordingSourceManager public void removeRecording( Recording recording ) { /* Remove the config from the settings manager */ - mResourceManager.getSettingsManager().removeRecordingConfiguration( + mSettingsManager.removeRecordingConfiguration( recording.getRecordingConfiguration() ); mRecordings.remove( recording ); @@ -132,24 +130,17 @@ public class RecordingSourceManager private void loadRecordings() { - if( mResourceManager != null ) - { - SettingsManager settingsManager = mResourceManager.getSettingsManager(); - - if( settingsManager != null ) - { - ArrayList recordingConfigurations = - settingsManager.getRecordingConfigurations(); + List recordingConfigurations = + mSettingsManager.getRecordingConfigurations(); - mLog.info( "discovered [" + recordingConfigurations.size() + "] recording configurations" ); - - for( RecordingConfiguration config: recordingConfigurations ) - { - Recording recording = new Recording( mResourceManager, config ); - - mRecordings.add( recording ); - } - } - } - } + mLog.info( "RecordingSourceManager - discovered [" + + recordingConfigurations.size() + "] recording configurations" ); + + for( RecordingConfiguration config: recordingConfigurations ) + { + Recording recording = new Recording( mSettingsManager, config ); + + mRecordings.add( recording ); + } + } } diff --git a/src/source/tuner/TunerGroupNode.java b/src/source/tuner/ITunerChannelProvider.java similarity index 57% rename from src/source/tuner/TunerGroupNode.java rename to src/source/tuner/ITunerChannelProvider.java index 4a3c8247..b3a69a63 100644 --- a/src/source/tuner/TunerGroupNode.java +++ b/src/source/tuner/ITunerChannelProvider.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,19 +17,28 @@ ******************************************************************************/ package source.tuner; -import controller.BaseNode; +import java.util.concurrent.RejectedExecutionException; -public class TunerGroupNode extends BaseNode +import source.SourceException; + +public interface ITunerChannelProvider { - private static final long serialVersionUID = 1L; + /** + * Returns a tuner frequency channel source, tuned to the correct frequency + * + * @param frequency - desired frequency + * + * @return - source for 48k sample rate + */ + public abstract TunerChannelSource getChannel( TunerChannel channel ) + throws RejectedExecutionException, SourceException; - public TunerGroupNode() - { - super( null ); - } - - public String toString() - { - return "Tuners [" + this.getChildCount() + "]"; - } + /** + * Releases the tuned channel resources + * + * @param channel - previously obtained tuner channel + */ + public abstract void releaseChannel( TunerChannelSource source ); + + } diff --git a/src/source/tuner/MixerTuner.java b/src/source/tuner/MixerTuner.java index 42786ca8..37f79bed 100644 --- a/src/source/tuner/MixerTuner.java +++ b/src/source/tuner/MixerTuner.java @@ -46,11 +46,12 @@ public abstract class MixerTuner extends Tuner implements Listener)this ); } - public MixerTuner( String name, + public MixerTuner( String name, + TunerController tunerController, MixerTunerDataLine mixerTuner, ISampleAdapter sampleAdapter ) { this( name, + tunerController, mixerTuner.getMixerTunerType(), mixerTuner.getTargetDataLine(), sampleAdapter ); diff --git a/src/source/tuner/Tuner.java b/src/source/tuner/Tuner.java index 2697ac9d..cd6a3e4a 100644 --- a/src/source/tuner/Tuner.java +++ b/src/source/tuner/Tuner.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,47 +17,70 @@ ******************************************************************************/ package source.tuner; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.RejectedExecutionException; -import javax.swing.JPanel; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; import sample.complex.ComplexBuffer; import source.SourceException; -import source.tuner.frequency.FrequencyChangeBroadcaster; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeEvent.Event; -import source.tuner.frequency.FrequencyChangeListener; -import controller.ResourceManager; +import source.tuner.frequency.IFrequencyChangeProcessor; /** * Tuner - provides tuner channel sources, representing a channel frequency */ -public abstract class Tuner implements FrequencyChangeBroadcaster, - FrequencyChangeListener, - TunerChannelProvider +public abstract class Tuner implements ITunerChannelProvider { private final static Logger mLog = LoggerFactory.getLogger( Tuner.class ); private String mName; - + private TunerController mTunerController; /** * Sample Listeners - these will typically be the DFT processor for spectral * display, or it will be one or more tuner channel sources */ - protected CopyOnWriteArrayList> - mSampleListeners = new CopyOnWriteArrayList>(); + protected List> mSampleListeners = + new CopyOnWriteArrayList<>(); - protected CopyOnWriteArrayList - mFrequencyChangeListeners = new CopyOnWriteArrayList(); - - public Tuner( String name ) + protected List> mTunerChangeListeners = + new ArrayList<>(); + + public Tuner( String name, TunerController tunerController ) { mName = name; + mTunerController = tunerController; + + //Rebroadcast frequency and sample rate change events as tuner events + mTunerController.addListener( new IFrequencyChangeProcessor() + { + @Override + public void frequencyChanged( FrequencyChangeEvent event ) + { + switch( event.getEvent() ) + { + case NOTIFICATION_FREQUENCY_CHANGE: + broadcast( new TunerEvent( Tuner.this, + source.tuner.TunerEvent.Event.FREQUENCY )); + break; + case NOTIFICATION_SAMPLE_RATE_CHANGE: + broadcast( new TunerEvent( Tuner.this, + source.tuner.TunerEvent.Event.SAMPLE_RATE )); + break; + default: + break; + } + } + } ); + } + + public TunerController getTunerController() + { + return mTunerController; } public String toString() @@ -68,7 +91,6 @@ public abstract class Tuner implements FrequencyChangeBroadcaster, public void dispose() { mSampleListeners.clear(); - mFrequencyChangeListeners.clear(); } public void setName( String name ) @@ -76,18 +98,6 @@ public abstract class Tuner implements FrequencyChangeBroadcaster, mName = name; } - /** - * Return an editor panel for the tuner - */ - public abstract JPanel getEditor( ResourceManager resourceManager ); - - - /** - * Applies the settings from a saved tuner configuration setting object - */ - public abstract void apply( TunerConfiguration config ) - throws SourceException; - /** * Unique identifier for this tuner, used to lookup a tuner configuration * from the settings manager. @@ -118,23 +128,11 @@ public abstract class Tuner implements FrequencyChangeBroadcaster, return mName; } - /** - * Sample rate of complex data coming from the Tuner - * @return - */ - public abstract int getSampleRate(); - /** * Sample size in bits */ public abstract double getSampleSize(); - /** - * Tuner Local Oscillator Frequency - * @return - frequency in Hertz - */ - public abstract long getFrequency() throws SourceException; - /** * Returns a tuner frequency channel source, tuned to the correct frequency * @@ -186,67 +184,30 @@ public abstract class Tuner implements FrequencyChangeBroadcaster, } } - /** - * Adds the frequency change listener to receive frequency and/or bandwidth - * change events - */ - public void addListener( FrequencyChangeListener listener ) - { - mFrequencyChangeListeners.add( listener ); - } + /** + * Registers the listener + */ + public void addTunerChangeListener( Listener listener ) + { + mTunerChangeListeners.add( listener ); + } /** - * Removes the frequency change listener + * Removes the registered listener */ - public void removeListener( FrequencyChangeListener listener ) + public void removeTunerChangeListener( Listener listener ) { - mFrequencyChangeListeners.remove( listener ); + mTunerChangeListeners.remove( listener ); } /** - * Broadcasts a frequency change event to all registered listeners + * Broadcasts the tuner change event */ - public void broadcastFrequencyChange( long frequency ) + public void broadcast( TunerEvent event ) { - broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.FREQUENCY_CHANGE_NOTIFICATION, frequency ) ); - } - - /** - * Broadcasts a sample rate change event to all registered listeners - */ - public void broadcastSampleRateChange( int sampleRate ) - { - broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.SAMPLE_RATE_CHANGE_NOTIFICATION, sampleRate ) ); - } - - /** - * Broadcasts actual sample rate change event to all registered listeners - */ - public void broadcastActualSampleRateChange( int actualSampleRate ) - { - broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.SAMPLE_RATE_CHANGE_NOTIFICATION, actualSampleRate ) ); - } - - /** - * Broadcasts a frequency change event to all registered listeners - */ - public void broadcastFrequencyChangeEvent( FrequencyChangeEvent event ) - { - for( FrequencyChangeListener listener: mFrequencyChangeListeners ) + for( Listener listener: mTunerChangeListeners ) { - listener.frequencyChanged( event ); + listener.receive( event ); } } - - /** - * Frequency change listener method. We receive change events from the - * controller and rebroadcast them to all registered listeners. - */ - public void frequencyChanged( FrequencyChangeEvent event ) - { - broadcastFrequencyChangeEvent( event ); - } } diff --git a/src/source/tuner/TunerChannel.java b/src/source/tuner/TunerChannel.java index 625d7a98..568e5424 100644 --- a/src/source/tuner/TunerChannel.java +++ b/src/source/tuner/TunerChannel.java @@ -97,13 +97,13 @@ public class TunerChannel implements Comparable } @Override - public int compareTo( TunerChannel otherTunerChannel ) + public int compareTo( TunerChannel otherLock ) { - if( this.equals( otherTunerChannel ) ) + if( this.equals( otherLock ) ) { return 0; } - else if( mFrequency < otherTunerChannel.getFrequency() ) + else if( mFrequency < otherLock.getFrequency() ) { return -1; } diff --git a/src/source/tuner/TunerChannelProvider.java b/src/source/tuner/TunerChannelProvider.java index f8b7a49b..8f53f144 100644 --- a/src/source/tuner/TunerChannelProvider.java +++ b/src/source/tuner/TunerChannelProvider.java @@ -3,7 +3,6 @@ package source.tuner; import java.util.concurrent.RejectedExecutionException; import source.SourceException; -import controller.ThreadPoolManager; public interface TunerChannelProvider { diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index c847544c..61cc1b57 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,15 +37,14 @@ import source.ComplexSource; import source.SourceException; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeEvent.Event; -import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeProcessor; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; import dsp.filter.cic.ComplexPrimeCICDecimate; import dsp.mixer.Oscillator; public class TunerChannelSource extends ComplexSource - implements FrequencyChangeListener, - Listener + implements IFrequencyChangeProcessor, Listener { private final static Logger mLog = LoggerFactory.getLogger( TunerChannelSource.class ); @@ -60,7 +59,7 @@ public class TunerChannelSource extends ComplexSource private Oscillator mMixer; private ComplexPrimeCICDecimate mDecimationFilter; private Listener mListener; - private FrequencyChangeListener mFrequencyChangeListener; + private IFrequencyChangeProcessor mFrequencyChangeProcessor; private ScheduledFuture mTaskHandle; private long mTunerFrequency = 0; @@ -69,82 +68,122 @@ public class TunerChannelSource extends ComplexSource private DecimationProcessor mDecimationProcessor = new DecimationProcessor(); + private AtomicBoolean mRunning = new AtomicBoolean(); + private boolean mExpended = false; + + /** + * Provides a Digital Drop Channel (DDC) to decimate the IQ output from a + * tuner down to a 48 kHz IQ channel rate. + * + * Note: this class can only be used once (started and stopped) and a new + * tuner channel source must be requested from the tuner once this object + * has been stopped. This is because channels are managed dynamically and + * center tuned frequency may have changed since this source was obtained + * and thus the tuner might no longer be able to source this channel once it + * has been stopped. + * + * @param threadPoolManager to use for decimation processing task + * @param tuner to obtain wideband IQ samples from + * @param tunerChannel specifying the center frequency for the DDC + * @throws RejectedExecutionException if the thread pool manager cannot + * accept the decimation processing task + * @throws SourceException if the tuner has an issue providing IQ samples + */ public TunerChannelSource( Tuner tuner, TunerChannel tunerChannel ) - throws RejectedExecutionException, SourceException + throws RejectedExecutionException, SourceException { - super( "Tuner Channel Source" ); - mTuner = tuner; - mTuner.addListener( (FrequencyChangeListener)this ); + mTuner.getTunerController().addListener( (IFrequencyChangeProcessor)this ); mTunerChannel = tunerChannel; - mTunerFrequency = mTuner.getFrequency(); + mTunerFrequency = mTuner.getTunerController().getFrequency(); /* Setup the frequency translator to the current source frequency */ long frequencyOffset = mTunerFrequency - mTunerChannel.getFrequency(); - mMixer = new Oscillator( frequencyOffset, mTuner.getSampleRate() ); + mMixer = new Oscillator( frequencyOffset, + mTuner.getTunerController().getSampleRate() ); /* Fire a sample rate change event to setup the decimation chain */ frequencyChanged( new FrequencyChangeEvent( - Event.SAMPLE_RATE_CHANGE_NOTIFICATION, mTuner.getSampleRate() ) ); + Event.NOTIFICATION_SAMPLE_RATE_CHANGE, + mTuner.getTunerController().getSampleRate() ) ); } - public void start( ScheduledExecutorService scheduledExecutorService ) - { - /* Schedule the decimation task to run every 10 ms (100 iterations/second) */ - mTaskHandle = scheduledExecutorService.scheduleAtFixedRate( - mDecimationProcessor, 0, 10, TimeUnit.MILLISECONDS ); - - /* Finally, register to receive samples from the tuner */ - mTuner.addListener( (Listener)this ); - } - @Override + public void start( ScheduledExecutorService executor ) + { + if( mExpended ) + { + throw new IllegalStateException( "Attempt to re-start an expended " + + "tuner channel source. TunerChannelSource objects can only " + + "be used once. " ); + } + + if( mRunning.compareAndSet( false, true )) + { + /* Schedule the decimation task to run every 10 ms (100 iterations/second) */ + mTaskHandle = executor.scheduleAtFixedRate( mDecimationProcessor, + 0, 10, TimeUnit.MILLISECONDS ); + + /* Finally, register to receive samples from the tuner */ + mTuner.addListener( (Listener)this ); + } + else + { + mLog.warn( "Attempt to start() and already running tuner channel source was ignored" ); + } + } + + @Override + public void reset() + { + } + + @Override + public void stop() + { + if( mRunning.compareAndSet( true, false )) + { + mTuner.releaseChannel( this ); + mDecimationProcessor.shutdown(); + + if( mTaskHandle != null ) + { + mTaskHandle.cancel( true ); + mTaskHandle = null; + } + + mBuffer.clear(); + + mExpended = true; + } + else + { + mLog.warn( "Attempt to stop() and already stopped tuner channel source was ignored" ); + } + } + + + @Override public void dispose() { - /* Tell the tuner to release/unregister our resources */ - mTuner.removeListener( (FrequencyChangeListener)this ); - mTuner.releaseChannel( this ); - mTuner = null; - mTunerChannel = null; - - /* Flag the decimation processor to shutdown */ - if( mDecimationProcessor != null ) - { - mDecimationProcessor.dispose(); - } - + if( !mRunning.get() ) + { + /* Tell the tuner to release/unregister our resources */ + mTuner.getTunerController().removeListener( this ); + mTuner = null; + mTunerChannel = null; + mBuffer = null; + mFrequencyChangeProcessor = null; + mListener = null; + mMixer = null; + mDecimationFilter.dispose(); + mDecimationFilter = null; + } } - /** - * Cleanup method to allow the decimation processor to perform final cleanup - * once it gracefully shuts itself down - */ - private void cleanup() - { - if( mTaskHandle != null ) - { - mTaskHandle.cancel( true ); - mTaskHandle = null; - } - - synchronized( mBuffer ) - { - mBuffer.clear(); - mBuffer = null; - } - - mFrequencyChangeListener = null; - mListener = null; - - mMixer = null; - - mDecimationFilter.dispose(); - mDecimationFilter = null; - } - public Tuner getTuner() { return mTuner; @@ -158,24 +197,21 @@ public class TunerChannelSource extends ComplexSource @Override public void receive( ComplexBuffer buffer ) { - synchronized( mBuffer ) + if( mRunning.get() ) { - if( mBuffer != null ) - { - mBuffer.add( buffer ); - } + mBuffer.add( buffer ); } } - public void setFrequencyChangeListener( FrequencyChangeListener listener ) + public void setFrequencyChangeListener( IFrequencyChangeProcessor processor ) { - mFrequencyChangeListener = listener; + mFrequencyChangeProcessor = processor; } @Override public void setListener( Listener listener ) { - /* Get a pointer to the listener so that if we have to change the + /* Save a pointer to the listener so that if we have to change the * decimation filter, we can re-add the listener */ mListener = listener; @@ -196,31 +232,31 @@ public class TunerChannelSource extends ComplexSource public void frequencyChanged( FrequencyChangeEvent event ) { // Echo the event to the registered event listener - if( mFrequencyChangeListener != null ) + if( mFrequencyChangeProcessor != null ) { - mFrequencyChangeListener.frequencyChanged( event ); + mFrequencyChangeProcessor.frequencyChanged( event ); } switch( event.getEvent() ) { - case FREQUENCY_CHANGE_NOTIFICATION: + case NOTIFICATION_FREQUENCY_CHANGE: mTunerFrequency = event.getValue().longValue(); updateMixerFrequencyOffset(); break; - case CHANNEL_FREQUENCY_CORRECTION_CHANGE_REQUEST: + case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE: mChannelFrequencyCorrection = event.getValue().intValue(); updateMixerFrequencyOffset(); - if( mFrequencyChangeListener != null ) + if( mFrequencyChangeProcessor != null ) { - mFrequencyChangeListener.frequencyChanged( + mFrequencyChangeProcessor.frequencyChanged( new FrequencyChangeEvent( - Event.CHANNEL_FREQUENCY_CORRECTION_CHANGE_NOTIFICATION, + Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE, mChannelFrequencyCorrection ) ); } break; - case SAMPLE_RATE_CHANGE_NOTIFICATION: + case NOTIFICATION_SAMPLE_RATE_CHANGE: int sampleRate = event.getValue().intValue(); if( mTunerSampleRate != sampleRate ) @@ -276,12 +312,12 @@ public class TunerChannelSource extends ComplexSource */ public class DecimationProcessor implements Runnable { - private AtomicBoolean mDispose = new AtomicBoolean( false ); + private boolean mProcessing = true; private List mSampleBuffers = new ArrayList(); - public void dispose() + public void shutdown() { - mDispose.set( true ); + mProcessing = false; } @Override @@ -292,64 +328,69 @@ public class TunerChannelSource extends ComplexSource * run the program out of memory */ try { - if( mBuffer != null ) + if( mProcessing ) { - mBuffer.drainTo( mSampleBuffers, 8 ); - - for( Buffer buffer: mSampleBuffers ) { - /* Check to see if we've been shutdown */ - if( mDispose.get() ) +// long start = System.currentTimeMillis(); + + mBuffer.drainTo( mSampleBuffers, 8 ); + + for( Buffer buffer: mSampleBuffers ) { - cleanup(); - return; - } - else - { - float[] samples = buffer.getSamples(); - - /* We make a copy of the buffer so that we don't affect - * anyone else that is using the same buffer, like other - * channels or the spectral display */ - float[] translated = new float[ samples.length ]; - - /* Perform frequency translation */ - for( int x = 0; x < samples.length; x += 2 ) + /* Check to see if we've been shutdown */ + if( !mProcessing ) { - mMixer.rotate(); + mBuffer.clear(); + return; + } + else + { + float[] samples = buffer.getSamples(); + + /* We make a copy of the buffer so that we don't affect + * anyone else that is using the same buffer, like other + * channels or the spectral display */ + float[] translated = new float[ samples.length ]; - translated[ x ] = Complex.multiplyInphase( - samples[ x ], samples[ x + 1 ], mMixer.inphase(), mMixer.quadrature() ); - - translated[ x + 1 ] = Complex.multiplyQuadrature( + /* Perform frequency translation */ + for( int x = 0; x < samples.length; x += 2 ) + { + mMixer.rotate(); + + translated[ x ] = Complex.multiplyInphase( samples[ x ], samples[ x + 1 ], mMixer.inphase(), mMixer.quadrature() ); - } - - if( mDecimationFilter != null ) - { - mDecimationFilter.receive( new ComplexBuffer( translated ) ); + + translated[ x + 1 ] = Complex.multiplyQuadrature( + samples[ x ], samples[ x + 1 ], mMixer.inphase(), mMixer.quadrature() ); + } + + if( mProcessing && mDecimationFilter != null ) + { + mDecimationFilter.receive( new ComplexBuffer( translated ) ); + } } } + +// mLog.debug( "Processed [" + mSampleBuffers.size() + "] in " + ( System.currentTimeMillis() - start ) ); + + mSampleBuffers.clear(); } - - mSampleBuffers.clear(); } } catch( Exception e ) { - /* Only log the stack trace if we've haven't been shutdown and - * this is a true error */ - if( !mDispose.get() ) + /* Only log the stack trace if we're still processing */ + if( mProcessing ) { mLog.error( "Error encountered during decimation process", e ); } } /* Check to see if we've been shutdown */ - if( mDispose.get() ) + if( !mProcessing ) { + mBuffer.clear(); mSampleBuffers.clear(); - cleanup(); } } } diff --git a/src/source/tuner/TunerClass.java b/src/source/tuner/TunerClass.java index 4bcbd05a..124aefe5 100644 --- a/src/source/tuner/TunerClass.java +++ b/src/source/tuner/TunerClass.java @@ -21,7 +21,7 @@ import javax.usb.UsbDeviceDescriptor; public enum TunerClass { - AIRSPY( TunerType.AIRSPY_R820T, "1D50", "60A1", "Airspy.com", "Airspy" ), + AIRSPY( TunerType.AIRSPY_R820T, "1D50", "60A1", "Airspy", "Airspy" ), GENERIC_2832( TunerType.RTL2832_VARIOUS, "0BDA", "2832", "RTL2832", "SDR" ), GENERIC_2838( TunerType.RTL2832_VARIOUS, "0BDA", "2838", "RTL2832", "SDR" ), COMPRO_VIDEOMATE_U620F( TunerType.ELONICS_E4000, "185B", "0620", "Compro", "Videomate U620F" ), diff --git a/src/source/tuner/TunerConfigurationAssignment.java b/src/source/tuner/TunerConfigurationAssignment.java deleted file mode 100644 index 0cb450bf..00000000 --- a/src/source/tuner/TunerConfigurationAssignment.java +++ /dev/null @@ -1,64 +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 - ******************************************************************************/ -package source.tuner; - -import javax.xml.bind.annotation.XmlAttribute; - -public class TunerConfigurationAssignment -{ - private TunerType mTunerType; - private String mUniqueID; - private String mTunerConfigurationName; - - public TunerConfigurationAssignment() - { - } - - @XmlAttribute( name = "type" ) - public TunerType getTunerType() - { - return mTunerType; - } - - public void setTunerType( TunerType tunerType ) - { - mTunerType = tunerType; - } - - @XmlAttribute( name = "id" ) - public String getUniqueID() - { - return mUniqueID; - } - - public void setUniqueID( String uniqueID ) - { - mUniqueID = uniqueID; - } - - @XmlAttribute( name = "configuration_name" ) - public String getTunerConfigurationName() - { - return mTunerConfigurationName; - } - - public void setTunerConfigurationName( String tunerConfigurationName ) - { - mTunerConfigurationName = tunerConfigurationName; - } -} diff --git a/src/source/tuner/TunerController.java b/src/source/tuner/TunerController.java index 46e6b0a2..b0c240aa 100644 --- a/src/source/tuner/TunerController.java +++ b/src/source/tuner/TunerController.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,12 +25,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import source.SourceException; -import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeEvent.Event; import source.tuner.frequency.FrequencyController; import source.tuner.frequency.FrequencyController.Tunable; -import controller.ThreadPoolManager; +import source.tuner.frequency.IFrequencyChangeProcessor; -public abstract class TunerController implements Tunable +public abstract class TunerController implements Tunable, IFrequencyChangeProcessor { private final static Logger mLog = LoggerFactory.getLogger( TunerController.class ); @@ -72,8 +74,27 @@ public abstract class TunerController implements Tunable */ public abstract void apply( TunerConfiguration config ) throws SourceException; - - public int getBandwidth() + + /** + * Responds to requests to set the frequency + */ + @Override + public void frequencyChanged( FrequencyChangeEvent event ) + { + if( event.getEvent() == Event.REQUEST_FREQUENCY_CHANGE ) + { + try + { + setFrequency( event.getValue().longValue() ); + } + catch ( SourceException e ) + { + mLog.error( "Error setting frequency from external controller", e ); + } + } + } + + public int getBandwidth() { return mFrequencyController.getBandwidth(); } @@ -98,6 +119,11 @@ public abstract class TunerController implements Tunable { return mFrequencyController.getFrequency(); } + + public int getSampleRate() + { + return mFrequencyController.getBandwidth(); + } public double getFrequencyCorrection() { @@ -221,6 +247,11 @@ public abstract class TunerController implements Tunable return source; } + + public int getChannelCount() + { + return mTunedChannels.size(); + } /** * Indicates if the tuner's LO frequency must be updated in order to @@ -358,16 +389,16 @@ public abstract class TunerController implements Tunable * Note: this is normally used by the Tuner. Any additional listeners can * be registered on the tuner. */ - public void addListener( FrequencyChangeListener listener ) + public void addListener( IFrequencyChangeProcessor processor ) { - mFrequencyController.addListener( listener ); + mFrequencyController.addListener( processor ); } /** * Removes the frequency change listener */ - public void removeListener( FrequencyChangeListener listener ) + public void removeListener( IFrequencyChangeProcessor processor ) { - mFrequencyController.removeListener( listener ); + mFrequencyController.removeFrequencyChangeProcessor( processor ); } } diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java index 15b3d650..f1a0d972 100644 --- a/src/source/tuner/TunerEditor.java +++ b/src/source/tuner/TunerEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,45 +15,342 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ + package source.tuner; import gui.control.JFrequencyControl; -import source.SourceEditor; -import source.config.SourceConfigTuner; -import source.config.SourceConfiguration; -import controller.ResourceManager; +import gui.editor.Editor; +import gui.editor.EmptyEditor; -public class TunerEditor extends SourceEditor +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.RowFilter; +import javax.swing.ScrollPaneConstants; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.TableRowSorter; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationFactory; +import source.tuner.configuration.TunerConfigurationModel; + +import com.jidesoft.swing.JideSplitPane; + +public class TunerEditor extends Editor { - private static final long serialVersionUID = 1L; - private JFrequencyControl mFrequencyControl; - - public TunerEditor( ResourceManager resourceManager, - SourceConfiguration config ) - { - super( resourceManager, config ); - - initGUI(); - } + private static final long serialVersionUID = 1L; - public void reset() + private final static Logger mLog = LoggerFactory.getLogger( TunerEditor.class ); + + private TunerConfigurationModel mTunerConfigurationModel; + private JTable mTunerConfigurationTable; + private TableRowSorter mRowSorter; + private JFrequencyControl mFrequencyControl = new JFrequencyControl(); + private JLabel mSelectedTunerType = new JLabel( "No Tuner Selected" ); + private JButton mNewConfigurationButton = new JButton( "New" ); + private JButton mDeleteConfigurationButton = new JButton( "Delete" ); + private JButton mAssignConfigurationButton = new JButton( "Assign" ); + private JScrollPane mEditorScroller; + private Editor mEditor = new EmptyEditor<>( "a tuner" ); + private JideSplitPane mEditorSplitPane = new JideSplitPane(); + + public TunerEditor( TunerConfigurationModel tunerConfigurationModel ) { - mFrequencyControl.setFrequency( - ((SourceConfigTuner)mConfig).getFrequency(), false ); + mTunerConfigurationModel = tunerConfigurationModel; + init(); } + public void init() + { + setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill]" ) ); + + JPanel listPanel = new JPanel(); + listPanel.setLayout( new MigLayout( "fill,wrap 3", "[grow,fill]", "[][][][grow,fill][]" ) ); + + listPanel.add( mSelectedTunerType, "span" ); + + mFrequencyControl.setEnabled( false ); + listPanel.add( mFrequencyControl, "span" ); + + mRowSorter = new TableRowSorter<>( mTunerConfigurationModel ); + mTunerConfigurationTable = new JTable( mTunerConfigurationModel ); + mTunerConfigurationTable.setRowSorter( mRowSorter ); + mTunerConfigurationTable.setEnabled( false ); + mTunerConfigurationTable.getSelectionModel().addListSelectionListener( new ListSelectionListener() + { + @Override + public void valueChanged( ListSelectionEvent event ) + { + if( !event.getValueIsAdjusting() ) + { + int row = mTunerConfigurationTable.getSelectedRow(); + + if( row >= 0 ) + { + int modelRow = mTunerConfigurationTable.convertRowIndexToModel( row ); + + if( modelRow >= 0 ) + { + setTunerConfiguration( mTunerConfigurationModel.getTunerConfiguration( modelRow ) ); + } + } + } + } + } ); + + JScrollPane tableScroller = new JScrollPane( mTunerConfigurationTable ); + tableScroller.setHorizontalScrollBarPolicy( + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER ); + listPanel.add( new JLabel( "Tuner Configurations" ), "span" ); + listPanel.add( tableScroller, "span" ); + + mNewConfigurationButton.setEnabled( false ); + mNewConfigurationButton.setToolTipText( "Create a new configuration for the currently selected tuner" ); + mNewConfigurationButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + Tuner tuner = getItem(); + + String name = mTunerConfigurationModel + .getDistinctName( tuner.getTunerType(), tuner.getUniqueID() ); + + TunerConfiguration config = TunerConfigurationFactory + .getTunerConfiguration( tuner.getTunerType(), + tuner.getUniqueID(), name ); + + mTunerConfigurationModel.addTunerConfiguration( config ); + } + } ); + listPanel.add( mNewConfigurationButton ); + + mDeleteConfigurationButton.setEnabled( false ); + mDeleteConfigurationButton.setToolTipText( "Deletes the currently selected tuner" ); + mDeleteConfigurationButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + int choice = JOptionPane.showConfirmDialog( TunerEditor.this, + "Do you want to delete this tuner configuration?", + "Delete Tuner Configuration?", JOptionPane.YES_NO_OPTION ); + + if( choice == JOptionPane.YES_OPTION ) + { + TunerConfiguration selected = getSelectedTunerConfiguration(); + + mTunerConfigurationModel.removeTunerConfiguration( selected ); + mTunerConfigurationTable.setRowSelectionInterval( 0, 0 ); + } + } + } ); + listPanel.add( mDeleteConfigurationButton ); + + mAssignConfigurationButton.setEnabled( false ); + mAssignConfigurationButton.setToolTipText( "Assigns to the currently selected tuner configuration to the selected tuner" ); + mAssignConfigurationButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + TunerConfiguration selected = getSelectedTunerConfiguration(); + + mTunerConfigurationModel.assignTunerConfiguration( selected ); + setTunerConfiguration( getSelectedTunerConfiguration() ); + } + } ); + listPanel.add( mAssignConfigurationButton ); + + //Set preferred size on both scrollers to same width, so they split the space + listPanel.setPreferredSize( new Dimension( 100, 80 ) ); + JScrollPane listScroller = new JScrollPane( listPanel ); + listScroller.setPreferredSize( new Dimension( 100, 80 ) ); + + mEditorScroller = new JScrollPane( mEditor ); + mEditorScroller.setPreferredSize( new Dimension( 100, 80 ) ); + + mEditorSplitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); + mEditorSplitPane.add( listScroller ); + mEditorSplitPane.add( mEditorScroller ); + + add( mEditorSplitPane ); + } + + private TunerConfiguration getSelectedTunerConfiguration() + { + int tableRow = mTunerConfigurationTable.getSelectedRow(); + + if( tableRow >= 0 ) + { + int modelRow = mTunerConfigurationTable.convertRowIndexToModel( tableRow ); + + if( modelRow >= 0 ) + { + return mTunerConfigurationModel.getTunerConfiguration( modelRow ); + } + } + + return null; + } + + @Override public void save() { - ((SourceConfigTuner)mConfig).setFrequency( mFrequencyControl.getFrequency() ); + //Unused + } + + @Override + public void setItem( Tuner tuner ) + { + //Cleanup previously selected tuner + if( hasItem() ) + { + Tuner previous = getItem(); + previous.getTunerController().removeListener( mFrequencyControl ); + mFrequencyControl.removeListener( previous.getTunerController() ); + } + + super.setItem( tuner ); + + if( hasItem() ) + { + mSelectedTunerType.setText( tuner.getName() ); + mFrequencyControl.setEnabled( true ); + mTunerConfigurationTable.setEnabled( true ); + mNewConfigurationButton.setEnabled( true ); + + //Change to an editor for this config's tuner type + mEditor = TunerConfigurationFactory.getEditor( getItem(), mTunerConfigurationModel ); + + mRowSorter.setRowFilter( new ConfigurationRowFilter( tuner.getTunerType(), + tuner.getUniqueID() ) ); + + TunerConfiguration assigned = mTunerConfigurationModel + .getTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() ); + + int modelIndex = mTunerConfigurationModel.getRowIndex( assigned ); + + if( modelIndex >= 0 ) + { + int viewIndex = mTunerConfigurationTable + .convertRowIndexToView( modelIndex ); + + if( viewIndex >= 0 ) + { + mTunerConfigurationTable + .setRowSelectionInterval( viewIndex, viewIndex ); + } + } + + //Link frequency control to the tuner + mFrequencyControl.addListener( tuner.getTunerController() ); + + //Link tuner to frequency control + tuner.getTunerController().addListener( mFrequencyControl ); + + //Set the displayed frequency without adjusting the tuner's frequency + mFrequencyControl.setFrequency( + tuner.getTunerController().getFrequency(), false ); + } + else + { + mSelectedTunerType.setText( "No Tuner Selected" ); + mFrequencyControl.setEnabled( false ); + mTunerConfigurationTable.setEnabled( false ); + mNewConfigurationButton.setEnabled( false ); + mRowSorter.setRowFilter( null ); + mEditor = new EmptyEditor(); + } + + //Swap out the editor + int split = mEditorSplitPane.getDividerLocation( 0 ); + mEditorSplitPane.remove( mEditorScroller ); + mEditorScroller = new JScrollPane( mEditor ); + mEditorSplitPane.add( mEditorScroller ); + mEditorSplitPane.setDividerLocation( 0, split ); } - private void initGUI() + private void setTunerConfiguration( TunerConfiguration config ) { - mFrequencyControl = new JFrequencyControl(); + if( config != null ) + { + if( !config.isAssigned() ) + { + if( !mAssignConfigurationButton.isEnabled() ) + { + mAssignConfigurationButton.setEnabled( true ); + } + if( !mDeleteConfigurationButton.isEnabled() ) + { + mDeleteConfigurationButton.setEnabled( true ); + } + } + else + { + if( mAssignConfigurationButton.isEnabled() ) + { + mAssignConfigurationButton.setEnabled( false ); + } + + if( mDeleteConfigurationButton.isEnabled() ) + { + mDeleteConfigurationButton.setEnabled( false ); + } + } + } + else + { + if( mDeleteConfigurationButton.isEnabled() ) + { + mDeleteConfigurationButton.setEnabled( false ); + } + + if( mAssignConfigurationButton.isEnabled() ) + { + mAssignConfigurationButton.setEnabled( false ); + } + } - mFrequencyControl.setFrequency( - ((SourceConfigTuner)mConfig).getFrequency(), false ); - - add( mFrequencyControl ); + mEditor.setItem( config ); } + + public class ConfigurationRowFilter extends RowFilter + { + private TunerType mTunerType; + private String mUniqueID; + + public ConfigurationRowFilter( TunerType type, String uniqueID ) + { + mTunerType = type; + mUniqueID = uniqueID; + } + + @Override + public boolean include( javax.swing.RowFilter.Entry entry ) + { + TunerConfiguration config = entry.getModel() + .getTunerConfiguration( entry.getIdentifier() ); + + return config != null && + config.getTunerType() == mTunerType && + config.getUniqueID() != null && + config.getUniqueID().contentEquals( mUniqueID ); + } + } + } diff --git a/src/source/tuner/TunerEvent.java b/src/source/tuner/TunerEvent.java new file mode 100644 index 00000000..f34a1fd4 --- /dev/null +++ b/src/source/tuner/TunerEvent.java @@ -0,0 +1,34 @@ +package source.tuner; + +public class TunerEvent +{ + private Tuner mTuner; + private Event mEvent; + + public TunerEvent( Tuner tuner, Event event ) + { + mTuner = tuner; + mEvent = event; + } + + public Tuner getTuner() + { + return mTuner; + } + + public Event getEvent() + { + return mEvent; + } + + public enum Event + { + ADD, + REMOVE, + CHANNEL_COUNT, + FREQUENCY, + SAMPLE_RATE, + REQUEST_MAIN_SPECTRAL_DISPLAY, + REQUEST_NEW_SPECTRAL_DISPLAY; + } +} diff --git a/src/source/tuner/TunerManager.java b/src/source/tuner/TunerManager.java index f9a008b7..2cc230f0 100644 --- a/src/source/tuner/TunerManager.java +++ b/src/source/tuner/TunerManager.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,11 +17,7 @@ ******************************************************************************/ package source.tuner; -import java.util.ArrayList; import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.RejectedExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,10 +26,7 @@ import org.usb4java.DeviceDescriptor; import org.usb4java.DeviceList; import org.usb4java.LibUsb; -import source.Source; import source.SourceException; -import source.config.SourceConfigTuner; -import source.config.SourceConfiguration; import source.mixer.MixerManager; import source.tuner.airspy.AirspyTuner; import source.tuner.airspy.AirspyTunerController; @@ -46,22 +39,24 @@ import source.tuner.rtl.RTL2832Tuner; import source.tuner.rtl.RTL2832TunerController; import source.tuner.rtl.e4k.E4KTunerController; import source.tuner.rtl.r820t.R820TTunerController; -import controller.ResourceManager; +import controller.ThreadPoolManager; public class TunerManager { private final static Logger mLog = LoggerFactory.getLogger( TunerManager.class ); - private ResourceManager mResourceManager; - private ArrayList mTuners = new ArrayList(); + private MixerManager mMixerManager; + private TunerModel mTunerModel; + private ThreadPoolManager mThreadPoolManager; - public TunerManager( ResourceManager resourceManager ) + public TunerManager( MixerManager mixerManager, + TunerModel tunerModel, + ThreadPoolManager threadPoolManager ) { - mResourceManager = resourceManager; - - /* initialize mixers */ - MixerManager.getInstance(); + mMixerManager = mixerManager; + mTunerModel = tunerModel; + mThreadPoolManager = threadPoolManager; initTuners(); } @@ -74,57 +69,6 @@ public class TunerManager LibUsb.exit( null ); } - /** - * Iterates current tuners to get a tuner channel source for the frequency - * specified in the channel config's source config object - */ - public Source getSource( SourceConfiguration config, int bandwidth ) - { - TunerChannelSource retVal = null; - - if( config instanceof SourceConfigTuner ) - { - SourceConfigTuner tunerConfig = (SourceConfigTuner)config; - - TunerChannel tunerChannel = tunerConfig.getTunerChannel(); - - tunerChannel.setBandwidth( bandwidth ); - - Iterator it = mTuners.iterator(); - - Tuner tuner; - - while( it.hasNext() && retVal == null ) - { - tuner = it.next(); - - try - { - retVal = tuner.getChannel( tunerChannel ); - } - catch ( RejectedExecutionException ree ) - { - mLog.error( "couldn't provide tuner channel source", ree ); - } - catch ( SourceException e ) - { - mLog.error( "error obtaining channel from tuner [" + - tuner.getName() + "]", e ); - } - } - } - - return retVal; - } - - /** - * Get list of current tuners - */ - public List getTuners() - { - return mTuners; - } - /** * Loads all USB tuners and USB/Mixer tuner devices */ @@ -182,9 +126,19 @@ public class TunerManager if( status.isLoaded() ) { Tuner tuner = status.getTuner(); - sb.append( "] LOADED: " ); - sb.append( tuner.toString() ); - mTuners.add( tuner ); + + try + { + mTunerModel.addTuner( tuner ); + sb.append( "] LOADED: " ); + sb.append( tuner.toString() ); + } + catch( Exception e ) + { + sb.append( "] NOT LOADED: " ); + sb.append( status.getInfo() ); + sb.append( " Error:" + e.getMessage() ); + } } else { @@ -261,19 +215,12 @@ public class TunerManager try { AirspyTunerController airspyController = - new AirspyTunerController( device, mResourceManager.getThreadPoolManager() ); + new AirspyTunerController( device, mThreadPoolManager ); airspyController.init(); AirspyTuner tuner = new AirspyTuner( airspyController ); - TunerConfiguration config = getTunerConfiguration( tuner ); - - if( config != null ) - { - tuner.apply( config ); - } - return new TunerInitStatus( tuner, "LOADED" ); } catch( SourceException se ) @@ -292,11 +239,6 @@ public class TunerManager { return new TunerInitStatus( null, "Ettus B100 tuner not currently " + "supported" ); -// case ETTUS_USRP_B100: -// mTuners.add( new B100Tuner( device ) ); -// status = sLOADED; -// reason = null; -// break; } private TunerInitStatus initFuncubeProTuner( Device device, @@ -319,13 +261,6 @@ public class TunerManager FCDTuner tuner = new FCDTuner( dataline, controller ); - TunerConfiguration config = getTunerConfiguration( tuner ); - - if( config != null ) - { - tuner.apply( config ); - } - return new TunerInitStatus( tuner, "LOADED" ); } catch ( SourceException e ) @@ -364,13 +299,6 @@ public class TunerManager FCDTuner tuner = new FCDTuner( dataline, controller ); - TunerConfiguration config = getTunerConfiguration( tuner ); - - if( config != null ) - { - tuner.apply( config ); - } - return new TunerInitStatus( tuner, "LOADED" ); } catch ( SourceException e ) @@ -397,19 +325,12 @@ public class TunerManager { HackRFTunerController hackRFController = new HackRFTunerController( device, descriptor, - mResourceManager.getThreadPoolManager() ); + mThreadPoolManager ); hackRFController.init(); HackRFTuner tuner = new HackRFTuner( hackRFController ); - TunerConfiguration config = getTunerConfiguration( tuner ); - - if( config != null ) - { - tuner.apply( config ); - } - return new TunerInitStatus( tuner, "LOADED" ); } catch( SourceException se ) @@ -449,21 +370,13 @@ public class TunerManager { E4KTunerController controller = new E4KTunerController( device, deviceDescriptor, - mResourceManager.getThreadPoolManager() ); + mThreadPoolManager ); controller.init(); RTL2832Tuner rtlTuner = new RTL2832Tuner( tunerClass, controller ); - TunerConfiguration config = - getTunerConfiguration( rtlTuner ); - - if( config != null ) - { - rtlTuner.apply( config ); - } - return new TunerInitStatus( rtlTuner, "LOADED" ); } catch( SourceException se ) @@ -476,21 +389,13 @@ public class TunerManager { R820TTunerController controller = new R820TTunerController( device, deviceDescriptor, - mResourceManager.getThreadPoolManager() ); + mThreadPoolManager ); controller.init(); RTL2832Tuner rtlTuner = new RTL2832Tuner( tunerClass, controller ); - TunerConfiguration config = - getTunerConfiguration( rtlTuner ); - - if( config != null ) - { - rtlTuner.apply( config ); - } - return new TunerInitStatus( rtlTuner, "LOADED" ); } catch( SourceException se ) @@ -513,55 +418,6 @@ public class TunerManager return new TunerInitStatus( null, reason ); } - - /** - * Get a stored tuner configuration or create a default tuner configuration - * for the tuner type, connected at the address. - * - * Note: a named tuner configuration will be stored for for each tuner type - * and address combination. - * @param type - tuner type - * @param address - current usb address/port - * @return - stored tuner configuration or default configuration - */ - private TunerConfiguration getTunerConfiguration( Tuner tuner ) - { - TunerConfigurationAssignment selected = mResourceManager - .getSettingsManager().getSelectedTunerConfiguration( - tuner.getTunerType(), tuner.getUniqueID() ); - - ArrayList configs = mResourceManager - .getSettingsManager().getTunerConfigurations( tuner.getTunerType() ); - - TunerConfiguration config = null; - - if( selected != null ) - { - for( TunerConfiguration tunerConfig: configs ) - { - if( tunerConfig.getName().contentEquals( selected.getTunerConfigurationName() ) ) - { - config = tunerConfig; - } - } - } - - //If we're still null at this point, create a default config - if( config == null ) - { - String name = "Default-" + tuner.getUniqueID(); - - config = mResourceManager.getSettingsManager() - .addNewTunerConfiguration( tuner.getTunerType(), name ); - } - - //Persist the config as the selected tuner configuration. The method - //auto-saves the setting - mResourceManager.getSettingsManager().setSelectedTunerConfiguration( - tuner.getTunerType(), tuner.getUniqueID(), config ); - - return config; - } /** * Gets the first tuner mixer dataline that corresponds to the tuner class. @@ -577,7 +433,7 @@ public class TunerManager private MixerTunerDataLine getMixerTunerDataLine( TunerType tunerClass ) { Collection datalines = - MixerManager.getInstance().getMixerTunerDataLines(); + mMixerManager.getMixerTunerDataLines(); for( MixerTunerDataLine mixerTDL: datalines ) { diff --git a/src/source/tuner/TunerModel.java b/src/source/tuner/TunerModel.java new file mode 100644 index 00000000..079d04a7 --- /dev/null +++ b/src/source/tuner/TunerModel.java @@ -0,0 +1,297 @@ +package source.tuner; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.RejectedExecutionException; + +import javax.swing.table.AbstractTableModel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import source.Source; +import source.SourceException; +import source.config.SourceConfigTuner; +import source.tuner.TunerEvent.Event; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationModel; + +public class TunerModel extends AbstractTableModel implements Listener +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = LoggerFactory.getLogger( TunerModel.class ); + + public static final int TUNER_TYPE = 0; + public static final int TUNER_ID = 1; + public static final int SAMPLE_RATE = 2; + public static final int FREQUENCY = 3; + public static final int CHANNEL_COUNT = 4; + public static final int SPECTRAL_DISPLAY_MAIN = 5; + public static final int SPECTRAL_DISPLAY_NEW = 6; + + private static final String MHZ = " MHz"; + private static final String[] COLUMNS = + { "Tuner", "ID", "Sample Rate", "Frequency", "Channels", "Spectral", "Display" }; + + private List mTuners = new ArrayList<>(); + private List> mTunerEventListeners = new ArrayList<>(); + private DecimalFormat mFrequencyFormat = new DecimalFormat( "0.00000" ); + private DecimalFormat mSampleRateFormat = new DecimalFormat( "0.000" ); + + private TunerConfigurationModel mTunerConfigurationModel; + + public TunerModel( TunerConfigurationModel tunerConfigurationModel ) + { + mTunerConfigurationModel = tunerConfigurationModel; + } + + public TunerConfigurationModel getTunerConfigurationModel() + { + return mTunerConfigurationModel; + } + + /** + * List of Tuners currently in the model + */ + public List getTuners() + { + return mTuners; + } + + public Tuner getTuner( int index ) + { + if( index < mTuners.size() ) + { + return mTuners.get( index ); + } + + return null; + } + + public void addTuners( List Tuners ) + { + for( Tuner tuner: Tuners ) + { + addTuner( tuner ); + } + } + + /** + * Adds the Tuner to this model + */ + public void addTuner( Tuner tuner ) + { + if( !mTuners.contains( tuner ) ) + { + //Get the tuner configuration and apply it to the tuner - this + //call should always produce a tuner configuration + TunerConfiguration config = mTunerConfigurationModel + .getTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() ); + + try + { + tuner.getTunerController().apply( config ); + + mTuners.add( tuner ); + + int index = mTuners.indexOf( tuner ); + + fireTableRowsInserted( index, index ); + + tuner.addTunerChangeListener( this ); + } + catch( SourceException se ) + { + mLog.error( "Couldn't apply tuner configuration to tuner - [" + + tuner.getTunerType().name() + "] with id [" + + tuner.getUniqueID() + "] - tuner will not be included" ); + } + + } + } + + /** + * Removes the Tuner from this model + */ + public void removeTuner( Tuner tuner ) + { + if( mTuners.contains( tuner ) ) + { + tuner.removeTunerChangeListener( this ); + + int index = mTuners.indexOf( tuner ); + + mTuners.remove( tuner ); + + fireTableRowsDeleted( index, index ); + } + } + + public void addListener( Listener listener ) + { + mTunerEventListeners.add( listener ); + } + + public void removeListener( Listener listener ) + { + mTunerEventListeners.remove( listener ); + } + + public void broadcast( TunerEvent event ) + { + for( Listener listener: mTunerEventListeners ) + { + listener.receive( event ); + } + } + + /** + * Requests to display the first tuner in this model. Invoke this method + * after all listeners have registered and tuners have been added to this + * model, in order to inform the primary display to use the first tuner. + */ + public void requestFirstTunerDisplay() + { + if( mTuners.size() > 0 ) + { + broadcast( new TunerEvent( mTuners.get( 0 ), + Event.REQUEST_MAIN_SPECTRAL_DISPLAY ) ); + } + } + + @Override + public void receive( TunerEvent event ) + { + if( event.getTuner() != null ) + { + int index = mTuners.indexOf( event.getTuner() ); + + if( index >= 0 ) + { + switch( event.getEvent() ) + { + case CHANNEL_COUNT: + fireTableCellUpdated( index, CHANNEL_COUNT ); + break; + case FREQUENCY: + fireTableCellUpdated( index, FREQUENCY ); + break; + case SAMPLE_RATE: + fireTableCellUpdated( index, SAMPLE_RATE ); + break; + case REQUEST_MAIN_SPECTRAL_DISPLAY: + fireTableCellUpdated( index, SPECTRAL_DISPLAY_MAIN ); + default: + break; + } + } + } + + broadcast( event ); + } + + @Override + public int getRowCount() + { + return mTuners.size(); + } + + @Override + public int getColumnCount() + { + return COLUMNS.length; + } + + @Override + public Object getValueAt( int rowIndex, int columnIndex ) + { + if( rowIndex < mTuners.size() ) + { + Tuner tuner = mTuners.get( rowIndex ); + + switch( columnIndex ) + { + case TUNER_TYPE: + return tuner.getTunerType().getLabel(); + case TUNER_ID: + return tuner.getUniqueID(); + case SAMPLE_RATE: + int sampleRate = tuner.getTunerController().getSampleRate(); + + return mSampleRateFormat.format( sampleRate / 1E6D ) + MHZ; + case FREQUENCY: + try + { + long frequency = tuner.getTunerController().getFrequency(); + + return mFrequencyFormat.format( frequency / 1E6D ) + MHZ; + } + catch( Exception e ) + { + return 0; + } + case CHANNEL_COUNT: + return tuner.getTunerController().getChannelCount(); + case SPECTRAL_DISPLAY_MAIN: + return "Main"; + case SPECTRAL_DISPLAY_NEW: + return "New"; + default: + break; + } + } + + return null; + } + + @Override + public String getColumnName( int columnIndex ) + { + return COLUMNS[ columnIndex ]; + } + + /** + * Iterates current tuners to get a tuner channel source for the frequency + * specified in the channel config's source config object. + * + * Returns null if no tuner can source the channel + */ + public Source getSource( SourceConfigTuner config, int bandwidth ) + { + TunerChannelSource retVal = null; + + TunerChannel tunerChannel = config.getTunerChannel(); + + tunerChannel.setBandwidth( bandwidth ); + + Iterator it = mTuners.iterator(); + + Tuner tuner; + + while( it.hasNext() && retVal == null ) + { + tuner = it.next(); + + try + { + retVal = tuner.getChannel( tunerChannel ); + } + catch ( RejectedExecutionException ree ) + { + mLog.error( "couldn't provide tuner channel source", ree ); + } + catch ( SourceException e ) + { + mLog.error( "error obtaining channel from tuner [" + + tuner.getName() + "]", e ); + } + } + + return retVal; + } + +} diff --git a/src/source/tuner/TunerNode.java b/src/source/tuner/TunerNode.java deleted file mode 100644 index 9d2f4d99..00000000 --- a/src/source/tuner/TunerNode.java +++ /dev/null @@ -1,156 +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 - ******************************************************************************/ -package source.tuner; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.text.DecimalFormat; - -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.SourceException; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; -import spectrum.SpectrumFrame; -import controller.BaseNode; -import controller.ResourceManager; - -public class TunerNode extends BaseNode implements FrequencyChangeListener -{ - private final static Logger mLog = - LoggerFactory.getLogger( TunerNode.class ); - - private static final long serialVersionUID = 1L; - private static DecimalFormat FREQUENCY_FORMAT = new DecimalFormat( "0.00000" ); - private static DecimalFormat SAMPLE_RATE_FORMAT = new DecimalFormat( "0.000" ); - - private long mFrequency; - private int mSampleRate; - - public TunerNode( Tuner tuner ) - { - super( tuner ); //does this make it a super tuner? - - tuner.addListener( this ); - - try - { - mFrequency = tuner.getFrequency(); - mSampleRate = tuner.getSampleRate(); - } - catch ( SourceException e ) - { - mLog.error( "TunerNode - error reading frequency and sample rate " - + "from tuner", e ); - } - } - - @Override - public JPanel getEditor() - { - return getTuner().getEditor( getModel().getResourceManager() ); - } - - public Tuner getTuner() - { - return (Tuner)getUserObject(); - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getTuner().getName() ); - - sb.append( " " ); - sb.append( FREQUENCY_FORMAT.format( (double)mFrequency / 1E6d ) ); - sb.append( " [" ); - sb.append( SAMPLE_RATE_FORMAT.format( (double)mSampleRate / 1E6d ) ); - sb.append( "] MHz" ); - - return sb.toString(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem showSpectrumItem = new JMenuItem( "Show in Main Spectrum" ); - showSpectrumItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - getModel().fireTunerSelectedEvent( getTuner() ); - } - } ); - - retVal.add( showSpectrumItem ); - - JMenuItem newSpectrumItem = new JMenuItem( "New Spectrum Window" ); - newSpectrumItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - ResourceManager rm = getModel().getResourceManager(); - - SpectrumFrame frame = new SpectrumFrame( rm.getChannelManager(), - rm.getController(), rm.getPlaylistManager(), - rm.getSettingsManager(), getTuner() ); - } - } ); - - retVal.add( newSpectrumItem ); - - return retVal; - } - - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - switch( event.getEvent() ) - { - case FREQUENCY_CHANGE_NOTIFICATION: - long frequency = event.getValue().longValue(); - - if( mFrequency != frequency ) - { - mFrequency = frequency; - getModel().nodeChanged( TunerNode.this ); - } - break; - case SAMPLE_RATE_CHANGE_NOTIFICATION: - int sampleRate = event.getValue().intValue(); - - if( mSampleRate != sampleRate ) - { - mSampleRate = sampleRate; - getModel().nodeChanged( TunerNode.this ); - } - break; - default: - break; - } - } -} diff --git a/src/source/tuner/TunerSelectionListener.java b/src/source/tuner/TunerSelectionListener.java deleted file mode 100644 index 52ebff6d..00000000 --- a/src/source/tuner/TunerSelectionListener.java +++ /dev/null @@ -1,23 +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 - ******************************************************************************/ -package source.tuner; - -public interface TunerSelectionListener -{ - public void tunerSelected( Tuner tuner ); -} diff --git a/src/source/tuner/TunerSourceEditor.java b/src/source/tuner/TunerSourceEditor.java new file mode 100644 index 00000000..4973e6b7 --- /dev/null +++ b/src/source/tuner/TunerSourceEditor.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * 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; + +import gui.control.JFrequencyControl; +import gui.editor.Editor; +import net.miginfocom.swing.MigLayout; +import source.config.SourceConfigTuner; +import source.config.SourceConfiguration; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.IFrequencyChangeProcessor; +import controller.channel.Channel; + +public class TunerSourceEditor extends Editor +{ + private static final long serialVersionUID = 1L; + private JFrequencyControl mFrequencyControl; + + public TunerSourceEditor() + { + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0", "[left]", "" ) ); + mFrequencyControl = new JFrequencyControl(); + mFrequencyControl.setEnabled( false ); + mFrequencyControl.addListener( new IFrequencyChangeProcessor() + { + @Override + public void frequencyChanged( FrequencyChangeEvent event ) + { + setModified( true ); + } + } ); + add( mFrequencyControl ); + } + + public void save() + { + if( hasItem() && isModified() ) + { + SourceConfigTuner config = new SourceConfigTuner(); + config.setFrequency( mFrequencyControl.getFrequency() ); + getItem().setSourceConfiguration( config ); + } + + setModified( false ); + } + + @Override + public void setItem( Channel item ) + { + super.setItem( item ); + + if( hasItem() ) + { + SourceConfiguration config = getItem().getSourceConfiguration(); + + mFrequencyControl.setEnabled( true ); + + if( config instanceof SourceConfigTuner ) + { + mFrequencyControl.setFrequency( ((SourceConfigTuner)config).getFrequency(), false ); + setModified( false ); + } + else + { + mFrequencyControl.setFrequency( 101000000, false ); + setModified( true ); + } + } + else + { + mFrequencyControl.setEnabled( false ); + setModified( false ); + } + } +} diff --git a/src/source/tuner/TunerSpectralDisplayManager.java b/src/source/tuner/TunerSpectralDisplayManager.java new file mode 100644 index 00000000..2f595bbd --- /dev/null +++ b/src/source/tuner/TunerSpectralDisplayManager.java @@ -0,0 +1,55 @@ +package source.tuner; + +import java.awt.EventQueue; + +import sample.Listener; +import settings.SettingsManager; +import spectrum.SpectralDisplayPanel; +import spectrum.SpectrumFrame; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; + +public class TunerSpectralDisplayManager implements Listener +{ + private SpectralDisplayPanel mSpectralDisplayPanel; + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; + private SettingsManager mSettingsManager; + + public TunerSpectralDisplayManager( SpectralDisplayPanel panel, + ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + SettingsManager settingsManager ) + { + mSpectralDisplayPanel = panel; + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; + mSettingsManager = settingsManager; + } + + @Override + public void receive( TunerEvent event ) + { + switch( event.getEvent() ) + { + case REQUEST_MAIN_SPECTRAL_DISPLAY: + mSpectralDisplayPanel.showTuner( event.getTuner() ); + break; + case REQUEST_NEW_SPECTRAL_DISPLAY: + final SpectrumFrame frame = new SpectrumFrame( mChannelModel, + mChannelProcessingManager, mSettingsManager, event.getTuner() ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + frame.setVisible( true ); + } + } ); + break; + default: + break; + } + } +} diff --git a/src/source/tuner/TunerType.java b/src/source/tuner/TunerType.java index ea58a9a7..65b293f3 100644 --- a/src/source/tuner/TunerType.java +++ b/src/source/tuner/TunerType.java @@ -19,7 +19,7 @@ package source.tuner; public enum TunerType { - AIRSPY_R820T( "R820T" ), + AIRSPY_R820T( "Airspy R820T" ), ELONICS_E4000( "E4000" ), ETTUS_WBX( "WBX" ), ETTUS_VARIOUS( "Ettus Tuner" ), diff --git a/src/source/tuner/TunerViewPanel.java b/src/source/tuner/TunerViewPanel.java new file mode 100644 index 00000000..306f2adb --- /dev/null +++ b/src/source/tuner/TunerViewPanel.java @@ -0,0 +1,178 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ + +package source.tuner; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.RowSorter; +import javax.swing.SortOrder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableRowSorter; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import source.tuner.TunerEvent.Event; + +import com.jidesoft.swing.JideSplitPane; + +public class TunerViewPanel extends JPanel +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( TunerViewPanel.class ); + + private TunerModel mTunerModel; + private JTable mTunerTable; + private TableRowSorter mRowSorter; + private JideSplitPane mSplitPane; + private TunerEditor mTunerEditor; + + public TunerViewPanel( TunerModel tunerModel ) + { + mTunerModel = tunerModel; + mTunerEditor = new TunerEditor( mTunerModel.getTunerConfigurationModel() ); + + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0", "[fill,grow]", "[fill,grow]" ) ); + + mRowSorter = new TableRowSorter<>( mTunerModel ); + List sortKeys = new ArrayList<>(); + sortKeys.add( new RowSorter.SortKey( TunerModel.TUNER_TYPE, SortOrder.ASCENDING ) ); + sortKeys.add( new RowSorter.SortKey( TunerModel.TUNER_ID, SortOrder.ASCENDING ) ); + mRowSorter.setSortKeys( sortKeys ); + + mTunerTable = new JTable( mTunerModel ); + mTunerTable.setRowSorter( mRowSorter ); + mTunerTable.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + mTunerTable.getSelectionModel().addListSelectionListener( new ListSelectionListener() + { + @Override + public void valueChanged( ListSelectionEvent event ) + { + if( !event.getValueIsAdjusting() ) + { + int row = mTunerTable.getSelectedRow(); + int modelRow = mTunerTable.convertRowIndexToModel( row ); + + mTunerEditor.setItem( mTunerModel.getTuner( modelRow ) ); + } + } + } ); + mTunerTable.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + int column = mTunerTable.columnAtPoint( e.getPoint() ); + + if( column == TunerModel.SPECTRAL_DISPLAY_MAIN ) + { + int tableRow = mTunerTable.rowAtPoint( e.getPoint() ); + int modelRow = mTunerTable.convertRowIndexToModel( tableRow ); + + Tuner tuner = mTunerModel.getTuner( modelRow ); + + if( tuner != null ) + { + mTunerModel.broadcast( new TunerEvent( tuner, + Event.REQUEST_MAIN_SPECTRAL_DISPLAY ) ); + } + } + else if( column == TunerModel.SPECTRAL_DISPLAY_NEW ) + { + int tableRow = mTunerTable.rowAtPoint( e.getPoint() ); + int modelRow = mTunerTable.convertRowIndexToModel( tableRow ); + + Tuner tuner = mTunerModel.getTuner( modelRow ); + + if( tuner != null ) + { + mTunerModel.broadcast( new TunerEvent( tuner, + Event.REQUEST_NEW_SPECTRAL_DISPLAY ) ); + } + } + } + } ); + + TableCellRenderer renderer = new LinkCellRenderer(); + + mTunerTable.getColumnModel().getColumn( 5 ).setCellRenderer( renderer ); + mTunerTable.getColumnModel().getColumn( 6 ).setCellRenderer( renderer ); + + JScrollPane listScroller = new JScrollPane( mTunerTable ); + listScroller.setPreferredSize( new Dimension( 400, 20 ) ); + + JScrollPane editorScroller = new JScrollPane( mTunerEditor ); + editorScroller.setPreferredSize( new Dimension( 400, 80 ) ); + + mSplitPane = new JideSplitPane(); + mSplitPane.setOrientation( JideSplitPane.VERTICAL_SPLIT ); + mSplitPane.add( listScroller ); + mSplitPane.add( editorScroller ); + + add( mSplitPane ); + } + + public class LinkCellRenderer extends DefaultTableCellRenderer + { + private static final long serialVersionUID = 1L; + + @Override + public Component getTableCellRendererComponent( JTable table, + Object value, boolean isSelected, boolean hasFocus, int row, + int column ) + { + JLabel label = (JLabel)super.getTableCellRendererComponent( table, + value, isSelected, hasFocus, row, column ); + + label.setForeground( Color.BLUE.brighter() ); + + if( column == TunerModel.SPECTRAL_DISPLAY_MAIN ) + { + label.setToolTipText( "Show this tuner in the main spectral display" ); + } + else if( column == TunerModel.SPECTRAL_DISPLAY_NEW ) + { + label.setToolTipText( "Show this tuner in a new spectral display" ); + } + + return label; + } + } +} diff --git a/src/source/tuner/airspy/AirspyInformationPanel.java b/src/source/tuner/airspy/AirspyInformationPanel.java deleted file mode 100644 index 6b3ef2cb..00000000 --- a/src/source/tuner/airspy/AirspyInformationPanel.java +++ /dev/null @@ -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 - ******************************************************************************/ - -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() ) ); - } -} diff --git a/src/source/tuner/airspy/AirspySampleAdapter.java b/src/source/tuner/airspy/AirspySampleAdapter.java index 91a67395..abf42a44 100644 --- a/src/source/tuner/airspy/AirspySampleAdapter.java +++ b/src/source/tuner/airspy/AirspySampleAdapter.java @@ -18,15 +18,10 @@ package source.tuner.airspy; * along with this program. If not, see ******************************************************************************/ -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sample.adapter.ISampleAdapter; public class AirspySampleAdapter implements ISampleAdapter { - private final static Logger mLog = LoggerFactory.getLogger( AirspySampleAdapter.class ); - private static final float SCALE_SIGNED_12_BIT_TO_FLOAT = 1.0f / 2048.0f; private boolean mSamplePacking = false; diff --git a/src/source/tuner/airspy/AirspyTuner.java b/src/source/tuner/airspy/AirspyTuner.java index 9c85b201..48c06d3b 100644 --- a/src/source/tuner/airspy/AirspyTuner.java +++ b/src/source/tuner/airspy/AirspyTuner.java @@ -19,8 +19,6 @@ package source.tuner.airspy; import java.util.concurrent.RejectedExecutionException; -import javax.swing.JPanel; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,44 +29,22 @@ import source.tuner.Tuner; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; -import source.tuner.TunerConfiguration; +import source.tuner.TunerEvent; import source.tuner.TunerType; import source.tuner.airspy.AirspyTunerController.BoardID; -import controller.ResourceManager; -import controller.ThreadPoolManager; public class AirspyTuner extends Tuner { private final static Logger mLog = LoggerFactory.getLogger( AirspyTuner.class ); - AirspyTunerController mController; - public AirspyTuner( AirspyTunerController controller ) { - super( "Airspy" ); - - mController = controller; - - /* Register for frequency/sample rate changes so that we can rebroadcast - * to any registered listeners */ - mController.addListener( this ); - } - - @Override - public JPanel getEditor( ResourceManager resourceManager ) - { - return new AirspyTunerEditorPanel( this, resourceManager ); + super( "Airspy " + controller.getDeviceInfo().getSerialNumber(), controller ); } public AirspyTunerController getController() { - return mController; - } - - @Override - public void apply( TunerConfiguration config ) throws SourceException - { - mController.apply( config ); + return (AirspyTunerController)getTunerController(); } @Override @@ -76,11 +52,11 @@ public class AirspyTuner extends Tuner { try { - return mController.getDeviceInfo().getSerialNumber(); + return getController().getDeviceInfo().getSerialNumber(); } catch( Exception e ) { - mLog.error( "error gettting serial number", e ); + mLog.error( "error getting serial number", e ); } return BoardID.AIRSPY.getLabel(); @@ -98,35 +74,12 @@ public class AirspyTuner extends Tuner return TunerType.AIRSPY_R820T; } - @Override - public int getSampleRate() - { - int rate = 0; - - try - { - rate = mController.getCurrentSampleRate(); - } - catch ( SourceException e ) - { - mLog.error( "Error while getting sample rate from controller", e ); - } - - return rate; - } - @Override public double getSampleSize() { return 13.0; } - @Override - public long getFrequency() throws SourceException - { - return mController.getFrequency(); - } - @Override public TunerChannelSource getChannel( TunerChannel channel ) throws RejectedExecutionException, SourceException @@ -138,9 +91,16 @@ public class AirspyTuner extends Tuner //Consider implementing a fractional resampler to get a correct 48 kHz //output sample rate - return mController.getChannel( this, channel ); - } + TunerChannelSource source = getController().getChannel( this, channel ); + if( source != null ) + { + broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) ); + } + + return source; + } + @Override public void releaseChannel( TunerChannelSource source ) { @@ -150,19 +110,19 @@ public class AirspyTuner extends Tuner /* Tell the controller to release the channel and cleanup */ if( source != null ) { - mController.releaseChannel( source ); + getController().releaseChannel( source ); } } @Override public void addListener( Listener listener ) { - mController.addListener( listener ); + getController().addListener( listener ); } @Override public void removeListener( Listener listener ) { - mController.removeListener( listener ); + getController().removeListener( listener ); } } diff --git a/src/source/tuner/airspy/AirspyTunerConfiguration.java b/src/source/tuner/airspy/AirspyTunerConfiguration.java index 52a3a818..9a62bdbe 100644 --- a/src/source/tuner/airspy/AirspyTunerConfiguration.java +++ b/src/source/tuner/airspy/AirspyTunerConfiguration.java @@ -19,9 +19,9 @@ package source.tuner.airspy; import javax.xml.bind.annotation.XmlAttribute; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; import source.tuner.airspy.AirspyTunerController.Gain; +import source.tuner.configuration.TunerConfiguration; public class AirspyTunerConfiguration extends TunerConfiguration { @@ -30,6 +30,7 @@ public class AirspyTunerConfiguration extends TunerConfiguration private int mIFGain = AirspyTunerController.IF_GAIN_DEFAULT; private int mMixerGain = AirspyTunerController.MIXER_GAIN_DEFAULT; private int mLNAGain = AirspyTunerController.LNA_GAIN_DEFAULT; + private double mFrequencyCorrection = 0.0d; private boolean mMixerAGC = false; private boolean mLNAAGC = false; @@ -39,12 +40,11 @@ public class AirspyTunerConfiguration extends TunerConfiguration */ public AirspyTunerConfiguration() { - this( "Default" ); } - public AirspyTunerConfiguration( String name ) + public AirspyTunerConfiguration( String uniqueID, String name ) { - super( name ); + super( uniqueID, name ); } @Override @@ -129,4 +129,16 @@ public class AirspyTunerConfiguration extends TunerConfiguration { mLNAAGC = enabled; } + + @XmlAttribute( name = "frequency_correction" ) + public double getFrequencyCorrection() + { + return mFrequencyCorrection; + } + + public void setFrequencyCorrection( double value ) + { + mFrequencyCorrection = value; + } + } diff --git a/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java b/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java deleted file mode 100644 index e2d31475..00000000 --- a/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java +++ /dev/null @@ -1,705 +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 - ******************************************************************************/ -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.ArrayList; -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 source.SourceException; -import source.tuner.TunerConfiguration; -import source.tuner.TunerConfigurationAssignment; -import source.tuner.TunerType; -import source.tuner.airspy.AirspyTunerController.Gain; -import source.tuner.airspy.AirspyTunerController.GainMode; -import controller.ResourceManager; - -public class AirspyTunerConfigurationPanel extends JPanel -{ - private final static Logger mLog = - LoggerFactory.getLogger( AirspyTunerConfigurationPanel.class ); - - private static final long serialVersionUID = 1L; - - private ResourceManager mResourceManager; - private AirspyTunerController mController; - private AirspyTunerConfiguration mSelectedConfig; - - private JButton mNewConfiguration; - private JButton mDeleteConfiguration; - private JComboBox mComboConfigurations; - - private JTextField mName; - - private JComboBox mSampleRateCombo; - - private JComboBox 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( ResourceManager resourceManager, - AirspyTunerController controller ) - { - mResourceManager = resourceManager; - 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(); - - 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 = mResourceManager - .getSettingsManager().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 ) - { - mResourceManager.getSettingsManager() - .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 rates = mController.getSampleRates(); - - mSampleRateCombo = new JComboBox( - new DefaultComboBoxModel( 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( 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 = - mResourceManager.getSettingsManager() - .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 ) - { - mResourceManager.getSettingsManager() - .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() ); - - mResourceManager.getSettingsManager().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 getModel() - { - ArrayList configs = - mResourceManager.getSettingsManager() - .getTunerConfigurations( TunerType.AIRSPY_R820T ); - - DefaultComboBoxModel model = - new DefaultComboBoxModel(); - - for( TunerConfiguration config: configs ) - { - model.addElement( (AirspyTunerConfiguration)config ); - } - - return model; - } - - /** - * Retrieves a specific (named) tuner configuration - */ - private AirspyTunerConfiguration getNamedConfiguration( String name ) - { - ArrayList configs = - mResourceManager.getSettingsManager() - .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() - { - mResourceManager.getSettingsManager().save(); - } -} diff --git a/src/source/tuner/airspy/AirspyTunerController.java b/src/source/tuner/airspy/AirspyTunerController.java index 947d1f40..aaf23295 100644 --- a/src/source/tuner/airspy/AirspyTunerController.java +++ b/src/source/tuner/airspy/AirspyTunerController.java @@ -6,7 +6,6 @@ import java.text.DecimalFormat; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -28,8 +27,8 @@ import sample.Broadcaster; import sample.Listener; import sample.complex.ComplexBuffer; import source.SourceException; -import source.tuner.TunerConfiguration; import source.tuner.TunerController; +import source.tuner.configuration.TunerConfiguration; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; import dsp.filter.dc.DCRemovalFilter_RB; @@ -262,30 +261,31 @@ public class AirspyTunerController extends TunerController } } - public static String getTransferStatus( int status ) - { - switch( status ) - { - case 0: - return "TRANSFER COMPLETED (0)"; - case 1: - return "TRANSFER ERROR (1)"; - case 2: - return "TRANSFER TIMED OUT (2)"; - case 3: - return "TRANSFER CANCELLED (3)"; - case 4: - return "TRANSFER STALL (4)"; - case 5: - return "TRANSFER NO DEVICE (5)"; - case 6: - return "TRANSFER OVERFLOW (6)"; - default: - return "UNKNOWN TRANSFER STATUS (" + status + ")"; - } - } + public static String getTransferStatus( int status ) + { + switch( status ) + { + case 0: + return "TRANSFER COMPLETED (0)"; + case 1: + return "TRANSFER ERROR (1)"; + case 2: + return "TRANSFER TIMED OUT (2)"; + case 3: + return "TRANSFER CANCELLED (3)"; + case 4: + return "TRANSFER STALL (4)"; + case 5: + return "TRANSFER NO DEVICE (5)"; + case 6: + return "TRANSFER OVERFLOW (6)"; + default: + return "UNKNOWN TRANSFER STATUS (" + status + ")"; + } + } - @Override + + @Override public void apply( TunerConfiguration config ) throws SourceException { if( config instanceof AirspyTunerConfiguration ) @@ -426,7 +426,7 @@ public class AirspyTunerController extends TunerController /** * Airspy sample rate object that matches the current sample rate setting. */ - public AirspySampleRate getSampleRate() + public AirspySampleRate getAirspySampleRate() { return getSampleRate( mSampleRate ); } @@ -1215,8 +1215,8 @@ public class AirspyTunerController extends TunerController */ public void addListener( Listener listener ) { - synchronized( mComplexBufferBroadcaster ) - { + synchronized ( mComplexBufferBroadcaster ) + { mComplexBufferBroadcaster.addListener( listener ); if( mBufferProcessor == null || !mBufferProcessor.isRunning() ) @@ -1229,7 +1229,7 @@ public class AirspyTunerController extends TunerController thread.start(); } - } + } } /** @@ -1238,15 +1238,15 @@ public class AirspyTunerController extends TunerController */ public void removeListener( Listener listener ) { - synchronized( mComplexBufferBroadcaster ) - { + synchronized ( mComplexBufferBroadcaster ) + { mComplexBufferBroadcaster.removeListener( listener ); if( !mComplexBufferBroadcaster.hasListeners() ) { mBufferProcessor.stop(); } - } + } } /** @@ -1273,31 +1273,31 @@ public class AirspyTunerController extends TunerController ThreadType.SOURCE_SAMPLE_PROCESSING, new BufferDispatcher(), 20, TimeUnit.MILLISECONDS ); - mLibUsbHandlerStatus = ByteBuffer.allocateDirect( 4 ); - + mLibUsbHandlerStatus = ByteBuffer.allocateDirect( 4 ); + List transfers = new ArrayList<>(); mCancel = false; - while( mRunning.get() ) + while( mRunning.get() ) { - mAvailableTransfers.drainTo( transfers ); - - for( Transfer transfer: transfers ) - { - int result = LibUsb.submitTransfer( transfer ); - - if( result == LibUsb.SUCCESS ) - { - mTransfersInProgress.add( transfer ); - } + mAvailableTransfers.drainTo( transfers ); + + for( Transfer transfer: transfers ) + { + int result = LibUsb.submitTransfer( transfer ); + + if( result == LibUsb.SUCCESS ) + { + mTransfersInProgress.add( transfer ); + } else { - mLog.error( "error submitting transfer [" + - LibUsb.errorName( result ) + "]" ); - } - } - + mLog.error( "error submitting transfer [" + + LibUsb.errorName( result ) + "]" ); + } + } + int result = LibUsb.handleEventsTimeoutCompleted( null, USB_TIMEOUT_MS, mLibUsbHandlerStatus.asIntBuffer() ); @@ -1310,20 +1310,20 @@ public class AirspyTunerController extends TunerController mLibUsbHandlerStatus.rewind(); } - - if( mCancel ) - { - for( Transfer transfer: mTransfersInProgress ) - { - LibUsb.cancelTransfer( transfer ); - } - - int result = LibUsb.handleEventsTimeoutCompleted( null, - USB_TIMEOUT_MS, mLibUsbHandlerStatus.asIntBuffer() ); + + if( mCancel ) + { + for( Transfer transfer: mTransfersInProgress ) + { + LibUsb.cancelTransfer( transfer ); + } + + int result = LibUsb.handleEventsTimeoutCompleted( null, + USB_TIMEOUT_MS, mLibUsbHandlerStatus.asIntBuffer() ); if( result != LibUsb.SUCCESS ) { - mLog.error( "error handling events for libusb" ); + mLog.error( "error handling events for libusb during cancel" ); } mLibUsbHandlerStatus.rewind(); diff --git a/src/source/tuner/airspy/AirspyTunerEditor.java b/src/source/tuner/airspy/AirspyTunerEditor.java new file mode 100644 index 00000000..10e1e567 --- /dev/null +++ b/src/source/tuner/airspy/AirspyTunerEditor.java @@ -0,0 +1,770 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ +package source.tuner.airspy; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.text.DecimalFormat; +import java.util.List; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +import javax.swing.JSlider; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.usb.UsbException; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.usb4java.LibUsbException; + +import source.SourceException; +import source.tuner.airspy.AirspyTunerController.Gain; +import source.tuner.airspy.AirspyTunerController.GainMode; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationEditor; +import source.tuner.configuration.TunerConfigurationEvent; +import source.tuner.configuration.TunerConfigurationModel; +import source.tuner.configuration.TunerConfigurationEvent.Event; + +public class AirspyTunerEditor extends TunerConfigurationEditor +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = + LoggerFactory.getLogger( AirspyTunerEditor.class ); + + private JTextField mConfigurationName; + private JButton mTunerInfo; + + private JComboBox mSampleRateCombo; + private JSpinner mFrequencyCorrection; + + private JComboBox mGainModeCombo; + + private JSlider mMasterGain; + private JLabel mMasterGainValueLabel; + + private JSlider mIFGain; + private JLabel mIFGainValueLabel; + + private JSlider mLNAGain; + private JLabel mLNAGainValueLabel; + + private JSlider mMixerGain; + private JLabel mMixerGainValueLabel; + + private JCheckBox mLNAAGC; + private JCheckBox mMixerAGC; + + private AirspyTunerController mController; + private boolean mLoading; + + public AirspyTunerEditor( TunerConfigurationModel tunerConfigurationModel, + AirspyTuner tuner ) + { + super( tunerConfigurationModel ); + mController = tuner.getController(); + + init(); + } + + private AirspyTunerConfiguration getConfiguration() + { + if( hasItem() ) + { + return (AirspyTunerConfiguration)getItem(); + } + + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][grow]" ) ); + + add( new JLabel( "Airspy Tuner Configuration" ), "span,align center" ); + + mConfigurationName = new JTextField(); + mConfigurationName.setEnabled( false ); + mConfigurationName.addFocusListener( new FocusListener() + { + @Override + public void focusLost( FocusEvent e ) + { + save(); + } + @Override + public void focusGained( FocusEvent e ) {} + } ); + + add( new JLabel( "Name:" ) ); + add( mConfigurationName, "span 2" ); + + mTunerInfo = new JButton( "Tuner Info" ); + mTunerInfo.setEnabled( false ); + mTunerInfo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + JOptionPane.showMessageDialog( AirspyTunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( mTunerInfo ); + + /** + * Sample Rate + */ + add( new JLabel( "Sample Rate:" ) ); + + List rates = mController.getSampleRates(); + + mSampleRateCombo = new JComboBox( + new DefaultComboBoxModel( 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( GainMode.values() ); + mGainModeCombo.setEnabled( false ); + mGainModeCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + GainMode mode = (GainMode)mGainModeCombo.getSelectedItem(); + + updateGainComponents( mode ); + + if( hasItem() ) + { + AirspyTunerConfiguration airspy = getConfiguration(); + + Gain gain = airspy.getGain(); + + if( mode == GainMode.CUSTOM ) + { + mIFGain.setValue( airspy.getIFGain() ); + mMixerGain.setValue( airspy.getMixerGain() ); + mMixerAGC.setSelected( airspy.isMixerAGC() ); + mLNAGain.setValue( airspy.getLNAGain() ); + mLNAAGC.setSelected( airspy.isLNAAGC() ); + } + else + { + mMixerAGC.setSelected( false ); + mLNAAGC.setSelected( false ); + mMasterGain.setValue( gain.getValue() ); + mIFGain.setValue( gain.getIF() ); + mMixerGain.setValue( gain.getMixer() ); + mLNAGain.setValue( gain.getLNA() ); + } + } + + save(); + } + } ); + + add( mGainModeCombo, "wrap" ); + + /** + * Gain + */ + add( new JLabel( "Master:" ) ); + + mMasterGain = new JSlider( JSlider.HORIZONTAL, + AirspyTunerController.GAIN_MIN, + AirspyTunerController.GAIN_MAX, + AirspyTunerController.GAIN_MIN ); + mMasterGain.setMajorTickSpacing( 1 ); + mMasterGain.setPaintTicks( true ); + + mMasterGain.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent event ) + { + GainMode mode = (GainMode)mGainModeCombo.getSelectedItem(); + int value = mMasterGain.getValue(); + Gain gain = Gain.getGain( mode, value ); + + try + { + mController.setGain( gain ); + save(); + mMasterGainValueLabel.setText( String.valueOf( gain.getValue() ) ); + } + catch ( Exception e ) + { + mLog.error( "Couldn't set airspy gain to:" + gain.name(), e ); + JOptionPane.showMessageDialog( mMasterGain, "Couldn't set gain value to " + + gain.getValue() ); + } + } + } ); + + add( mMasterGain, "span 2" ); + + mMasterGainValueLabel = new JLabel( "0" ); + add( mMasterGainValueLabel ); + + add( new JLabel( "IF:" ) ); + + mIFGain = new JSlider( JSlider.HORIZONTAL, + AirspyTunerController.IF_GAIN_MIN, + AirspyTunerController.IF_GAIN_MAX, + AirspyTunerController.IF_GAIN_MIN ); + + mIFGain.setMajorTickSpacing( 1 ); + mIFGain.setPaintTicks( true ); + + mIFGain.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent event ) + { + int gain = mIFGain.getValue(); + + try + { + mController.setIFGain( gain ); + save(); + mIFGainValueLabel.setText( String.valueOf( gain ) ); + } + catch ( Exception e ) + { + mLog.error( "Couldn't set airspy IF gain to:" + gain, e ); + + JOptionPane.showMessageDialog( mIFGain, "Couldn't set IF gain value to " + gain ); + } + } + } ); + + add( mIFGain, "span 2" ); + + mIFGainValueLabel = new JLabel( "0" ); + add( mIFGainValueLabel ); + + /** + * Mixer Gain + */ + add( new JLabel( "Mixer:" ) ); + + mMixerGain = new JSlider( JSlider.HORIZONTAL, + AirspyTunerController.MIXER_GAIN_MIN, + AirspyTunerController.MIXER_GAIN_MAX, + AirspyTunerController.MIXER_GAIN_MIN ); + + mMixerGain.setMajorTickSpacing( 1 ); + mMixerGain.setPaintTicks( true ); + + mMixerGain.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent event ) + { + int gain = mMixerGain.getValue(); + + try + { + mController.setMixerGain( gain ); + save(); + mMixerGainValueLabel.setText( String.valueOf( gain ) ); + } + catch ( Exception e ) + { + mLog.error( "Couldn't set airspy Mixer gain to:" + gain, e ); + JOptionPane.showMessageDialog( mIFGain, "Couldn't set Mixer gain value to " + gain ); + } + } + } ); + + add( mMixerGain, "span 2" ); + + mMixerGainValueLabel = new JLabel( "0" ); + add( mMixerGainValueLabel ); + + add( new JLabel( "LNA:" ) ); + + mLNAGain = new JSlider( JSlider.HORIZONTAL, + AirspyTunerController.LNA_GAIN_MIN, + AirspyTunerController.LNA_GAIN_MAX, + AirspyTunerController.LNA_GAIN_MIN ); + + mLNAGain.setMajorTickSpacing( 1 ); + mLNAGain.setPaintTicks( true ); + + mLNAGain.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent event ) + { + int gain = mLNAGain.getValue(); + + try + { + mController.setLNAGain( gain ); + save(); + mLNAGainValueLabel.setText( String.valueOf( gain ) ); + } + catch ( Exception e ) + { + mLog.error( "Couldn't set airspy LNA gain to:" + gain, e ); + JOptionPane.showMessageDialog( mIFGain, "Couldn't set LNA gain value to " + gain ); + } + } + } ); + + add( mLNAGain, "span 2" ); + + mLNAGainValueLabel = new JLabel( "0" ); + add( mLNAGainValueLabel ); + + mMixerAGC = new JCheckBox( "Mixer AGC" ); + mMixerAGC.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + try + { + mController.setMixerAGC( mMixerAGC.isSelected() ); + mMixerGain.setEnabled( !mMixerAGC.isSelected() ); + + save(); + } + catch ( Exception e1 ) + { + mLog.error( "Error setting Mixer AGC Enabled" ); + } + } + } ); + + add( mMixerAGC, "span 2,center" ); + + mLNAAGC = new JCheckBox( "LNA AGC" ); + mLNAAGC.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + try + { + mController.setLNAAGC( mLNAAGC.isSelected() ); + mLNAGain.setEnabled( !mLNAAGC.isSelected() ); + + save(); + } + catch ( Exception e1 ) + { + mLog.error( "Error setting LNA AGC Enabled" ); + } + + } + } ); + + add( mLNAAGC, "span 2,center" ); + } + + /** + * Updates the enabled state of each of the gain controls according to the + * specified gain mode. The master gain control is enabled for linearity + * and sensitivity and the individual gain controls are disabled, and + * vice-versa for custom mode. + */ + private void updateGainComponents( GainMode mode ) + { + switch( mode ) + { + case LINEARITY: + if( !mMasterGain.isEnabled() ) + { + mMasterGain.setEnabled( true ); + mMasterGainValueLabel.setEnabled( true ); + } + if( mIFGain.isEnabled() ) + { + mIFGain.setEnabled( false ); + mIFGainValueLabel.setEnabled( false ); + } + if( mLNAGain.isEnabled() ) + { + mLNAAGC.setEnabled( false ); + mLNAGainValueLabel.setEnabled( false ); + mLNAGain.setEnabled( false ); + } + if( mMixerGain.isEnabled() ) + { + mMixerGainValueLabel.setEnabled( false ); + mMixerAGC.setEnabled( false ); + mMixerGain.setEnabled( false ); + } + break; + case SENSITIVITY: + if( !mMasterGain.isEnabled() ) + { + mMasterGain.setEnabled( true ); + mMasterGainValueLabel.setEnabled( true ); + } + if( mIFGain.isEnabled() ) + { + mIFGain.setEnabled( false ); + mIFGainValueLabel.setEnabled( false ); + } + if( mLNAGain.isEnabled() ) + { + mLNAAGC.setEnabled( false ); + mLNAGainValueLabel.setEnabled( false ); + mLNAGain.setEnabled( false ); + } + if( mMixerGain.isEnabled() ) + { + mMixerGainValueLabel.setEnabled( false ); + mMixerAGC.setEnabled( false ); + mMixerGain.setEnabled( false ); + } + break; + case CUSTOM: + if( mMasterGain.isEnabled() ) + { + mMasterGain.setEnabled( false ); + mMasterGainValueLabel.setEnabled( false ); + } + if( !mIFGain.isEnabled() ) + { + mIFGain.setEnabled( true ); + mIFGainValueLabel.setEnabled( true ); + } + if( !mLNAGain.isEnabled() ) + { + mLNAAGC.setEnabled( true ); + mLNAGainValueLabel.setEnabled( true ); + mLNAGain.setEnabled( true ); + } + if( !mMixerGain.isEnabled() ) + { + mMixerGainValueLabel.setEnabled( true ); + mMixerAGC.setEnabled( true ); + mMixerGain.setEnabled( true ); + } + default: + break; + } + } + + @Override + public void save() + { + if( hasItem() && !mLoading ) + { + AirspyTunerConfiguration config = getConfiguration(); + config.setName( mConfigurationName.getText() ); + config.setSampleRate( ((AirspySampleRate)mSampleRateCombo.getSelectedItem()).getRate() ); + + double value = ((SpinnerNumberModel)mFrequencyCorrection.getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + + Gain gain = Gain.getGain( (GainMode)mGainModeCombo.getSelectedItem(), mMasterGain.getValue() ); + + config.setGain( gain ); + config.setIFGain( mIFGain.getValue() ); + config.setMixerGain( mMixerGain.getValue() ); + config.setLNAGain( mLNAGain.getValue() ); + config.setMixerAGC( mMixerAGC.isSelected() ); + config.setLNAAGC( mLNAAGC.isSelected() ); + + getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( config, Event.CHANGE ) ); + } + } + + private AirspySampleRate fromValue( int value ) + { + List rates = mController.getSampleRates(); + + for( AirspySampleRate rate: rates ) + { + if( rate.getRate() == value ) + { + return rate; + } + } + + if( rates.size() > 0 ) + { + return rates.get( 0 ); + } + + return null; + } + + /** + * Sets all controls to the argument enabled state + */ + private void setControlsEnabled( boolean enabled ) + { + if( mConfigurationName.isEnabled() != enabled ) + { + mConfigurationName.setEnabled( enabled ); + } + + if( mTunerInfo.isEnabled() != enabled ) + { + mTunerInfo.setEnabled( enabled ); + } + + if( mSampleRateCombo.isEnabled() != enabled ) + { + mSampleRateCombo.setEnabled( enabled ); + } + + if( mFrequencyCorrection.isEnabled() != enabled ) + { + mFrequencyCorrection.setEnabled( enabled ); + } + + if( mGainModeCombo.isEnabled() != enabled ) + { + mGainModeCombo.setEnabled( enabled ); + } + + if( mMasterGain.isEnabled() != enabled ) + { + mMasterGain.setEnabled( enabled ); + } + + if( mIFGain.isEnabled() != enabled ) + { + mIFGain.setEnabled( enabled ); + } + + if( mMixerGain.isEnabled() != enabled ) + { + mMixerGain.setEnabled( enabled ); + } + + if( mMixerAGC.isEnabled() != enabled ) + { + mMixerAGC.setEnabled( enabled ); + } + + if( mLNAGain.isEnabled() != enabled ) + { + mLNAGain.setEnabled( enabled ); + } + + if( mLNAAGC.isEnabled() != enabled ) + { + mLNAAGC.setEnabled( enabled ); + } + } + + /** + * Applies the tuner configuration to the editor. If the configuration is assigned to the + * currently selected tuner, then the settings are applied. + */ + @Override + public void setItem( TunerConfiguration config ) + { + super.setItem( config ); + + mLoading = true; + + if( hasItem() && getItem().isAssigned() ) + { + AirspyTunerConfiguration airspy = getConfiguration(); + + setControlsEnabled( true ); + + mConfigurationName.setText( airspy.getName() ); + + AirspySampleRate rate = fromValue( airspy.getSampleRate() ); + + if( rate != null ) + { + mSampleRateCombo.setSelectedItem( rate ); + } + + mFrequencyCorrection.setValue( airspy.getFrequencyCorrection() ); + + GainMode mode = Gain.getGainMode( airspy.getGain() ); + + //This will set the master, IF, Mixer, LNA and AGC controls + mGainModeCombo.setSelectedItem( mode ); + + if( mode == GainMode.CUSTOM ) + { + mMasterGain.setValue( airspy.getGain().getValue() ); + mIFGain.setValue( airspy.getIFGain() ); + + if( airspy.isMixerAGC() ) + { + mMixerAGC.setSelected( airspy.isMixerAGC() ); + mMixerGain.setEnabled( false ); + } + else + { + mMixerGain.setValue( airspy.getMixerGain() ); + } + + if( airspy.isLNAAGC() ) + { + mLNAAGC.setSelected( airspy.isLNAAGC() ); + mLNAGain.setEnabled( false ); + } + else + { + mLNAGain.setValue( airspy.getLNAGain() ); + } + } + else + { + mMixerAGC.setSelected( false ); + mLNAAGC.setSelected( false ); + + Gain gain = airspy.getGain(); + + mMasterGain.setValue( gain.getValue() ); + mIFGain.setValue( gain.getIF() ); + mMixerGain.setValue( gain.getMixer() ); + mLNAGain.setValue( gain.getLNA() ); + } + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( hasItem() ? getItem().getName() : "" ); + } + + mLoading = false; + } + + private String getTunerInfo() + { + StringBuilder sb = new StringBuilder(); + + AirspyDeviceInformation info = mController.getDeviceInfo(); + + sb.append( "

    Airspy Tuner

    " ); + sb.append( "Serial: " ); + sb.append( info.getSerialNumber() ); + sb.append( "
    " ); + + sb.append( "Firmware: " ); + String[] firmware = info.getVersion().split( " " ); + sb.append( firmware.length > 1 ? firmware[ 0 ] : info.getVersion() ); + sb.append( "
    " ); + + sb.append( "Part: " ); + sb.append( info.getPartNumber() ); + sb.append( "
    " ); + + sb.append( "Board ID: " ); + sb.append( info.getBoardID().getLabel() ); + sb.append( "
    " ); + + return sb.toString(); + } +} diff --git a/src/source/tuner/airspy/AirspyTunerEditorPanel.java b/src/source/tuner/airspy/AirspyTunerEditorPanel.java deleted file mode 100644 index d15037f7..00000000 --- a/src/source/tuner/airspy/AirspyTunerEditorPanel.java +++ /dev/null @@ -1,113 +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 - ******************************************************************************/ -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 source.SourceException; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeEvent.Event; -import source.tuner.frequency.FrequencyChangeListener; - -import com.jidesoft.swing.JideTabbedPane; - -import controller.ResourceManager; - -public class AirspyTunerEditorPanel extends JPanel - implements FrequencyChangeListener -{ - 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 ResourceManager mResourceManager; - - public AirspyTunerEditorPanel( AirspyTuner tuner, - ResourceManager resourceManager ) - { - mTuner = tuner; - mController = mTuner.getController(); - mResourceManager = resourceManager; - - 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.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( - mResourceManager, mTuner.getController() ) ); - - tabs.add( "Info", new AirspyInformationPanel( mTuner.getController() ) ); - } - - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - if( event.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) - { - try - { - mController.setFrequency( event.getValue().longValue() ); - } - catch ( SourceException e ) - { - mLog.error( "error setting frequency [" + - event.getValue().longValue() + "]", e ); - } - } - } -} diff --git a/src/source/tuner/TunerConfiguration.java b/src/source/tuner/configuration/TunerConfiguration.java similarity index 75% rename from src/source/tuner/TunerConfiguration.java rename to src/source/tuner/configuration/TunerConfiguration.java index d82977da..040b829f 100644 --- a/src/source/tuner/TunerConfiguration.java +++ b/src/source/tuner/configuration/TunerConfiguration.java @@ -15,11 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package source.tuner; +package source.tuner.configuration; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlType; +import source.tuner.TunerType; + /** * Abstract class to hold a named configuration for a specific type of tuner */ @@ -27,13 +29,23 @@ import javax.xml.bind.annotation.XmlType; public abstract class TunerConfiguration { protected String mName; + protected String mUniqueID; + protected boolean mAssigned; /** * Default constructor to support JAXB */ public TunerConfiguration() { - this( "Unknown" ); + } + + /** + * Normal constructor + */ + public TunerConfiguration( String uniqueID, String name ) + { + mUniqueID = uniqueID; + mName = name; } public String toString() @@ -41,14 +53,6 @@ public abstract class TunerConfiguration return mName; } - /** - * Normal constructor - */ - public TunerConfiguration( String name ) - { - mName = name; - } - @XmlAttribute( name = "name" ) public String getName() { @@ -59,6 +63,28 @@ public abstract class TunerConfiguration { mName = name; } + + @XmlAttribute( name = "unique_id" ) + public String getUniqueID() + { + return mUniqueID; + } + + public void setUniqueID( String id ) + { + mUniqueID = id;; + } + + @XmlAttribute( name = "assigned" ) + public boolean isAssigned() + { + return mAssigned; + } + + public void setAssigned( boolean assigned ) + { + mAssigned = assigned; + } @XmlAttribute( name = "tuner_type" ) public abstract TunerType getTunerType(); diff --git a/src/source/tuner/configuration/TunerConfigurationEditor.java b/src/source/tuner/configuration/TunerConfigurationEditor.java new file mode 100644 index 00000000..1c02f4b0 --- /dev/null +++ b/src/source/tuner/configuration/TunerConfigurationEditor.java @@ -0,0 +1,20 @@ +package source.tuner.configuration; + +import gui.editor.Editor; + +public abstract class TunerConfigurationEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + private TunerConfigurationModel mTunerConfigurationModel; + + public TunerConfigurationEditor( TunerConfigurationModel model ) + { + mTunerConfigurationModel = model; + } + + public TunerConfigurationModel getTunerConfigurationModel() + { + return mTunerConfigurationModel; + } +} diff --git a/src/source/tuner/configuration/TunerConfigurationEvent.java b/src/source/tuner/configuration/TunerConfigurationEvent.java new file mode 100644 index 00000000..02a0769d --- /dev/null +++ b/src/source/tuner/configuration/TunerConfigurationEvent.java @@ -0,0 +1,30 @@ +package source.tuner.configuration; + +public class TunerConfigurationEvent +{ + private TunerConfiguration mTunerConfiguration; + private Event mEvent; + + public TunerConfigurationEvent( TunerConfiguration configuration, Event event ) + { + mTunerConfiguration = configuration; + mEvent = event; + } + + public TunerConfiguration getConfiguration() + { + return mTunerConfiguration; + } + + public Event getEvent() + { + return mEvent; + } + + public enum Event + { + ADD, + REMOVE, + CHANGE; + } +} diff --git a/src/source/tuner/configuration/TunerConfigurationFactory.java b/src/source/tuner/configuration/TunerConfigurationFactory.java new file mode 100644 index 00000000..181440b4 --- /dev/null +++ b/src/source/tuner/configuration/TunerConfigurationFactory.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ + +package source.tuner.configuration; + +import gui.editor.Editor; +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.FCDTuner; +import source.tuner.fcd.proV1.FCD1TunerConfiguration; +import source.tuner.fcd.proV1.FCD1TunerEditor; +import source.tuner.fcd.proplusV2.FCD2TunerConfiguration; +import source.tuner.fcd.proplusV2.FCD2TunerEditor; +import source.tuner.hackrf.HackRFTuner; +import source.tuner.hackrf.HackRFTunerConfiguration; +import source.tuner.hackrf.HackRFTunerEditor; +import source.tuner.rtl.RTL2832Tuner; +import source.tuner.rtl.e4k.E4KTunerConfiguration; +import source.tuner.rtl.e4k.E4KTunerEditor; +import source.tuner.rtl.r820t.R820TTunerConfiguration; +import source.tuner.rtl.r820t.R820TTunerEditor; + +public class TunerConfigurationFactory +{ + /** + * Creates a tuner configuration for the specified tuner type, unique ID and name + */ + public static TunerConfiguration getTunerConfiguration( TunerType type, String uniqueID, + String name ) + { + switch( type ) + { + case AIRSPY_R820T: + return new AirspyTunerConfiguration( uniqueID, name ); + case ELONICS_E4000: + return new E4KTunerConfiguration( uniqueID, name ); + case FUNCUBE_DONGLE_PRO: + return new FCD1TunerConfiguration( uniqueID, name ); + case FUNCUBE_DONGLE_PRO_PLUS: + return new FCD2TunerConfiguration( uniqueID, name ); + case HACKRF: + return new HackRFTunerConfiguration( uniqueID, name ); + case RAFAELMICRO_R820T: + return new R820TTunerConfiguration( uniqueID, name ); + default: + throw new IllegalArgumentException( "Unrecognized tuner type [" + + type.name() + "] - can't create named [" + name + "] tuner" + + " configuration" ); + } + } + + /** + * Creates a tuner editor gui for the specified tuner + */ + public static Editor getEditor( Tuner tuner, TunerConfigurationModel model ) + { + switch( tuner.getTunerType() ) + { + case AIRSPY_R820T: + return new AirspyTunerEditor( model, (AirspyTuner)tuner ); + case ELONICS_E4000: + return new E4KTunerEditor( model, (RTL2832Tuner)tuner ); + case FUNCUBE_DONGLE_PRO: + return new FCD1TunerEditor( model, (FCDTuner)tuner ); + case FUNCUBE_DONGLE_PRO_PLUS: + return new FCD2TunerEditor( model, (FCDTuner)tuner ); + case HACKRF: + return new HackRFTunerEditor( model, (HackRFTuner)tuner ); + case RAFAELMICRO_R820T: + return new R820TTunerEditor( model, (RTL2832Tuner)tuner ); + case UNKNOWN: + default: + throw new IllegalArgumentException( "Unrecognized Tuner: " + tuner.getName() ); + } + } +} diff --git a/src/source/tuner/configuration/TunerConfigurationModel.java b/src/source/tuner/configuration/TunerConfigurationModel.java new file mode 100644 index 00000000..717e1790 --- /dev/null +++ b/src/source/tuner/configuration/TunerConfigurationModel.java @@ -0,0 +1,371 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ + +package source.tuner.configuration; + +import java.util.ArrayList; +import java.util.List; + +import javax.swing.table.AbstractTableModel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import source.tuner.TunerType; +import source.tuner.configuration.TunerConfigurationEvent.Event; + +public class TunerConfigurationModel extends AbstractTableModel +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = + LoggerFactory.getLogger( TunerConfigurationModel.class ); + + public static final int TUNER_TYPE = 0; + public static final int CONFIGURATION_NAME = 1; + public static final int ASSIGNED = 2; + private static final String[] COLUMN_HEADERS = { "Tuner Type", "Name", "Assigned" }; + + private List> mConfigurationChangeListeners = + new ArrayList<>(); + private List mTunerConfigurations = new ArrayList<>(); + + /** + * Table model for managing tuner configurations. Provides tuner configuration events to + * registered listeners + */ + public TunerConfigurationModel() + { + } + + /** + * Adds the list of tuner configurations to the model + */ + public void addTunerConfigurations( List configs ) + { + for( TunerConfiguration config: configs ) + { + addTunerConfiguration( config ); + } + } + + /** + * Returns of list of tuner configurations from the model + */ + public List getTunerConfigurations() + { + return mTunerConfigurations; + } + + /** + * Adds the tuner configuration to the model firing a table model row add + * event and broadcasting a tuner configuration event. + */ + public void addTunerConfiguration( TunerConfiguration config ) + { + if( !mTunerConfigurations.contains( config ) && + config.getUniqueID() != null && + config.getName() != null ) + { + mTunerConfigurations.add( config ); + + int index = mTunerConfigurations.indexOf( config ); + + fireTableRowsInserted( index, index ); + + broadcast( new TunerConfigurationEvent( config, Event.ADD ) ); + } + } + + /** + * Removes the tuner configuration from the model firing a table model row + * delete event and broadcasting a tuner configuration event. + */ + public void removeTunerConfiguration( TunerConfiguration config ) + { + if( mTunerConfigurations.contains( config ) ) + { + int index = mTunerConfigurations.indexOf( config ); + + mTunerConfigurations.remove( config ); + + fireTableRowsDeleted( index, index ); + + broadcast( new TunerConfigurationEvent( config, Event.REMOVE ) ); + } + } + + /** + * Removes all tuner configurations and assignments from the model and + * fires a table clear event and broadcasts a tuner configuration event for + * each row removed. + */ + public void clear() + { + for( TunerConfiguration config: mTunerConfigurations ) + { + broadcast( new TunerConfigurationEvent( config, Event.REMOVE ) ); + } + + int size = mTunerConfigurations.size(); + + mTunerConfigurations.clear(); + + fireTableRowsDeleted( 0, size - 1 ); + } + + /** + * Registers a tuner configuration event listener + */ + public void addListener( Listener listener ) + { + mConfigurationChangeListeners.add( listener ); + } + + /** + * Removes the tuner configuration event listener + */ + public void removeListener( Listener listener ) + { + mConfigurationChangeListeners.remove( listener ); + } + + /** + * Broadcasts a tuner configuration event + */ + public void broadcast( TunerConfigurationEvent event ) + { + for( Listener listener: mConfigurationChangeListeners ) + { + listener.receive( event ); + } + } + + /** + * Designates the configuration argument as the currently assigned configuration and unassigns + * any other configurations with the same tuner type and unique ID. + */ + public void assignTunerConfiguration( TunerConfiguration configToAssign ) + { + if( configToAssign != null ) + { + List configs = + getTunerConfigurations( configToAssign.getTunerType(), configToAssign.getUniqueID() ); + + for( TunerConfiguration config: configs ) + { + if( config.isAssigned() ) + { + config.setAssigned( false ); + + int index = mTunerConfigurations.indexOf( config ); + fireTableCellUpdated( index, ASSIGNED ); + broadcast( new TunerConfigurationEvent( config, Event.CHANGE ) ); + } + } + + configToAssign.setAssigned( true ); + int index = mTunerConfigurations.indexOf( configToAssign ); + fireTableCellUpdated( index, ASSIGNED ); + broadcast( new TunerConfigurationEvent( configToAssign, Event.CHANGE ) ); + } + } + + /** + * Returns the tuner configuration that is assigned to the tuner with the + * tuner type and uniqueID, or selects the first existing configuration that + * matches the tuner type and unique ID and assigns it, or creates a new + * tuner configuration and assigns it to the tuner. + * + * This method will always return a tuner configuration that is assigned to + * the tuner type and unique ID. + */ + public TunerConfiguration getTunerConfiguration( TunerType type, String uniqueID ) + { + List configs = getTunerConfigurations( type, uniqueID ); + + for( TunerConfiguration config: configs ) + { + if( config.isAssigned() && + config.getUniqueID() != null && + config.getUniqueID().contentEquals( uniqueID ) ) + { + return config; + } + } + + //Reuse an existing configuration to assign + if( !configs.isEmpty() ) + { + configs.get( 0 ).setAssigned( true ); + return configs.get( 0 ); + } + + String name = getDistinctName( type, uniqueID ); + + //We didn't find the config so create a new one and assign it + TunerConfiguration config = TunerConfigurationFactory + .getTunerConfiguration( type, uniqueID, name ); + + config.setAssigned( true ); + + addTunerConfiguration( config ); + + return config; + } + + /** + * Indicates if a configuration exists in this model matching the tuner type + * and unique ID and name. + */ + public boolean hasTunerConfiguration( TunerType type, String uniqueID, String name ) + { + for( TunerConfiguration config: mTunerConfigurations ) + { + if( config.getTunerType() == type && + config.getUniqueID() != null && + config.getUniqueID().contentEquals( uniqueID ) && + config.getName() != null && + config.getName().contentEquals( name ) ) + { + return true; + } + } + + return false; + } + + /** + * Creates a configuration name for the tuner configuration that is distinct + * among the existing tuner configurations of the same type and uniqueID + */ + public String getDistinctName( TunerType type, String uniqueID ) + { + int counter = 0; + + String name = "New"; + + while( hasTunerConfiguration( type, uniqueID, name ) ) + { + counter++; + + name = "New (" + counter + ")"; + } + + return name; + } + + /** + * Returns all tuner configurations for the specified tuner type + */ + public List getTunerConfigurations( TunerType type, String uniqueID ) + { + List configs = new ArrayList<>(); + + for( TunerConfiguration config: mTunerConfigurations ) + { + if( config.getTunerType() == type && + config.getUniqueID() != null && + config.getUniqueID().contentEquals( uniqueID ) ) + { + configs.add( config ); + } + } + + return configs; + } + + /** + * Column names for the table model + */ + @Override + public String getColumnName( int column ) + { + return COLUMN_HEADERS[ column ]; + } + + /** + * Tuner configuration model row count + */ + @Override + public int getRowCount() + { + return mTunerConfigurations.size(); + } + + /** + * Tuner configuration model column count + */ + @Override + public int getColumnCount() + { + return COLUMN_HEADERS.length; + } + + /** + * Returns the row index for the specified configuration or -1 + */ + public int getRowIndex( TunerConfiguration configuration ) + { + if( mTunerConfigurations.contains( configuration ) ) + { + return mTunerConfigurations.indexOf( configuration ); + } + + return -1; + } + + /** + * Returns the configuration at the specified index or null + */ + public TunerConfiguration getTunerConfiguration( int index ) + { + if( index >= 0 && index < mTunerConfigurations.size() ) + { + return mTunerConfigurations.get( index ); + } + + return null; + } + + /** + * Returns the value at the indicated table cell + */ + @Override + public Object getValueAt( int row, int column ) + { + if( row >= 0 && row < mTunerConfigurations.size() ) + { + TunerConfiguration config = mTunerConfigurations.get( row ); + + switch( column ) + { + case TUNER_TYPE: + return config.getTunerType().getLabel(); + case CONFIGURATION_NAME: + return config.getName(); + case ASSIGNED: + return config.isAssigned() ? "*" : null; + default: + break; + } + } + + return null; + } +} diff --git a/src/source/tuner/ettus/B100Tuner.java b/src/source/tuner/ettus/B100Tuner.java index c8cddf02..be5271f8 100644 --- a/src/source/tuner/ettus/B100Tuner.java +++ b/src/source/tuner/ettus/B100Tuner.java @@ -19,36 +19,22 @@ package source.tuner.ettus; import java.util.concurrent.RejectedExecutionException; -import javax.swing.JPanel; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - 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.usb.USBTunerDevice; -import controller.ResourceManager; -import controller.ThreadPoolManager; public class B100Tuner extends Tuner { - private final static Logger mLog = LoggerFactory.getLogger( B100Tuner.class ); - - private USBTunerDevice mDevice; - public B100Tuner( USBTunerDevice device ) { //TODO: this should extend a USBTuner class, so that we can push the //complex sample listener and the frequency change listener up to that class - super( "Ettus B100 with WBX Tuner" ); - - mDevice = device; + super( "Ettus B100 with WBX Tuner", null ); } public void dispose() @@ -68,36 +54,12 @@ public class B100Tuner extends Tuner return TunerType.ETTUS_WBX; } - @Override - public JPanel getEditor( ResourceManager resourceManager ) - { - return new B100TunerEditorPanel( this, resourceManager ); - } - - @Override - public void apply( TunerConfiguration config ) throws SourceException - { - mLog.error( "B100 Tuner - can't apply tuner configuration" ); - } - - @Override - public int getSampleRate() - { - return 0; - } - @Override public double getSampleSize() { return 16.0; } - @Override - public long getFrequency() throws SourceException - { - return 0; - } - @Override public TunerChannelSource getChannel( TunerChannel channel ) throws RejectedExecutionException, SourceException @@ -105,7 +67,7 @@ public class B100Tuner extends Tuner //TODO: pass this call to the tuner controller return null; } - + @Override public void releaseChannel( TunerChannelSource source ) { diff --git a/src/source/tuner/ettus/B100TunerEditorPanel.java b/src/source/tuner/ettus/B100TunerEditorPanel.java index 964ac4ee..ed30ed68 100644 --- a/src/source/tuner/ettus/B100TunerEditorPanel.java +++ b/src/source/tuner/ettus/B100TunerEditorPanel.java @@ -22,20 +22,14 @@ import javax.swing.JLabel; import javax.swing.JPanel; import net.miginfocom.swing.MigLayout; -import controller.ResourceManager; +import settings.SettingsManager; public class B100TunerEditorPanel extends JPanel { private static final long serialVersionUID = 1L; - private B100Tuner mTuner; - private ResourceManager mResourceManager; - - public B100TunerEditorPanel( B100Tuner tuner, ResourceManager resourceManager ) + public B100TunerEditorPanel( B100Tuner tuner, SettingsManager settingsManager ) { - mTuner = tuner; - mResourceManager = resourceManager; - init(); } diff --git a/src/source/tuner/fcd/FCDTuner.java b/src/source/tuner/fcd/FCDTuner.java index 6c6324f4..9c32abfd 100644 --- a/src/source/tuner/fcd/FCDTuner.java +++ b/src/source/tuner/fcd/FCDTuner.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,8 +19,6 @@ package source.tuner.fcd; import java.util.concurrent.RejectedExecutionException; -import javax.swing.JPanel; - import sample.Listener; import sample.adapter.ShortAdapter; import sample.complex.ComplexBuffer; @@ -30,75 +28,50 @@ import source.tuner.MixerTunerDataLine; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; -import source.tuner.TunerConfiguration; +import source.tuner.TunerEvent; import source.tuner.TunerType; -import source.tuner.frequency.FrequencyChangeListener; -import controller.ResourceManager; -import controller.ThreadPoolManager; public class FCDTuner extends MixerTuner { - private FCDTunerController mController; - public FCDTuner( MixerTunerDataLine mixerTDL, FCDTunerController controller ) { - super( controller.getConfiguration().toString(), + super( controller.getConfiguration().toString(), + controller, mixerTDL, new ShortAdapter() ); - - mController = controller; - mController.addListener( (FrequencyChangeListener)this ); } public void dispose() { //TODO: release the mixer tuner data line as well - mController.dispose(); + getController().dispose(); } public FCDTunerController getController() { - return mController; + return (FCDTunerController)getTunerController(); } @Override public TunerClass getTunerClass() { - return mController.getTunerClass(); + return getController().getTunerClass(); } @Override public TunerType getTunerType() { - return mController.getTunerType(); + return getController().getTunerType(); } @Override public String getUniqueID() { - return mController.getUSBAddress(); + return getController().getUSBAddress(); } - @Override - public JPanel getEditor( ResourceManager resourceManager ) - { - return mController.getEditor( this, resourceManager ); - } - - @Override - public void apply( TunerConfiguration config )throws SourceException - { - mController.apply( config ); - } - - @Override - public int getSampleRate() - { - return (int)mMixerTunerType.getAudioFormat().getSampleRate(); - } - @Override public double getSampleSize() { @@ -106,16 +79,17 @@ public class FCDTuner extends MixerTuner } @Override - public long getFrequency() throws SourceException - { - return mController.getFrequency(); - } - - @Override - public TunerChannelSource getChannel( TunerChannel tunerChannel ) + public TunerChannelSource getChannel( TunerChannel tunerChannel ) throws RejectedExecutionException, SourceException { - return mController.getChannel( this, tunerChannel ); + TunerChannelSource source = getController().getChannel( this, tunerChannel ); + + if( source != null ) + { + broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) ); + } + + return source; } /** @@ -132,7 +106,7 @@ public class FCDTuner extends MixerTuner /* Tell the controller to release the channel and cleanup */ /* This will release the channel as a frequency change listener */ - mController.releaseChannel( source ); + getController().releaseChannel( source ); } } } diff --git a/src/source/tuner/fcd/FCDTunerController.java b/src/source/tuner/fcd/FCDTunerController.java index 8e6ff030..0f2d56fc 100644 --- a/src/source/tuner/fcd/FCDTunerController.java +++ b/src/source/tuner/fcd/FCDTunerController.java @@ -21,7 +21,6 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.IntBuffer; -import javax.swing.JPanel; import javax.usb.UsbClaimException; import javax.usb.UsbException; @@ -35,11 +34,10 @@ import org.usb4java.LibUsbException; import source.SourceException; import source.tuner.TunerClass; -import source.tuner.TunerConfiguration; import source.tuner.TunerController; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.fcd.proV1.FCD1TunerController.Block; -import controller.ResourceManager; public abstract class FCDTunerController extends TunerController { @@ -70,6 +68,7 @@ public abstract class FCDTunerController extends TunerController */ public FCDTunerController( Device device, DeviceDescriptor descriptor, + int sampleRate, int minTunableFrequency, int maxTunableFrequency ) { @@ -77,7 +76,7 @@ public abstract class FCDTunerController extends TunerController maxTunableFrequency, DC_SPIKE_AVOID_BUFFER, USABLE_BANDWIDTH_PERCENT ); - + mFrequencyController.setSampleRate( sampleRate ); mDevice = device; mDeviceDescriptor = descriptor; } @@ -146,23 +145,10 @@ public abstract class FCDTunerController extends TunerController */ public abstract TunerType getTunerType(); - /** - * Editor panel (GUI) component. - */ - public abstract JPanel getEditor( FCDTuner tuner, - ResourceManager resourceManager ); - /** * Applies the settings in the tuner configuration */ - public abstract void apply( TunerConfiguration config ) - throws SourceException; - - /** - * Returns the currently applied tuner configuration or null if one hasn't - * yet been applied - */ - public abstract TunerConfiguration getTunerConfiguration(); + public abstract void apply( TunerConfiguration config ) throws SourceException; /** * USB address (bus/port) diff --git a/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java b/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java index 29a7b0f4..b045893f 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java @@ -19,8 +19,8 @@ package source.tuner.fcd.proV1; import javax.xml.bind.annotation.XmlAttribute; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.fcd.proV1.FCD1TunerController.LNAEnhance; import source.tuner.fcd.proV1.FCD1TunerController.LNAGain; import source.tuner.fcd.proV1.FCD1TunerController.MixerGain; @@ -42,12 +42,11 @@ public class FCD1TunerConfiguration extends TunerConfiguration */ public FCD1TunerConfiguration() { - this( "Default" ); } - public FCD1TunerConfiguration( String name ) + public FCD1TunerConfiguration( String uniqueID, String name ) { - super( name ); + super( uniqueID, name ); } @Override @@ -56,6 +55,7 @@ public class FCD1TunerConfiguration extends TunerConfiguration return TunerType.FUNCUBE_DONGLE_PRO; } + @XmlAttribute( name = "lna_gain" ) public LNAGain getLNAGain() { return mLNAGain; @@ -66,12 +66,12 @@ public class FCD1TunerConfiguration extends TunerConfiguration mLNAGain = gain; } - @XmlAttribute( name = "lna_gain" ) public LNAEnhance getLNAEnhance() { return mLNAEnhance; } + @XmlAttribute( name = "lna_enhance" ) public void setLNAEnhance( LNAEnhance enhance ) { mLNAEnhance = enhance; diff --git a/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java b/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java deleted file mode 100644 index bfbe7fbb..00000000 --- a/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java +++ /dev/null @@ -1,663 +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 - ******************************************************************************/ -package source.tuner.fcd.proV1; - -import java.awt.Color; -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.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.UsbClaimException; -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.TunerType; -import source.tuner.fcd.proV1.FCD1TunerController.LNAEnhance; -import source.tuner.fcd.proV1.FCD1TunerController.LNAGain; -import source.tuner.fcd.proV1.FCD1TunerController.MixerGain; - -import com.jidesoft.swing.JideTabbedPane; - -import controller.ResourceManager; - -public class FCD1TunerConfigurationPanel extends JPanel -{ - private final static Logger mLog = - LoggerFactory.getLogger( FCD1TunerConfigurationPanel.class ); - - private static final long serialVersionUID = 1L; - private ResourceManager mResourceManager; - private FCD1TunerController mController; - - private FCD1TunerConfiguration mSelectedConfig; - private JButton mNewConfiguration; - private JButton mDeleteConfiguration; - private JComboBox mComboConfigurations; - private JComboBox mComboLNAGain; - private JComboBox mComboLNAEnhance; - private JComboBox mComboMixerGain; - private JTextField mName; - private JSpinner mCorrectionFrequency; - private CorrectionSpinner mCorrectionDCI; - private CorrectionSpinner mCorrectionDCQ; - private CorrectionSpinner mCorrectionGain; - private CorrectionSpinner mCorrectionPhase; - - public FCD1TunerConfigurationPanel( ResourceManager resourceManager, - FCD1TunerController controller ) - { - mResourceManager = resourceManager; - mController = controller; - mSelectedConfig = controller.getTunerConfiguration(); - - init(); - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][grow]" ) ); - - /** - * Tuner configurations combo - */ - mComboConfigurations = new JComboBox(); - mComboConfigurations.setToolTipText( "Select a tuner configuration. " - + "Create a separate tuner configuration for each of your " - + "funcube dongles by using the NEW button and typing a " - + "descriptive name" ); - - mComboConfigurations.setModel( getModel() ); - - mComboConfigurations.setSelectedItem( mSelectedConfig ); - - mComboConfigurations.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - FCD1TunerConfiguration selected = - (FCD1TunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - update( selected ); - } - } - }); - - add( new JLabel( "Config:" ) ); - add( mComboConfigurations, "growx,push" ); - - /** - * Tuner Configuration - Name - */ - mName = new JTextField( mSelectedConfig.getName() ); - mName.setToolTipText( "Name for the selected tuner configuration" ); - - 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" ); - - JideTabbedPane tabs = new JideTabbedPane(); - tabs.setFont( this.getFont() ); - tabs.setForeground( Color.BLACK ); - add( tabs, "span,grow,push" ); - - JPanel gainPanel = new JPanel(); - gainPanel.setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); - - /** - * LNA Gain - */ - mComboLNAGain = new JComboBox( LNAGain.values() ); - - mComboLNAGain.setToolTipText( "Adjust the low noise amplifier gain " - + "setting. Default value is 20db" ); - - mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); - - mComboLNAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - LNAGain gain = (LNAGain)mComboLNAGain.getSelectedItem(); - - mSelectedConfig.setLNAGain( gain ); - - try - { - mController.setLNAGain( gain ); - } - catch ( UsbClaimException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - cannot claim FCD " - + "Controller to apply LNA gain [" + gain.toString() + "]" ); - - mLog.error( "FuncubeDonglePro Controller - usb claim " - + "exception while applying LNA gain value [" + - gain.toString() + "]", e1 ); - } - catch ( UsbException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - FCD Controller " - + "cannot apply LNA gain [" + gain.toString() + "]" ); - - mLog.error( "FuncubeDonglePro Controller - " - + "exception while applying LNA gain value [" + - gain.toString() + "]", e1 ); - } - - save(); - } - - } ); - - gainPanel.add( new JLabel( "LNA:" ) ); - gainPanel.add( mComboLNAGain, "growx,push" ); - - /** - * LNA Enhance - */ - mComboLNAEnhance = new JComboBox( LNAEnhance.values() ); - mComboLNAEnhance.setToolTipText( "Adjust the LNA enhance setting. " - + "Default value is OFF" ); - - mComboLNAEnhance.setSelectedItem( mSelectedConfig.getLNAEnhance() ); - - mComboLNAEnhance.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - LNAEnhance enhance = (LNAEnhance)mComboLNAEnhance.getSelectedItem(); - - mSelectedConfig.setLNAEnhance( enhance ); - - try - { - mController.setLNAEnhance( enhance ); - } - catch ( UsbClaimException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - cannot claim FCD " - + "Controller to apply LNA enhance [" + enhance.toString() + "]" ); - - mLog.error( "FuncubeDonglePro Controller - usb claim " - + "exception while applying LNA enhance value [" + - enhance.toString() + "]", e1 ); - } - catch ( UsbException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - FCD Controller " - + "cannot apply LNA enhance [" + enhance.toString() + "]" ); - - mLog.error( "FuncubeDonglePro Controller - " - + "exception while applying LNA enhance value [" + - enhance.toString() + "]", e1 ); - } - - save(); - } - - } ); - - gainPanel.add( new JLabel( "LNA Enhance:" ) ); - gainPanel.add( mComboLNAEnhance, "growx,push" ); - - /** - * Mixer Gain - */ - mComboMixerGain = new JComboBox( MixerGain.values() ); - mComboMixerGain.setToolTipText( "Adjust mixer gain setting" ); - - mComboMixerGain.setSelectedItem( mSelectedConfig.getMixerGain() ); - - mComboMixerGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - MixerGain gain = (MixerGain)mComboMixerGain.getSelectedItem(); - - mSelectedConfig.setMixerGain( gain ); - - try - { - mController.setMixerGain( gain ); - } - catch ( UsbClaimException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - cannot claim FCD " - + "Controller to apply Mixer gain [" + gain.toString() + "]" ); - - mLog.error( "FuncubeDonglePro Controller - usb claim " - + "exception while applying Mixer gain value [" + - gain.toString() + "]", e1 ); - } - catch ( UsbException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - FCD Controller " - + "cannot apply Mixer gain [" + gain.toString() + "]" ); - - mLog.error( "FuncubeDonglePro Controller - " - + "exception while applying Mixer gain value [" + - gain.toString() + "]", e1 ); - } - - save(); - } - - } ); - - gainPanel.add( new JLabel( "Mixer:" ) ); - gainPanel.add( mComboMixerGain, "growx,push" ); - - tabs.add( "Gain", gainPanel ); - - JPanel correctionPanel = new JPanel(); - correctionPanel.setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][grow]" ) ); - - /** - * Frequency Correction - */ - SpinnerModel model = - new SpinnerNumberModel( 0.0, -1000.0, 1000.0, 0.1 ); //initial,min,max,step - - mCorrectionFrequency = new JSpinner( model ); - - mCorrectionFrequency.setToolTipText( "Frequency Correction: adjust " - + "value +/- to align the displayed frequency label with the " - + "actual signals" ); - - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mCorrectionFrequency.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - mCorrectionFrequency.setValue( mSelectedConfig.getFrequencyCorrection() ); - - mCorrectionFrequency.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - double value = ((SpinnerNumberModel)mCorrectionFrequency - .getModel()).getNumber().doubleValue(); - - try - { - mSelectedConfig.setFrequencyCorrection( value ); - mController.setFrequencyCorrection( value ); - save(); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - couldn't " - + "apply frequency correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "FuncubeDonglePro Controller - couldn't apply " - + "frequency correction value: " + value, e1 ); - } - } - } ); - - correctionPanel.add( new JLabel( "Frequency PPM:" ) ); - correctionPanel.add( mCorrectionFrequency, "growx,push" ); - - /** - * Inphase DC Correction - */ - mCorrectionDCI = new CorrectionSpinner( Correction.DC_INPHASE, - mSelectedConfig.getInphaseDCCorrection(), 0.00001, 5 ); - mCorrectionDCI.setToolTipText( "DC Bias Correction/Inphase " - + "Component: valid values are -1.0 to 1.0 (default: 0.0)" ); - correctionPanel.add( new JLabel( "DC Inphase:" ) ); - correctionPanel.add( mCorrectionDCI, "growx,push" ); - - /** - * Quadrature DC Correction - */ - mCorrectionDCQ = new CorrectionSpinner( Correction.DC_QUADRATURE, - mSelectedConfig.getQuadratureDCCorrection(), 0.00001, 5 ); - mCorrectionDCQ.setToolTipText( "DC Bias Correction/Quadrature " - + "Component: valid values are -1.0 to 1.0 (default: 0.0)" ); - correctionPanel.add( new JLabel( "DC Quadrature:" ) ); - correctionPanel.add( mCorrectionDCQ, "growx,push" ); - - /** - * Gain Correction - */ - mCorrectionGain = new CorrectionSpinner( Correction.GAIN, - mSelectedConfig.getGainCorrection(), 0.00001, 5 ); - mCorrectionGain.setToolTipText( "Gain Correction: valid values are " - + "-1.0 to 1.0 (default: 0.0)" ); - correctionPanel.add( new JLabel( "Gain:" ) ); - correctionPanel.add( mCorrectionGain, "growx,push" ); - - /** - * Phase Correction - */ - mCorrectionPhase = new CorrectionSpinner( Correction.PHASE, - mSelectedConfig.getPhaseCorrection(), 0.00001, 5 ); - mCorrectionPhase.setToolTipText( "Phase Correction: valid values are " - + "-1.0 to 1.0 (default: 0.0)" ); - correctionPanel.add( new JLabel( "Phase:" ) ); - correctionPanel.add( mCorrectionPhase, "growx,push" ); - - tabs.add( "Correction", correctionPanel ); - - /** - * Create a new configuration - */ - mNewConfiguration = new JButton( "New" ); - mNewConfiguration.setToolTipText( "Create a new tuner configuration " - + "for each of your dongles to keep track of the specific " - + "settings for each dongle" ); - - mNewConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - TunerConfiguration config = - mResourceManager.getSettingsManager() - .addNewTunerConfiguration( - TunerType.FUNCUBE_DONGLE_PRO, - "New Configuration" ); - - mComboConfigurations.setModel( getModel() ); - - mComboConfigurations.setSelectedItem( config ); - - repaint(); - } - } ); - - add( mNewConfiguration, "growx,push" ); - - /** - * Delete the currently selected configuration - */ - mDeleteConfiguration = new JButton( "Delete" ); - mDeleteConfiguration.setToolTipText( "Deletes the currently selected " - + "tuner configuration. Note: the DEFAULT tuner " - + "configuration will be recreated if you delete it, " - + "after you restart the application" ); - - mDeleteConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - FCD1TunerConfiguration selected = - (FCD1TunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - int n = JOptionPane.showConfirmDialog( - FCD1TunerConfigurationPanel.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" ); - } - - private void update( FCD1TunerConfiguration config ) - { - mSelectedConfig = config; - - try - { - mController.apply( config ); - - mName.setText( config.getName() ); - - mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); - mComboLNAEnhance.setSelectedItem( mSelectedConfig.getLNAEnhance() ); - mComboMixerGain.setSelectedItem( mSelectedConfig.getMixerGain() ); - - mCorrectionFrequency.setValue( config.getFrequencyCorrection() ); - mCorrectionDCI.setValue( config.getInphaseDCCorrection() ); - mCorrectionDCQ.setValue( config.getQuadratureDCCorrection() ); - mCorrectionGain.setValue( config.getGainCorrection() ); - mCorrectionPhase.setValue( config.getPhaseCorrection() ); - - mResourceManager.getSettingsManager() - .setSelectedTunerConfiguration( - TunerType.FUNCUBE_DONGLE_PRO, - mController.getUSBAddress(), config ); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - couldn't " - + "apply the tuner configuration settings - " + - e1.getLocalizedMessage() ); - - mLog.error( "FuncubeDonglePro Controller - couldn't apply " - + "config [" + config.getName() + "]", e1 ); - } - } - - private ComboBoxModel getModel() - { - ArrayList configs = - mResourceManager.getSettingsManager() - .getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO ); - - DefaultComboBoxModel model = - new DefaultComboBoxModel(); - - for( TunerConfiguration config: configs ) - { - model.addElement( (FCD1TunerConfiguration)config ); - } - - return model; - } - - @SuppressWarnings( "unused" ) - private FCD1TunerConfiguration getNamedConfiguration( String name ) - { - ArrayList configs = - mResourceManager.getSettingsManager() - .getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO ); - - for( TunerConfiguration config: configs ) - { - if( config.getName().contentEquals( name ) ) - { - return (FCD1TunerConfiguration)config; - } - } - - return null; - } - - private void save() - { - mResourceManager.getSettingsManager().save(); - } - - public enum Correction { GAIN, PHASE, DC_INPHASE, DC_QUADRATURE }; - - public class CorrectionSpinner extends JSpinner - { - private static final long serialVersionUID = 1L; - private static final double sMIN_VALUE = -1.0d; - private static final double sMAX_VALUE = 1.0d; - - private Correction mCorrectionComponent; - - public CorrectionSpinner( Correction component, - double initialValue, - double step, - int decimalPlaces ) - { - mCorrectionComponent = component; - - SpinnerModel model = new SpinnerNumberModel( initialValue, - sMIN_VALUE, sMAX_VALUE, step ); - - setModel( model ); - - JSpinner.NumberEditor editor = (JSpinner.NumberEditor)getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( decimalPlaces ); - - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - double value = - ((SpinnerNumberModel)getModel()).getNumber().doubleValue(); - - try - { - switch( mCorrectionComponent ) - { - case DC_INPHASE: - mSelectedConfig.setInphaseDCCorrection( value ); - mController.setDCCorrectionInPhase( value ); - break; - case DC_QUADRATURE: - mSelectedConfig.setQuadratureDCCorrection( value ); - mController.setDCCorrectionQuadrature( value ); - break; - case GAIN: - mSelectedConfig.setGainCorrection( value ); - mController.setGainCorrection( value ); - break; - case PHASE: - mSelectedConfig.setPhaseCorrection( value ); - mController.setPhaseCorrection( value ); - break; - } - - //Save the change(s) to the selected tuner config - save(); - } - catch ( UsbClaimException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - cannot claim FCD " - + "Controller to apply " - + mCorrectionComponent.toString() + - " correction value [" + value + "]" ); - - mLog.error( "FuncubeDonglePro Controller - usb claim " - + "exception while applying " - + mCorrectionComponent.toString() - + " correction value [" + value + "]", e1 ); - } - catch ( UsbException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - USB error from FCD " - + "Controller while applying " - + mCorrectionComponent.toString() + - " correction value [" + value + "]" ); - - mLog.error( "FuncubeDonglePro Controller - usb " - + "exception while applying " - + mCorrectionComponent.toString() - + " correction value [" + value + "]", e1 ); - } - } - } ); - } - } -} diff --git a/src/source/tuner/fcd/proV1/FCD1TunerController.java b/src/source/tuner/fcd/proV1/FCD1TunerController.java index 9d56b0f4..b897922a 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerController.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerController.java @@ -20,7 +20,6 @@ package source.tuner.fcd.proV1; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import javax.swing.JPanel; import javax.usb.UsbClaimException; import javax.usb.UsbException; @@ -30,13 +29,12 @@ import org.usb4java.Device; import org.usb4java.DeviceDescriptor; import source.SourceException; +import source.tuner.MixerTunerType; import source.tuner.TunerClass; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.fcd.FCDCommand; -import source.tuner.fcd.FCDTuner; import source.tuner.fcd.FCDTunerController; -import controller.ResourceManager; public class FCD1TunerController extends FCDTunerController { @@ -55,12 +53,13 @@ public class FCD1TunerController extends FCDTunerController private LNAEnhance mLNAEnhance; private MixerGain mMixerGain; private FCD1TunerConfiguration mTunerConfiguration; - private FCD1TunerEditorPanel mEditor; + private FCD1TunerEditor mEditor; public FCD1TunerController( Device device, DeviceDescriptor descriptor ) { super( device, - descriptor, + descriptor, + (int)MixerTunerType.FUNCUBE_DONGLE_PRO.getAudioFormat().getSampleRate(), sMINIMUM_TUNABLE_FREQUENCY, sMAXIMUM_TUNABLE_FREQUENCY ); } @@ -147,17 +146,6 @@ public class FCD1TunerController extends FCDTunerController } } - @Override - public JPanel getEditor( FCDTuner tuner, ResourceManager resourceManager ) - { - if( mEditor == null ) - { - mEditor = new FCD1TunerEditorPanel( tuner, resourceManager ); - } - - return mEditor; - } - /** * Gets the current LNA Gain value from the controller and stores it */ diff --git a/src/source/tuner/fcd/proV1/FCD1TunerEditor.java b/src/source/tuner/fcd/proV1/FCD1TunerEditor.java new file mode 100644 index 00000000..5c8f834b --- /dev/null +++ b/src/source/tuner/fcd/proV1/FCD1TunerEditor.java @@ -0,0 +1,528 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package source.tuner.fcd.proV1; + +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 javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +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 net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import source.SourceException; +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.fcd.FCDTuner; +import source.tuner.fcd.proV1.FCD1TunerController.LNAEnhance; +import source.tuner.fcd.proV1.FCD1TunerController.LNAGain; +import source.tuner.fcd.proV1.FCD1TunerController.MixerGain; + +public class FCD1TunerEditor extends TunerConfigurationEditor +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( FCD1TunerEditor.class ); + + private JTextField mConfigurationName; + private JButton mTunerInfo; + private JSpinner mFrequencyCorrection; + private JComboBox mComboLNAGain; + private JComboBox mComboLNAEnhance; + private JComboBox mComboMixerGain; + private CorrectionSpinner mCorrectionDCI; + private CorrectionSpinner mCorrectionDCQ; + private CorrectionSpinner mCorrectionGain; + private CorrectionSpinner mCorrectionPhase; + private boolean mLoading; + + private FCD1TunerController mController; + + public FCD1TunerEditor( TunerConfigurationModel tunerConfigurationModel, FCDTuner tuner ) + { + super( tunerConfigurationModel ); + mController = (FCD1TunerController)tuner.getController(); + + init(); + } + + private FCD1TunerConfiguration getConfiguration() + { + if( hasItem() ) + { + return (FCD1TunerConfiguration)getItem(); + } + + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][][][][][][][grow]" ) ); + + add( new JLabel( "FCD Pro Tuner Configuration" ), "span,align center" ); + + mConfigurationName = new JTextField(); + mConfigurationName.setEnabled( false ); + mConfigurationName.addFocusListener( new FocusListener() + { + @Override + public void focusLost( FocusEvent e ) + { + save(); + } + @Override + public void focusGained( FocusEvent e ) {} + } ); + + add( new JLabel( "Name:" ) ); + add( mConfigurationName, "span 2" ); + + mTunerInfo = new JButton( "Tuner Info" ); + mTunerInfo.setEnabled( false ); + mTunerInfo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( mTunerInfo ); + + 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 ) + { + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + + try + { + mController.setFrequencyCorrection( value ); + save(); + } + catch ( SourceException e1 ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCD Pro Tuner " + + "Controller - couldn't apply frequency correction value: " + value + + e1.getLocalizedMessage() ); + + mLog.error( "FuncubeDonglePro Controller - couldn't apply " + + "frequency correction value: " + value, e1 ); + } + } + } ); + + add( new JLabel( "PPM:" ) ); + add( mFrequencyCorrection, "wrap" ); + + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); + add( new JLabel( "Gain" ), "span,align center" ); + + mComboLNAGain = new JComboBox( LNAGain.values() ); + mComboLNAGain.setEnabled( false ); + mComboLNAGain.setToolTipText( "Adjust the low noise amplifier gain setting." ); + mComboLNAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + LNAGain gain = (LNAGain)mComboLNAGain.getSelectedItem(); + + + try + { + mController.setLNAGain( gain ); + save(); + } + catch ( Exception e ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCD Pro Tuner Controller" + + " - error setting LNA gain [" + gain.toString() + "]" ); + + mLog.error( "FuncubeDonglePro Controller - error setting gain [" + gain.toString() + "]", e ); + } + } + } ); + + add( new JLabel( "LNA:" ) ); + add( mComboLNAGain ); + + /** + * LNA Enhance + */ + mComboLNAEnhance = new JComboBox( LNAEnhance.values() ); + mComboLNAEnhance.setToolTipText( "Adjust the LNA enhance setting. Default value is OFF" ); + + mComboLNAEnhance.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + LNAEnhance enhance = (LNAEnhance)mComboLNAEnhance.getSelectedItem(); + + try + { + mController.setLNAEnhance( enhance ); + save(); + } + catch ( Exception e1 ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCD Pro Tuner" + + " error setting LNA enhance gain [" + enhance.toString() + "]" ); + + mLog.error( "FCDPro - error setting LNA enhance [" + enhance.toString() + "]", e1 ); + } + } + } ); + + add( new JLabel( "Enhance:" ) ); + add( mComboLNAEnhance ); + + /** + * Mixer Gain + */ + mComboMixerGain = new JComboBox( MixerGain.values() ); + mComboMixerGain.setToolTipText( "Adjust mixer gain setting" ); + + + mComboMixerGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + MixerGain gain = (MixerGain)mComboMixerGain.getSelectedItem(); + + try + { + mController.setMixerGain( gain ); + save(); + } + catch ( Exception e1 ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCDPro - error setting" + + " mixer gain [" + gain.toString() + "]" ); + + mLog.error( "FCDPro - error setting mixer gain [" + gain.toString() + "]", e1 ); + } + } + } ); + + add( new JLabel( "Mixer:" ) ); + add( mComboMixerGain, "wrap" ); + + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); + add( new JLabel( "Correction" ), "span,align center" ); + + /** + * Inphase DC Correction + */ + mCorrectionDCI = new CorrectionSpinner( Correction.DC_INPHASE, 0.0, 0.00001, 5 ); + mCorrectionDCI.setEnabled( false ); + mCorrectionDCI.setToolTipText( "DC Bias Correction/Inphase " + + "Component: valid values are -1.0 to 1.0 (default: 0.0)" ); + add( new JLabel( "DC Inphase:" ) ); + add( mCorrectionDCI ); + + /** + * Quadrature DC Correction + */ + mCorrectionDCQ = new CorrectionSpinner( Correction.DC_QUADRATURE, 0.0, 0.00001, 5 ); + mCorrectionDCQ.setToolTipText( "DC Bias Correction/Quadrature " + + "Component: valid values are -1.0 to 1.0 (default: 0.0)" ); + add( new JLabel( "DC Quadrature:" ) ); + add( mCorrectionDCQ ); + + /** + * Gain Correction + */ + mCorrectionGain = new CorrectionSpinner( Correction.GAIN, 0.0, 0.00001, 5 ); + mCorrectionGain.setToolTipText( "Gain Correction: valid values are " + + "-1.0 to 1.0 (default: 0.0)" ); + add( new JLabel( "Gain:" ) ); + add( mCorrectionGain ); + + /** + * Phase Correction + */ + mCorrectionPhase = new CorrectionSpinner( Correction.PHASE, 0.0, 0.00001, 5 ); + mCorrectionPhase.setToolTipText( "Phase Correction: valid values are " + + "-1.0 to 1.0 (default: 0.0)" ); + add( new JLabel( "Phase:" ) ); + add( mCorrectionPhase ); + } + + /** + * Sets each of the tuner configuration controls to the enabled argument state + */ + private void setControlsEnabled( boolean enabled ) + { + if( mConfigurationName.isEnabled() != enabled ) + { + mConfigurationName.setEnabled( enabled ); + } + + if( mTunerInfo.isEnabled() != enabled ) + { + mTunerInfo.setEnabled( enabled ); + } + + if( mFrequencyCorrection.isEnabled() != enabled ) + { + mFrequencyCorrection.setEnabled( enabled ); + } + + if( mComboLNAEnhance.isEnabled() != enabled ) + { + mComboLNAEnhance.setEnabled( enabled ); + } + + if( mComboLNAGain.isEnabled() != enabled ) + { + mComboLNAGain.setEnabled( enabled ); + } + + if( mComboMixerGain.isEnabled() != enabled ) + { + mComboMixerGain.setEnabled( enabled ); + } + + if( mCorrectionDCI.isEnabled() != enabled ) + { + mCorrectionDCI.setEnabled( enabled ); + } + + if( mCorrectionDCQ.isEnabled() != enabled ) + { + mCorrectionDCQ.setEnabled( enabled ); + } + + if( mCorrectionGain.isEnabled() != enabled ) + { + mCorrectionGain.setEnabled( enabled ); + } + + if( mCorrectionPhase.isEnabled() != enabled ) + { + mCorrectionPhase.setEnabled( enabled ); + } + } + + private String getTunerInfo() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "

    Funcube Dongle Pro Tuner

    " ); + + sb.append( "USB ID: " ); + sb.append( mController.getUSBID() ); + sb.append( "
    " ); + + sb.append( "USB Address: " ); + sb.append( mController.getUSBAddress() ); + sb.append( "
    " ); + + sb.append( "USB Speed: " ); + sb.append( mController.getUSBSpeed() ); + sb.append( "
    " ); + + sb.append( "Cellular Band: " ); + sb.append( mController.getConfiguration().getBandBlocking() ); + sb.append( "
    " ); + + sb.append( "Firmware: " ); + sb.append( mController.getConfiguration().getFirmware() ); + sb.append( "
    " ); + + return sb.toString(); + } + + @Override + public void save() + { + if( hasItem() && !mLoading ) + { + FCD1TunerConfiguration config = getConfiguration(); + + config.setName( mConfigurationName.getText() ); + + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + config.setLNAEnhance( (LNAEnhance)mComboLNAEnhance.getSelectedItem() ); + config.setLNAGain( (LNAGain)mComboLNAGain.getSelectedItem() ); + config.setMixerGain( (MixerGain)mComboMixerGain.getSelectedItem() ); + + double dci = ((SpinnerNumberModel)mCorrectionDCI.getModel()).getNumber().doubleValue(); + config.setInphaseDCCorrection( dci ); + + double dcq = ((SpinnerNumberModel)mCorrectionDCQ.getModel()).getNumber().doubleValue(); + config.setQuadratureDCCorrection( dcq ); + + double gain = ((SpinnerNumberModel)mCorrectionGain.getModel()).getNumber().doubleValue(); + config.setGainCorrection( gain ); + + double phase = ((SpinnerNumberModel)mCorrectionPhase.getModel()).getNumber().doubleValue(); + config.setPhaseCorrection( phase ); + + getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); + } + } + + @Override + public void setItem( TunerConfiguration tunerConfiguration ) + { + super.setItem( tunerConfiguration ); + + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + + if( hasItem() ) + { + FCD1TunerConfiguration config = getConfiguration(); + + if( tunerConfiguration.isAssigned() ) + { + setControlsEnabled( true ); + mConfigurationName.setText( config.getName() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mComboLNAEnhance.setSelectedItem( config.getLNAEnhance() ); + mComboLNAGain.setSelectedItem( config.getLNAGain() ); + mComboMixerGain.setSelectedItem( config.getMixerGain() ); + mCorrectionDCI.setValue( config.getInphaseDCCorrection() ); + mCorrectionDCQ.setValue( config.getQuadratureDCCorrection() ); + mCorrectionGain.setValue( config.getGainCorrection() ); + mCorrectionPhase.setValue( config.getPhaseCorrection() ); + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( config.getName() ); + } + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( "" ); + } + + mLoading = false; + } + + public enum Correction { GAIN, PHASE, DC_INPHASE, DC_QUADRATURE }; + + public class CorrectionSpinner extends JSpinner + { + private static final long serialVersionUID = 1L; + private static final double MIN_VALUE = -1.0d; + private static final double MAX_VALUE = 1.0d; + + private Correction mCorrectionComponent; + + public CorrectionSpinner( Correction component, double initialValue, double step, int decimalPlaces ) + { + mCorrectionComponent = component; + + SpinnerModel model = new SpinnerNumberModel( initialValue, MIN_VALUE, MAX_VALUE, step ); + setModel( model ); + + JSpinner.NumberEditor editor = (JSpinner.NumberEditor)getEditor(); + + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( decimalPlaces ); + + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + + addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + double value = + ((SpinnerNumberModel)getModel()).getNumber().doubleValue(); + + try + { + switch( mCorrectionComponent ) + { + case DC_INPHASE: + mController.setDCCorrectionInPhase( value ); + break; + case DC_QUADRATURE: + mController.setDCCorrectionQuadrature( value ); + break; + case GAIN: + mController.setGainCorrection( value ); + break; + case PHASE: + mController.setPhaseCorrection( value ); + break; + } + + save(); + } + catch ( Exception e1 ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCDPro - error " + + "applying " + mCorrectionComponent.toString() + " correction value [" + + value + "]" ); + + mLog.error( "FCDPro - error applying "+ mCorrectionComponent.toString() + + " correction value [" + value + "]", e1 ); + } + } + } ); + } + } +} diff --git a/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java b/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java deleted file mode 100644 index 317fcaeb..00000000 --- a/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java +++ /dev/null @@ -1,111 +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 - ******************************************************************************/ -package source.tuner.fcd.proV1; - -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 source.SourceException; -import source.tuner.fcd.FCDTuner; -import source.tuner.fcd.FCDTunerDetailsPanel; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; -import source.tuner.frequency.FrequencyChangeEvent.Event; - -import com.jidesoft.swing.JideTabbedPane; - -import controller.ResourceManager; - -public class FCD1TunerEditorPanel extends JPanel implements FrequencyChangeListener -{ - private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( FCD1TunerEditorPanel.class ); - - private FCDTuner mTuner; - private FCD1TunerController mController; - private ResourceManager mResourceManager; - - private JFrequencyControl mFrequencyControl; - - public FCD1TunerEditorPanel( FCDTuner tuner, ResourceManager resourceManager ) - { - mTuner = tuner; - mController = (FCD1TunerController)mTuner.getController(); - mResourceManager = resourceManager; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[grow,right][grow]", "[][][grow]" ) ); - - add( new JLabel( "Funcube Dongle Pro Tuner" ), "span, align center" ); - - mFrequencyControl = new JFrequencyControl(); - - mFrequencyControl.addListener( this ); - - /* Add frequency control as listener to tuner to get frequency changes - * that are invoked elsewhere, to keep the control in sync with the - * frequency */ - 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 ); - - tabs.add( "Configuration", new FCD1TunerConfigurationPanel( - mResourceManager, mController ) ); - - tabs.add( "Info", new FCDTunerDetailsPanel( mController ) ); - - add( tabs, "span,push,grow" ); - } - - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - if( event.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) - { - try - { - mController.setFrequency( event.getValue().longValue() ); - } - catch ( SourceException e ) - { - mLog.error( "FCTProController - error setting frequency [" + - event.getValue().longValue() + "]", e ); - } - } - } -} diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java b/src/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java index 06a4de4d..0f001b13 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java @@ -19,8 +19,8 @@ package source.tuner.fcd.proplusV2; import javax.xml.bind.annotation.XmlAttribute; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; public class FCD2TunerConfiguration extends TunerConfiguration { @@ -33,12 +33,11 @@ public class FCD2TunerConfiguration extends TunerConfiguration */ public FCD2TunerConfiguration() { - this( "Default" ); } - public FCD2TunerConfiguration( String name ) + public FCD2TunerConfiguration( String uniqueID, String name ) { - super( name ); + super( uniqueID, name ); } @Override diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java b/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java deleted file mode 100644 index bb6acbea..00000000 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java +++ /dev/null @@ -1,380 +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 - ******************************************************************************/ -package source.tuner.fcd.proplusV2; - -import java.awt.ComponentOrientation; -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.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.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 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 controller.ResourceManager; - -public class FCD2TunerConfigurationPanel extends JPanel -{ - private final static Logger mLog = - LoggerFactory.getLogger( FCD2TunerConfigurationPanel.class ); - - private static final long serialVersionUID = 1L; - private ResourceManager mResourceManager; - private FCD2TunerController mController; - private FCD2TunerConfiguration mSelectedConfig; - private JButton mNewConfiguration; - private JButton mDeleteConfiguration; - private JComboBox mComboConfigurations; - private JTextField mName; - private JCheckBox mLNAGain; - private JCheckBox mMixerGain; - private JSpinner mFrequencyCorrection; - - public FCD2TunerConfigurationPanel( ResourceManager resourceManager, - FCD2TunerController controller ) - { - mResourceManager = resourceManager; - mController = controller; - - init(); - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][grow]" ) ); - - mComboConfigurations = new JComboBox(); - - mComboConfigurations.setModel( getModel() ); - - mComboConfigurations.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - FCD2TunerConfiguration selected = - (FCD2TunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - update( selected ); - } - } - }); - - add( new JLabel( "Config:" ) ); - add( mComboConfigurations, "growx,push" ); - - mName = new JTextField(); - - 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.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try - { - mController.setFrequencyCorrection( value ); - mSelectedConfig.setFrequencyCorrection( value ); - save(); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - FCD2TunerConfigurationPanel.this, - "FCD Pro Plus Tuner Controller - couldn't " - + "apply frequency correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "FuncubeDongleProPlus Controller - couldn't apply " - + "frequency correction value: " + value, e1 ); - } - } - } ); - - add( new JLabel( "Correction PPM:" ) ); - add( mFrequencyCorrection, "growx,push" ); - - /** - * LNA Gain - */ - mLNAGain = new JCheckBox( "LNA Gain:" ); - mLNAGain.setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT ); - - mLNAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - mSelectedConfig.setGainLNA( mLNAGain.isSelected() ); - - save(); - - update( mSelectedConfig ); - } - } ); - - add( mLNAGain ); - - /** - * Mixer Gain - */ - mMixerGain = new JCheckBox( "Mixer Gain:" ); - mMixerGain.setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT ); - - mMixerGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - mSelectedConfig.setGainMixer( mMixerGain.isSelected() ); - - save(); - - update( mSelectedConfig ); - } - } ); - - add( mMixerGain ); - - /** - * Lookup the save config and apply that config to update all of the - * controls - */ - TunerConfigurationAssignment savedConfig = mResourceManager - .getSettingsManager().getSelectedTunerConfiguration( - TunerType.FUNCUBE_DONGLE_PRO_PLUS, - mController.getUSBAddress() ); - - if( savedConfig != null ) - { - mSelectedConfig = getNamedConfiguration( - savedConfig.getTunerConfigurationName() ); - } - - if( mSelectedConfig == null ) - { - mSelectedConfig = mComboConfigurations.getItemAt( 0 ); - - //Store this config as the default for this tuner at this address - mResourceManager.getSettingsManager() - .setSelectedTunerConfiguration( - TunerType.FUNCUBE_DONGLE_PRO_PLUS, - mController.getUSBAddress(), mSelectedConfig ); - } - - mComboConfigurations.setSelectedItem( mSelectedConfig ); - - /** - * Create a new configuration - */ - mNewConfiguration = new JButton( "New" ); - - mNewConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - TunerConfiguration config = - mResourceManager.getSettingsManager() - .addNewTunerConfiguration( - TunerType.FUNCUBE_DONGLE_PRO_PLUS, - "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 ) - { - FCD2TunerConfiguration selected = - (FCD2TunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - int n = JOptionPane.showConfirmDialog( - FCD2TunerConfigurationPanel.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" ); - } - - private void update( FCD2TunerConfiguration config ) - { - mSelectedConfig = config; - - try - { - mController.apply( config ); - - mName.setText( config.getName() ); - mLNAGain.setSelected( config.getGainLNA() ); - mMixerGain.setSelected( config.getGainMixer() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - - mResourceManager.getSettingsManager().setSelectedTunerConfiguration( - TunerType.FUNCUBE_DONGLE_PRO_PLUS, - mController.getUSBAddress(), config ); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - FCD2TunerConfigurationPanel.this, - "FCD Pro Plus Tuner Controller - couldn't " - + "apply the tuner configuration settings - " + - e1.getLocalizedMessage() ); - - mLog.error( "FuncubeDongleProPlus Controller - couldn't apply " - + "config [" + config.getName() + "]", e1 ); - } - } - - private ComboBoxModel getModel() - { - ArrayList configs = - mResourceManager.getSettingsManager() - .getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO_PLUS ); - - DefaultComboBoxModel model = - new DefaultComboBoxModel(); - - for( TunerConfiguration config: configs ) - { - model.addElement( (FCD2TunerConfiguration)config ); - } - - return model; - } - - private FCD2TunerConfiguration getNamedConfiguration( String name ) - { - ArrayList configs = - mResourceManager.getSettingsManager() - .getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO_PLUS ); - - for( TunerConfiguration config: configs ) - { - if( config.getName().contentEquals( name ) ) - { - return (FCD2TunerConfiguration)config; - } - } - - return null; - } - - private void save() - { - mResourceManager.getSettingsManager().save(); - } -} diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerController.java b/src/source/tuner/fcd/proplusV2/FCD2TunerController.java index 8640ea50..7a27c286 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerController.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerController.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,47 +20,39 @@ package source.tuner.fcd.proplusV2; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import javax.swing.JPanel; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.Device; import org.usb4java.DeviceDescriptor; import source.SourceException; +import source.tuner.MixerTunerType; import source.tuner.TunerClass; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.fcd.FCDCommand; -import source.tuner.fcd.FCDTuner; import source.tuner.fcd.FCDTunerController; -import controller.ResourceManager; public class FCD2TunerController extends FCDTunerController { - private final static Logger mLog = - LoggerFactory.getLogger( FCD2TunerController.class ); + private final static Logger mLog = LoggerFactory.getLogger( FCD2TunerController.class ); - public static final int sMINIMUM_TUNABLE_FREQUENCY = 150000; - public static final int sMAXIMUM_TUNABLE_FREQUENCY = 2050000000; - public static final int sSAMPLE_RATE = 192000; - - private FCD2TunerConfiguration mTunerConfiguration; - private FCD2TunerEditorPanel mEditor; + public static final int MINIMUM_TUNABLE_FREQUENCY = 150000; + public static final int MAXIMUM_TUNABLE_FREQUENCY = 2050000000; + public static final int SAMPLE_RATE = 192000; public FCD2TunerController( Device device, DeviceDescriptor descriptor ) { - super( device, - descriptor, - sMINIMUM_TUNABLE_FREQUENCY, - sMAXIMUM_TUNABLE_FREQUENCY ); + super( device, descriptor, + (int)MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS.getAudioFormat().getSampleRate(), + MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY ); } public void init() throws SourceException { super.init(); - mFrequencyController.setSampleRate( sSAMPLE_RATE ); + mFrequencyController.setSampleRate( SAMPLE_RATE ); try { @@ -74,7 +66,7 @@ public class FCD2TunerController extends FCDTunerController public int getCurrentSampleRate() { - return sSAMPLE_RATE; + return SAMPLE_RATE; } @Override @@ -88,71 +80,44 @@ public class FCD2TunerController extends FCDTunerController { return TunerType.FUNCUBE_DONGLE_PRO_PLUS; } - - public JPanel getEditor( FCDTuner tuner, ResourceManager resourceManager ) - { - if( mEditor == null ) - { - mEditor = new FCD2TunerEditorPanel( tuner, resourceManager ); - } - - return mEditor; - } + public void setLNAGain( boolean enabled ) throws SourceException + { + try + { + send( FCDCommand.APP_SET_LNA_GAIN, enabled ? 1 : 0 ); + } + catch ( Exception e ) + { + throw new SourceException( "error while setting LNA Gain", e ); + } + } + + public void setMixerGain( boolean enabled ) throws SourceException + { + try + { + send( FCDCommand.APP_SET_MIXER_GAIN, enabled ? 1 : 0 ); + } + catch ( Exception e ) + { + throw new SourceException( "error while setting Mixer Gain", e ); + } + } + @Override public void apply( TunerConfiguration config ) throws SourceException { if( config instanceof FCD2TunerConfiguration ) { - FCD2TunerConfiguration plusConfig = - (FCD2TunerConfiguration)config; + FCD2TunerConfiguration fcd2 = (FCD2TunerConfiguration)config; - setFrequencyCorrection( plusConfig.getFrequencyCorrection() ); - - try - { - if( plusConfig.getGainLNA() ) - { - send( FCDCommand.APP_SET_LNA_GAIN, 1 ); - - } - else - { - send( FCDCommand.APP_SET_LNA_GAIN, 0 ); - } - } - catch ( Exception e ) - { - throw new SourceException( "error while setting LNA Gain", e ); - } - - try - { - if( plusConfig.getGainMixer() ) - { - send( FCDCommand.APP_SET_MIXER_GAIN, 1 ); - - } - else - { - send( FCDCommand.APP_SET_MIXER_GAIN, 0 ); - } - } - catch ( Exception e ) - { - throw new SourceException( "error while setting Mixer Gain", e ); - } - - mTunerConfiguration = plusConfig; + setFrequencyCorrection( fcd2.getFrequencyCorrection() ); + setLNAGain( fcd2.getGainLNA() ); + setMixerGain( fcd2.getGainMixer() ); } } - @Override - public TunerConfiguration getTunerConfiguration() - { - return mTunerConfiguration; - } - public int getDCCorrection() { int dcCorrection = -999; diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java b/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java new file mode 100644 index 00000000..1e0ab570 --- /dev/null +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java @@ -0,0 +1,328 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package source.tuner.fcd.proplusV2; + +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 javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +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 net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import source.SourceException; +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.fcd.FCDTuner; + +public class FCD2TunerEditor extends TunerConfigurationEditor +{ + private final static Logger mLog = LoggerFactory.getLogger( FCD2TunerEditor.class ); + private static final long serialVersionUID = 1L; + + private JTextField mConfigurationName; + private JButton mTunerInfo; + private JSpinner mFrequencyCorrection; + private JCheckBox mLNAGain; + private JCheckBox mMixerGain; + private boolean mLoading; + + private FCD2TunerController mController; + + public FCD2TunerEditor( TunerConfigurationModel tunerConfigurationModel, FCDTuner tuner ) + { + super( tunerConfigurationModel ); + mController = (FCD2TunerController)tuner.getController(); + + init(); + } + + private FCD2TunerConfiguration getConfiguration() + { + if( hasItem() ) + { + return (FCD2TunerConfiguration)getItem(); + } + + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][grow]" ) ); + + add( new JLabel( "FCD Pro+ Tuner Configuration" ), "span,align center" ); + + mConfigurationName = new JTextField(); + mConfigurationName.setEnabled( false ); + mConfigurationName.addFocusListener( new FocusListener() + { + @Override + public void focusLost( FocusEvent e ) + { + save(); + } + @Override + public void focusGained( FocusEvent e ) {} + } ); + + add( new JLabel( "Name:" ) ); + add( mConfigurationName, "span 2" ); + + mTunerInfo = new JButton( "Tuner Info" ); + mTunerInfo.setEnabled( false ); + mTunerInfo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + JOptionPane.showMessageDialog( FCD2TunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( mTunerInfo ); + + 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 ) + { + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + + try + { + mController.setFrequencyCorrection( value ); + save(); + } + catch ( SourceException e1 ) + { + JOptionPane.showMessageDialog( FCD2TunerEditor.this, "FCD Pro Plus Tuner " + + "Controller - couldn't apply frequency correction value: " + value + + e1.getLocalizedMessage() ); + + mLog.error( "FuncubeDongleProPlus Controller - couldn't apply " + + "frequency correction value: " + value, e1 ); + } + } + } ); + + add( new JLabel( "PPM:" ) ); + add( mFrequencyCorrection, "wrap" ); + + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); + + mLNAGain = new JCheckBox( "LNA Gain" ); + mLNAGain.setEnabled( false ); + mLNAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent event ) + { + try + { + mController.setLNAGain( mLNAGain.isSelected() ); + save(); + } + catch( SourceException e ) + { + mLog.error( "Couldn't set LNA gain for FCD2", e ); + } + } + } ); + + add( new JLabel( "" ) ); + add( mLNAGain ); + + /** + * Mixer Gain + */ + mMixerGain = new JCheckBox( "Mixer Gain" ); + + mMixerGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent event ) + { + try + { + mController.setMixerGain( mMixerGain.isSelected() ); + save(); + } + catch( SourceException e ) + { + mLog.error( "Couldn't set mixer gain for FCD2", e ); + } + } + } ); + + add( new JLabel( "" ) ); + add( mMixerGain ); + } + + /** + * Sets each of the tuner configuration controls to the enabled argument state + */ + private void setControlsEnabled( boolean enabled ) + { + if( mConfigurationName.isEnabled() != enabled ) + { + mConfigurationName.setEnabled( enabled ); + } + + if( mTunerInfo.isEnabled() != enabled ) + { + mTunerInfo.setEnabled( enabled ); + } + + if( mFrequencyCorrection.isEnabled() != enabled ) + { + mFrequencyCorrection.setEnabled( enabled ); + } + + if( mLNAGain.isEnabled() != enabled ) + { + mLNAGain.setEnabled( enabled ); + } + + if( mMixerGain.isEnabled() != enabled ) + { + mMixerGain.setEnabled( enabled ); + } + } + + private String getTunerInfo() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "

    Funcube Dongle Pro Plus Tuner

    " ); + + sb.append( "USB ID: " ); + sb.append( mController.getUSBID() ); + sb.append( "
    " ); + + sb.append( "USB Address: " ); + sb.append( mController.getUSBAddress() ); + sb.append( "
    " ); + + sb.append( "USB Speed: " ); + sb.append( mController.getUSBSpeed() ); + sb.append( "
    " ); + + sb.append( "Cellular Band: " ); + sb.append( mController.getConfiguration().getBandBlocking() ); + sb.append( "
    " ); + + sb.append( "Firmware: " ); + sb.append( mController.getConfiguration().getFirmware() ); + sb.append( "
    " ); + + return sb.toString(); + } + + @Override + public void save() + { + if( hasItem() && !mLoading ) + { + FCD2TunerConfiguration config = getConfiguration(); + + config.setName( mConfigurationName.getText() ); + + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + + config.setGainLNA( mLNAGain.isSelected() ); + config.setGainMixer( mMixerGain.isSelected() ); + + getTunerConfigurationModel().broadcast( + new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); + } + } + + @Override + public void setItem( TunerConfiguration tunerConfiguration ) + { + super.setItem( tunerConfiguration ); + + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + + if( hasItem() ) + { + FCD2TunerConfiguration config = getConfiguration(); + + + if( tunerConfiguration.isAssigned() ) + { + setControlsEnabled( true ); + mConfigurationName.setText( config.getName() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mLNAGain.setSelected( config.getGainLNA() ); + mMixerGain.setSelected( config.getGainMixer() ); + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( config.getName() ); + } + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( "" ); + } + + mLoading = false; + } +} diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java b/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java deleted file mode 100644 index e372a544..00000000 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java +++ /dev/null @@ -1,111 +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 - ******************************************************************************/ -package source.tuner.fcd.proplusV2; - -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 source.SourceException; -import source.tuner.fcd.FCDTuner; -import source.tuner.fcd.FCDTunerDetailsPanel; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; -import source.tuner.frequency.FrequencyChangeEvent.Event; - -import com.jidesoft.swing.JideTabbedPane; - -import controller.ResourceManager; - -public class FCD2TunerEditorPanel extends JPanel implements FrequencyChangeListener -{ - private final static Logger mLog = - LoggerFactory.getLogger( FCD2TunerEditorPanel.class ); - private static final long serialVersionUID = 1L; - - private FCDTuner mTuner; - private FCD2TunerController mController; - private ResourceManager mResourceManager; - - private JFrequencyControl mFrequencyControl; - - public FCD2TunerEditorPanel( FCDTuner tuner, ResourceManager resourceManager ) - { - mTuner = tuner; - mController = (FCD2TunerController)mTuner.getController(); - mResourceManager = resourceManager; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][grow]" ) ); - - add( new JLabel( "Funcube Dongle Pro Plus 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.addListener( mFrequencyControl ); - - mFrequencyControl.setFrequency( mController.getFrequency(), false ); - - add( mFrequencyControl, "span,align center" ); - - JideTabbedPane tabs = new JideTabbedPane(); - tabs.setFont( this.getFont() ); - tabs.setForeground( Color.BLACK ); - - tabs.add( "Configuration", new FCD2TunerConfigurationPanel( - mResourceManager, mController ) ); - - tabs.add( "Info", new FCDTunerDetailsPanel( mController ) ); - - add( tabs, "span,grow,push" ); - } - - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - if( event.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) - { - //Disregard the bandwidth, which shouldn't be changing - try - { - mController.setFrequency( event.getValue().longValue() ); - } - catch ( SourceException e ) - { - mLog.error( "FCDProController - error setting frequency [" + - event.getValue().longValue() + "]", e ); - } - } - } -} diff --git a/src/source/tuner/frequency/AutomaticFrequencyControl_R.java b/src/source/tuner/frequency/AutomaticFrequencyControl_R.java deleted file mode 100644 index 994ff0c6..00000000 --- a/src/source/tuner/frequency/AutomaticFrequencyControl_R.java +++ /dev/null @@ -1,181 +0,0 @@ -package source.tuner.frequency; - -/******************************************************************************* - * 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 - ******************************************************************************/ - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.real.RealSampleListener; -import source.tuner.frequency.FrequencyChangeEvent.Event; -import buffer.FloatAveragingBuffer; - -public class AutomaticFrequencyControl_R extends FrequencyCorrectionControl - implements RealSampleListener -{ - private final static Logger mLog = - LoggerFactory.getLogger( AutomaticFrequencyControl_R.class ); - - private static float LARGE_ERROR = 0.300f; - private static float MEDIUM_ERROR = 0.150f; - private static float SMALL_ERROR = 0.030f; - private static float FINE_ERROR = 0.015f; - - private static int LARGE_FREQUENCY_CORRECTION = 1500; //Hertz - private static int MEDIUM_FREQUENCY_CORRECTION = 500; - private static int SMALL_FREQUENCY_CORRECTION = 100; - private static int FINE_FREQUENCY_CORRECTION = 50; - - private static int SAMPLE_THRESHOLD = 15000; - private Mode mMode = Mode.FAST; - - private FloatAveragingBuffer mBuffer = - new FloatAveragingBuffer( SAMPLE_THRESHOLD ); - - private int mSampleCounter = 0; - private int mSkipCounter = 0; - private int mSkipThreshold = 4; - private int mErrorCorrection = 0; - - public enum Mode - { - NORMAL,FAST; - } - - /** - * Automatic Frequency Control. Monitors DC bias present in an FM - * demodulated audio stream and issues automatic frequency corrections to - * maintain the DC bias close to zero. - * - * @param maximum - maximum allowable (+/-) frequency correction value. - */ - public AutomaticFrequencyControl_R( int maximum ) - { - super( maximum ); - } - - public void dispose() - { - super.dispose(); - - mBuffer = null; - } - - /** - * Resets the error correction value to zero and broadcasts a change event - */ - public void reset() - { - mErrorCorrection = 0; - mMode = Mode.FAST; - dispatch(); - } - - /** - * Broadcasts a frequency correction change event to the registered listener - */ - private void dispatch() - { - if( mListener != null ) - { - mListener.frequencyChanged( - new FrequencyChangeEvent( Event.CHANNEL_FREQUENCY_CORRECTION_CHANGE_NOTIFICATION, - mErrorCorrection ) ); - } - } - - @Override - public void receive( float sample ) - { - if( mMode == Mode.FAST ) - { - float average = mBuffer.get( sample ); - - mSampleCounter++; - - if( mSampleCounter >= SAMPLE_THRESHOLD ) - { - mSampleCounter = 0; - - update( average ); - } - } - else - { - mSkipCounter++; - - if( mSkipCounter >= mSkipThreshold ) - { - mSkipCounter = 0; - - float average = mBuffer.get( sample ); - - mSampleCounter++; - - if( mSampleCounter >= SAMPLE_THRESHOLD ) - { - mSampleCounter = 0; - - update( average ); - } - } - } - } - - private void update( float average ) - { - mMode = Mode.NORMAL; - - if( average > LARGE_ERROR ) - { - adjust( -LARGE_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average > MEDIUM_ERROR ) - { - adjust( -MEDIUM_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average > SMALL_ERROR ) - { - adjust( -SMALL_FREQUENCY_CORRECTION ); - } - else if( average > FINE_ERROR ) - { - adjust( -FINE_FREQUENCY_CORRECTION ); - } - else if( average < -LARGE_ERROR ) - { - adjust( LARGE_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average < -MEDIUM_ERROR ) - { - adjust( MEDIUM_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average < -SMALL_ERROR ) - { - adjust( SMALL_FREQUENCY_CORRECTION ); - } - else if( average < -FINE_ERROR ) - { - adjust( FINE_FREQUENCY_CORRECTION ); - } - } -} diff --git a/src/source/tuner/frequency/AutomaticFrequencyControl_RB.java b/src/source/tuner/frequency/AutomaticFrequencyControl_RB.java deleted file mode 100644 index 73970ec2..00000000 --- a/src/source/tuner/frequency/AutomaticFrequencyControl_RB.java +++ /dev/null @@ -1,197 +0,0 @@ -package source.tuner.frequency; - -/******************************************************************************* - * 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 - ******************************************************************************/ - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.Listener; -import sample.real.RealBuffer; -import source.tuner.frequency.FrequencyChangeEvent.Event; -import buffer.FloatAveragingBuffer; - -public class AutomaticFrequencyControl_RB extends FrequencyCorrectionControl - implements Listener -{ - private final static Logger mLog = - LoggerFactory.getLogger( AutomaticFrequencyControl_RB.class ); - - private static float LARGE_ERROR = 0.300f; - private static float MEDIUM_ERROR = 0.150f; - private static float SMALL_ERROR = 0.030f; - private static float FINE_ERROR = 0.015f; - - private static int LARGE_FREQUENCY_CORRECTION = 1500; //Hertz - private static int MEDIUM_FREQUENCY_CORRECTION = 500; - private static int SMALL_FREQUENCY_CORRECTION = 100; - private static int FINE_FREQUENCY_CORRECTION = 50; - - private static int SAMPLE_THRESHOLD = 15000; - private Mode mMode = Mode.FAST; - - private FloatAveragingBuffer mBuffer = - new FloatAveragingBuffer( SAMPLE_THRESHOLD ); - - private int mSampleCounter = 0; - private int mSkipCounter = 0; - private int mSkipThreshold = 4; - private int mErrorCorrection = 0; - - public enum Mode - { - NORMAL,FAST; - } - - /** - * Automatic Frequency Control. Monitors DC bias present in an FM - * demodulated audio stream and issues automatic frequency corrections to - * maintain the DC bias close to zero. - * - * @param maximum - maximum allowable (+/-) frequency correction value. - */ - public AutomaticFrequencyControl_RB( int maximum ) - { - super( maximum ); - } - - public void dispose() - { - super.dispose(); - - mBuffer = null; - } - - /** - * Resets the error correction value to zero and broadcasts a change event - */ - public void reset() - { - super.reset(); - - mMode = Mode.FAST; - dispatch(); - } - - /** - * Broadcasts a frequency correction change event to the registered listener - */ - private void dispatch() - { - if( mListener != null ) - { - mListener.frequencyChanged( - new FrequencyChangeEvent( Event.CHANNEL_FREQUENCY_CORRECTION_CHANGE_NOTIFICATION, - mErrorCorrection ) ); - } - } - - @Override - public void receive( RealBuffer t ) - { - float average = 0.0f; - - for( float sample: t.getSamples() ) - { - average = mBuffer.get( sample ); - } - - //TODO: fix this - -// update( average ); - } - - public void receive( float sample ) - { - if( mMode == Mode.FAST ) - { - float average = mBuffer.get( sample ); - - mSampleCounter++; - - if( mSampleCounter >= SAMPLE_THRESHOLD ) - { - mSampleCounter = 0; - - update( average ); - } - } - else - { - mSkipCounter++; - - if( mSkipCounter >= mSkipThreshold ) - { - mSkipCounter = 0; - - float average = mBuffer.get( sample ); - - mSampleCounter++; - - if( mSampleCounter >= SAMPLE_THRESHOLD ) - { - mSampleCounter = 0; - - update( average ); - } - } - } - } - - private void update( float average ) - { - mMode = Mode.NORMAL; - - if( average > LARGE_ERROR ) - { - adjust( -LARGE_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average > MEDIUM_ERROR ) - { - adjust( -MEDIUM_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average > SMALL_ERROR ) - { - adjust( -SMALL_FREQUENCY_CORRECTION ); - } - else if( average > FINE_ERROR ) - { - adjust( -FINE_FREQUENCY_CORRECTION ); - } - else if( average < -LARGE_ERROR ) - { - adjust( LARGE_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average < -MEDIUM_ERROR ) - { - adjust( MEDIUM_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average < -SMALL_ERROR ) - { - adjust( SMALL_FREQUENCY_CORRECTION ); - } - else if( average < -FINE_ERROR ) - { - adjust( FINE_FREQUENCY_CORRECTION ); - } - } -} diff --git a/src/source/tuner/frequency/DCTrackingFrequencyControl.java b/src/source/tuner/frequency/DCTrackingFrequencyControl.java new file mode 100644 index 00000000..7cfe5648 --- /dev/null +++ b/src/source/tuner/frequency/DCTrackingFrequencyControl.java @@ -0,0 +1,128 @@ +package source.tuner.frequency; +/******************************************************************************* + * SDR Trunk + * Copyright (C) 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 + ******************************************************************************/ +import java.util.concurrent.ScheduledExecutorService; + +import module.Module; +import sample.Listener; +import sample.real.RealBuffer; +import source.tuner.frequency.FrequencyChangeEvent.Event; + +public class DCTrackingFrequencyControl extends Module + implements Listener, IFrequencyChangeProvider +{ + private static float COARSE_THRESHOLD = 0.1f; + private static float LOOP_GAIN = 0.1f; + private static float COARSE_GAIN = 1000.0f; + private static float FINE_GAIN = 100.0f; + private int mErrorCorrection = 0; + private int mMaximumFrequencyCorrection = 0; + private Listener mListener; + + /** + * DC offset Automatic Frequency Control. Monitors DC bias present in an FM + * demodulated audio stream and issues automatic frequency corrections to + * maintain the DC bias close to zero. + * + * @param maximum - maximum allowable (+/-) frequency correction value. + */ + public DCTrackingFrequencyControl( int maximum ) + { + mMaximumFrequencyCorrection = maximum; + } + + public void dispose() + { + mListener = null; + } + + @Override + public void receive( RealBuffer buffer ) + { + double sum = 0.0d; + + for( float sample: buffer.getSamples() ) + { + sum += sample; + } + + float mean = (float)( sum / (double)buffer.getSamples().length ); + + float adjustment = 0.0f; + + if( mean > COARSE_THRESHOLD || mean < - COARSE_THRESHOLD ) + { + adjustment = mean * COARSE_GAIN; + } + else + { + adjustment = mean * FINE_GAIN; + } + + setErrorCorrection( mErrorCorrection + (int)( (float)adjustment * LOOP_GAIN ) ); + } + + private void setErrorCorrection( int correction ) + { + if( mListener != null ) + { + if( correction > mMaximumFrequencyCorrection ) + { + correction = mMaximumFrequencyCorrection; + } + else if( correction < -mMaximumFrequencyCorrection ) + { + correction = -mMaximumFrequencyCorrection; + } + + if( correction != mErrorCorrection ) + { + mListener.receive( new FrequencyChangeEvent( + Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, correction ) ); + } + } + } + + @Override + public void setFrequencyChangeListener( Listener listener ) + { + mListener = listener; + } + + @Override + public void removeFrequencyChangeListener() + { + mListener = null; + } + + @Override + public void start( ScheduledExecutorService executor ) + { + } + + @Override + public void stop() + { + } + + @Override + public void reset() + { + setErrorCorrection( 0 ); + } +} diff --git a/src/source/tuner/frequency/FrequencyChangeEvent.java b/src/source/tuner/frequency/FrequencyChangeEvent.java index 87f82f08..9ceb3685 100644 --- a/src/source/tuner/frequency/FrequencyChangeEvent.java +++ b/src/source/tuner/frequency/FrequencyChangeEvent.java @@ -4,11 +4,13 @@ public class FrequencyChangeEvent { public enum Event { - FREQUENCY_CHANGE_NOTIFICATION, - FREQUENCY_CORRECTION_CHANGE_NOTIFICATION, - CHANNEL_FREQUENCY_CORRECTION_CHANGE_NOTIFICATION, - CHANNEL_FREQUENCY_CORRECTION_CHANGE_REQUEST, - SAMPLE_RATE_CHANGE_NOTIFICATION; + NOTIFICATION_FREQUENCY_CHANGE, + NOTIFICATION_FREQUENCY_CORRECTION_CHANGE, + NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE, + NOTIFICATION_SAMPLE_RATE_CHANGE, + + REQUEST_FREQUENCY_CHANGE, + REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE; } private Event mEvent; diff --git a/src/source/tuner/frequency/FrequencyChangeProcessorWrapper.java b/src/source/tuner/frequency/FrequencyChangeProcessorWrapper.java new file mode 100644 index 00000000..1b96ae8b --- /dev/null +++ b/src/source/tuner/frequency/FrequencyChangeProcessorWrapper.java @@ -0,0 +1,32 @@ +package source.tuner.frequency; + +import sample.Listener; + +public class FrequencyChangeProcessorWrapper + implements IFrequencyChangeListener, Listener +{ + private IFrequencyChangeProcessor mProcessor; + + /** + * Provides IFrequencyChangeListener wrapper for an IFrequencyChangeProcessor + */ + public FrequencyChangeProcessorWrapper( IFrequencyChangeProcessor handler ) + { + mProcessor = handler; + } + + @Override + public Listener getFrequencyChangeListener() + { + return this; + } + + @Override + public void receive( FrequencyChangeEvent event ) + { + if( mProcessor != null ) + { + mProcessor.frequencyChanged( event ); + } + } +} diff --git a/src/source/tuner/frequency/FrequencyController.java b/src/source/tuner/frequency/FrequencyController.java index 51bab16f..f1c6753d 100644 --- a/src/source/tuner/frequency/FrequencyController.java +++ b/src/source/tuner/frequency/FrequencyController.java @@ -37,8 +37,7 @@ public class FrequencyController private double mFrequencyCorrection = 0d; private int mSampleRate = 0; - private ArrayList mListeners = - new ArrayList(); + private ArrayList mProcessors = new ArrayList<>(); public FrequencyController( Tunable tunable, long minFrequency, @@ -181,20 +180,21 @@ public class FrequencyController /** * Adds listener to receive frequency change events */ - public void addListener( FrequencyChangeListener listener ) +//TODO: rename this to addFrequencyChangeProcessor() + public void addListener( IFrequencyChangeProcessor processor ) { - if( !mListeners.contains( listener ) ) + if( !mProcessors.contains( processor ) ) { - mListeners.add( listener ); + mProcessors.add( processor ); } } /** * Removes listener from receiving frequency change events */ - public void removeListener( FrequencyChangeListener listener ) + public void removeFrequencyChangeProcessor( IFrequencyChangeProcessor processor ) { - mListeners.remove( listener ); + mProcessors.remove( processor ); } @@ -205,7 +205,7 @@ public class FrequencyController protected void broadcastFrequencyChange() { broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.FREQUENCY_CHANGE_NOTIFICATION, mFrequency ) ); + new FrequencyChangeEvent( Event.NOTIFICATION_FREQUENCY_CHANGE, mFrequency ) ); } /** @@ -214,7 +214,7 @@ public class FrequencyController protected void broadcastFrequencyErrorChange() { broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.FREQUENCY_CORRECTION_CHANGE_NOTIFICATION, mFrequencyCorrection ) ); + new FrequencyChangeEvent( Event.NOTIFICATION_FREQUENCY_CORRECTION_CHANGE, mFrequencyCorrection ) ); } /** @@ -223,14 +223,14 @@ public class FrequencyController protected void broadcastSampleRateChange() { broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.SAMPLE_RATE_CHANGE_NOTIFICATION, mSampleRate ) ); + new FrequencyChangeEvent( Event.NOTIFICATION_SAMPLE_RATE_CHANGE, mSampleRate ) ); } public void broadcastFrequencyChangeEvent( FrequencyChangeEvent event ) { - for( FrequencyChangeListener listener: mListeners ) + for( IFrequencyChangeProcessor processor: mProcessors ) { - listener.frequencyChanged( event ); + processor.frequencyChanged( event ); } } diff --git a/src/source/tuner/frequency/FrequencyCorrectionControl.java b/src/source/tuner/frequency/FrequencyCorrectionControl.java deleted file mode 100644 index 77255bb0..00000000 --- a/src/source/tuner/frequency/FrequencyCorrectionControl.java +++ /dev/null @@ -1,161 +0,0 @@ -package source.tuner.frequency; - -/******************************************************************************* - * 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 - ******************************************************************************/ -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.tuner.frequency.FrequencyChangeEvent.Event; - -/** - * Frequency control for providing frequency error correction to a tuned channel - */ -public class FrequencyCorrectionControl implements FrequencyChangeListener -{ - private final static Logger mLog = - LoggerFactory.getLogger( FrequencyCorrectionControl.class ); - - protected FrequencyChangeListener mListener; - protected FrequencyCorrectionResetListener mResetListener; - - protected int mChannelFrequencyCorrection = 0; - private int mMaximumCorrection = 3000; - - /** - * Frequency correction controller. - * - * @param maximum - defines the maximum +/- allowable frequency correction - * value - */ - public FrequencyCorrectionControl( int maximum ) - { - mMaximumCorrection = maximum; - } - - /** - * Cleanup - */ - public void dispose() - { - mListener = null; - mResetListener = null; - } - - /** - * Registers a listener to receive frequency correction events - */ - public void setFrequencyChangeListener( FrequencyChangeListener listener ) - { - mListener = listener; - } - - /** - * Registers a listener to receive frequency correction resets - */ - public void setListener( FrequencyCorrectionResetListener listener ) - { - mResetListener = listener; - } - - /** - * Listener for source frequency or frequency offset value changes to - * reset this frequency controller value to zero. - */ - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - Event attribute = event.getEvent(); - - switch( attribute ) - { - //Direct the listener to reset when frequency correction (PPM) or - //sample rate changes - case FREQUENCY_CORRECTION_CHANGE_NOTIFICATION: - case SAMPLE_RATE_CHANGE_NOTIFICATION: - reset(); - break; - default: - break; - } - } - - /** - * Resets frequency correction value to zero and notifies any reset - * listeners - */ - public void reset() - { - if( mResetListener != null ) - { - mResetListener.resetFrequencyCorrection(); - } - } - - /** - * Sets frequency correction to the specified value and broadcasts a change - * request - */ - public void setFrequencyCorrection( int correction ) - { - mChannelFrequencyCorrection = correction; - - /* Limit frequency correction to +/- max correction value */ - if( mChannelFrequencyCorrection > mMaximumCorrection ) - { - mChannelFrequencyCorrection = mMaximumCorrection; - } - else if( mChannelFrequencyCorrection < -mMaximumCorrection ) - { - mChannelFrequencyCorrection = -mMaximumCorrection; - } - - /* Broadcast a change request */ - if( mListener != null ) - { - mListener.frequencyChanged( new FrequencyChangeEvent( - Event.CHANNEL_FREQUENCY_CORRECTION_CHANGE_REQUEST, - mChannelFrequencyCorrection ) ); - } - } - - /** - * Increments the frequency error correction value by the specified adjustment - */ - public void adjust( int adjustment ) - { - setFrequencyCorrection( getErrorCorrection() + adjustment ); - } - - /** - * Current frequency error correction value - * @return - */ - public int getErrorCorrection() - { - return mChannelFrequencyCorrection; - } - - /** - * Listener interface to be notified when this control is reset to a zero - * frequency correction value - */ - public interface FrequencyCorrectionResetListener - { - public void resetFrequencyCorrection(); - } -} diff --git a/src/source/tuner/frequency/FrequencyChangeListener.java b/src/source/tuner/frequency/IFrequencyChangeListener.java similarity index 90% rename from src/source/tuner/frequency/FrequencyChangeListener.java rename to src/source/tuner/frequency/IFrequencyChangeListener.java index fab9f28a..99b75804 100644 --- a/src/source/tuner/frequency/FrequencyChangeListener.java +++ b/src/source/tuner/frequency/IFrequencyChangeListener.java @@ -17,6 +17,8 @@ ******************************************************************************/ package source.tuner.frequency; +import sample.Listener; + /** * Interface for broadcasting changes to frequency, bandwidth, sample rate, @@ -26,7 +28,7 @@ package source.tuner.frequency; * frequency value. All frequency correction aspects should be handled within * the device that implements the tuning of the frequency. */ -public interface FrequencyChangeListener +public interface IFrequencyChangeListener { - public void frequencyChanged( FrequencyChangeEvent event ); + public Listener getFrequencyChangeListener(); } diff --git a/src/source/tuner/frequency/IFrequencyChangeProcessor.java b/src/source/tuner/frequency/IFrequencyChangeProcessor.java new file mode 100644 index 00000000..479d8e8d --- /dev/null +++ b/src/source/tuner/frequency/IFrequencyChangeProcessor.java @@ -0,0 +1,6 @@ +package source.tuner.frequency; + +public interface IFrequencyChangeProcessor +{ + public void frequencyChanged( FrequencyChangeEvent event ); +} diff --git a/src/source/tuner/frequency/IFrequencyChangeProvider.java b/src/source/tuner/frequency/IFrequencyChangeProvider.java new file mode 100644 index 00000000..919126e7 --- /dev/null +++ b/src/source/tuner/frequency/IFrequencyChangeProvider.java @@ -0,0 +1,9 @@ +package source.tuner.frequency; + +import sample.Listener; + +public interface IFrequencyChangeProvider +{ + public void setFrequencyChangeListener( Listener listener ); + public void removeFrequencyChangeListener(); +} diff --git a/src/source/tuner/frequency/IFrequencyCorrectionController.java b/src/source/tuner/frequency/IFrequencyCorrectionController.java deleted file mode 100644 index e937bd4a..00000000 --- a/src/source/tuner/frequency/IFrequencyCorrectionController.java +++ /dev/null @@ -1,8 +0,0 @@ -package source.tuner.frequency; - - -public interface IFrequencyCorrectionController -{ - public boolean hasFrequencyCorrectionControl(); - public FrequencyCorrectionControl getFrequencyCorrectionControl(); -} diff --git a/src/source/tuner/hackrf/HackRFInformationPanel.java b/src/source/tuner/hackrf/HackRFInformationPanel.java deleted file mode 100644 index f3825577..00000000 --- a/src/source/tuner/hackrf/HackRFInformationPanel.java +++ /dev/null @@ -1,104 +0,0 @@ -package source.tuner.hackrf; - -/******************************************************************************* - * 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 - ******************************************************************************/ - -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 index e847b58e..2194a7de 100644 --- a/src/source/tuner/hackrf/HackRFTuner.java +++ b/src/source/tuner/hackrf/HackRFTuner.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,8 +19,6 @@ package source.tuner.hackrf; import java.util.concurrent.RejectedExecutionException; -import javax.swing.JPanel; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,26 +29,22 @@ import source.tuner.Tuner; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; -import source.tuner.TunerConfiguration; +import source.tuner.TunerEvent; import source.tuner.TunerType; import source.tuner.hackrf.HackRFTunerController.BoardID; -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 ); + super( "HackRF", controller ); + } + + public HackRFTunerController getController() + { + return (HackRFTunerController)getTunerController(); } public void dispose() @@ -58,11 +52,6 @@ public class HackRFTuner extends Tuner //TODO: dispose of something here } - public HackRFTunerController getController() - { - return mController; - } - @Override public TunerClass getTunerClass() { @@ -75,41 +64,24 @@ public class HackRFTuner extends Tuner return TunerType.HACKRF; } - @Override - public JPanel getEditor( ResourceManager resourceManager ) - { - return new HackRFTunerEditorPanel( this, resourceManager ); - } - - @Override - public void apply( TunerConfiguration config ) throws SourceException - { - mController.apply( config ); - } - - @Override - public int getSampleRate() - { - return mController.getSampleRate(); - } - @Override public double getSampleSize() { return 11.0; } - @Override - public long getFrequency() throws SourceException - { - return mController.getFrequency(); - } - @Override public TunerChannelSource getChannel( TunerChannel channel ) throws RejectedExecutionException, SourceException { - return mController.getChannel( this, channel ); + TunerChannelSource source = getController().getChannel( this, channel ); + + if( source != null ) + { + broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) ); + } + + return source; } @Override @@ -121,7 +93,7 @@ public class HackRFTuner extends Tuner /* Tell the controller to release the channel and cleanup */ if( source != null ) { - mController.releaseChannel( source ); + getController().releaseChannel( source ); } } @@ -130,7 +102,7 @@ public class HackRFTuner extends Tuner { try { - return mController.getSerial().getSerialNumber(); + return getController().getSerial().getSerialNumber(); } catch( Exception e ) { @@ -143,12 +115,12 @@ public class HackRFTuner extends Tuner @Override public void addListener( Listener listener ) { - mController.addListener( listener ); + getController().addListener( listener ); } @Override public void removeListener( Listener listener ) { - mController.removeListener( listener ); + getController().removeListener( listener ); } } diff --git a/src/source/tuner/hackrf/HackRFTunerConfiguration.java b/src/source/tuner/hackrf/HackRFTunerConfiguration.java index 66d85e25..14cd6a66 100644 --- a/src/source/tuner/hackrf/HackRFTunerConfiguration.java +++ b/src/source/tuner/hackrf/HackRFTunerConfiguration.java @@ -20,8 +20,8 @@ package source.tuner.hackrf; import javax.xml.bind.annotation.XmlAttribute; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.hackrf.HackRFTunerController.HackRFLNAGain; import source.tuner.hackrf.HackRFTunerController.HackRFSampleRate; import source.tuner.hackrf.HackRFTunerController.HackRFVGAGain; @@ -39,12 +39,11 @@ public class HackRFTunerConfiguration extends TunerConfiguration */ public HackRFTunerConfiguration() { - this( "Default" ); } - public HackRFTunerConfiguration( String name ) + public HackRFTunerConfiguration( String uniqueID, String name ) { - super( name ); + super( uniqueID, name ); } @Override diff --git a/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java b/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java deleted file mode 100644 index b5ec8acb..00000000 --- a/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java +++ /dev/null @@ -1,562 +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 - ******************************************************************************/ -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.JToggleButton; -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 JToggleButton mAmplifier; - 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( "", - "[][right][grow,fill][right][grow,fill]", "[grow,fill]" ) ); - gainPanel.setBorder( BorderFactory.createTitledBorder( "Gain" ) ); - - /* Amplifier */ - mAmplifier = new JToggleButton( "Amp" ); - mAmplifier.setSelected( mSelectedConfig.getAmplifierEnabled() ); - mAmplifier.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - mController.setAmplifierEnabled( mAmplifier.isSelected() ); - mSelectedConfig.setAmplifierEnabled( mAmplifier.isSelected() ); - save(); - } - catch ( UsbException e ) - { - mLog.error( "couldn't enable/disable amplifier", e ); - mAmplifier.setEnabled( !mAmplifier.isSelected() ); - - JOptionPane.showMessageDialog( - HackRFTunerConfigurationPanel.this, - "Couldn't change amplifier setting", - "Error changing amplifier setting", - JOptionPane.ERROR_MESSAGE ); - } - } - } ); - gainPanel.add( mAmplifier ); - - /* 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. Adjust to set " - + "the IF gain" ); - 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. Adjust to set the " - + "baseband gain" ); - gainPanel.add( new JLabel( "VGA" ) ); - gainPanel.add( mComboVGAGain, "wrap" ); - - add( gainPanel, "span,growx" ); - - /** - * 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 index 6c1074ba..791736cb 100644 --- a/src/source/tuner/hackrf/HackRFTunerController.java +++ b/src/source/tuner/hackrf/HackRFTunerController.java @@ -1,4 +1,3 @@ -package source.tuner.hackrf; /******************************************************************************* * SDR Trunk * Copyright (C) 2014 Dennis Sheirer @@ -52,12 +51,13 @@ package source.tuner.hackrf; * 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.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -81,8 +81,8 @@ import sample.Listener; import sample.adapter.ByteSampleAdapter; import sample.complex.ComplexBuffer; import source.SourceException; -import source.tuner.TunerConfiguration; import source.tuner.TunerController; +import source.tuner.configuration.TunerConfiguration; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; @@ -92,7 +92,7 @@ public class HackRFTunerController extends TunerController private final static Logger mLog = LoggerFactory.getLogger( HackRFTunerController.class ); - public final static long USB_TIMEOUT_MS = 1000000l; //uSeconds + public final static long USB_TIMEOUT_US = 1000000l; //uSeconds public static final byte USB_ENDPOINT = (byte)0x81; public static final byte USB_INTERFACE = (byte)0x0; @@ -395,7 +395,7 @@ public class HackRFTunerController extends TunerController (short)value, (short)index, buffer, - USB_TIMEOUT_MS ); + USB_TIMEOUT_US ); if( transferred < 0 ) { @@ -468,7 +468,7 @@ public class HackRFTunerController extends TunerController (short)value, (short)index, buffer, - USB_TIMEOUT_MS ); + USB_TIMEOUT_US ); if( transferred < 0 ) { @@ -887,8 +887,8 @@ public class HackRFTunerController extends TunerController */ public void addListener( Listener listener ) { - synchronized( mComplexBufferBroadcaster ) - { + synchronized ( mComplexBufferBroadcaster ) + { mComplexBufferBroadcaster.addListener( listener ); if( mBufferProcessor == null || !mBufferProcessor.isRunning() ) @@ -901,7 +901,7 @@ public class HackRFTunerController extends TunerController thread.start(); } - } + } } /** @@ -910,15 +910,15 @@ public class HackRFTunerController extends TunerController */ public void removeListener( Listener listener ) { - synchronized( mComplexBufferBroadcaster ) - { + synchronized ( mComplexBufferBroadcaster ) + { mComplexBufferBroadcaster.removeListener( listener ); if( !mComplexBufferBroadcaster.hasListeners() ) { mBufferProcessor.stop(); } - } + } } /** @@ -928,12 +928,12 @@ public class HackRFTunerController extends TunerController public class BufferProcessor implements Runnable, TransferCallback { private ScheduledFuture mSampleDispatcherTask; + private AtomicBoolean mRunning = new AtomicBoolean(); private LinkedTransferQueue mAvailableTransfers; private LinkedTransferQueue mTransfersInProgress = new LinkedTransferQueue<>(); - private AtomicBoolean mRunning = new AtomicBoolean(); private ByteBuffer mLibUsbHandlerStatus; private boolean mCancel = false; - + @Override public void run() { @@ -955,23 +955,23 @@ public class HackRFTunerController extends TunerController { mAvailableTransfers.drainTo( transfers ); - for( Transfer transfer: transfers ) - { - int result = LibUsb.submitTransfer( transfer ); - - if( result == LibUsb.SUCCESS ) - { - mTransfersInProgress.add( transfer ); - } - else - { - mLog.error( "error submitting transfer [" + - LibUsb.errorName( result ) + "]" ); - } - } + for( Transfer transfer: transfers ) + { + int result = LibUsb.submitTransfer( transfer ); + + if( result == LibUsb.SUCCESS ) + { + mTransfersInProgress.add( transfer ); + } + else + { + mLog.error( "error submitting transfer [" + + LibUsb.errorName( result ) + "]" ); + } + } int result = LibUsb.handleEventsTimeoutCompleted( - null, USB_TIMEOUT_MS, mLibUsbHandlerStatus.asIntBuffer() ); + null, USB_TIMEOUT_US, mLibUsbHandlerStatus.asIntBuffer() ); if( result != LibUsb.SUCCESS ) { @@ -990,16 +990,16 @@ public class HackRFTunerController extends TunerController LibUsb.cancelTransfer( transfer ); } - int result = LibUsb.handleEventsTimeoutCompleted( - null, USB_TIMEOUT_MS, mLibUsbHandlerStatus.asIntBuffer() ); + int result = LibUsb.handleEventsTimeoutCompleted( null, + USB_TIMEOUT_US, mLibUsbHandlerStatus.asIntBuffer() ); if( result != LibUsb.SUCCESS ) - { - mLog.error( "error cancelling transfers in progress" ); - } - - mLibUsbHandlerStatus.rewind(); - } + { + mLog.error( "error cancelling transfers in progress" ); + } + + mLibUsbHandlerStatus.rewind(); + } } } @@ -1089,7 +1089,7 @@ public class HackRFTunerController extends TunerController ByteBuffer.allocateDirect( mBufferSize ); LibUsb.fillBulkTransfer( transfer, mDeviceHandle,USB_ENDPOINT, - buffer, BufferProcessor.this, "Buffer", USB_TIMEOUT_MS ); + buffer, BufferProcessor.this, "Buffer", USB_TIMEOUT_US ); mAvailableTransfers.add( transfer ); } diff --git a/src/source/tuner/hackrf/HackRFTunerEditor.java b/src/source/tuner/hackrf/HackRFTunerEditor.java new file mode 100644 index 00000000..1b0095d1 --- /dev/null +++ b/src/source/tuner/hackrf/HackRFTunerEditor.java @@ -0,0 +1,464 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package source.tuner.hackrf; + +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 javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.JToggleButton; +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.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.hackrf.HackRFTunerController.BoardID; +import source.tuner.hackrf.HackRFTunerController.HackRFLNAGain; +import source.tuner.hackrf.HackRFTunerController.HackRFSampleRate; +import source.tuner.hackrf.HackRFTunerController.HackRFVGAGain; +import source.tuner.hackrf.HackRFTunerController.Serial; + +public class HackRFTunerEditor extends TunerConfigurationEditor +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = LoggerFactory.getLogger( HackRFTunerEditor.class ); + + private JTextField mConfigurationName; + private JButton mTunerInfo; + private JComboBox mComboSampleRate; + private JSpinner mFrequencyCorrection; + private JToggleButton mAmplifier; + private JComboBox mComboLNAGain; + private JComboBox mComboVGAGain; + private boolean mLoading; + + private HackRFTunerController mController; + + public HackRFTunerEditor( TunerConfigurationModel tunerConfigurationModel, HackRFTuner tuner ) + { + super( tunerConfigurationModel ); + + mController = tuner.getController(); + + init(); + } + + private HackRFTunerConfiguration getConfiguration() + { + if( hasItem() ) + { + return (HackRFTunerConfiguration)getItem(); + } + + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][][grow]" ) ); + + add( new JLabel( "HackRF Tuner Configuration" ), "span,align center" ); + + mConfigurationName = new JTextField(); + mConfigurationName.setEnabled( false ); + mConfigurationName.addFocusListener( new FocusListener() + { + @Override + public void focusLost( FocusEvent e ) + { + save(); + } + @Override + public void focusGained( FocusEvent e ) {} + } ); + + add( new JLabel( "Name:" ) ); + add( mConfigurationName, "span 2" ); + + mTunerInfo = new JButton( "Tuner Info" ); + mTunerInfo.setEnabled( false ); + mTunerInfo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + JOptionPane.showMessageDialog( HackRFTunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( mTunerInfo ); + + mComboSampleRate = new JComboBox<>( HackRFSampleRate.values() ); + mComboSampleRate.setEnabled( false ); + mComboSampleRate.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + HackRFSampleRate sampleRate = + (HackRFSampleRate)mComboSampleRate.getSelectedItem(); + try + { + + mController.setSampleRate( sampleRate ); + save(); + } + catch ( UsbException e2 ) + { + JOptionPane.showMessageDialog( HackRFTunerEditor.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 ); + + 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 ); + save(); + } + catch ( SourceException e1 ) + { + JOptionPane.showMessageDialog( HackRFTunerEditor.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( "PPM:" ) ); + add( mFrequencyCorrection ); + + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); + add( new JLabel( "Gain" ) ); + add( new JLabel( "" ), "span 2" ); //filler + + mAmplifier = new JToggleButton( "Amplifier" ); + mAmplifier.setEnabled( false ); + mAmplifier.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + mController.setAmplifierEnabled( mAmplifier.isSelected() ); + save(); + } + catch ( UsbException e ) + { + mLog.error( "couldn't enable/disable amplifier", e ); + + JOptionPane.showMessageDialog( HackRFTunerEditor.this, "Couldn't change amplifier setting", + "Error changing amplifier setting", JOptionPane.ERROR_MESSAGE ); + } + } + } ); + add( mAmplifier ); + + mComboLNAGain = new JComboBox( HackRFLNAGain.values() ); + mComboLNAGain.setEnabled( false ); + mComboLNAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + HackRFLNAGain lnaGain = (HackRFLNAGain)mComboLNAGain.getSelectedItem(); + + if ( lnaGain == null ) + { + lnaGain = HackRFLNAGain.GAIN_0; + } + + mController.setLNAGain( lnaGain ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( HackRFTunerEditor.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. Adjust to set the IF gain" ); + add( new JLabel( "LNA:" ) ); + add( mComboLNAGain ); + + mComboVGAGain = new JComboBox( HackRFVGAGain.values() ); + mComboVGAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + HackRFVGAGain vgaGain = (HackRFVGAGain)mComboVGAGain.getSelectedItem(); + + if( vgaGain == null ) + { + vgaGain = HackRFVGAGain.GAIN_4; + } + + mController.setVGAGain( vgaGain ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( HackRFTunerEditor.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. Adjust to set the baseband gain" ); + add( new JLabel( "VGA:" ) ); + add( mComboVGAGain ); + } + + /** + * Sets each of the tuner configuration controls to the enabled argument state + */ + private void setControlsEnabled( boolean enabled ) + { + if( mConfigurationName.isEnabled() != enabled ) + { + mConfigurationName.setEnabled( enabled ); + } + + if( mTunerInfo.isEnabled() != enabled ) + { + mTunerInfo.setEnabled( enabled ); + } + + if( mFrequencyCorrection.isEnabled() != enabled ) + { + mFrequencyCorrection.setEnabled( enabled ); + } + + if( mComboSampleRate.isEnabled() != enabled ) + { + mComboSampleRate.setEnabled( enabled ); + } + + if( mAmplifier.isEnabled() != enabled ) + { + mAmplifier.setEnabled( enabled ); + } + + if( mComboLNAGain.isEnabled() != enabled ) + { + mComboLNAGain.setEnabled( enabled ); + } + + if( mComboVGAGain.isEnabled() != enabled ) + { + mComboVGAGain.setEnabled( enabled ); + } + } + + private String getTunerInfo() + { + BoardID board = BoardID.INVALID; + + try + { + board = mController.getBoardID(); + } + catch ( UsbException e ) + { + mLog.error( "couldn't read HackRF board identifier", e ); + } + + StringBuilder sb = new StringBuilder(); + + sb.append( "

    HackRF Tuner

    " ); + + sb.append( "Board: " ); + sb.append( board.getLabel() ); + sb.append( "
    " ); + + Serial serial = null; + + try + { + serial = mController.getSerial(); + } + catch( Exception e ) + { + mLog.error( "couldn't read HackRF serial number", e ); + } + + if( serial != null ) + { + sb.append( "Serial: " ); + sb.append( serial.getSerialNumber() ); + sb.append( "
    " ); + + sb.append( "Part: " ); + sb.append( serial.getPartID() ); + sb.append( "
    " ); + } + else + { + sb.append( "Serial: Unknown
    " ); + sb.append( "Part: Unknown
    " ); + } + + String firmware = null; + + try + { + firmware = mController.getFirmwareVersion(); + } + catch( Exception e ) + { + mLog.error( "couldn't read HackRF firmware version", e ); + } + + if( firmware != null ) + { + sb.append( "Firmware: " ); + sb.append( firmware ); + sb.append( "
    " ); + } + else + { + sb.append( "Firmware: Unknown
    " ); + } + + return sb.toString(); + } + + @Override + public void setItem( TunerConfiguration tunerConfiguration ) + { + super.setItem( tunerConfiguration ); + + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + + if( hasItem() ) + { + HackRFTunerConfiguration config = getConfiguration(); + + if( tunerConfiguration.isAssigned() ) + { + setControlsEnabled( true ); + + mConfigurationName.setText( config.getName() ); + mComboSampleRate.setSelectedItem( config.getSampleRate() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mAmplifier.setSelected( config.getAmplifierEnabled() ); + mComboLNAGain.setSelectedItem( config.getLNAGain() ); + mComboVGAGain.setSelectedItem( config.getVGAGain() ); + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( config.getName() ); + } + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( "" ); + } + + mLoading = false; + } + + @Override + public void save() + { + if( hasItem() && !mLoading ) + { + HackRFTunerConfiguration config = getConfiguration(); + + config.setName( mConfigurationName.getText() ); + + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + config.setSampleRate( (HackRFSampleRate)mComboSampleRate.getSelectedItem() ); + config.setAmplifierEnabled( mAmplifier.isSelected() ); + config.setLNAGain( (HackRFLNAGain)mComboLNAGain.getSelectedItem() ); + config.setVGAGain( (HackRFVGAGain)mComboVGAGain.getSelectedItem() ); + + getTunerConfigurationModel().broadcast( + new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); + } + } +} diff --git a/src/source/tuner/hackrf/HackRFTunerEditorPanel.java b/src/source/tuner/hackrf/HackRFTunerEditorPanel.java deleted file mode 100644 index 9c4d8490..00000000 --- a/src/source/tuner/hackrf/HackRFTunerEditorPanel.java +++ /dev/null @@ -1,125 +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 - ******************************************************************************/ -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.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; -import source.tuner.frequency.FrequencyChangeEvent.Event; -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.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) - { - try - { - mController.setFrequency( event.getValue().longValue() ); - } - catch ( SourceException e ) - { - mLog.error( "error setting frequency [" + event.getValue().longValue() + "]", e ); - } - } - } -} diff --git a/src/source/tuner/rtl/RTL2832InfoPanel.java b/src/source/tuner/rtl/RTL2832InfoPanel.java deleted file mode 100644 index f65bfbc9..00000000 --- a/src/source/tuner/rtl/RTL2832InfoPanel.java +++ /dev/null @@ -1,76 +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 - ******************************************************************************/ -package source.tuner.rtl; - -import javax.swing.JLabel; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; -import source.tuner.rtl.RTL2832TunerController.Descriptor; - -public class RTL2832InfoPanel extends JPanel -{ - RTL2832TunerController mController; - - public RTL2832InfoPanel( RTL2832TunerController controller ) - { - mController = controller; - - init(); - } - - public void dispose() - { - mController = null; - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][][][grow]" ) ); - - Descriptor descriptor = mController.getDescriptor(); - - add( new JLabel( "RTL-2832 EEPROM Descriptor" ), "span,align center" ); - - if( descriptor == null ) - { - add( new JLabel( "No descriptor" ), "wrap" ) ; - } - else - { - add( new JLabel( "USB ID:" ) ) ; - add( new JLabel( descriptor.getVendorID() + ":" + - descriptor.getProductID() ), "wrap" ) ; - - add( new JLabel( "Vendor:" ) ) ; - add( new JLabel( descriptor.getVendorLabel() ), "wrap" ) ; - - add( new JLabel( "Product:" ) ) ; - add( new JLabel( descriptor.getProductLabel() ), "wrap" ) ; - - add( new JLabel( "Serial:" ) ) ; - add( new JLabel( descriptor.getSerial() ), "wrap" ) ; - - add( new JLabel( "IR Enabled:" ) ) ; - add( new JLabel( ( descriptor.irEnabled() ? "Yes" : "No" ) ), "wrap" ) ; - - add( new JLabel( "Remote Wake:" ) ) ; - add( new JLabel( ( descriptor.remoteWakeupEnabled() ? "Yes" : "No" ) ), "wrap" ) ; - } - } -} diff --git a/src/source/tuner/rtl/RTL2832Tuner.java b/src/source/tuner/rtl/RTL2832Tuner.java index e7f4a3ac..20b746ea 100644 --- a/src/source/tuner/rtl/RTL2832Tuner.java +++ b/src/source/tuner/rtl/RTL2832Tuner.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,8 +19,6 @@ package source.tuner.rtl; import java.util.concurrent.RejectedExecutionException; -import javax.swing.JPanel; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.LibUsbException; @@ -32,11 +30,9 @@ import source.tuner.Tuner; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; -import source.tuner.TunerConfiguration; +import source.tuner.TunerEvent; import source.tuner.TunerType; -import source.tuner.frequency.FrequencyChangeListener; import source.tuner.rtl.RTL2832TunerController.SampleRate; -import controller.ResourceManager; public class RTL2832Tuner extends Tuner { @@ -44,19 +40,16 @@ public class RTL2832Tuner extends Tuner LoggerFactory.getLogger( RTL2832Tuner.class ); private TunerClass mTunerClass; - protected RTL2832TunerController mController; public RTL2832Tuner( TunerClass tunerClass, RTL2832TunerController controller ) throws SourceException { super( tunerClass.getVendorDeviceLabel() + "/" + - controller.getTunerType().getLabel() + " #" + - controller.getUniqueID() ); + controller.getTunerType().getLabel() + " " + + controller.getUniqueID(), controller ); mTunerClass = tunerClass; - mController = controller; - mController.addListener( (FrequencyChangeListener)this ); } public void dispose() @@ -66,13 +59,13 @@ public class RTL2832Tuner extends Tuner public RTL2832TunerController getController() { - return mController; + return (RTL2832TunerController)getTunerController(); } @Override public String getUniqueID() { - return mController.getUniqueID(); + return getController().getUniqueID(); } @Override @@ -84,41 +77,14 @@ public class RTL2832Tuner extends Tuner @Override public TunerType getTunerType() { - return mController.getTunerType(); - } - - @Override - public JPanel getEditor( ResourceManager resourceManager ) - { - return mController.getEditor( resourceManager ); - } - - @Override - public void apply( TunerConfiguration config ) throws SourceException - { - mController.apply( config ); - } - - @Override - public int getSampleRate() - { - try - { - return mController.getCurrentSampleRate(); - } - catch( SourceException e ) - { - mLog.error( "RTL2832 Tuner - couldn't get sample rate", e ); - } - - return 0; + return getController().getTunerType(); } public void setSampleRate( SampleRate sampleRate ) throws SourceException { try { - mController.setSampleRate( sampleRate ); + getController().setSampleRate( sampleRate ); } catch( LibUsbException e ) { @@ -135,26 +101,26 @@ public class RTL2832Tuner extends Tuner return 11.0; } - @Override - public long getFrequency() throws SourceException - { - return mController.getFrequency(); - } - public void setFrequency( int frequency ) throws SourceException { - mController.setFrequency( frequency ); + getController().setFrequency( frequency ); } @Override public TunerChannelSource getChannel( TunerChannel channel ) - throws RejectedExecutionException, SourceException + throws RejectedExecutionException, + SourceException { TunerChannelSource source = null; try { - source = mController.getChannel( this, channel ); + source = getController().getChannel( this, channel ); + + if( source != null ) + { + broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) ); + } } catch( Exception e ) { @@ -163,6 +129,7 @@ public class RTL2832Tuner extends Tuner return source; } + /** * Releases the tuned channel so that the tuner controller can tune to * other frequencies as needed. @@ -176,19 +143,19 @@ public class RTL2832Tuner extends Tuner /* Tell the controller to release the channel and cleanup */ if( source != null ) { - mController.releaseChannel( source ); + getController().releaseChannel( source ); } } @Override public void addListener( Listener listener ) { - mController.addListener( listener ); + getController().addListener( listener ); } @Override public void removeListener( Listener listener ) { - mController.removeListener( listener ); + getController().removeListener( listener ); } } diff --git a/src/source/tuner/rtl/RTL2832TunerController.java b/src/source/tuner/rtl/RTL2832TunerController.java index fe37f8ad..140b6058 100644 --- a/src/source/tuner/rtl/RTL2832TunerController.java +++ b/src/source/tuner/rtl/RTL2832TunerController.java @@ -34,7 +34,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import javax.swing.JPanel; import javax.usb.UsbDisconnectedException; import javax.usb.UsbException; @@ -56,7 +55,6 @@ import source.SourceException; import source.tuner.TunerController; import source.tuner.TunerType; import buffer.FloatAveragingBuffer; -import controller.ResourceManager; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; @@ -113,8 +111,6 @@ public abstract class RTL2832TunerController extends TunerController private SampleRateMonitor mSampleRateMonitor; private AtomicInteger mSampleCounter = new AtomicInteger(); - private double mSampleRateAverageSum; - private int mSampleRateAverageCount; private static final DecimalFormat mDecimalFormatter = new DecimalFormat( "###,###,###.0" ); private static final DecimalFormat mPercentFormatter = @@ -149,6 +145,7 @@ public abstract class RTL2832TunerController extends TunerController mDevice = device; mDeviceDescriptor = deviceDescriptor; } + public void init() throws SourceException { @@ -362,8 +359,6 @@ public abstract class RTL2832TunerController extends TunerController } } - public abstract JPanel getEditor( ResourceManager resourceManager ); - public abstract void setSampleRateFilters( int sampleRate ) throws SourceException; @@ -977,7 +972,7 @@ public abstract class RTL2832TunerController extends TunerController return mSampleRate.getRate(); } - public int getSampleRate() throws SourceException + public int getSampleRateFromTuner() throws SourceException { try { @@ -1426,17 +1421,17 @@ public abstract class RTL2832TunerController extends TunerController { mLog.error( "NPE! = tpm null: " + ( mThreadPoolManager == null ), npe ); } - + mLibUsbHandlerStatus = ByteBuffer.allocateDirect( 4 ); List transfers = new ArrayList<>(); - + mCancel = false; - while( mRunning.get() ) + while( mRunning.get() ) { mAvailableTransfers.drainTo( transfers ); - + for( Transfer transfer: transfers ) { int result = LibUsb.submitTransfer( transfer ); @@ -1453,7 +1448,7 @@ public abstract class RTL2832TunerController extends TunerController } int result = LibUsb.handleEventsTimeoutCompleted( - null, TIMEOUT_US, mLibUsbHandlerStatus.asIntBuffer() ); + null, TIMEOUT_US, mLibUsbHandlerStatus.asIntBuffer() ); if( result != LibUsb.SUCCESS ) { @@ -1472,21 +1467,17 @@ public abstract class RTL2832TunerController extends TunerController LibUsb.cancelTransfer( transfer ); } - int result = LibUsb.handleEventsTimeoutCompleted( - null, TIMEOUT_US, mLibUsbHandlerStatus.asIntBuffer() ); - - if( result != LibUsb.SUCCESS ) - { - mLog.error( "error handling events for libusb" ); - } - - mLibUsbHandlerStatus.rewind(); + int result = LibUsb.handleEventsTimeoutCompleted( null, + TIMEOUT_US, mLibUsbHandlerStatus.asIntBuffer() ); + + if( result != LibUsb.SUCCESS ) + { + mLog.error( "error handling events for libusb during cancel" ); + } + + mLibUsbHandlerStatus.rewind(); } } - else - { - mLog.error( "Couldn't start buffer processor - already running!" ); - } } /** @@ -1516,7 +1507,7 @@ public abstract class RTL2832TunerController extends TunerController } @Override - public void processTransfer( Transfer transfer ) + public void processTransfer( Transfer transfer ) { mTransfersInProgress.remove( transfer ); @@ -1636,6 +1627,79 @@ public abstract class RTL2832TunerController extends TunerController } } + + + public class USBEventHandlingThread implements Runnable + { + /** If thread should abort. */ + private volatile boolean mAbort; + + /** + * Aborts the event handling thread. + */ + public void abort() + { + mAbort = true; + } + + @Override + public void run() + { + while ( !mAbort ) + { + int result = LibUsb.handleEventsTimeout( null, 1000 ); + + if ( result != LibUsb.SUCCESS ) + { + mAbort = true; + + mLog.error( "error handling usb events [" + + LibUsb.errorName( result ) + "]" ); + + throw new LibUsbException("Unable to handle USB " + + "events", result ); + } + } + } + } + + public enum TunerTypeCheck + { + E4K( 0xC8, 0x02, 0x40 ), + FC0012( 0xC6, 0x00, 0xA1 ), + FC0013( 0xC6, 0x00, 0xA3 ), + FC2580( 0xAC, 0x01, 0x56 ), + R820T( 0x34, 0x00, 0x69 ), + R828D( 0x74, 0x00, 0x69 ); + + private int mI2CAddress; + private int mCheckAddress; + private int mCheckValue; + + private TunerTypeCheck( int i2c, int address, int value ) + { + mI2CAddress = i2c; + mCheckAddress = address; + mCheckValue = value; + } + + public byte getI2CAddress() + { + return (byte)mI2CAddress; + } + + public byte getCheckAddress() + { + return (byte)mCheckAddress; + } + + public byte getCheckValue() + { + return (byte)mCheckValue; + } + } + + /** * Averages the sample rate over a 10-second period. The count is for the * number of bytes received from the tuner. There are two bytes for each @@ -1730,7 +1794,6 @@ public abstract class RTL2832TunerController extends TunerController } } } - /** * RTL2832 EEPROM byte array descriptor parsing class */ @@ -1901,74 +1964,4 @@ public abstract class RTL2832TunerController extends TunerController } } - public class USBEventHandlingThread implements Runnable - { - /** If thread should abort. */ - private volatile boolean mAbort; - - /** - * Aborts the event handling thread. - */ - public void abort() - { - mAbort = true; - } - - @Override - public void run() - { - while ( !mAbort ) - { - int result = LibUsb.handleEventsTimeout( null, 1000 ); - - if ( result != LibUsb.SUCCESS ) - { - mAbort = true; - - mLog.error( "error handling usb events [" + - LibUsb.errorName( result ) + "]" ); - - throw new LibUsbException("Unable to handle USB " - + "events", result ); - } - } - } - } - - public enum TunerTypeCheck - { - E4K( 0xC8, 0x02, 0x40 ), - FC0012( 0xC6, 0x00, 0xA1 ), - FC0013( 0xC6, 0x00, 0xA3 ), - FC2580( 0xAC, 0x01, 0x56 ), - R820T( 0x34, 0x00, 0x69 ), - R828D( 0x74, 0x00, 0x69 ); - - private int mI2CAddress; - private int mCheckAddress; - private int mCheckValue; - - private TunerTypeCheck( int i2c, int address, int value ) - { - mI2CAddress = i2c; - mCheckAddress = address; - mCheckValue = value; - } - - public byte getI2CAddress() - { - return (byte)mI2CAddress; - } - - public byte getCheckAddress() - { - return (byte)mCheckAddress; - } - - public byte getCheckValue() - { - return (byte)mCheckValue; - } - } - } diff --git a/src/source/tuner/rtl/RTL2832TunerType.java b/src/source/tuner/rtl/RTL2832TunerType.java deleted file mode 100644 index 72233f32..00000000 --- a/src/source/tuner/rtl/RTL2832TunerType.java +++ /dev/null @@ -1,28 +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 - ******************************************************************************/ -package source.tuner.rtl; - -public enum RTL2832TunerType -{ - E4000, - FC0012, - FC0013, - R820T, - R828D, - UNKNOWN; -} diff --git a/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java b/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java index d7d82a62..3d4c9eff 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java +++ b/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java @@ -19,8 +19,8 @@ package source.tuner.rtl.e4k; import javax.xml.bind.annotation.XmlAttribute; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.rtl.RTL2832TunerController.SampleRate; import source.tuner.rtl.e4k.E4KTunerController.E4KEnhanceGain; import source.tuner.rtl.e4k.E4KTunerController.E4KGain; @@ -29,24 +29,23 @@ import source.tuner.rtl.e4k.E4KTunerController.E4KMixerGain; public class E4KTunerConfiguration extends TunerConfiguration { - private E4KGain mMasterGain = E4KGain.AUTOMATIC; - private E4KMixerGain mMixerGain = E4KMixerGain.AUTOMATIC; - private E4KLNAGain mLNAGain = E4KLNAGain.AUTOMATIC; - private E4KEnhanceGain mEnhanceGain = E4KEnhanceGain.AUTOMATIC; - private double mFrequencyCorrection = 8.9d; - private SampleRate mSampleRate = SampleRate.RATE_1_920MHZ; + private E4KGain mMasterGain = E4KGain.MANUAL; + private E4KMixerGain mMixerGain = E4KMixerGain.GAIN_4; + private E4KLNAGain mLNAGain = E4KLNAGain.GAIN_PLUS_200; + private E4KEnhanceGain mEnhanceGain = E4KEnhanceGain.GAIN_3; + private double mFrequencyCorrection = 0.0d; + private SampleRate mSampleRate = SampleRate.RATE_2_400MHZ; /** * Default constructor for JAXB */ public E4KTunerConfiguration() { - this( "Default" ); } - public E4KTunerConfiguration( String name ) + public E4KTunerConfiguration( String uniqueID, String name ) { - super( name ); + super( uniqueID, name ); } @Override diff --git a/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java b/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java deleted file mode 100644 index cce675b4..00000000 --- a/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java +++ /dev/null @@ -1,691 +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 - ******************************************************************************/ -package source.tuner.rtl.e4k; - -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 org.usb4java.LibUsbException; - -import source.SourceException; -import source.tuner.TunerConfiguration; -import source.tuner.TunerConfigurationAssignment; -import source.tuner.TunerType; -import source.tuner.rtl.RTL2832TunerController.SampleRate; -import source.tuner.rtl.e4k.E4KTunerController.E4KEnhanceGain; -import source.tuner.rtl.e4k.E4KTunerController.E4KGain; -import source.tuner.rtl.e4k.E4KTunerController.E4KLNAGain; -import source.tuner.rtl.e4k.E4KTunerController.E4KMixerGain; -import controller.ResourceManager; - -public class E4KTunerConfigurationPanel extends JPanel -{ - private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( E4KTunerConfigurationPanel.class ); - - private ResourceManager mResourceManager; - private E4KTunerController mController; - private E4KTunerConfiguration mSelectedConfig; - - private JButton mNewConfiguration; - private JButton mDeleteConfiguration; - private JComboBox mComboConfigurations; - - private JTextField mName; - - private JSpinner mFrequencyCorrection; - private JSpinner mSampleRateCorrection; - private JComboBox mComboMasterGain; - private JComboBox mComboMixerGain; - private JComboBox mComboLNAGain; - private JComboBox mComboEnhanceGain; - - private JComboBox mComboSampleRate; - - public E4KTunerConfigurationPanel( ResourceManager resourceManager, - E4KTunerController 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 = mResourceManager - .getSettingsManager().getSelectedTunerConfiguration( - TunerType.ELONICS_E4000, mController.getUniqueID() ); - - 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 - mResourceManager.getSettingsManager() - .setSelectedTunerConfiguration( - TunerType.ELONICS_E4000, - mController.getUniqueID(), mSelectedConfig ); - } - - mComboConfigurations.setSelectedItem( mSelectedConfig ); - - mComboConfigurations.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - E4KTunerConfiguration selected = - (E4KTunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - update( selected ); - } - } - }); - - add( new JLabel( "Config:" ) ); - add( mComboConfigurations, "growx,push" ); - - /** - * Tuner Configuration Name - */ - add( new JLabel( "Name:" ) ); - mName = new JTextField(); - mName.setText( mSelectedConfig.getName() ); - - mName.addFocusListener( new FocusListener() - { - @Override - public void focusLost( FocusEvent e ) - { - /* Update the config name */ - mSelectedConfig.setName( mName.getText() ); - - /* Reset this named config as the assigned config for this - * tuner */ - mResourceManager.getSettingsManager() - .setSelectedTunerConfiguration( - TunerType.ELONICS_E4000, - mController.getUniqueID(), mSelectedConfig ); - - save(); - } - @Override - public void focusGained( FocusEvent e ) {} - } ); - - 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( - E4KTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't apply " - + "frequency correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply " - + "frequency correction value: " + value, e1 ); - } - } - } ); - } - } ); - - add( new JLabel( "Correction PPM:" ) ); - add( mFrequencyCorrection, "growx,push" ); - - /** - * Sample Rate - */ - mComboSampleRate = new JComboBox<>( SampleRate.values() ); - mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - mComboSampleRate.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - SampleRate sampleRate = - (SampleRate)mComboSampleRate.getSelectedItem(); - try - { - - mController.setSampleRate( sampleRate ); - - mSelectedConfig.setSampleRate( sampleRate ); - save(); - } - catch ( SourceException | LibUsbException eSampleRate ) - { - JOptionPane.showMessageDialog( - E4KTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't apply the sample " - + "rate setting [" + sampleRate.getLabel() + "] " + - eSampleRate.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply sample " - + "rate setting [" + sampleRate.getLabel() + "]", - eSampleRate ); - } - } - } ); - add( new JLabel( "Sample Rate:" ) ); - add( mComboSampleRate, "growx,push" ); - -// /** -// * Sample Rate Correction -// */ -// SpinnerModel sampleRateCorrectionModel = -// new SpinnerNumberModel( 0, //initial value -// -32768, //min -// 32767, //max -// 1 ); //step -// -// mSampleRateCorrection = new JSpinner( sampleRateCorrectionModel ); -// -// JSpinner.NumberEditor sampleRateCorrectionEditor = -// (JSpinner.NumberEditor)mSampleRateCorrection.getEditor(); -// -// DecimalFormat sampleRateCorrectionFormat = -// sampleRateCorrectionEditor.getFormat(); -// -// sampleRateCorrectionFormat.setMinimumFractionDigits( 0 ); -// -// sampleRateCorrectionEditor.getTextField() -// .setHorizontalAlignment( SwingConstants.CENTER ); -// -// mSampleRateCorrection.addChangeListener( new ChangeListener() -// { -// @Override -// public void stateChanged( ChangeEvent e ) -// { -// final int value = ((SpinnerNumberModel)mSampleRateCorrection -// .getModel()).getNumber().intValue(); -// -// EventQueue.invokeLater( new Runnable() -// { -// @Override -// public void run() -// { -// try -// { -// mController.setSampleRateFrequencyCorrection( value ); -// } -// catch ( SourceException | UsbException e1 ) -// { -// JOptionPane.showMessageDialog( -// E4KTunerConfigurationPanel.this, -// "E4K Tuner Controller - couldn't apply " -// + "sample rate correction value: " + value + -// e1.getLocalizedMessage() ); -// -// Log.error( "E4K Tuner Controller - couldn't apply " -// + "sample rate correction value: " + value + -// e1.getLocalizedMessage() ); -// } -// } -// } ); -// } -// } ); -// -// add( mSampleRateCorrection ); -// add( new JLabel( "Sample Rate Correction (ppm)" ), "wrap" ); - - /** - * Gain Controls - */ - JPanel gainPanel = new JPanel(); - gainPanel.setLayout( new MigLayout( "fill", "[right][left][right][left]", "" ) ); - gainPanel.setBorder( BorderFactory.createTitledBorder( "Gain" ) ); - - /* Master Gain Control */ - mComboMasterGain = new JComboBox( E4KGain.values() ); - mComboMasterGain.setSelectedItem( mSelectedConfig.getMasterGain() ); - - mComboMasterGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - E4KGain gain = (E4KGain)mComboMasterGain.getSelectedItem(); - - mController.setGain( (E4KGain)mComboMasterGain.getSelectedItem(), true ); - - if( gain == E4KGain.MANUAL ) - { - mComboMixerGain.setSelectedItem( - mController.getMixerGain( true ) ); - mComboMixerGain.setEnabled( true ); - - mComboLNAGain.setSelectedItem( - mController.getLNAGain( true ) ); - mComboLNAGain.setEnabled( true ); - - mComboEnhanceGain.setEnabled( true ); - mComboEnhanceGain.setSelectedItem( - mController.getEnhanceGain( true ) ); - } - else - { - mComboMixerGain.setEnabled( false ); - mComboMixerGain.setSelectedItem( gain.getMixerGain() ); - - mComboLNAGain.setEnabled( false ); - mComboLNAGain.setSelectedItem( gain.getLNAGain() ); - - mComboEnhanceGain.setEnabled( false ); - mComboEnhanceGain.setSelectedItem( gain.getEnhanceGain() ); - } - - mSelectedConfig.setMasterGain( gain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - E4KTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't apply the gain " - + "setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply " - + "gain setting", e ); - } - } - } ); - mComboMasterGain.setToolTipText( "Select AUTOMATIC for auto " - + "gain, MANUAL to enable
    independent control of " - + "Mixer, LNA and Enhance gain
    " - + "settings, or one of the individual gain settings for
    " - + "semi-manual gain control" ); - gainPanel.add( new JLabel( "Master" ) ); - gainPanel.add( mComboMasterGain ); - - /* Mixer Gain Control */ - mComboMixerGain = new JComboBox( E4KMixerGain.values() ); - mComboMixerGain.setSelectedItem( mSelectedConfig.getMixerGain() ); - mComboMixerGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - E4KMixerGain mixerGain = - (E4KMixerGain)mComboMixerGain.getSelectedItem(); - - if( mComboMixerGain.isEnabled() ) - { - mController.setMixerGain( mixerGain, true ); - } - - mSelectedConfig.setMixerGain( mixerGain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - E4KTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't apply the mixer " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply mixer " - + "gain setting", e ); - } - } - } ); - } - } ); - mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " - + "to MASTER to enable adjustment" ); - mComboMixerGain.setEnabled( false ); - gainPanel.add( new JLabel( "Mixer" ) ); - gainPanel.add( mComboMixerGain, "wrap" ); - - /* LNA Gain Control */ - mComboLNAGain = new JComboBox( E4KLNAGain.values() ); - mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); - mComboLNAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - E4KLNAGain lnaGain = (E4KLNAGain)mComboLNAGain.getSelectedItem(); - - if( mComboLNAGain.isEnabled() ) - { - mController.setLNAGain( lnaGain, true ); - } - - mSelectedConfig.setLNAGain( lnaGain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - E4KTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't apply the LNA " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply LNA " - + "gain setting - ", e ); - } - } - } ); - mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - mComboLNAGain.setEnabled( false ); - gainPanel.add( new JLabel( "LNA" ) ); - gainPanel.add( mComboLNAGain ); - - /* Enhance Gain Control */ - mComboEnhanceGain = new JComboBox( E4KEnhanceGain.values() ); - mComboEnhanceGain.setSelectedItem( mSelectedConfig.getEnhanceGain() ); - mComboEnhanceGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - E4KEnhanceGain enhanceGain = - (E4KEnhanceGain)mComboEnhanceGain.getSelectedItem(); - - if( mComboEnhanceGain.isEnabled() ) - { - mController.setEnhanceGain( enhanceGain, true ); - } - - mSelectedConfig.setEnhanceGain( enhanceGain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - E4KTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't apply the enhance " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply enhance " - + "gain setting", e ); - } - } - } ); - mComboEnhanceGain.setToolTipText( "Enhance Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - mComboEnhanceGain.setEnabled( false ); - gainPanel.add( new JLabel( "Enhance" ) ); - gainPanel.add( mComboEnhanceGain, "wrap" ); - - add( gainPanel, "span,grow,push" ); - - /** - * Create a new configuration - */ - mNewConfiguration = new JButton( "New" ); - - mNewConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - TunerConfiguration config = - mResourceManager.getSettingsManager() - .addNewTunerConfiguration( - TunerType.ELONICS_E4000, - "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 ) - { - E4KTunerConfiguration selected = - (E4KTunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - int n = JOptionPane.showConfirmDialog( - E4KTunerConfigurationPanel.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( E4KTunerConfiguration config ) - { - mSelectedConfig = config; - - try - { - mController.apply( config ); - - mName.setText( config.getName() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - - mComboMixerGain.setSelectedItem( mSelectedConfig.getMixerGain() ); - mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); - mComboEnhanceGain.setSelectedItem( mSelectedConfig.getEnhanceGain() ); - - /* Apply master gain last so that the Mixer, LNA, and Enhance gain - * settings are updated with the master setting where necessary */ - mComboMasterGain.setSelectedItem( mSelectedConfig.getMasterGain() ); - - mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - - mResourceManager.getSettingsManager().setSelectedTunerConfiguration( - TunerType.ELONICS_E4000, mController.getUniqueID(), config ); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - E4KTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't " - + "apply the tuner configuration settings - " + - e1.getLocalizedMessage() ); - - mLog.error( "E4K 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.ELONICS_E4000 ); - - DefaultComboBoxModel model = - new DefaultComboBoxModel(); - - for( TunerConfiguration config: configs ) - { - model.addElement( (E4KTunerConfiguration)config ); - } - - return model; - } - - /** - * Retrieves a specific (named) tuner configuration - */ - private E4KTunerConfiguration getNamedConfiguration( String name ) - { - ArrayList configs = - mResourceManager.getSettingsManager() - .getTunerConfigurations( TunerType.ELONICS_E4000 ); - - for( TunerConfiguration config: configs ) - { - if( config.getName().contentEquals( name ) ) - { - return (E4KTunerConfiguration)config; - } - } - - return null; - } - - /** - * Saves current tuner configuration settings - */ - private void save() - { - mResourceManager.getSettingsManager().save(); - } -} diff --git a/src/source/tuner/rtl/e4k/E4KTunerController.java b/src/source/tuner/rtl/e4k/E4KTunerController.java index 6bdfb149..a5f92bda 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerController.java +++ b/src/source/tuner/rtl/e4k/E4KTunerController.java @@ -19,7 +19,6 @@ package source.tuner.rtl.e4k; import java.util.Arrays; -import javax.swing.JPanel; import javax.usb.UsbException; import org.slf4j.Logger; @@ -31,10 +30,9 @@ import org.usb4java.LibUsb; import org.usb4java.LibUsbException; import source.SourceException; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.rtl.RTL2832TunerController; -import controller.ResourceManager; import controller.ThreadPoolManager; public class E4KTunerController extends RTL2832TunerController @@ -110,8 +108,6 @@ public class E4KTunerController extends RTL2832TunerController public static final byte CHFCALIB_CMD = (byte)0x01; - private E4KTunerEditorPanel mEditor; - public E4KTunerController( Device device, DeviceDescriptor deviceDescriptor, ThreadPoolManager threadPoolManager ) @@ -252,16 +248,6 @@ public class E4KTunerController extends RTL2832TunerController } } - public JPanel getEditor( ResourceManager resourceManager ) - { - if( mEditor == null ) - { - mEditor = new E4KTunerEditorPanel( this, resourceManager ); - } - - return mEditor; - } - /** * Sets the IF filters (mixer, channel and RC) to the correct filter setting * for the selected bandwidth/sample rate @@ -1070,7 +1056,7 @@ public class E4KTunerController extends RTL2832TunerController (byte)( ( temp & ~mask ) | ( value & mask ) ), controlI2CRepeater ); - int temp2 = readE4KRegister( register, controlI2CRepeater ); + readE4KRegister( register, controlI2CRepeater ); } } diff --git a/src/source/tuner/rtl/e4k/E4KTunerEditor.java b/src/source/tuner/rtl/e4k/E4KTunerEditor.java new file mode 100644 index 00000000..5989fdf2 --- /dev/null +++ b/src/source/tuner/rtl/e4k/E4KTunerEditor.java @@ -0,0 +1,499 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package source.tuner.rtl.e4k; + +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 javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +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.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationEditor; +import source.tuner.configuration.TunerConfigurationEvent; +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.e4k.E4KTunerController.E4KEnhanceGain; +import source.tuner.rtl.e4k.E4KTunerController.E4KGain; +import source.tuner.rtl.e4k.E4KTunerController.E4KLNAGain; +import source.tuner.rtl.e4k.E4KTunerController.E4KMixerGain; + +public class E4KTunerEditor extends TunerConfigurationEditor +{ + private final static Logger mLog = LoggerFactory.getLogger( E4KTunerEditor.class ); + + private static final long serialVersionUID = 1L; + + private JTextField mConfigurationName; + private JButton mTunerInfo; + private JComboBox mComboSampleRate; + private JSpinner mFrequencyCorrection; + private JComboBox mComboMasterGain; + private JComboBox mComboMixerGain; + private JComboBox mComboLNAGain; + private JComboBox mComboEnhanceGain; + + private E4KTunerController mController; + private boolean mLoading; + + public E4KTunerEditor( TunerConfigurationModel tunerConfigurationModel, RTL2832Tuner tuner ) + { + super( tunerConfigurationModel ); + mController = (E4KTunerController)tuner.getController(); + + init(); + } + + private E4KTunerConfiguration getConfiguration() + { + if( hasItem() ) + { + return (E4KTunerConfiguration)getItem(); + } + + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][][][grow]" ) ); + + add( new JLabel( "E4000 Tuner Configuration" ), "span,align center" ); + + /** + * Tuner Configuration Name + */ + add( new JLabel( "Name:" ) ); + mConfigurationName = new JTextField(); + mConfigurationName.setEnabled( false ); + mConfigurationName.addFocusListener( new FocusListener() + { + @Override + public void focusLost( FocusEvent e ) + { + save(); + } + @Override + public void focusGained( FocusEvent e ) {} + } ); + + add( mConfigurationName, "span 2" ); + + mTunerInfo = new JButton( "Tuner Info" ); + mTunerInfo.setEnabled( false ); + mTunerInfo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( mTunerInfo ); + + mComboSampleRate = new JComboBox<>( SampleRate.values() ); + mComboSampleRate.setEnabled( false ); + mComboSampleRate.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + SampleRate sampleRate = (SampleRate)mComboSampleRate.getSelectedItem(); + + try + { + mController.setSampleRate( sampleRate ); + save(); + } + catch ( SourceException | LibUsbException eSampleRate ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, + "E4K Tuner Controller - couldn't apply the sample rate setting [" + + sampleRate.getLabel() + "] " + eSampleRate.getLocalizedMessage() ); + + mLog.error( "E4K Tuner Controller - couldn't apply sample rate setting [" + + sampleRate.getLabel() + "]", eSampleRate ); + } + } + } ); + add( new JLabel( "Sample Rate:" ) ); + add( mComboSampleRate ); + + 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 ); + save(); + } + catch ( SourceException e1 ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " + + "couldn't apply frequency correction value: " + value + e1.getLocalizedMessage() ); + + mLog.error( "E4K Tuner Controller - couldn't apply frequency correction value:" + + value, e1 ); + } + } + } ); + + add( new JLabel( "PPM:" ) ); + add( mFrequencyCorrection ); + + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); + add( new JLabel( "Gain" ), "wrap" ); + + mComboMasterGain = new JComboBox( E4KGain.values() ); + mComboMasterGain.setEnabled( false ); + mComboMasterGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + E4KGain gain = (E4KGain)mComboMasterGain.getSelectedItem(); + + mController.setGain( (E4KGain)mComboMasterGain.getSelectedItem(), true ); + + if( gain == E4KGain.MANUAL ) + { + mComboMixerGain.setSelectedItem( mController.getMixerGain( true ) ); + mComboMixerGain.setEnabled( true ); + + mComboLNAGain.setSelectedItem( mController.getLNAGain( true ) ); + mComboLNAGain.setEnabled( true ); + + mComboEnhanceGain.setSelectedItem( mController.getEnhanceGain( true ) ); + mComboEnhanceGain.setEnabled( true ); + } + else + { + mComboMixerGain.setEnabled( false ); + mComboMixerGain.setSelectedItem( gain.getMixerGain() ); + + mComboLNAGain.setEnabled( false ); + mComboLNAGain.setSelectedItem( gain.getLNAGain() ); + + mComboEnhanceGain.setEnabled( false ); + mComboEnhanceGain.setSelectedItem( gain.getEnhanceGain() ); + } + + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " + + "couldn't apply the gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "E4K Tuner Controller - couldn't apply gain setting", e ); + } + } + } ); + + mComboMasterGain.setToolTipText( "Select AUTOMATIC for auto " + + "gain, MANUAL to enable
    independent control of " + + "Mixer, LNA and Enhance gain
    " + + "settings, or one of the individual gain settings for
    " + + "semi-manual gain control" ); + add( new JLabel( "Master:" ) ); + add( mComboMasterGain ); + + mComboMixerGain = new JComboBox( E4KMixerGain.values() ); + mComboMixerGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + E4KMixerGain mixerGain = (E4KMixerGain)mComboMixerGain.getSelectedItem(); + mController.setMixerGain( mixerGain, true ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " + + "couldn't apply the mixer gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "E4K Tuner Controller - couldn't apply mixer gain setting", e ); + } + } + } ); + mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " + + "to MASTER to enable adjustment" ); + mComboMixerGain.setEnabled( false ); + add( new JLabel( "Mixer:" ) ); + add( mComboMixerGain ); + + mComboLNAGain = new JComboBox( E4KLNAGain.values() ); + mComboLNAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + E4KLNAGain lnaGain = (E4KLNAGain)mComboLNAGain.getSelectedItem(); + mController.setLNAGain( lnaGain, true ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " + + "couldn't apply the LNA gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "E4K Tuner Controller - couldn't apply LNA gain setting - ", e ); + } + } + } ); + mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " + + "to MANUAL to enable adjustment" ); + mComboLNAGain.setEnabled( false ); + add( new JLabel( "LNA:" ) ); + add( mComboLNAGain ); + + mComboEnhanceGain = new JComboBox( E4KEnhanceGain.values() ); + mComboEnhanceGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + E4KEnhanceGain enhance = (E4KEnhanceGain)mComboEnhanceGain.getSelectedItem(); + mController.setEnhanceGain( enhance, true ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " + + "couldn't apply the enhance gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "E4K Tuner Controller - couldn't apply enhance gain setting", e ); + } + } + } ); + mComboEnhanceGain.setToolTipText( "Enhance Gain. Set master gain " + + "to MANUAL to enable adjustment" ); + mComboEnhanceGain.setEnabled( false ); + add( new JLabel( "Enhance:" ) ); + add( mComboEnhanceGain ); + } + + /** + * Sets each of the tuner configuration controls to the enabled argument state + */ + private void setControlsEnabled( boolean enabled ) + { + if( mConfigurationName.isEnabled() != enabled ) + { + mConfigurationName.setEnabled( enabled ); + } + + if( mTunerInfo.isEnabled() != enabled ) + { + mTunerInfo.setEnabled( enabled ); + } + + if( mFrequencyCorrection.isEnabled() != enabled ) + { + mFrequencyCorrection.setEnabled( enabled ); + } + + if( mComboSampleRate.isEnabled() != enabled ) + { + mComboSampleRate.setEnabled( enabled ); + } + + if( mComboMasterGain.isEnabled() != enabled ) + { + mComboMasterGain.setEnabled( enabled ); + } + + if( mComboMixerGain.isEnabled() != enabled ) + { + mComboMixerGain.setEnabled( enabled ); + } + + if( mComboLNAGain.isEnabled() != enabled ) + { + mComboLNAGain.setEnabled( enabled ); + } + + if( mComboEnhanceGain.isEnabled() != enabled ) + { + mComboEnhanceGain.setEnabled( enabled ); + } + } + + private String getTunerInfo() + { + StringBuilder sb = new StringBuilder(); + + Descriptor descriptor = mController.getDescriptor(); + + sb.append( "

    RTL-2832 with E4000 Tuner

    " ); + + if( descriptor == null ) + { + sb.append( "No EEPROM Descriptor Available" ); + } + else + { + sb.append( "USB ID: " ); + sb.append( descriptor.getVendorID() ); + sb.append( ":" ); + sb.append( descriptor.getProductID() ); + sb.append( "
    " ); + + sb.append( "Vendor: " ); + sb.append( descriptor.getVendorLabel() ); + sb.append( "
    " ); + + sb.append( "Product: " ); + sb.append( descriptor.getProductLabel() ); + sb.append( "
    " ); + + sb.append( "Serial: " ); + sb.append( descriptor.getSerial() ); + sb.append( "
    " ); + + sb.append( "IR Enabled: " ); + sb.append( descriptor.irEnabled() ); + sb.append( "
    " ); + + sb.append( "Remote Wake: " ); + sb.append( descriptor.remoteWakeupEnabled() ); + sb.append( "
    " ); + } + + return sb.toString(); + } + + @Override + public void save() + { + if( hasItem() && !mLoading ) + { + E4KTunerConfiguration config = getConfiguration(); + + config.setName( mConfigurationName.getText() ); + + config.setSampleRate( (SampleRate)mComboSampleRate.getSelectedItem() ); + + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + + config.setMasterGain( (E4KGain)mComboMasterGain.getSelectedItem() ); + config.setMixerGain( (E4KMixerGain)mComboMixerGain.getSelectedItem() ); + config.setLNAGain( (E4KLNAGain)mComboLNAGain.getSelectedItem() ); + config.setEnhanceGain( (E4KEnhanceGain)mComboEnhanceGain.getSelectedItem() ); + + getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( + getConfiguration(), TunerConfigurationEvent.Event.CHANGE ) ); + } + + } + + @Override + public void setItem( TunerConfiguration tunerConfiguration ) + { + super.setItem( tunerConfiguration ); + + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + + if( hasItem() ) + { + E4KTunerConfiguration config = getConfiguration(); + + if( tunerConfiguration.isAssigned() ) + { + setControlsEnabled( true ); + + mConfigurationName.setText( config.getName() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mComboSampleRate.setSelectedItem( config.getSampleRate() ); + mComboMasterGain.setSelectedItem( config.getMasterGain() ); + mComboMixerGain.setSelectedItem( config.getMixerGain() ); + mComboLNAGain.setSelectedItem( config.getLNAGain() ); + mComboEnhanceGain.setSelectedItem( config.getEnhanceGain() ); + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( config.getName() ); + } + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( "" ); + } + + mLoading = false; + } +} diff --git a/src/source/tuner/rtl/e4k/E4KTunerEditorPanel.java b/src/source/tuner/rtl/e4k/E4KTunerEditorPanel.java deleted file mode 100644 index 5c883748..00000000 --- a/src/source/tuner/rtl/e4k/E4KTunerEditorPanel.java +++ /dev/null @@ -1,123 +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 - ******************************************************************************/ -package source.tuner.rtl.e4k; - -import gui.control.JFrequencyControl; - -import java.awt.Color; -import java.awt.EventQueue; - -import javax.swing.JLabel; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.SourceException; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; -import source.tuner.frequency.FrequencyChangeEvent.Event; -import source.tuner.rtl.RTL2832InfoPanel; - -import com.jidesoft.swing.JideTabbedPane; - -import controller.ResourceManager; - -public class E4KTunerEditorPanel extends JPanel implements FrequencyChangeListener -{ - private final static Logger mLog = - LoggerFactory.getLogger( E4KTunerEditorPanel.class ); - - private static final long serialVersionUID = 1L; - - private E4KTunerController mController; - private ResourceManager mResourceManager; - - private JFrequencyControl mFrequencyControl; - - public E4KTunerEditorPanel( E4KTunerController controller, - ResourceManager resourceManager ) - { - mController = controller; - mResourceManager = resourceManager; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][grow]", "[][][grow]" ) ); - - add( new JLabel( "RTL2832 with E4000 Tuner" ), "span 2, align center, wrap" ); - - mFrequencyControl = new JFrequencyControl(); - - mFrequencyControl.addListener( this ); - - /* Add frequency control as listener on the controller, to maintain - * sync with the tuned frequency value */ - mController.addListener( mFrequencyControl ); - - mFrequencyControl.setFrequency( mController.getFrequency(), false ); - - add( mFrequencyControl, "span 2, align center, wrap" ); - - JideTabbedPane tabs = new JideTabbedPane(); - tabs.setFont( this.getFont() ); - tabs.setForeground( Color.BLACK ); - - tabs.addTab( "Configuration", - new E4KTunerConfigurationPanel( mResourceManager, mController ) ); - - tabs.addTab( "Info", new RTL2832InfoPanel( mController ) ); - - add( tabs, "grow,push,span" ); - } - - /** - * Frequency change listener for the frequency control gui component to - * apply end-user requested frequency changes against the tuner - */ - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - if( event.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) - { - final long frequency = event.getValue().longValue(); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - mController.setFrequency( frequency ); - } - catch ( SourceException e ) - { - mLog.error( "E4KTunerController - error setting frequency [" + - frequency + "]", e ); - } - } - } ); - } - } -} diff --git a/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java b/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java index 8ecc7795..f1d92e1a 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java +++ b/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java @@ -19,8 +19,8 @@ package source.tuner.rtl.r820t; import javax.xml.bind.annotation.XmlAttribute; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.rtl.RTL2832TunerController.SampleRate; import source.tuner.rtl.r820t.R820TTunerController.R820TGain; import source.tuner.rtl.r820t.R820TTunerController.R820TLNAGain; @@ -41,12 +41,11 @@ public class R820TTunerConfiguration extends TunerConfiguration */ public R820TTunerConfiguration() { - this( "Default" ); } - public R820TTunerConfiguration( String name ) + public R820TTunerConfiguration( String uniqueID, String name ) { - super( name ); + super( uniqueID, name ); } @Override diff --git a/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java b/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java deleted file mode 100644 index fe08d0c3..00000000 --- a/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java +++ /dev/null @@ -1,698 +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 - ******************************************************************************/ -package source.tuner.rtl.r820t; - -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 org.usb4java.LibUsbException; - -import source.SourceException; -import source.tuner.TunerConfiguration; -import source.tuner.TunerConfigurationAssignment; -import source.tuner.TunerType; -import source.tuner.rtl.RTL2832TunerController.SampleRate; -import source.tuner.rtl.r820t.R820TTunerController.R820TGain; -import source.tuner.rtl.r820t.R820TTunerController.R820TLNAGain; -import source.tuner.rtl.r820t.R820TTunerController.R820TMixerGain; -import source.tuner.rtl.r820t.R820TTunerController.R820TVGAGain; -import controller.ResourceManager; - -public class R820TTunerConfigurationPanel extends JPanel -{ - private final static Logger mLog = - LoggerFactory.getLogger( R820TTunerConfigurationPanel.class ); - - private static final long serialVersionUID = 1L; - private static final R820TGain DEFAULT_GAIN = R820TGain.GAIN_279; - - private ResourceManager mResourceManager; - private R820TTunerController mController; - private R820TTunerConfiguration mSelectedConfig; - - private JButton mNewConfiguration; - private JButton mDeleteConfiguration; - private JComboBox mComboConfigurations; - - private JTextField mName; - - private JSpinner mFrequencyCorrection; - private JSpinner mSampleRateCorrection; - private JComboBox mComboMasterGain; - private JComboBox mComboMixerGain; - private JComboBox mComboLNAGain; - private JComboBox mComboVGAGain; - - private JComboBox mComboSampleRate; - - public R820TTunerConfigurationPanel( ResourceManager resourceManager, - R820TTunerController 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 = mResourceManager - .getSettingsManager().getSelectedTunerConfiguration( - TunerType.RAFAELMICRO_R820T, mController.getUniqueID() ); - - 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 - mResourceManager.getSettingsManager() - .setSelectedTunerConfiguration( - TunerType.RAFAELMICRO_R820T, - mController.getUniqueID(), mSelectedConfig ); - } - - mComboConfigurations.setSelectedItem( mSelectedConfig ); - - mComboConfigurations.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - R820TTunerConfiguration selected = - (R820TTunerConfiguration)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( - R820TTunerConfigurationPanel.this, - "R820T Tuner Controller - couldn't apply " - + "frequency correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply " - + "frequency correction value: " + value, e1 ); - } - } - } ); - } - } ); - - add( new JLabel( "Correction PPM:" ) ); - add( mFrequencyCorrection, "growx,push" ); - - /** - * Sample Rate - */ - mComboSampleRate = new JComboBox<>( SampleRate.values() ); - mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - mComboSampleRate.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - SampleRate sampleRate = - (SampleRate)mComboSampleRate.getSelectedItem(); - try - { - - mController.setSampleRate( sampleRate ); - - mSelectedConfig.setSampleRate( sampleRate ); - save(); - } - catch ( SourceException | LibUsbException eSampleRate ) - { - JOptionPane.showMessageDialog( - R820TTunerConfigurationPanel.this, - "R820T Tuner Controller - couldn't apply the sample " - + "rate setting [" + sampleRate.getLabel() + "] " + - eSampleRate.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply sample " - + "rate setting [" + sampleRate.getLabel() + "]", - eSampleRate ); - } - } - } ); - add( new JLabel( "Sample Rate:" ) ); - add( mComboSampleRate, "growx,push" ); - -// /** -// * Sample Rate Correction -// */ -// SpinnerModel sampleRateCorrectionModel = -// new SpinnerNumberModel( 0, //initial value -// -32768, //min -// 32767, //max -// 1 ); //step -// -// mSampleRateCorrection = new JSpinner( sampleRateCorrectionModel ); -// -// JSpinner.NumberEditor sampleRateCorrectionEditor = -// (JSpinner.NumberEditor)mSampleRateCorrection.getEditor(); -// -// DecimalFormat sampleRateCorrectionFormat = -// sampleRateCorrectionEditor.getFormat(); -// -// sampleRateCorrectionFormat.setMinimumFractionDigits( 0 ); -// -// sampleRateCorrectionEditor.getTextField() -// .setHorizontalAlignment( SwingConstants.CENTER ); -// -// mSampleRateCorrection.addChangeListener( new ChangeListener() -// { -// @Override -// public void stateChanged( ChangeEvent e ) -// { -// final int value = ((SpinnerNumberModel)mSampleRateCorrection -// .getModel()).getNumber().intValue(); -// -// EventQueue.invokeLater( new Runnable() -// { -// @Override -// public void run() -// { -// try -// { -// mController.setSampleRateFrequencyCorrection( value ); -// } -// catch ( SourceException | LibUsbException e1 ) -// { -// JOptionPane.showMessageDialog( -// R820TTunerConfigurationPanel.this, -// "E4K Tuner Controller - couldn't apply " -// + "sample rate correction value: " + value + -// e1.getLocalizedMessage() ); -// -// mLog.error( "E4K Tuner Controller - couldn't apply " -// + "sample rate correction value: " + value + -// e1.getLocalizedMessage() ); -// } -// } -// } ); -// } -// } ); -// -// add( mSampleRateCorrection ); -// add( new JLabel( "Sample Rate Correction (ppm)" ), "grow,push" ); - - /** - * Gain Controls - */ - JPanel gainPanel = new JPanel(); - gainPanel.setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]" ) ); - gainPanel.setBorder( BorderFactory.createTitledBorder( "Gain" ) ); - - /* Master Gain Control */ - mComboMasterGain = new JComboBox( R820TGain.values() ); - mComboMasterGain.setSelectedItem( mSelectedConfig.getMasterGain() ); - - mComboMasterGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - R820TGain gain = (R820TGain)mComboMasterGain.getSelectedItem(); - - mController.setGain( (R820TGain)mComboMasterGain.getSelectedItem(), true ); - - if( gain == R820TGain.MANUAL ) - { - mComboMixerGain.setSelectedItem( gain.getMixerGain() ); - mComboMixerGain.setEnabled( true ); - - mComboLNAGain.setSelectedItem( gain.getLNAGain() ); - mComboLNAGain.setEnabled( true ); - - mComboVGAGain.setSelectedItem( gain.getVGAGain() ); - mComboVGAGain.setEnabled( true ); - } - else - { - mComboMixerGain.setEnabled( false ); - mComboMixerGain.setSelectedItem( gain.getMixerGain() ); - - mComboLNAGain.setEnabled( false ); - mComboLNAGain.setSelectedItem( gain.getLNAGain() ); - - mComboVGAGain.setEnabled( false ); - mComboVGAGain.setSelectedItem( gain.getVGAGain() ); - } - - mSelectedConfig.setMasterGain( gain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - R820TTunerConfigurationPanel.this, - "R820T Tuner Controller - couldn't apply the gain " - + "setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply " - + "gain setting - ", e ); - } - } - } ); - mComboMasterGain.setToolTipText( "Select AUTOMATIC for auto " - + "gain, MANUAL to enable
    independent control of " - + "Mixer, LNA and Enhance gain
    " - + "settings, or one of the individual gain settings for
    " - + "semi-manual gain control" ); - gainPanel.add( new JLabel( "Master" ) ); - gainPanel.add( mComboMasterGain ); - - /* Mixer Gain Control */ - mComboMixerGain = new JComboBox( R820TMixerGain.values() ); - mComboMixerGain.setSelectedItem( mSelectedConfig.getMixerGain() ); - mComboMixerGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - R820TMixerGain mixerGain = - (R820TMixerGain)mComboMixerGain.getSelectedItem(); - - if( mixerGain == null ) - { - mixerGain = DEFAULT_GAIN.getMixerGain(); - } - - if( mComboMixerGain.isEnabled() ) - { - mController.setMixerGain( mixerGain, true ); - } - - mSelectedConfig.setMixerGain( mixerGain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - R820TTunerConfigurationPanel.this, - "R820T Tuner Controller - couldn't apply the mixer " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply mixer " - + "gain setting - ", e ); - } - } - } ); - } - } ); - mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " - + "to MASTER to enable adjustment" ); - mComboMixerGain.setEnabled( false ); - gainPanel.add( new JLabel( "Mixer" ) ); - gainPanel.add( mComboMixerGain, "wrap" ); - - /* LNA Gain Control */ - mComboLNAGain = new JComboBox( R820TLNAGain.values() ); - mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); - mComboLNAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - R820TLNAGain lnaGain = - (R820TLNAGain)mComboLNAGain.getSelectedItem(); - - if ( lnaGain == null ) - { - lnaGain = DEFAULT_GAIN.getLNAGain(); - } - - if( mComboLNAGain.isEnabled() ) - { - mController.setLNAGain( lnaGain, true ); - } - - mSelectedConfig.setLNAGain( lnaGain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - R820TTunerConfigurationPanel.this, - "R820T Tuner Controller - couldn't apply the LNA " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply LNA " - + "gain setting - ", e ); - } - } - } ); - mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - mComboLNAGain.setEnabled( false ); - gainPanel.add( new JLabel( "LNA" ) ); - gainPanel.add( mComboLNAGain ); - - /* VGA Gain Control */ - mComboVGAGain = new JComboBox( R820TVGAGain.values() ); - mComboVGAGain.setSelectedItem( mSelectedConfig.getVGAGain() ); - mComboVGAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - R820TVGAGain vgaGain = - (R820TVGAGain)mComboVGAGain.getSelectedItem(); - - if( vgaGain == null ) - { - vgaGain = DEFAULT_GAIN.getVGAGain(); - } - - if( mComboVGAGain.isEnabled() ) - { - mController.setVGAGain( vgaGain, true ); - } - - mSelectedConfig.setVGAGain( vgaGain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - R820TTunerConfigurationPanel.this, - "R820T Tuner Controller - couldn't apply the VGA " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply VGA " - + "gain setting", e ); - } - } - } ); - mComboVGAGain.setToolTipText( "VGA Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - mComboVGAGain.setEnabled( false ); - 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.RAFAELMICRO_R820T, - "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 ) - { - R820TTunerConfiguration selected = - (R820TTunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - int n = JOptionPane.showConfirmDialog( - R820TTunerConfigurationPanel.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( R820TTunerConfiguration config ) - { - mSelectedConfig = config; - - try - { - mController.apply( config ); - - mName.setText( config.getName() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - - mComboMixerGain.setSelectedItem( mSelectedConfig.getMixerGain() ); - mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); - mComboVGAGain.setSelectedItem( mSelectedConfig.getVGAGain() ); - - /* Apply master gain last so that the Mixer, LNA, and VGA gain - * settings are updated with the master setting where necessary */ - mComboMasterGain.setSelectedItem( mSelectedConfig.getMasterGain() ); - - mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - - mResourceManager.getSettingsManager().setSelectedTunerConfiguration( - TunerType.RAFAELMICRO_R820T, mController.getUniqueID(), config ); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - R820TTunerConfigurationPanel.this, - "R820T Tuner Controller - couldn't " - + "apply the tuner configuration settings - " + - e1.getLocalizedMessage() ); - - mLog.error( "R820T 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.RAFAELMICRO_R820T ); - - DefaultComboBoxModel model = - new DefaultComboBoxModel(); - - for( TunerConfiguration config: configs ) - { - model.addElement( (R820TTunerConfiguration)config ); - } - - return model; - } - - /** - * Retrieves a specific (named) tuner configuration - */ - private R820TTunerConfiguration getNamedConfiguration( String name ) - { - ArrayList configs = - mResourceManager.getSettingsManager() - .getTunerConfigurations( TunerType.RAFAELMICRO_R820T ); - - for( TunerConfiguration config: configs ) - { - if( config.getName().contentEquals( name ) ) - { - return (R820TTunerConfiguration)config; - } - } - - return null; - } - - /** - * Saves current tuner configuration settings - */ - private void save() - { - mResourceManager.getSettingsManager().save(); - } -} diff --git a/src/source/tuner/rtl/r820t/R820TTunerController.java b/src/source/tuner/rtl/r820t/R820TTunerController.java index 4713697d..766c1091 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerController.java +++ b/src/source/tuner/rtl/r820t/R820TTunerController.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * Java port of librtlsdr * @@ -34,10 +34,9 @@ import org.usb4java.DeviceDescriptor; import org.usb4java.LibUsbException; import source.SourceException; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.rtl.RTL2832TunerController; -import controller.ResourceManager; import controller.ThreadPoolManager; public class R820TTunerController extends RTL2832TunerController @@ -136,19 +135,6 @@ public class R820TTunerController extends RTL2832TunerController } } - /** - * Controller gui editor component for this tuner - */ - public JPanel getEditor( ResourceManager resourceManager ) - { - if( mEditor == null ) - { - mEditor = new R820TTunerEditorPanel( this, resourceManager ); - } - - return mEditor; - } - /** * Not implemented. */ diff --git a/src/source/tuner/rtl/r820t/R820TTunerEditor.java b/src/source/tuner/rtl/r820t/R820TTunerEditor.java new file mode 100644 index 00000000..0d55490c --- /dev/null +++ b/src/source/tuner/rtl/r820t/R820TTunerEditor.java @@ -0,0 +1,559 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package source.tuner.rtl.r820t; + +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 javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +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.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; +import source.tuner.rtl.r820t.R820TTunerController.R820TMixerGain; +import source.tuner.rtl.r820t.R820TTunerController.R820TVGAGain; + +public class R820TTunerEditor extends TunerConfigurationEditor +{ + private final static Logger mLog = LoggerFactory.getLogger( R820TTunerEditor.class ); + + private static final long serialVersionUID = 1L; + private static final R820TGain DEFAULT_GAIN = R820TGain.GAIN_279; + + private R820TTunerController mController; + + private JTextField mConfigurationName; + private JButton mTunerInfo; + private JComboBox mComboSampleRate; + private JSpinner mFrequencyCorrection; + private JComboBox mComboMasterGain; + private JComboBox mComboMixerGain; + private JComboBox mComboLNAGain; + private JComboBox mComboVGAGain; + private boolean mLoading; + + public R820TTunerEditor( TunerConfigurationModel tunerConfigurationModel, RTL2832Tuner tuner ) + { + super( tunerConfigurationModel ); + mController = (R820TTunerController)tuner.getController(); + + init(); + } + + private R820TTunerConfiguration getConfiguration() + { + if( hasItem() ) + { + return (R820TTunerConfiguration)getItem(); + } + + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][][][grow]" ) ); + + add( new JLabel( "R820T Tuner Configuration" ), "span,align center" ); + + mConfigurationName = new JTextField(); + mConfigurationName.setEnabled( false ); + mConfigurationName.addFocusListener( new FocusListener() + { + @Override + public void focusLost( FocusEvent e ) + { + save(); + } + @Override + public void focusGained( FocusEvent e ) {} + } ); + + add( new JLabel( "Name:" ) ); + add( mConfigurationName, "span 2" ); + + mTunerInfo = new JButton( "Tuner Info" ); + mTunerInfo.setEnabled( false ); + mTunerInfo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + JOptionPane.showMessageDialog( R820TTunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( mTunerInfo ); + + mComboSampleRate = new JComboBox<>( SampleRate.values() ); + mComboSampleRate.setEnabled( false ); + mComboSampleRate.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + SampleRate sampleRate = + (SampleRate)mComboSampleRate.getSelectedItem(); + try + { + + mController.setSampleRate( sampleRate ); + save(); + } + catch ( SourceException | LibUsbException eSampleRate ) + { + JOptionPane.showMessageDialog( + R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the sample " + + "rate setting [" + sampleRate.getLabel() + "] " + + eSampleRate.getLocalizedMessage() ); + + mLog.error( "R820T Tuner Controller - couldn't apply sample " + + "rate setting [" + sampleRate.getLabel() + "]", + eSampleRate ); + } + } + } ); + add( new JLabel( "Sample Rate:" ) ); + add( mComboSampleRate ); + + /** + * 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( JSeparator.HORIZONTAL ), "span,grow" ); + + /** + * Gain Controls + */ + add( new JLabel( "Gain" ), "warp" ); + + /* Master Gain Control */ + mComboMasterGain = new JComboBox( R820TGain.values() ); + mComboMasterGain.setEnabled( false ); + mComboMasterGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + R820TGain gain = (R820TGain)mComboMasterGain.getSelectedItem(); + + mController.setGain( (R820TGain)mComboMasterGain.getSelectedItem(), true ); + + if( gain == R820TGain.MANUAL ) + { + mComboMixerGain.setSelectedItem( gain.getMixerGain() ); + mComboMixerGain.setEnabled( true ); + + mComboLNAGain.setSelectedItem( gain.getLNAGain() ); + mComboLNAGain.setEnabled( true ); + + mComboVGAGain.setSelectedItem( gain.getVGAGain() ); + mComboVGAGain.setEnabled( true ); + } + else + { + mComboMixerGain.setEnabled( false ); + mComboMixerGain.setSelectedItem( gain.getMixerGain() ); + + mComboLNAGain.setEnabled( false ); + mComboLNAGain.setSelectedItem( gain.getLNAGain() ); + + mComboVGAGain.setEnabled( false ); + mComboVGAGain.setSelectedItem( gain.getVGAGain() ); + } + + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( + R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the gain " + + "setting - " + e.getLocalizedMessage() ); + + mLog.error( "R820T Tuner Controller - couldn't apply " + + "gain setting - ", e ); + } + } + } ); + mComboMasterGain.setToolTipText( "Select AUTOMATIC for auto " + + "gain, MANUAL to enable
    independent control of " + + "Mixer, LNA and Enhance gain
    " + + "settings, or one of the individual gain settings for
    " + + "semi-manual gain control" ); + add( new JLabel( "Master:" ) ); + add( mComboMasterGain ); + + /* Mixer Gain Control */ + mComboMixerGain = new JComboBox( R820TMixerGain.values() ); + mComboMixerGain.setEnabled( false ); + mComboMixerGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + R820TMixerGain mixerGain = + (R820TMixerGain)mComboMixerGain.getSelectedItem(); + + if( mixerGain == null ) + { + mixerGain = DEFAULT_GAIN.getMixerGain(); + } + + if( mComboMixerGain.isEnabled() ) + { + mController.setMixerGain( mixerGain, true ); + } + + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the mixer " + + "gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "R820T Tuner Controller - couldn't apply mixer " + + "gain setting - ", e ); + } + } + } ); + mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " + + "to MASTER to enable adjustment" ); + + add( new JLabel( "Mixer:" ) ); + add( mComboMixerGain ); + + /* LNA Gain Control */ + mComboLNAGain = new JComboBox( R820TLNAGain.values() ); + mComboLNAGain.setEnabled( false ); + mComboLNAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + R820TLNAGain lnaGain = + (R820TLNAGain)mComboLNAGain.getSelectedItem(); + + if ( lnaGain == null ) + { + lnaGain = DEFAULT_GAIN.getLNAGain(); + } + + if( mComboLNAGain.isEnabled() ) + { + mController.setLNAGain( lnaGain, true ); + } + + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the LNA " + + "gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "R820T Tuner Controller - couldn't apply LNA " + + "gain setting - ", e ); + } + } + } ); + mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " + + "to MANUAL to enable adjustment" ); + + add( new JLabel( "LNA:" ) ); + add( mComboLNAGain ); + + /* VGA Gain Control */ + mComboVGAGain = new JComboBox( R820TVGAGain.values() ); + mComboVGAGain.setEnabled( false ); + mComboVGAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + R820TVGAGain vgaGain = + (R820TVGAGain)mComboVGAGain.getSelectedItem(); + + if( vgaGain == null ) + { + vgaGain = DEFAULT_GAIN.getVGAGain(); + } + + if( mComboVGAGain.isEnabled() ) + { + mController.setVGAGain( vgaGain, true ); + } + + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the VGA " + + "gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "R820T Tuner Controller - couldn't apply VGA " + + "gain setting", e ); + } + } + } ); + mComboVGAGain.setToolTipText( "VGA Gain. Set master gain " + + "to MANUAL to enable adjustment" ); + add( new JLabel( "VGA:" ) ); + add( mComboVGAGain ); + } + + /** + * Sets each of the tuner configuration controls to the enabled argument state + */ + private void setControlsEnabled( boolean enabled ) + { + if( mConfigurationName.isEnabled() != enabled ) + { + mConfigurationName.setEnabled( enabled ); + } + + if( mTunerInfo.isEnabled() != enabled ) + { + mTunerInfo.setEnabled( enabled ); + } + + if( mFrequencyCorrection.isEnabled() != enabled ) + { + mFrequencyCorrection.setEnabled( enabled ); + } + + if( mComboSampleRate.isEnabled() != enabled ) + { + mComboSampleRate.setEnabled( enabled ); + } + + if( mComboMasterGain.isEnabled() != enabled ) + { + mComboMasterGain.setEnabled( enabled ); + } + + if( mComboMixerGain.isEnabled() != enabled ) + { + mComboMixerGain.setEnabled( enabled ); + } + + if( mComboLNAGain.isEnabled() != enabled ) + { + mComboLNAGain.setEnabled( enabled ); + } + + if( mComboVGAGain.isEnabled() != enabled ) + { + mComboVGAGain.setEnabled( enabled ); + } + } + + private String getTunerInfo() + { + StringBuilder sb = new StringBuilder(); + + Descriptor descriptor = mController.getDescriptor(); + + sb.append( "

    RTL-2832 with R820T Tuner

    " ); + + if( descriptor == null ) + { + sb.append( "No EEPROM Descriptor Available" ); + } + else + { + sb.append( "USB ID: " ); + sb.append( descriptor.getVendorID() ); + sb.append( ":" ); + sb.append( descriptor.getProductID() ); + sb.append( "
    " ); + + sb.append( "Vendor: " ); + sb.append( descriptor.getVendorLabel() ); + sb.append( "
    " ); + + sb.append( "Product: " ); + sb.append( descriptor.getProductLabel() ); + sb.append( "
    " ); + + sb.append( "Serial: " ); + sb.append( descriptor.getSerial() ); + sb.append( "
    " ); + + sb.append( "IR Enabled: " ); + sb.append( descriptor.irEnabled() ); + sb.append( "
    " ); + + sb.append( "Remote Wake: " ); + sb.append( descriptor.remoteWakeupEnabled() ); + sb.append( "
    " ); + } + + return sb.toString(); + } + + @Override + public void save() + { + if( hasItem() && !mLoading ) + { + R820TTunerConfiguration config = getConfiguration(); + + config.setName( mConfigurationName.getText() ); + + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + + config.setSampleRate( (SampleRate)mComboSampleRate.getSelectedItem() ); + + R820TGain gain = (R820TGain)mComboMasterGain.getSelectedItem(); + config.setMasterGain( gain ); + + R820TMixerGain mixerGain = (R820TMixerGain)mComboMixerGain.getSelectedItem(); + config.setMixerGain( mixerGain ); + + R820TLNAGain lnaGain = (R820TLNAGain)mComboLNAGain.getSelectedItem(); + config.setLNAGain( lnaGain ); + + R820TVGAGain vgaGain = (R820TVGAGain)mComboVGAGain.getSelectedItem(); + config.setVGAGain( vgaGain ); + + getTunerConfigurationModel().broadcast( + new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); + } + } + + @Override + public void setItem( TunerConfiguration tunerConfiguration ) + { + super.setItem( tunerConfiguration ); + + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + + if( hasItem() ) + { + R820TTunerConfiguration config = getConfiguration(); + + if( tunerConfiguration.isAssigned() ) + { + setControlsEnabled( true ); + mConfigurationName.setText( config.getName() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mComboSampleRate.setSelectedItem( config.getSampleRate() ); + mComboMasterGain.setSelectedItem( config.getMasterGain() ); + mComboMixerGain.setSelectedItem( config.getMixerGain() ); + mComboLNAGain.setSelectedItem( config.getLNAGain() ); + mComboVGAGain.setSelectedItem( config.getVGAGain() ); + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( config.getName() ); + } + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( "" ); + } + + mLoading = false; + } +} diff --git a/src/source/tuner/rtl/r820t/R820TTunerEditorPanel.java b/src/source/tuner/rtl/r820t/R820TTunerEditorPanel.java deleted file mode 100644 index f59d29a7..00000000 --- a/src/source/tuner/rtl/r820t/R820TTunerEditorPanel.java +++ /dev/null @@ -1,122 +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 - ******************************************************************************/ -package source.tuner.rtl.r820t; - -import gui.control.JFrequencyControl; - -import java.awt.Color; -import java.awt.EventQueue; - -import javax.swing.JLabel; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.SourceException; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; -import source.tuner.frequency.FrequencyChangeEvent.Event; -import source.tuner.rtl.RTL2832InfoPanel; - -import com.jidesoft.swing.JideTabbedPane; - -import controller.ResourceManager; - -public class R820TTunerEditorPanel extends JPanel implements FrequencyChangeListener -{ - private final static Logger mLog = - LoggerFactory.getLogger( R820TTunerEditorPanel.class ); - - private static final long serialVersionUID = 1L; - - private R820TTunerController mController; - private ResourceManager mResourceManager; - - private JFrequencyControl mFrequencyControl; - - public R820TTunerEditorPanel( R820TTunerController controller, - ResourceManager resourceManager ) - { - mController = controller; - mResourceManager = resourceManager; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][grow]", "[][][grow]" ) ); - - add( new JLabel( "RTL2832 with R820T Tuner" ), "span 2, align center, wrap" ); - - mFrequencyControl = new JFrequencyControl(); - - mFrequencyControl.addListener( this ); - - /* Add frequency control as frequency change listener. */ - mController.addListener( mFrequencyControl ); - - mFrequencyControl.setFrequency( mController.getFrequency(), false ); - - add( mFrequencyControl, "span 2, align center, wrap" ); - - JideTabbedPane tabs = new JideTabbedPane(); - tabs.setFont( this.getFont() ); - tabs.setForeground( Color.BLACK ); - - tabs.addTab( "Configuration", - new R820TTunerConfigurationPanel( mResourceManager, mController ) ); - - tabs.addTab( "Info", new RTL2832InfoPanel( mController ) ); - - add( tabs, "grow,push,span" ); - } - - /** - * Frequency change listener for the frequency control gui component to - * apply end-user requested frequency changes against the tuner - */ - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - if( event.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) - { - final long frequency = event.getValue().longValue(); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - mController.setFrequency( frequency ); - } - catch ( SourceException e ) - { - mLog.error( "R820T Tuner Controller - error setting frequency [" + - frequency + "]", e ); - } - } - } ); - } - } -} diff --git a/src/source/wave/ComplexWaveSource.java b/src/source/wave/ComplexWaveSource.java index 235c4717..efa8b14b 100644 --- a/src/source/wave/ComplexWaveSource.java +++ b/src/source/wave/ComplexWaveSource.java @@ -20,6 +20,7 @@ package source.wave; import java.io.File; import java.io.IOException; import java.util.Arrays; +import java.util.concurrent.ScheduledExecutorService; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; @@ -51,11 +52,42 @@ public class ComplexWaveSource extends ComplexSource implements IControllableFil public ComplexWaveSource( File file ) throws IOException { - super( file.getAbsolutePath() ); - mFile = file; } + @Override + public void reset() + { + stop(); + start( null ); + } + + @Override + public void start( ScheduledExecutorService executor ) + { + try + { + open(); + } + catch ( IOException | UnsupportedAudioFileException e ) + { + mLog.error( "Error starting complex wave source" ); + } + } + + @Override + public void stop() + { + try + { + close(); + } + catch ( IOException e ) + { + mLog.error( "Error stopping complex wave source" ); + } + } + @Override public long getFrameCount() throws IOException { diff --git a/src/source/wave/RealWaveSource.java b/src/source/wave/RealWaveSource.java index a7251674..06e4fc25 100644 --- a/src/source/wave/RealWaveSource.java +++ b/src/source/wave/RealWaveSource.java @@ -20,6 +20,7 @@ package source.wave; import java.io.File; import java.io.IOException; import java.util.Arrays; +import java.util.concurrent.ScheduledExecutorService; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; @@ -52,11 +53,46 @@ public class RealWaveSource extends RealSource public RealWaveSource( File file ) throws IOException { - super( file.getAbsolutePath() ); - mFile = file; } + + @Override + public void reset() + { + stop(); + start( null ); + } + + + @Override + public void start( ScheduledExecutorService executor ) + { + try + { + open(); + } + catch ( IOException | UnsupportedAudioFileException e ) + { + mLog.error( "Error starting real wave source", e ); + } + } + + + @Override + public void stop() + { + try + { + close(); + } + catch ( IOException e ) + { + mLog.error( "Error stopping real wave source", e ); + } + } + + @Override public long getFrameCount() throws IOException { diff --git a/src/spectrum/ChannelSpectrumPanel.java b/src/spectrum/ChannelSpectrumPanel.java index 96cbefa1..397c3604 100644 --- a/src/spectrum/ChannelSpectrumPanel.java +++ b/src/spectrum/ChannelSpectrumPanel.java @@ -6,7 +6,6 @@ import java.awt.event.ComponentListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; -import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.JLayeredPane; @@ -16,15 +15,12 @@ import javax.swing.JPopupMenu; import javax.swing.JSeparator; import javax.swing.SwingUtilities; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import module.ProcessingChain; import net.miginfocom.swing.MigLayout; import sample.Listener; import sample.SampleType; import sample.complex.ComplexBuffer; import sample.real.RealBuffer; -import sample.real.RealSampleListener; import settings.ColorSetting.ColorSettingName; import settings.ColorSettingMenuItem; import settings.Setting; @@ -40,15 +36,13 @@ import spectrum.menu.FFTWindowTypeItem; import spectrum.menu.FrameRateItem; import spectrum.menu.SmoothingItem; import spectrum.menu.SmoothingTypeItem; -import controller.ResourceManager; import controller.channel.Channel; import controller.channel.ChannelEvent; import controller.channel.ChannelEventListener; +import controller.channel.ChannelProcessingManager; import dsp.filter.Filters; -import dsp.filter.FloatHalfBandFilter; import dsp.filter.Window.WindowType; import dsp.filter.halfband.real.HalfBandFilter_RB_RB; -import dsp.filter.hilbert.HilbertTransform; import dsp.filter.smoothing.SmoothingFilter.SmoothingType; public class ChannelSpectrumPanel extends JPanel @@ -57,11 +51,8 @@ public class ChannelSpectrumPanel extends JPanel SettingChangeListener, SpectralDisplayAdjuster { - private static final Logger mLog = LoggerFactory.getLogger( ChannelSpectrumPanel.class ); - private static final long serialVersionUID = 1L; -// private ResourceManager mResourceManager; private DFTProcessor mDFTProcessor = new DFTProcessor( SampleType.REAL ); private DFTResultsConverter mDFTConverter = new RealDecibelConverter(); private JLayeredPane mLayeredPane; @@ -71,8 +62,6 @@ public class ChannelSpectrumPanel extends JPanel private Channel mCurrentChannel; private int mSampleBufferSize = 2400; - private float[] mSamples = new float[ mSampleBufferSize ]; - private int mSamplePointer = 0; private HalfBandFilter_RB_RB mDecimatingFilter = new HalfBandFilter_RB_RB( Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true ); @@ -80,10 +69,13 @@ public class ChannelSpectrumPanel extends JPanel private AtomicBoolean mEnabled = new AtomicBoolean(); private SettingsManager mSettingsManager; + private ChannelProcessingManager mChannelProcessingManager; - public ChannelSpectrumPanel( SettingsManager settingsManager ) + public ChannelSpectrumPanel( SettingsManager settingsManager, + ChannelProcessingManager channelProcessingManager ) { mSettingsManager = settingsManager; + mChannelProcessingManager = channelProcessingManager; if( mSettingsManager != null ) { @@ -99,7 +91,7 @@ public class ChannelSpectrumPanel extends JPanel /* Set the DFTProcessor to the decimated 24kHz sample rate */ mDFTProcessor.frequencyChanged( - new FrequencyChangeEvent( Event.SAMPLE_RATE_CHANGE_NOTIFICATION, 24000 ) ); + new FrequencyChangeEvent( Event.NOTIFICATION_SAMPLE_RATE_CHANGE, 24000 ) ); initGui(); } @@ -166,15 +158,18 @@ public class ChannelSpectrumPanel extends JPanel { switch( event.getEvent() ) { - case CHANGE_SELECTED: - if( !event.getChannel().isSelected() ) + case NOTIFICATION_SELECTION_CHANGE: + //ChannelSelectionManager ensures that only 1 channel can be + //selected and any previously selected channel will be first + //deselected before we get a new selection event + if( event.getChannel().isSelected() ) { - stop(); + if( mCurrentChannel != null ) + { + stop(); + mCurrentChannel = null; + } - mCurrentChannel = null; - } - else - { mCurrentChannel = event.getChannel(); if( mEnabled.get() ) @@ -182,17 +177,13 @@ public class ChannelSpectrumPanel extends JPanel start(); } } - if( event.getChannel().isSelected() && - mCurrentChannel != event.getChannel() ) + else { stop(); - - mCurrentChannel = event.getChannel(); - - start(); + mCurrentChannel = null; } break; - case CHANNEL_DISABLED: + case NOTIFICATION_PROCESSING_STOP: if( event.getChannel() == mCurrentChannel ) { if( mEnabled.get() ) @@ -203,32 +194,36 @@ public class ChannelSpectrumPanel extends JPanel mCurrentChannel = null; } break; - case CHANNEL_ENABLED: - if( event.getChannel() == mCurrentChannel && - !event.getChannel().isProcessing() ) - { - stop(); - mCurrentChannel = null; - } - break; } } private void start() { - if( mEnabled.get() && mCurrentChannel != null && mCurrentChannel.isProcessing() ) + if( mEnabled.get() && mCurrentChannel != null && mCurrentChannel.getEnabled() ) { - mCurrentChannel.getProcessingChain().addRealBufferListener( this ); - mDFTProcessor.start(); + ProcessingChain processingChain = mChannelProcessingManager + .getProcessingChain( mCurrentChannel ); + + if( processingChain != null ) + { + processingChain.addRealBufferListener( this ); + + mDFTProcessor.start(); + } } } private void stop() { - - if( mCurrentChannel != null && mCurrentChannel.isProcessing() ) + if( mCurrentChannel != null && mCurrentChannel.getEnabled() ) { - mCurrentChannel.getProcessingChain().removeRealBufferListener( this ); + ProcessingChain processingChain = mChannelProcessingManager + .getProcessingChain( mCurrentChannel ); + + if( processingChain != null ) + { + processingChain.removeRealBufferListener( this ); + } } mDFTProcessor.stop(); diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java index bddddbec..a90c432d 100644 --- a/src/spectrum/DFTProcessor.java +++ b/src/spectrum/DFTProcessor.java @@ -37,7 +37,7 @@ import sample.Listener; import sample.SampleType; import sample.complex.ComplexBuffer; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeProcessor; import spectrum.converter.DFTResultsConverter; import controller.NamingThreadFactory; import dsp.filter.Window; @@ -48,7 +48,7 @@ import dsp.filter.Window.WindowType; * of DFT results, using configurable fft size and output dispatch timelines. */ public class DFTProcessor implements Listener, - FrequencyChangeListener, + IFrequencyChangeProcessor, IDFTWidthChangeProcessor { private final static Logger mLog = @@ -61,9 +61,9 @@ public class DFTProcessor implements Listener, //sample rate tuner (10 MHz) producing 153 frames per second and the lowest //DFT processing frame rate of 12 frames per second. This means that the //maximum size needed is 153 / 12 = 11 frames per DFT processing iteration - //so we set it at 12. + //so we set it at 12. private BlockingQueue mQueue = new ArrayBlockingQueue<>( 12 ); - + private ScheduledExecutorService mScheduler = Executors .newScheduledThreadPool( 1, new NamingThreadFactory( "spectrum dft" ) ); @@ -97,15 +97,15 @@ public class DFTProcessor implements Listener, public DFTProcessor( SampleType sampleType ) { setSampleType( sampleType ); + setFrameRate( 20 ); loadSettings(); } private void loadSettings() { - int frameRate = SystemProperties.getInstance() - .get( DFTProcessor.FRAME_RATE_PROPERTY, 20 ); - + int frameRate = SystemProperties.getInstance().get( FRAME_RATE_PROPERTY, 20 ); + setFrameRate( frameRate ); } @@ -187,10 +187,10 @@ public class DFTProcessor implements Listener, } mFrameRate = framesPerSecond; - + SystemProperties properties = SystemProperties.getInstance(); + properties.set( FRAME_RATE_PROPERTY, String.valueOf( mFrameRate ) ); - properties.save(); calculateConsumptionRate(); @@ -491,7 +491,7 @@ public class DFTProcessor implements Listener, { switch( event.getEvent() ) { - case SAMPLE_RATE_CHANGE_NOTIFICATION: + case NOTIFICATION_SAMPLE_RATE_CHANGE: mSampleRate = event.getValue().intValue(); calculateConsumptionRate(); break; diff --git a/src/spectrum/OverlayPanel.java b/src/spectrum/OverlayPanel.java index b22896b5..ded12c2a 100644 --- a/src/spectrum/OverlayPanel.java +++ b/src/spectrum/OverlayPanel.java @@ -32,7 +32,6 @@ import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.text.DecimalFormat; import java.util.ArrayList; -import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import javax.swing.JPanel; @@ -47,16 +46,16 @@ import settings.SettingChangeListener; import settings.SettingsManager; import source.tuner.TunerChannel; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeProcessor; import controller.channel.Channel; import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent; import controller.channel.ChannelEventListener; -import controller.channel.ChannelManager; +import controller.channel.ChannelModel; public class OverlayPanel extends JPanel implements ChannelEventListener, - FrequencyChangeListener, + IFrequencyChangeProcessor, SettingChangeListener { private static final long serialVersionUID = 1L; @@ -101,8 +100,7 @@ public class OverlayPanel extends JPanel // private List mChannels = new ArrayList(); // //Currently visible/displayable channels - private CopyOnWriteArrayList mVisibleChannels = - new CopyOnWriteArrayList(); + private CopyOnWriteArrayList mVisibleChannels = new CopyOnWriteArrayList(); private ChannelDisplay mChannelDisplay = ChannelDisplay.ALL; //Defines the offset at the bottom of the spectral display to account for @@ -111,14 +109,14 @@ public class OverlayPanel extends JPanel private LabelSizeManager mLabelSizeMonitor = new LabelSizeManager(); private SettingsManager mSettingsManager; - private ChannelManager mChannelManager; + private ChannelModel mChannelModel; /** * Translucent overlay panel for displaying channel configurations, * processing channels, selected channels, frequency labels and lines, and * a cursor with a frequency readout. */ public OverlayPanel( SettingsManager settingsManager, - ChannelManager channelManager ) + ChannelModel channelModel ) { mSettingsManager = settingsManager; @@ -127,11 +125,11 @@ public class OverlayPanel extends JPanel mSettingsManager.addListener( this ); } - mChannelManager = channelManager; + mChannelModel = channelModel; - if( mChannelManager != null ) + if( mChannelModel != null ) { - mChannelManager.addListener( this ); + mChannelModel.addListener( this ); } addComponentListener( mLabelSizeMonitor ); @@ -145,14 +143,13 @@ public class OverlayPanel extends JPanel public void dispose() { - if( mChannelManager != null ) + if( mChannelModel != null ) { - mChannelManager.removeListener( this ); + mChannelModel.removeListener( this ); } - mChannelManager = null; + mChannelModel = null; -// mChannels.clear(); mVisibleChannels.clear(); if( mSettingsManager != null ) @@ -530,14 +527,14 @@ public class OverlayPanel extends JPanel { if( mChannelDisplay == ChannelDisplay.ALL || ( mChannelDisplay == ChannelDisplay.ENABLED && - channel.isProcessing() ) ) + channel.getEnabled() ) ) { //Choose the correct background color to use if( channel.isSelected() ) { graphics.setColor( mColorChannelConfigSelected ); } - else if( channel.isProcessing() ) + else if( channel.getEnabled() ) { graphics.setColor( mColorChannelConfigProcessing ); } @@ -571,7 +568,7 @@ public class OverlayPanel extends JPanel double yAxis = 0; //Draw the system label and adjust the y-axis position - String system = channel.hasSystem() ? channel.getSystem().getName() : " "; + String system = channel.hasSystem() ? channel.getSystem() : " "; yAxis += drawLabel( graphics, system, @@ -581,7 +578,7 @@ public class OverlayPanel extends JPanel width ); //Draw the site label and adjust the y-axis position - String site = channel.hasSite() ? channel.getSite().getName() : " "; + String site = channel.hasSite() ? channel.getSite() : " "; yAxis += drawLabel( graphics, site, @@ -599,27 +596,24 @@ public class OverlayPanel extends JPanel width ); //Draw the decoder label - drawLabel( graphics, - channel.getDecodeConfiguration().getDecoderType() - .getShortDisplayString(), - this.getFont(), - xAxis, - yAxis, - width ); - } - - long frequency = tunerChannel.getFrequency(); - - double frequencyAxis = getAxisFromFrequency( frequency ); - - drawChannelCenterLine( graphics, frequencyAxis ); - - /* Draw Automatic Frequency Control line */ - if( channel.hasFrequencyControl() ) - { - long error = frequency + channel.getFrequencyCorrection(); + drawLabel( graphics, channel.getDecodeConfiguration().getDecoderType().getShortDisplayString(), + this.getFont(), xAxis, yAxis, width ); + + long frequency = tunerChannel.getFrequency(); - drawAFC( graphics, frequencyAxis, getAxisFromFrequency( error ) ); + double frequencyAxis = getAxisFromFrequency( frequency ); + + drawChannelCenterLine( graphics, frequencyAxis ); + + /* Draw Automatic Frequency Control line */ + int correction = channel.getChannelFrequencyCorrection(); + + if( correction != 0 ) + { + long error = frequency + correction; + + drawAFC( graphics, frequencyAxis, getAxisFromFrequency( error ) ); + } } } } @@ -676,11 +670,11 @@ public class OverlayPanel extends JPanel { switch( event.getEvent() ) { - case SAMPLE_RATE_CHANGE_NOTIFICATION: + case NOTIFICATION_SAMPLE_RATE_CHANGE: mBandwidth = event.getValue().intValue(); mLabelSizeMonitor.update(); break; - case FREQUENCY_CHANGE_NOTIFICATION: + case NOTIFICATION_FREQUENCY_CHANGE: mFrequency = event.getValue().longValue(); mLabelSizeMonitor.update(); break; @@ -689,20 +683,12 @@ public class OverlayPanel extends JPanel } /** - * Reset the visible channel configs list + * Reset the visible channels list */ mVisibleChannels.clear(); - - long minimum = getMinFrequency(); - long maximum = getMaxFrequency(); - - for( Channel channel: mChannelManager.getChannels() ) - { - if( channel.isWithin( minimum, maximum ) ) - { - mVisibleChannels.add( channel ); - } - } + mVisibleChannels.addAll( + mChannelModel.getChannelsInFrequencyRange( getMinFrequency(), + getMaxFrequency() ) ); } /** @@ -716,30 +702,38 @@ public class OverlayPanel extends JPanel switch( event.getEvent() ) { -// case CHANNEL_ADDED: - case CHANNEL_ENABLED: -// if( !mChannels.contains( channel ) ) -// { -// mChannels.add( channel ); -// } - - if( channel.isWithin( getMinFrequency(), getMaxFrequency() ) && - !mVisibleChannels.contains( channel ) ) + case NOTIFICATION_ADD: + case NOTIFICATION_PROCESSING_START: + if( !mVisibleChannels.contains( channel ) && + channel.isWithin( getMinFrequency(), getMaxFrequency() ) ) { mVisibleChannels.add( channel ); } break; - case CHANNEL_DELETED: -// mChannels.remove( channel ); + case NOTIFICATION_DELETE: mVisibleChannels.remove( channel ); break; - case CHANNEL_PROCESSING_STOPPED: + case NOTIFICATION_PROCESSING_STOP: if( channel.getChannelType() == ChannelType.TRAFFIC ) { -// mChannels.remove( channel ); mVisibleChannels.remove( channel ); } break; + case NOTIFICATION_CONFIGURATION_CHANGE: + if( mVisibleChannels.contains( channel ) && + !channel.isWithin( getMinFrequency(), getMaxFrequency() ) ) + { + mVisibleChannels.remove( channel ); + } + + if( !mVisibleChannels.contains( channel ) && + channel.isWithin( getMinFrequency(), getMaxFrequency() ) ) + { + mVisibleChannels.add( channel ); + } + break; + default: + break; } repaint(); diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index d335f2a8..878a8e79 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,7 +28,6 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.text.DecimalFormat; import java.util.ArrayList; -import java.util.Arrays; import java.util.Hashtable; import javax.swing.JCheckBoxMenuItem; @@ -54,7 +53,6 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import playlist.PlaylistManager; import properties.SystemProperties; import sample.Listener; import sample.SampleType; @@ -62,12 +60,10 @@ import sample.complex.ComplexBuffer; import settings.ColorSetting.ColorSettingName; import settings.ColorSettingMenuItem; import settings.SettingsManager; -import source.SourceException; import source.tuner.Tuner; -import source.tuner.TunerSelectionListener; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeEvent.Event; -import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeProcessor; import spectrum.OverlayPanel.ChannelDisplay; import spectrum.converter.ComplexDecibelConverter; import spectrum.converter.DFTResultsConverter; @@ -80,18 +76,17 @@ import spectrum.menu.SmoothingTypeItem; import com.jidesoft.swing.JideSplitPane; -import controller.ConfigurationControllerModel; import controller.channel.Channel; -import controller.channel.ChannelManager; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; import controller.channel.ChannelUtils; import dsp.filter.Window.WindowType; import dsp.filter.smoothing.SmoothingFilter.SmoothingType; public class SpectralDisplayPanel extends JPanel implements Listener, - FrequencyChangeListener, - IDFTWidthChangeProcessor, - TunerSelectionListener + IFrequencyChangeProcessor, + IDFTWidthChangeProcessor { private static final long serialVersionUID = 1L; @@ -103,7 +98,6 @@ public class SpectralDisplayPanel extends JPanel public static final String FFT_SIZE_PROPERTY = "spectral.display.dft.size"; public static final int NO_ZOOM = 0; public static final int MAX_ZOOM = 6; - private DFTSize mDFTSize = DFTSize.FFT04096; private int mZoom = 0; @@ -116,9 +110,9 @@ public class SpectralDisplayPanel extends JPanel private OverlayPanel mOverlayPanel; private DFTProcessor mDFTProcessor; private DFTResultsConverter mDFTConverter; - private PlaylistManager mPlaylistManager; + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; private SettingsManager mSettingsManager; - private ConfigurationControllerModel mController; private Tuner mTuner; /** @@ -133,25 +127,18 @@ public class SpectralDisplayPanel extends JPanel * the DFT is translated to decibels for display in the spectrum and * waterfall components. */ - public SpectralDisplayPanel( ChannelManager channelManager, - ConfigurationControllerModel controller, - PlaylistManager playlistManager, + public SpectralDisplayPanel( ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, SettingsManager settingsManager ) { - mController = controller; - mPlaylistManager = playlistManager; + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; mSettingsManager = settingsManager; mSpectrumPanel = new SpectrumPanel( mSettingsManager ); - mOverlayPanel = new OverlayPanel( mSettingsManager, channelManager ); + mOverlayPanel = new OverlayPanel( mSettingsManager, mChannelModel ); mWaterfallPanel = new WaterfallPanel( mSettingsManager ); - //Register to receive tuner selection events - if( mController != null ) - { - mController.addListener( (TunerSelectionListener)this ); - } - init(); loadSettings(); @@ -163,9 +150,9 @@ public class SpectralDisplayPanel extends JPanel String rawSize = properties.get( FFT_SIZE_PROPERTY, DFTSize.FFT04096.name() ); - DFTSize size = null; - - if( rawSize != null ) + DFTSize size = null; + + if( rawSize != null ) { try { @@ -173,16 +160,16 @@ public class SpectralDisplayPanel extends JPanel } catch( Exception e ) { - //Do nothing, we couldn't parse the stored value + //Do nothing } } - - if( size == null ) - { - size = DFTSize.FFT04096; - } - - setDFTSize( size ); + + if( size == null ) + { + size = DFTSize.FFT04096; + } + + setDFTSize( size ); } public void dispose() @@ -190,13 +177,6 @@ public class SpectralDisplayPanel extends JPanel /* De-register from receiving samples when the window closes */ clearTuner(); - if( mController != null ) - { - mController.removeListener( (TunerSelectionListener)this ); - } - - mController = null; - mPlaylistManager = null; mSettingsManager = null; mDFTProcessor.dispose(); @@ -229,8 +209,8 @@ public class SpectralDisplayPanel extends JPanel mDFTSize = size; SystemProperties properties = SystemProperties.getInstance(); + properties.set( FFT_SIZE_PROPERTY, size.name() ); - properties.save(); setZoom( 0, 0, 0 ); } @@ -427,7 +407,6 @@ public class SpectralDisplayPanel extends JPanel /** * Receives frequency change events -- primarily from tuner components. */ - @Override public void frequencyChanged( FrequencyChangeEvent event ) { mOverlayPanel.frequencyChanged( event ); @@ -444,11 +423,10 @@ public class SpectralDisplayPanel extends JPanel } /** - * Responds to tuner selection event by deregistering from the current + * Responds to tuner event by deregistering from the current * complex sample buffer source and registering with the tuner argument. */ - @Override - public void tunerSelected( Tuner tuner ) + public void showTuner( Tuner tuner ) { clearTuner(); @@ -459,7 +437,7 @@ public class SpectralDisplayPanel extends JPanel if( mTuner != null ) { //Register to receive frequency change events - mTuner.addListener( (FrequencyChangeListener)this ); + mTuner.getTunerController().addListener( this ); //Register the dft processor to receive samples from the tuner mTuner.addListener( (Listener)mDFTProcessor ); @@ -468,19 +446,13 @@ public class SpectralDisplayPanel extends JPanel //Fire frequency and sample rate change events so that the spectrum //and overlay panels can synchronize - try - { - frequencyChanged( new FrequencyChangeEvent( - Event.FREQUENCY_CHANGE_NOTIFICATION, mTuner.getFrequency() ) ); - - frequencyChanged( new FrequencyChangeEvent( - Event.SAMPLE_RATE_CHANGE_NOTIFICATION, mTuner.getSampleRate() ) ); - } - catch ( SourceException e ) - { - mLog.info( "DFTProcessor - exception during new tuner setup - " - + "couldn't get frequency from the tuner", e ); - } + frequencyChanged( new FrequencyChangeEvent( + Event.NOTIFICATION_FREQUENCY_CHANGE, + mTuner.getTunerController().getFrequency() ) ); + + frequencyChanged( new FrequencyChangeEvent( + Event.NOTIFICATION_SAMPLE_RATE_CHANGE, + mTuner.getTunerController().getSampleRate() ) ); } } @@ -492,7 +464,7 @@ public class SpectralDisplayPanel extends JPanel if( mTuner != null ) { //Deregister for frequency change events from the tuner - mTuner.removeListener( (FrequencyChangeListener)this ); + mTuner.getTunerController().removeListener( this ); //Deregister the dft processor from receiving samples mTuner.removeListener( (Listener)mDFTProcessor ); @@ -653,8 +625,9 @@ public class SpectralDisplayPanel extends JPanel for( Channel channel: channels ) { - JMenu channelMenu = ChannelUtils.getContextMenu( - mPlaylistManager, channel, SpectralDisplayPanel.this ); + JMenu channelMenu = ChannelUtils.getContextMenu( mChannelModel, + mChannelProcessingManager, channel, + SpectralDisplayPanel.this ); if( channelMenu != null ) { @@ -676,10 +649,7 @@ public class SpectralDisplayPanel extends JPanel for( DecoderType type: DecoderType.getPrimaryDecoders() ) { - decoderMenu.add( new DecoderItem( mController, - frequency, - type ) ); - + decoderMenu.add( new DecoderItem( type, frequency ) ); } frequencyMenu.add( decoderMenu ); @@ -958,17 +928,14 @@ public class SpectralDisplayPanel extends JPanel { private static final long serialVersionUID = 1L; - private ConfigurationControllerModel mController; + private ChannelModel mChannelModel; private long mFrequency; private DecoderType mDecoder; - public DecoderItem( ConfigurationControllerModel controller, - long frequency, - DecoderType type ) + public DecoderItem( DecoderType type, long frequency ) { super( type.getDisplayString() ); - mController = controller; mFrequency = frequency; mDecoder = type; @@ -977,7 +944,7 @@ public class SpectralDisplayPanel extends JPanel @Override public void actionPerformed( ActionEvent e ) { - mController.createChannel( mFrequency, mDecoder ); + mChannelModel.createChannel( mDecoder, mFrequency ); } } ); } diff --git a/src/spectrum/SpectrumFrame.java b/src/spectrum/SpectrumFrame.java index 2305721c..a3e1e5d1 100644 --- a/src/spectrum/SpectrumFrame.java +++ b/src/spectrum/SpectrumFrame.java @@ -23,24 +23,20 @@ import java.awt.event.WindowListener; import javax.swing.JFrame; -import playlist.PlaylistManager; import net.miginfocom.swing.MigLayout; import settings.SettingsManager; import source.tuner.Tuner; -import controller.ConfigurationControllerModel; -import controller.ResourceManager; -import controller.channel.ChannelManager; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; public class SpectrumFrame extends JFrame implements WindowListener { private static final long serialVersionUID = 1L; - private ResourceManager mResourceManager; private SpectralDisplayPanel mSpectralDisplayPanel; - public SpectrumFrame( ChannelManager channelManager, - ConfigurationControllerModel controller, - PlaylistManager playlistManager, + public SpectrumFrame( ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, SettingsManager settingsManager, Tuner tuner ) { @@ -50,10 +46,10 @@ public class SpectrumFrame extends JFrame implements WindowListener setLayout( new MigLayout( "insets 0 0 0 0", "[grow]", "[grow]") ); - mSpectralDisplayPanel = new SpectralDisplayPanel( channelManager, - controller, playlistManager, settingsManager ); - - mSpectralDisplayPanel.tunerSelected( tuner ); + mSpectralDisplayPanel = new SpectralDisplayPanel( channelModel, + channelProcessingManager, settingsManager ); + + mSpectralDisplayPanel.showTuner( tuner ); add( mSpectralDisplayPanel, "grow" ); /* Register a shutdown listener */ diff --git a/src/spectrum/converter/ComplexDecibelConverter.java b/src/spectrum/converter/ComplexDecibelConverter.java index cb59cc67..ec7c55ab 100644 --- a/src/spectrum/converter/ComplexDecibelConverter.java +++ b/src/spectrum/converter/ComplexDecibelConverter.java @@ -17,8 +17,6 @@ ******************************************************************************/ package spectrum.converter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Converts complex DFT output to scaled dB values with a maximum amplitude of @@ -61,9 +59,6 @@ import org.slf4j.LoggerFactory; */ public class ComplexDecibelConverter extends DFTResultsConverter { - private final static Logger mLog = - LoggerFactory.getLogger( ComplexDecibelConverter.class ); - /** * Converts the output of the JTransforms FloatFFT_1D.complexForward() * calculation into the power spectrum in decibels, normalized to the