Initial code check in continued

This commit is contained in:
dennis.sheirer
2014-04-26 19:48:24 +00:00
parent 960a6f9952
commit 3dfedcf8d1
474 changed files with 76613 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source;
public interface BufferReceiver {
/**
* Method for receiving a buffer of bytes
* @param buffer
*/
public void receive( byte[] buffer );
}

View File

@@ -0,0 +1,15 @@
package source;
import java.util.List;
import sample.Provider;
import sample.complex.ComplexSample;
public abstract class ComplexSource extends Source
implements Provider<List<ComplexSample>>
{
public ComplexSource( String name )
{
super( name, SampleType.COMPLEX );
}
}

View File

@@ -0,0 +1,44 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source;
import javax.swing.JLabel;
import source.config.SourceConfiguration;
public class EmptySourceEditor extends SourceEditor
{
private static final long serialVersionUID = 1L;
public EmptySourceEditor( SourceConfiguration config )
{
super( config );
initGUI();
}
public void reset() {}
public void save() {}
private void initGUI()
{
JLabel selectLabel = new JLabel( "Please select a source" );
add( selectLabel );
}
}

View File

@@ -0,0 +1,12 @@
package source;
import sample.Provider;
import source.Source.SampleType;
public abstract class FloatArraySource extends Source implements Provider<Float[]>
{
public FloatArraySource( String name )
{
super( name, SampleType.FLOAT );
}
}

View File

@@ -0,0 +1,11 @@
package source;
import sample.Provider;
public abstract class FloatSource extends Source implements Provider<Float>
{
public FloatSource( String name )
{
super( name, SampleType.FLOAT );
}
}

View File

@@ -0,0 +1,28 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source;
import source.Source.SampleType;
public interface SampleProvider
{
/**
* Real or Complex
*/
public abstract SampleType getSampleType();
}

56
src/source/Source.java Normal file
View File

@@ -0,0 +1,56 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source;
/**
* Abstract class to define the minimum functionality of a sample data provider.
*/
public abstract class Source
{
protected String mName;
protected SampleType mSampleType;
public enum SampleType { COMPLEX, FLOAT }
public Source( String name, SampleType sampleType )
{
mName = name;
mSampleType = sampleType;
}
public String getName()
{
return mName;
}
public abstract int getSampleRate() throws SourceException;
public abstract int getFrequency() throws SourceException;
public abstract void dispose();
public SampleType getSampleType()
{
return mSampleType;
}
public void setSampleType( SampleType sampleType )
{
mSampleType = sampleType;
}
}

View File

@@ -0,0 +1,119 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
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<SourceType> mComboSources;
private SourceEditor mEditor;
public SourceComponentEditor( ChannelNode channelNode )
{
super( channelNode );
mComboSources = new JComboBox<SourceType>();
mComboSources.setModel( new DefaultComboBoxModel<SourceType>( 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( config );
//Add it to the jpanel
add( mEditor, "span 2" );
revalidate();
repaint();
}
}
});
add( mComboSources, "wrap" );
reset();
}
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 );
}
}

View File

@@ -0,0 +1,38 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source;
import source.config.SourceConfiguration;
import controller.Editor;
public abstract class SourceEditor extends Editor
{
private static final long serialVersionUID = 1L;
protected SourceConfiguration mConfig;
public SourceEditor( SourceConfiguration config )
{
mConfig = config;
}
public SourceConfiguration getConfig()
{
return mConfig;
}
}

View File

@@ -0,0 +1,46 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source;
import source.config.SourceConfiguration;
import source.mixer.MixerEditor;
import source.tuner.TunerEditor;
public class SourceEditorFactory
{
public static SourceEditor getPanel( SourceConfiguration config )
{
SourceEditor configuredPanel;
switch( config.getSourceType() )
{
case MIXER:
configuredPanel = new MixerEditor( config );
break;
case TUNER:
configuredPanel = new TunerEditor( config );
break;
case NONE:
default:
configuredPanel = new EmptySourceEditor( config );
break;
}
return configuredPanel;
}
}

View File

@@ -0,0 +1,33 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source;
public class SourceException extends Exception
{
private static final long serialVersionUID = 1L;
public SourceException( String message )
{
this( message, null );
}
public SourceException( String message, Throwable cause )
{
super( message, cause );
}
}

View File

@@ -0,0 +1,66 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source;
import source.mixer.MixerManager;
import controller.ResourceManager;
import controller.channel.ProcessingChain;
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 SourceManager( ResourceManager resourceManager )
{
mResourceManager = resourceManager;
}
public Source getSource( ProcessingChain channel )
{
Source retVal = null;
if( channel != null )
{
switch( channel.getChannel()
.getSourceConfiguration().getSourceType() )
{
case MIXER:
retVal = MixerManager.getInstance().getSource( channel );
break;
case TUNER:
retVal = mResourceManager.getTunerManager().getSource( channel );
break;
case NONE:
default:
break;
}
}
return retVal;
}
}

View File

@@ -0,0 +1,44 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source;
public enum SourceType
{
NONE( "No Source" ),
MIXER( "Mixer/Sound Card" ),
TUNER( "Tuner" );
// WAVE( "Wave File" );
private String mDisplayString;
SourceType( String displayString )
{
mDisplayString = displayString;
}
public String getDisplayString()
{
return mDisplayString;
}
@Override
public String toString()
{
return mDisplayString;
}
}

View File

@@ -0,0 +1,50 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.config;
import source.SourceType;
public class SourceConfigFactory
{
public static SourceConfiguration getDefaultSourceConfiguration()
{
return getSourceConfiguration( SourceType.NONE );
}
public static SourceConfiguration
getSourceConfiguration( SourceType source )
{
SourceConfiguration retVal;
switch( source )
{
case MIXER:
retVal = new SourceConfigMixer();
break;
case TUNER:
retVal = new SourceConfigTuner();
break;
case NONE:
default:
retVal = new SourceConfigNone();
break;
}
return retVal;
}
}

View File

@@ -0,0 +1,73 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.config;
import javax.xml.bind.annotation.XmlAttribute;
import source.SourceType;
import source.mixer.MixerChannel;
public class SourceConfigMixer extends SourceConfiguration
{
protected String mMixer;
protected MixerChannel mChannel = MixerChannel.LEFT; //default
public SourceConfigMixer()
{
super( SourceType.MIXER );
}
@Override
public String getDescription()
{
return getName();
}
public String getName()
{
StringBuilder sb = new StringBuilder();
sb.append( mMixer );
sb.append( "-" );
sb.append( mChannel.toString() );
return sb.toString();
}
@XmlAttribute( name= "mixer" )
public String getMixer()
{
return mMixer;
}
public void setMixer( String mixer )
{
mMixer = mixer;
}
@XmlAttribute( name = "channel" )
public MixerChannel getChannel()
{
return mChannel;
}
public void setChannel( MixerChannel channel )
{
mChannel = channel;
}
}

View File

@@ -0,0 +1,34 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.config;
import source.SourceType;
public class SourceConfigNone extends SourceConfiguration
{
public SourceConfigNone()
{
super( SourceType.NONE );
}
@Override
public String getDescription()
{
return "No Source";
}
}

View File

@@ -0,0 +1,53 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.config;
import java.text.DecimalFormat;
import javax.xml.bind.annotation.XmlAttribute;
import source.SourceType;
public class SourceConfigTuner extends SourceConfiguration
{
private static DecimalFormat sFORMAT = new DecimalFormat( "0.00000" );
private int mFrequency = 0;
public SourceConfigTuner()
{
super( SourceType.TUNER );
}
@XmlAttribute( name = "frequency" )
public int getFrequency()
{
return mFrequency;
}
public void setFrequency( int frequency )
{
mFrequency = frequency;
}
@Override
public String getDescription()
{
return sFORMAT.format( (double)mFrequency / 1000000.0d ) + " MHz";
}
}

View File

@@ -0,0 +1,59 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.config;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import source.SourceType;
import controller.config.Configuration;
@XmlSeeAlso( { SourceConfigMixer.class, SourceConfigNone.class,
SourceConfigTuner.class } )
@XmlRootElement( name = "source_configuration" )
public class SourceConfiguration extends Configuration
{
protected SourceType mSourceType;
public SourceConfiguration()
{
this( SourceType.NONE );
}
public SourceConfiguration( SourceType source )
{
mSourceType = source;
}
@XmlAttribute( name = "source_type" )
public SourceType getSourceType()
{
return mSourceType;
}
public void setSourceType( SourceType source )
{
mSourceType = source;
}
public String getDescription()
{
return "No Source";
}
}

View File

@@ -0,0 +1,85 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.mixer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import source.Source.SampleType;
/**
* Converts 16-bit little endian byte data into an array of floats
*/
public class ChannelShortAdapter extends SampleAdapter
{
private ByteOrder mByteOrder = ByteOrder.LITTLE_ENDIAN;
private MixerChannel mMixerChannel;
public ChannelShortAdapter( MixerChannel channel )
{
/* Only use with LEFT/RIGHT channels */
assert( channel != MixerChannel.MONO );
mMixerChannel = channel;
}
@Override
public Float[] convert( byte[] samples )
{
Float[] processed = new Float[ samples.length / 4 ];
int pointer = 0;
/* Wrap byte array in a byte buffer so we can process them as shorts */
ByteBuffer buffer = ByteBuffer.wrap( samples );
/* Set endian to correct byte ordering */
buffer.order( mByteOrder );
while( buffer.hasRemaining() )
{
if( mMixerChannel == MixerChannel.LEFT )
{
processed[ pointer ] = (float)buffer.getShort();
/* Throw away the right channel */
buffer.getShort();
}
else
{
/* Throw away the left channel */
buffer.getShort();
processed[ pointer ] = (float)buffer.getShort();
}
pointer++;
}
return processed;
}
/**
* Set byte interpretation to little or big endian. Defaults to LITTLE
* endian
*/
public void setByteOrder( ByteOrder order )
{
mByteOrder = order;
}
}

View File

@@ -0,0 +1,23 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.mixer;
public enum MixerChannel
{
LEFT,RIGHT,MONO;
}

View File

@@ -0,0 +1,149 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.mixer;
import java.util.Set;
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;
public class MixerEditor extends SourceEditor
{
private static final long serialVersionUID = 1L;
private JComboBox<String> mComboMixers;
private JComboBox<MixerChannel>mComboChannels;
protected Mixer.Info mSelectedMixer = null;
public MixerEditor( SourceConfiguration config )
{
super( config );
initGUI();
}
private void initGUI()
{
JLabel mixerLabel = new JLabel( "Mixer:" );
add( mixerLabel, "align right" );
mComboMixers = new JComboBox<String>();
add( mComboMixers, "wrap" );
resetMixers();
JLabel channelLabel = new JLabel( "Channel:" );
add( channelLabel, "align right" );
mComboChannels = new JComboBox<MixerChannel>();
add( mComboChannels, "wrap" );
resetChannels();
}
public void reset()
{
resetMixers();
resetChannels();
}
public void save()
{
SourceConfigMixer config = (SourceConfigMixer)mConfig;
String mixer = mComboMixers.getItemAt( mComboMixers.getSelectedIndex() );
if( mixer != null )
{
config.setMixer( mixer );
}
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()
{
Set<String> mixers = MixerManager.getInstance().getMixers();
String[] mixerStrings = mixers.toArray( new String[ mixers.size() ] );
mComboMixers.setModel(
new DefaultComboBoxModel<String>( mixerStrings ) );
SourceConfigMixer config = (SourceConfigMixer)mConfig;
String mixer = config.getMixer();
if( mixer != null && mixers.contains( mixer ) )
{
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.size() > 0 )
{
mComboMixers.setSelectedIndex( 0 );
config.setMixer( mComboMixers.getItemAt( 0 ) );
}
}
}
});
}
private void resetChannels()
{
javax.swing.SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
mComboChannels.setModel(
new DefaultComboBoxModel<MixerChannel>( MixerChannel.values() ) );
MixerChannel channel = ((SourceConfigMixer)mConfig).getChannel();
mComboChannels.setSelectedItem( channel );
}
});
}
}

View File

@@ -0,0 +1,258 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.mixer;
import java.util.Collection;
import java.util.HashMap;
import java.util.Set;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Line;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.TargetDataLine;
import log.Log;
import source.config.SourceConfigMixer;
import source.config.SourceConfiguration;
import source.tuner.MixerTunerDataLine;
import source.tuner.MixerTunerType;
import controller.channel.ProcessingChain;
public class MixerManager
{
private static MixerManager sInstance = null;
private HashMap<String,TargetDataLine> mMixerLines =
new HashMap<String,TargetDataLine>();
private HashMap<String,MixerTunerDataLine> mMixerTuners =
new HashMap<String,MixerTunerDataLine>();
public static AudioFormat MONO = new AudioFormat( 48000, //SampleRate
16, //Sample Size
1, //Channels
true, //Signed
false ); //Little Endian
public static AudioFormat STEREO = new AudioFormat( 48000, //SampleRate
16, //Sample Size
2, //Channels
true, //Signed
false ); //Little Endian
private MixerManager()
{
loadMixers();
}
public static MixerManager getInstance()
{
if( sInstance == null )
{
sInstance = new MixerManager();
}
return sInstance;
}
public MixerSource getSource( ProcessingChain channel )
{
MixerSource retVal = null;
SourceConfiguration config =
channel.getChannel().getSourceConfiguration();
if( config instanceof SourceConfigMixer )
{
SourceConfigMixer mixerConfig = (SourceConfigMixer)config;
//Get the name of the requested mixer
String mixerName = mixerConfig.getMixer();
if( mixerName != null )
{
TargetDataLine tdl = mMixerLines.get( mixerName );
if( tdl != null )
{
switch( mixerConfig.getChannel() )
{
case MONO:
retVal = new MixerSource( mMixerLines.get( mixerName ),
MONO,
mixerName,
new ShortAdapter() );
break;
case LEFT:
case RIGHT:
retVal = new MixerSource( mMixerLines.get( mixerName ),
STEREO,
mixerName,
new ChannelShortAdapter( mixerConfig.getChannel() ) );
break;
}
}
}
}
return retVal;
}
public Set<String> getMixers()
{
return mMixerLines.keySet();
}
public Collection<MixerTunerDataLine> getMixerTunerDataLines()
{
return mMixerTuners.values();
}
private void loadMixers()
{
StringBuilder sb = new StringBuilder();
sb.append( "MixerManager - loading system mixer devices\n" );
for( Mixer.Info mixerInfo: AudioSystem.getMixerInfo() )
{
//Sort between the mixers and the tuner mixers, and load each
MixerTunerType mixerTunerType = MixerTunerType.getMixerTunerType( mixerInfo );
if( mixerTunerType == MixerTunerType.UNKNOWN )
{
Mixer mixer = AudioSystem.getMixer( mixerInfo );
if( mixer != null )
{
TargetDataLine tdl = getTargetDataLine( mixerInfo, STEREO );
if( tdl != null )
{
mMixerLines.put( mixerInfo.getName(), tdl );
sb.append( "\t[LOADED] Mixer:" + mixerInfo.getName() + "\n" );
}
else
{
sb.append( "\t[NOT LOADED] Mixer:" + mixerInfo.getName() + " - audio format not supported\n" );
}
}
else
{
sb.append( "\t[NOT LOADED] Mixer:" + mixerInfo.getName() + " - couldn't get mixer\n" );
}
}
else //Process as a Mixer-based tuner data line
{
TargetDataLine tdl =
getTargetDataLine( mixerInfo,
mixerTunerType.getAudioFormat() );
if( tdl != null )
{
switch( mixerTunerType )
{
case FUNCUBE_DONGLE_PRO:
case FUNCUBE_DONGLE_PRO_PLUS:
mMixerTuners.put( mixerInfo.getName(),
new MixerTunerDataLine( tdl, mixerTunerType ) );
sb.append( "\t[LOADED] FunCube Dongle Mixer Tuner:" + mixerInfo.getName() + "[" + mixerTunerType.getDisplayString() + "]\n" );
break;
case UNKNOWN:
default:
sb.append( "\t[NOT LOADED] Tuner:" + mixerInfo.getName() + " - unrecognized tuner type\n" );
break;
}
}
else
{
sb.append( "\t[NOT LOADED] Tuner:" + mixerInfo.getName() + " - couldn't get target data line\n" );
}
}
}
Log.info( sb.toString() );
}
private TargetDataLine getTargetDataLine( Mixer.Info mixerInfo, AudioFormat format )
{
TargetDataLine retVal = null;
Mixer mixer = AudioSystem.getMixer( mixerInfo );
if( mixer != null )
{
try
{
DataLine.Info datalineInfo=
new DataLine.Info(TargetDataLine.class, format );
retVal = (TargetDataLine) mixer.getLine( datalineInfo );
}
catch( Exception e )
{
//Do nothing ... we couldn't get the TDL
}
}
return retVal;
}
public static String getMixerDevices()
{
StringBuilder sb = new StringBuilder();
for( Mixer.Info mixerInfo: AudioSystem.getMixerInfo() )
{
sb.append( "\n--------------------------------------------------" );
sb.append( "\nMIXER name:" + mixerInfo.getName() +
"\n desc:" + mixerInfo.getDescription() +
"\n vendor:" + mixerInfo.getVendor() +
"\n version:" + mixerInfo.getVersion() +
"\n" );
Mixer mixer = AudioSystem.getMixer( mixerInfo );
Line.Info[] sourceLines = mixer.getSourceLineInfo();
for( Line.Info lineInfo: sourceLines )
{
sb.append( " SOURCE LINE desc:" + lineInfo.toString() +
"\n class:" + lineInfo.getClass() +
"\n lineclass:" + lineInfo.getLineClass() +
"\n" );
}
Line.Info[] targetLines = mixer.getTargetLineInfo();
for( Line.Info lineInfo: targetLines )
{
sb.append( " TARGET LINE desc:" + lineInfo.toString() +
"\n class:" + lineInfo.getClass() +
"\n lineclass:" + lineInfo.getLineClass() +
"\n" );
}
}
return sb.toString();
}
}

View File

@@ -0,0 +1,274 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.mixer;
import java.util.Arrays;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;
import log.Log;
import sample.Listener;
import source.FloatArraySource;
import source.SourceException;
public class MixerSource extends FloatArraySource
{
private int mFrequency = 0;
private int mBufferSize = 16384;
private BufferReader mBufferReader = new BufferReader();
private TargetDataLine mTargetDataLine;
private AudioFormat mAudioFormat;
private String mDisplayName;
private int mBytesPerFrame = 0;
private SampleAdapter mSampleAdapter;
CopyOnWriteArrayList<Listener<Float[]>> mSampleListeners =
new CopyOnWriteArrayList<Listener<Float[]>>();
/**
* Complex Mixer Source - constructs a reader on the mixer/sound card target
* data line using the specified audio format (sample size, sample rate )
* and broadcasts float[] sample data to all registered listeners. Reads
* sample buffers sized to 10% of the sample rate specified in audio format.
*
* @param targetDataLine - mixer or sound card to be used
*
* @param format - audio format
*
* @param name - token name to use for this source
*
* @param sampleAdapter - adapter to convert byte array data read from the
* mixer into float array data. Can optionally invert the channel data if
* the left/right stereo channels are inverted.
*/
public MixerSource( TargetDataLine targetDataLine,
AudioFormat format,
String name,
SampleAdapter sampleAdapter )
{
super( name );
mTargetDataLine = targetDataLine;
mAudioFormat = format;
mSampleAdapter = sampleAdapter;
}
public void setListener( Listener<Float[]> listener )
{
mSampleListeners.add( listener );
/* If this is the first listener, start the reader thread */
if( !mBufferReader.isRunning() )
{
Thread thread = new Thread( mBufferReader );
thread.setName( getName() + " Sample Reader" );
thread.start();
}
}
public void removeListener( Listener<Float[]> listener )
{
mSampleListeners.remove( listener );
/* If this is the laster listener, stop the reader thread */
if( mSampleListeners.isEmpty() )
{
mBufferReader.stop();
}
}
public void broadcast( Float[] samples )
{
Iterator<Listener<Float[]>> it = mSampleListeners.iterator();
while( it.hasNext() )
{
Listener<Float[]> next = it.next();
/* if this is the last (or only) listener, send him the original
* buffer, otherwise send him a copy of the buffer */
if( it.hasNext() )
{
next.receive( Arrays.copyOf( samples, samples.length ) );
}
else
{
next.receive( samples );
}
}
}
@Override
public String toString()
{
return mDisplayName;
}
public int getSampleRate()
{
if( mTargetDataLine != null )
{
return (int)mTargetDataLine.getFormat().getSampleRate();
}
else
{
return 0;
}
}
/**
* Returns the frequency of this source. Default is 0.
*/
public int getFrequency() throws SourceException
{
return mFrequency;
}
/**
* Specify the frequency that will be returned from this source. This may
* be useful if you are streaming an external audio source in through the
* sound card and you want to specify a frequency for that source
*/
public void setFrequency( int frequency )
{
mFrequency = frequency;
}
/**
* Reader thread. Performs blocking read against the mixer target data
* line, converts the samples to an array of floats using the specified
* adapter. Dispatches float arrays to all registered listeners.
*/
public class BufferReader implements Runnable
{
private AtomicBoolean mRunning = new AtomicBoolean();
@Override
public void run()
{
if( mRunning.compareAndSet( false, true ) )
{
if( mTargetDataLine == null )
{
mRunning.set( false );
Log.error( "ComplexMixerSource - mixer target data line"
+ " is null" );
}
else
{
mBytesPerFrame = mAudioFormat.getSampleSizeInBits() / 8;
if( mBytesPerFrame == AudioSystem.NOT_SPECIFIED )
{
mBytesPerFrame = 2;
}
/* Set buffer size to 1/10 second of samples */
mBufferSize = (int)( mAudioFormat.getSampleRate()
* 0.05 ) * mBytesPerFrame;
/* We'll reuse the same buffer for each read */
byte[] buffer = new byte[ mBufferSize ];
try
{
mTargetDataLine.open( mAudioFormat );
mTargetDataLine.start();
}
catch ( LineUnavailableException e )
{
Log.error( "ComplexMixerSource - mixer target data line"
+ "not available to read data from - " +
e.getLocalizedMessage() );
mRunning.set( false );
}
while( mRunning.get() )
{
try
{
/* Blocking read - waits until the buffer fills */
mTargetDataLine.read( buffer, 0, buffer.length );
/* Convert samples to float array */
Float[] convertedSamples =
mSampleAdapter.convert( buffer );
/* Dispatch samples to registered listeners */
broadcast( convertedSamples );
}
catch ( Exception e )
{
Log.error( "ComplexMixerSource - error while reading"
+ "from the mixer target data line - " +
e.getLocalizedMessage() );
mRunning.set( false );
}
}
}
/* Close the data line if it is still open */
if( mTargetDataLine != null && mTargetDataLine.isOpen() )
{
mTargetDataLine.close();
}
}
}
/**
* Stops the reader thread
*/
public void stop()
{
if( mRunning.compareAndSet( true, false ) )
{
mTargetDataLine.stop();
mTargetDataLine.close();
}
}
/**
* Indicates if the reader thread is running
*/
public boolean isRunning()
{
return mRunning.get();
}
}
@Override
public void dispose()
{
if( mBufferReader != null )
{
mBufferReader.stop();
mSampleListeners.clear();
}
}
}

View File

@@ -0,0 +1,29 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.mixer;
import source.Source.SampleType;
/*
* Adapter to convert byte data received from a data stream into float[]
* samples
*/
public abstract class SampleAdapter
{
public abstract Float[] convert( byte[] samples );
}

View File

@@ -0,0 +1,63 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.mixer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import source.Source.SampleType;
/**
* Converts 16-bit little endian byte data into an array of floats
*/
public class ShortAdapter extends SampleAdapter
{
private ByteOrder mByteOrder = ByteOrder.LITTLE_ENDIAN;
@Override
public Float[] convert( byte[] samples )
{
Float[] processed = new Float[ samples.length / 2 ];
int pointer = 0;
/* Wrap byte array in a byte buffer so we can process them as shorts */
ByteBuffer buffer = ByteBuffer.wrap( samples );
/* Set endian to correct byte ordering */
buffer.order( mByteOrder );
while( buffer.hasRemaining() )
{
processed[ pointer ] = (float)buffer.getShort();
pointer++;
}
return processed;
}
/**
* Set byte interpretation to little or big endian. Defaults to LITTLE
* endian
*/
public void setByteOrder( ByteOrder order )
{
mByteOrder = order;
}
}

View File

@@ -0,0 +1,116 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.mixer;
import java.util.HashMap;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.Line;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.TargetDataLine;
/**
* THIS CLASS IS SCHEDULED FOR DELETION *****************************
*/
public class SystemMixerDevices {
/**
* Returns a hashmap of mixer names and target data lines that support the
* audio format argument
*
* @param format - requested audio format
* @return - mixer target data lines that support the requested audio format
*/
public static HashMap<String,TargetDataLine> getMixers( AudioFormat format )
{
HashMap<String,TargetDataLine> retVal = new HashMap<String,TargetDataLine>();
DataLine.Info datalineInfo=new DataLine.Info(TargetDataLine.class, format );
for( Mixer.Info mixerInfo: AudioSystem.getMixerInfo() )
{
Mixer mixer = AudioSystem.getMixer( mixerInfo );
if( mixer != null )
{
try
{
TargetDataLine tdl = (TargetDataLine) mixer.getLine( datalineInfo );
if( tdl != null )
{
retVal.put( mixerInfo.getName(), tdl );
}
}
catch( IllegalArgumentException iae )
{
//Do nothing ... mixer doesn't support the audio format
}
catch( LineUnavailableException e )
{
//Do nothing ... mixer doesn't support the audio format
}
}
}
return retVal;
}
public static String getSoundDevices()
{
StringBuilder sb = new StringBuilder();
for( Mixer.Info mixerInfo: AudioSystem.getMixerInfo() )
{
sb.append( "\n--------------------------------------------------" );
sb.append( "\nMIXER name:" + mixerInfo.getName() +
"\n desc:" + mixerInfo.getDescription() +
"\n vendor:" + mixerInfo.getVendor() +
"\n version:" + mixerInfo.getVersion() +
"\n" );
Mixer mixer = AudioSystem.getMixer( mixerInfo );
Line.Info[] sourceLines = mixer.getSourceLineInfo();
for( Line.Info lineInfo: sourceLines )
{
sb.append( " SOURCE LINE desc:" + lineInfo.toString() +
"\n class:" + lineInfo.getClass() +
"\n lineclass:" + lineInfo.getLineClass() +
"\n" );
}
Line.Info[] targetLines = mixer.getTargetLineInfo();
for( Line.Info lineInfo: targetLines )
{
sb.append( " TARGET LINE desc:" + lineInfo.toString() +
"\n class:" + lineInfo.getClass() +
"\n lineclass:" + lineInfo.getLineClass() +
"\n" );
}
}
return sb.toString();
}
}

View File

@@ -0,0 +1,152 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
import sample.Broadcaster;
import sample.Listener;
import sample.complex.ComplexSample;
import dsp.filter.ComplexFilter;
import dsp.filter.FilterFactory;
import dsp.filter.Window.WindowType;
@Deprecated
public class DigitalDropChannel implements Listener<ComplexSample>,
FrequencyChangeListener
{
/**
* Implements a frequency translating, decimating FIR filter channelizer.
*
* Uses multiple cascaded half-band low pass filters that each decimate by
* two, to achieve an overall power-of-two decimation factor.
*
* @param decimation - power of 2 decimation factor
*
* @param frequencyShift +/- frequency shift in hertz
*/
private static int sCHANNEL_RATE = 48000;
private static int sCHANNEL_PASS_FREQUENCY = 12500;
private NumericallyControlledOscillator mFrequencyTranslator;
private ComplexFilter[] mFilters;
private Broadcaster<ComplexSample> mComplexSampleBroadcaster =
new Broadcaster<ComplexSample>();
private int mSampleRate;
private int mFrequencyShift;
public DigitalDropChannel( int sampleRate, int frequencyShift )
{
if( sampleRate % sCHANNEL_RATE != 0 )
{
throw new IllegalArgumentException( "Sample rate must be an "
+ "integer multiple of 48000" );
}
mFrequencyShift = frequencyShift;
setSampleRate( sampleRate );
}
private void setSampleRate( int sampleRate )
{
if( sampleRate != mSampleRate )
{
/* Setup or adjust the frequency translator */
if( mFrequencyTranslator == null )
{
mFrequencyTranslator =
new NumericallyControlledOscillator( mFrequencyShift,
sampleRate );
}
else
{
mFrequencyTranslator.setSampleRate( sampleRate );
}
/* Get new decimation filters */
mFilters = FilterFactory
.getDecimationFilters( sampleRate,
sCHANNEL_RATE,
sCHANNEL_PASS_FREQUENCY,
48, //dB attenuation
WindowType.HAMMING );
/* wire the first filter stage to the frequency translator */
// mFrequencyTranslator.setListener( mFilters[ 0 ] );
/* wire the remaining filters together */
if( mFilters.length > 1 )
{
for( int x = 1; x < mFilters.length; x++ )
{
mFilters[ x - 1 ].setListener( mFilters[ x ] );
}
}
/* Add the sample broadcaster as a listener to the final filter */
// mFilters[ mFilters.length - 1 ].setListener( mComplexSampleBroadcaster );
mSampleRate = sampleRate;
}
}
@Override
public void frequencyChanged( int frequency, int bandwidth )
{
mFrequencyTranslator.setFrequency( frequency );
if( bandwidth != mSampleRate )
{
setSampleRate( bandwidth );
}
}
public int getFrequencyOffset()
{
return mFrequencyTranslator.getFrequency();
}
public void addListener( Listener<ComplexSample> listener )
{
mComplexSampleBroadcaster.addListener( listener );
}
public void removeListener( Listener<ComplexSample> listener )
{
mComplexSampleBroadcaster.removeListener( listener );
}
public int getListenerCount()
{
return mComplexSampleBroadcaster.getListenerCount();
}
public void removeListeners()
{
mComplexSampleBroadcaster.clear();
}
@Override
public void receive( ComplexSample sample )
{
mFrequencyTranslator.receive( sample );
}
}

View File

@@ -0,0 +1,25 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
public interface FrequencyChangeBroadcaster
{
public abstract void addListener( FrequencyChangeListener listener );
public abstract void removeListener( FrequencyChangeListener listener );
}

View File

@@ -0,0 +1,38 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
/**
* Interface for broadcasting changes to frequency and/or bandwidth/sample rate
* to all registered listeners.
*
* Note: broadcasted frequency should be the uncorrected, or displayable
* frequency value. All frequency correction aspects should be handled within
* the device that implements the tuning of the frequency.
*/
public interface FrequencyChangeListener
{
/**
* Broadcasts a change to the (uncorrected) frequency and/or bandwidth
* or sample rate.
*
* @param frequency - uncorrected frequency in hertz
* @param bandwidth - bandwidth in hertz
*/
public void frequencyChanged( int frequency, int bandwidth );
}

View File

@@ -0,0 +1,220 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
import java.util.ArrayList;
import source.SourceException;
public class FrequencyController
{
private Tunable mTunable;
private int mFrequency = 101100000;
private int mTunedFrequency = 101100000;
private int mMinimumFrequency;
private int mMaximumFrequency;
private double mFrequencyCorrection = 0d;
private int mBandwidth = 0;
private ArrayList<FrequencyChangeListener> mListeners =
new ArrayList<FrequencyChangeListener>();
public FrequencyController( Tunable tunable,
int minFrequency,
int maxFrequency,
double frequencyCorrection ) throws SourceException
{
mTunable = tunable;
mMinimumFrequency = minFrequency;
mMaximumFrequency = maxFrequency;
mFrequencyCorrection = frequencyCorrection;
}
/**
* Get bandwidth/sample rate in hertz
*/
public int getBandwidth()
{
return mBandwidth;
}
/**
* Set bandwidth/sample rate in hertz
*/
public void setBandwidth( int bandwidth )
{
mBandwidth = bandwidth;
broadcastFrequencyChange();
}
/**
* Get frequency in hertz
*/
public int getFrequency()
{
return mFrequency;
}
/**
* Set frequency
* @param frequency in hertz
* @throws SourceException if tunable doesn't support tuning a corrected
* version of the requested frequency
*/
public void setFrequency( int frequency ) throws SourceException
{
setFrequency( frequency, true );
}
/**
* Set frequency with optional broadcast of frequency change event. This
* method supports changing the frequency correction value without
* broadcasting a frequency change event.
*/
private void setFrequency( int frequency, boolean broadcastChange )
throws SourceException
{
int tunedFrequency = getTunedFrequency( frequency );
if( tunedFrequency < mMinimumFrequency ||
tunedFrequency > mMaximumFrequency )
{
throw new SourceException( "Cannot tune frequency ( requested: " +
frequency + " actual:" + tunedFrequency + "]" );
}
mFrequency = frequency;
mTunedFrequency = tunedFrequency;
if( mTunable != null )
{
mTunable.setTunedFrequency( mTunedFrequency );
}
/* Broadcast to all listeners that the frequency has changed */
if( broadcastChange )
{
broadcastFrequencyChange();
}
}
public int getTunedFrequency()
{
return mTunedFrequency;
}
public int getMinimumFrequency()
{
return mMinimumFrequency;
}
public int getMaximumFrequency()
{
return mMaximumFrequency;
}
/**
* Calculate the tuned frequency by adding frequency correction to the
* corrected frequency.
* @param correctedFrequency
*/
private int getTunedFrequency( int correctedFrequency )
{
return (int)( (double)correctedFrequency /
( 1.0 + ( mFrequencyCorrection / 1000000.0 ) ) );
}
/**
* Calculate the corrected frequency by subtracting frequency correction
* from the tuned frequency.
* @param tunedFrequency
*/
private int getCorrectedFrequency( int tunedFrequency )
{
return (int)( (double)tunedFrequency /
( 1.0 - ( mFrequencyCorrection / 1000000.0 ) ) );
}
public double getFrequencyCorrection()
{
return mFrequencyCorrection;
}
public void setFrequencyCorrection( double correction )
throws SourceException
{
mFrequencyCorrection = correction;
if( mFrequency > 0 )
{
/* Reset the frequency, but don't broadcast a change */
setFrequency( mFrequency, false );
}
}
/**
* Adds listener to receive frequency change events
*/
public void addListener( FrequencyChangeListener listener )
{
if( !mListeners.contains( listener ) )
{
mListeners.add( listener );
}
}
/**
* Removes listener from receiving frequency change events
*/
public void removeListener( FrequencyChangeListener listener )
{
mListeners.remove( listener );
}
/**
* Broadcasts a change/update to the current (uncorrected) frequency or the
* bandwidth/sample rate value.
*/
protected void broadcastFrequencyChange()
{
for( FrequencyChangeListener listener: mListeners )
{
listener.frequencyChanged( mFrequency, mBandwidth );
}
}
public interface Tunable
{
/**
* Gets the tuned frequency of the device
*/
public int getTunedFrequency() throws SourceException;
/**
* Sets the tuned frequency of the device
*/
public void setTunedFrequency( int frequency ) throws SourceException;
/**
* Gets the current bandwidth setting of the device
*/
public int getCurrentSampleRate() throws SourceException;
}
}

View File

@@ -0,0 +1,98 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
import java.util.ArrayList;
import javax.sound.sampled.TargetDataLine;
import sample.Listener;
import sample.complex.ComplexSampleListListener;
import source.mixer.MixerSource;
import source.mixer.SampleAdapter;
public abstract class MixerTuner extends Tuner
{
private TargetDataLine mTargetDataLine;
protected MixerTunerType mMixerTunerType;
protected MixerSource mMixerSource;
protected ArrayList<ComplexSampleListListener> mListeners =
new ArrayList<ComplexSampleListListener>();
/**
* Provides a wrapper around a MixerSource to treat it like a Tuner that
* can provide ComplexSample<Short> samples. Subclasses should couple this
* functionality with a tuner control class to allow tuning.
*/
public MixerTuner( String name,
MixerTunerType mixerTunerType,
TargetDataLine targetDataLine,
SampleAdapter sampleAdapter )
{
super( name );
mMixerTunerType = mixerTunerType;
mTargetDataLine = targetDataLine;
mMixerSource = new MixerSource( targetDataLine,
mMixerTunerType.getAudioFormat(),
name,
sampleAdapter );
}
public MixerTuner( String name,
MixerTunerDataLine mixerTuner,
SampleAdapter sampleAdapter )
{
this( name,
mixerTuner.getMixerTunerType(),
mixerTuner.getTargetDataLine(),
sampleAdapter );
}
public MixerTunerType getMixerTunerType()
{
return mMixerTunerType;
}
public TargetDataLine getTargetDataLine()
{
return mTargetDataLine;
}
/**
* Overrides the Tuner listener management methods and allows the
* complex mixer source to manage sample listeners
*/
@Override
public void addListener( Listener<Float[]> listener )
{
mMixerSource.setListener( listener );
}
/**
* Overrides the Tuner listener management methods and allows the
* complex mixer source to manage sample listeners
*/
@Override
public void removeListener( Listener<Float[]> listener )
{
mMixerSource.removeListener( listener );
}
}

View File

@@ -0,0 +1,42 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
import javax.sound.sampled.TargetDataLine;
public class MixerTunerDataLine
{
private TargetDataLine mTargetDataLine;
protected MixerTunerType mMixerTunerType;
public MixerTunerDataLine( TargetDataLine line, MixerTunerType type )
{
mTargetDataLine = line;
mMixerTunerType = type;
}
public TargetDataLine getTargetDataLine()
{
return mTargetDataLine;
}
public MixerTunerType getMixerTunerType()
{
return mMixerTunerType;
}
}

View File

@@ -0,0 +1,117 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.Mixer;
public enum MixerTunerType
{
FUNCUBE_DONGLE_PRO( TunerType.FUNCUBE_DONGLE_PRO,
"V10",
"FUNcube Dongle",
"FUNcube Dongle Pro V1.0",
new AudioFormat( 96000, //SampleRate
16, //Sample Size
2, //Channels
true, //Signed
false ) ), //Little Endian )
FUNCUBE_DONGLE_PRO_PLUS( TunerType.FUNCUBE_DONGLE_PRO_PLUS,
"V20",
"FUNcube Dongle V2.0",
"FUNcube Dongle Pro Plus V2.0",
new AudioFormat( 192000, //SampleRate
16, //Sample Size
2, //Channels
true, //Signed
false ) ), //Little Endian )
UNKNOWN( TunerType.UNKNOWN,
"UNK",
"Unknown",
"Unknown Mixer Tuner",
new AudioFormat( 48000, //SampleRate
16, //Sample Size
2, //Channels
true, //Signed
false ) ); //Little Endian )
private TunerType mTunerClass;
private String mMixerName;
private String mPartialDescription;
private String mDisplayString;
private AudioFormat mAudioFormat;
MixerTunerType( TunerType tunerClass,
String mixerName,
String partialDescription,
String displayString,
AudioFormat format )
{
mTunerClass = tunerClass;
mMixerName = mixerName;
mPartialDescription = partialDescription;
mDisplayString = displayString;
mAudioFormat = format;
}
public TunerType getTunerClass()
{
return mTunerClass;
}
public String getMixerName()
{
return mMixerName;
}
public String getPartialDescription()
{
return mPartialDescription;
}
public String getDisplayString()
{
return mDisplayString;
}
public AudioFormat getAudioFormat()
{
return mAudioFormat;
}
public static MixerTunerType getMixerTunerType( Mixer.Info info )
{
MixerTunerType retVal = UNKNOWN;
if( info.getName().contains( FUNCUBE_DONGLE_PRO_PLUS.mPartialDescription ) ||
info.getDescription().contains( FUNCUBE_DONGLE_PRO_PLUS.mPartialDescription ) )
{
retVal = FUNCUBE_DONGLE_PRO_PLUS;
}
else if( info.getName().contains( FUNCUBE_DONGLE_PRO.mPartialDescription ) ||
info.getDescription().contains( FUNCUBE_DONGLE_PRO.mPartialDescription ) )
{
retVal = FUNCUBE_DONGLE_PRO;
}
return retVal;
}
}

View File

@@ -0,0 +1,130 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
import log.Log;
import sample.Listener;
import sample.complex.ComplexSample;
import sample.complex.ComplexSampleUtils;
import util.Oscillator;
public class NumericallyControlledOscillator implements Listener<ComplexSample>,
FrequencyChangeListener
{
private int mFrequency;
private int mSampleRate;
private Oscillator mSineWaveGenerator;
private Listener<ComplexSample> mListener;
/**
* Translates (shifts) frequency spectrum up or down, depending on the sign
* of the frequency argument, by mixing received complex samples with a
* generated sine wave of the frequency argument. Positive frequency shifts
* the received spectrum higher in frequency. Negative frequency shifts the
* received spectrum lower in frequency. In both cases, spectrum that is
* shifted outside of the sampling bandwidth will wrap around, due to the
* nature of quadrature sampling.
*/
public NumericallyControlledOscillator( int frequencyShift, int sampleRate )
{
mFrequency = frequencyShift;
mSampleRate = sampleRate;
int defaultMixingSignalGain = 10;
mSineWaveGenerator = new Oscillator( mFrequency, mSampleRate );
}
/**
* Listener method for this class to receive samples from a Complex Sample
* Provider. This method multiplies the incoming sample by the generated
* mixing frequency to translate (ie shift +/-) the incoming signal, and
* then send the translated signal on to all registered listeners
*/
@Override
public void receive( ComplexSample sample )
{
if( mListener != null )
{
mListener.receive( ComplexSampleUtils.multiply( sample,
mSineWaveGenerator.nextComplex() ) );
}
}
@Override
public void frequencyChanged( int frequency, int bandwidth )
{
if( frequency != mFrequency )
{
setFrequency( frequency );
}
if( bandwidth != mSampleRate )
{
setSampleRate( bandwidth );
}
}
/**
* Changes the frequency value and resets the sine wave generator, so that
* all subsequent samples will be output at the new translated frequency
* @param frequency - hertz
*/
public void setFrequency( int frequency )
{
Log.info( "NumericallyControlledOscillator - setting sine wave gen to freq:" + frequency );
mFrequency = frequency;
mSineWaveGenerator.setFrequency( frequency );
}
/**
* Current frequency shift setting
* @return - frequency - hertz
*/
public int getFrequency()
{
return mFrequency;
}
/**
* Changes the sample rate, so that all new input/output values will be
* translated at the new sample rate
* @param sampleRate - hertz
*/
public void setSampleRate( int sampleRate )
{
mSampleRate = sampleRate;
mSineWaveGenerator.setSampleRate( sampleRate );
}
/**
* Add listener to receive frequency translated complex samples
*/
public void setListener( Listener<ComplexSample> listener )
{
mListener = listener;
}
/**
* Remove listener from receiving frequency translated complex samples.
*/
public void clearListener()
{
mListener = null;
}
}

190
src/source/tuner/Tuner.java Normal file
View File

@@ -0,0 +1,190 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.RejectedExecutionException;
import javax.swing.JPanel;
import sample.Listener;
import source.SourceException;
import controller.ResourceManager;
import controller.ThreadPoolManager;
/**
* Tuner - provides tuner channel sources, representing a channel frequency
*/
public abstract class Tuner implements FrequencyChangeBroadcaster,
FrequencyChangeListener
{
private String mName;
protected CopyOnWriteArrayList<Listener<Float[]>>
mSampleListeners = new CopyOnWriteArrayList<Listener<Float[]>>();
protected CopyOnWriteArrayList<FrequencyChangeListener>
mFrequencyChangeListeners = new CopyOnWriteArrayList<FrequencyChangeListener>();
public Tuner( String name )
{
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.
*
* @return - unique identifier like a serial number, or a usb bus location
* or ip address and port. Return some form of unique identification that
* allows this tuner to be identified from among several of the same types
* of tuners.
*/
public abstract String getUniqueID();
/**
* @return - tuner class enum entry
*/
public abstract TunerClass getTunerClass();
/**
* @return - tuner type enum entry
*/
public abstract TunerType getTunerType();
/**
* Name of this tuner object
* @return - string name of this tuner object
*/
public String getName()
{
return mName;
}
/**
* Sample rate of complex data coming from the Tuner
* @return
*/
public abstract int getSampleRate();
/**
* Tuner Local Oscillator Frequency
* @return - frequency in Hertz
*/
public abstract int getFrequency() throws SourceException;
/**
* Returns a tuner frequency channel source, tuned to the correct frequency
*
* Note: ensure the concrete implementation registers the returned
* tuner channel source as a listener on the tuner, to receive frequency
* change events.
*
* @param frequency - desired frequency
*
* @return - source for 48k sample rate
*/
public abstract TunerChannelSource getChannel( ThreadPoolManager threadPoolManager,
TunerChannel channel ) throws RejectedExecutionException, SourceException;
/**
* Releases the tuned channel resources
*
* Note: ensure the concrete implementation unregisters the tuner channel
* source from the tuner as a frequency change listener
*
* @param channel - previously obtained tuner channel
*/
public abstract void releaseChannel( TunerChannelSource source );
/**
* Registers the listener to receive complex float sample arrays
*/
public void addListener( Listener<Float[]> listener )
{
mSampleListeners.add( listener );
}
/**
* Removes the registered listener
*/
public void removeListener( Listener<Float[]> listener )
{
mSampleListeners.remove( listener );
}
/**
* Broadcasts the samples to all registered listeners
*/
public void broadcast( Float[] samples )
{
for( Listener<Float[]> listener: mSampleListeners )
{
listener.receive( samples );
}
}
/**
* Adds the frequency change listener to receive frequency and/or bandwidth
* change events
*/
public void addListener( FrequencyChangeListener listener )
{
mFrequencyChangeListeners.add( listener );
}
/**
* Removes the frequency change listener
*/
public void removeListener( FrequencyChangeListener listener )
{
mFrequencyChangeListeners.remove( listener );
}
/**
* Broadcasts a frequency change event to all registered listeners
*/
public void broadcastFrequencyChange( int frequency, int bandwidth )
{
for( FrequencyChangeListener listener: mFrequencyChangeListeners )
{
listener.frequencyChanged( frequency, bandwidth );
}
}
/**
* Frequency change listener method. We receive change events from the
* controller and rebroadcast them to all registered listeners.
*/
public void frequencyChanged( int frequency, int bandwidth )
{
broadcastFrequencyChange( frequency, bandwidth );
}
}

View File

@@ -0,0 +1,114 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
public class TunerChannel implements Comparable<TunerChannel>
{
private Type mType;
private int mFrequency;
private int mBandwidth;
public TunerChannel( Type type, int frequency, int bandwidth )
{
mType = type;
mFrequency = frequency;
mBandwidth = bandwidth;
}
public Type getType()
{
return mType;
}
public void setType( Type type )
{
mType = type;
}
public int getFrequency()
{
return mFrequency;
}
public void setFrequency( int frequency )
{
mFrequency = frequency;
}
public int getBandwidth()
{
return mBandwidth;
}
public void setBandwidth( int bandwidth )
{
mBandwidth = bandwidth;
}
public int getMinFrequency()
{
return mFrequency - ( mBandwidth / 2 );
}
public int getMaxFrequency()
{
return mFrequency + ( mBandwidth / 2 );
}
/**
* Indicates if any part of this tuner channel is contained within the
* minimum and maximum frequency values.
*/
public boolean isWithin( int minimum, int maximum )
{
return ( ( minimum <= getMinFrequency() && getMinFrequency() <= maximum ) ||
( minimum <= getMaxFrequency() && getMaxFrequency() <= maximum ) );
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( "Channel: " );
sb.append( ( mFrequency - ( mBandwidth / 2 ) ) );
sb.append( "-" );
sb.append( ( mFrequency + ( mBandwidth / 2 ) ) );
return sb.toString();
}
@Override
public int compareTo( TunerChannel otherLock )
{
if( mFrequency < otherLock.getFrequency() )
{
return -1;
}
else
{
return 1;
}
}
public enum Type
{
LOCKED, //Use for decoding channels, so that they cannot be tuned out of range
TRAFFIC, //Like locked, but for temporary allocated traffic channels
TUNABLE; //Tunable -- can be tuned out of range
}
}

View File

@@ -0,0 +1,273 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import sample.Listener;
import sample.Provider;
import sample.complex.ComplexSample;
import sample.complex.ComplexSampleAssembler;
import source.ComplexSource;
import source.Source.SampleType;
import source.SourceException;
import util.Oscillator;
import controller.ThreadPoolManager;
import controller.ThreadPoolManager.ThreadType;
import dsp.filter.ComplexFilter;
import dsp.filter.FilterFactory;
import dsp.filter.Window.WindowType;
public class TunerChannelSource extends ComplexSource
implements FrequencyChangeListener,
Listener<Float[]>
{
private static int sCHANNEL_RATE = 48000;
private static int sCHANNEL_PASS_FREQUENCY = 12500;
private LinkedTransferQueue<Float[]> mBuffer =
new LinkedTransferQueue<Float[]>();
private Tuner mTuner;
private TunerChannel mTunerChannel;
private Oscillator mSineWaveGenerator;
private ComplexFilter[] mDecimationFilters;
private DecimationProcessor mDecimationProcessor = new DecimationProcessor();
private ComplexSampleAssembler mBufferAssembler =
new ComplexSampleAssembler( 3000 );
private int mTunerFrequency;
private int mTunerSampleRate;
private ThreadPoolManager mThreadPoolManager;
private ScheduledFuture<?> mTaskHandle;
public TunerChannelSource( ThreadPoolManager threadPoolManager,
Tuner tuner,
TunerChannel tunerChannel )
throws RejectedExecutionException
{
super( "Tuner Channel Source" );
mThreadPoolManager = threadPoolManager;
/* Schedule the decimation task to run 20 times a second */
mTaskHandle = mThreadPoolManager.schedule( ThreadType.DECIMATION,
new DecimationProcessor(), 50, TimeUnit.MILLISECONDS );
mTuner = tuner;
mTuner.addListener( (FrequencyChangeListener)this );
mTunerChannel = tunerChannel;
/* Setup the frequency translator to the current source frequency */
try
{
frequencyChanged( mTuner.getFrequency(), mTuner.getSampleRate() );
}
catch ( SourceException e )
{
e.printStackTrace();
}
/* Finally, register to receive samples from the tuner */
mTuner.addListener( (Listener<Float[]>)this );
}
@Override
public void dispose()
{
//Tell the tuner to release our resources
mTuner.removeListener( (FrequencyChangeListener)this );
mTuner.releaseChannel( this );
if( mTaskHandle != null )
{
mThreadPoolManager.cancel( mTaskHandle );
}
mBuffer.clear();
mBuffer = null;
for( ComplexFilter filter: mDecimationFilters )
{
filter.dispose();
}
mBufferAssembler.dispose();
mTuner = null;
mTunerChannel = null;
mTunerChannel = null;
mTuner = null;
}
public Tuner getTuner()
{
return mTuner;
}
public TunerChannel getTunerChannel()
{
return mTunerChannel;
}
@Override
public void receive( Float[] samples )
{
mBuffer.add( samples );
}
@Override
public void setListener( Listener<List<ComplexSample>> listener )
{
mBufferAssembler.setListener( listener );
}
@Override
public void removeListener( Listener<List<ComplexSample>> listener )
{
mBufferAssembler.removeListener( listener );
mDecimationFilters[ mDecimationFilters.length - 1 ]
.setListener( null );
}
@Override
public void frequencyChanged( int frequency, int bandwidth )
{
/* Frequency change */
if( frequency != mTunerFrequency )
{
int frequencyOffset = frequency - mTunerChannel.getFrequency();
if( mSineWaveGenerator == null )
{
mSineWaveGenerator =
new Oscillator( frequencyOffset, bandwidth );
}
else
{
mSineWaveGenerator.setFrequency( frequencyOffset );
}
/* Clear the buffer for the frequency to take immediate effect */
mBuffer.clear();
mTunerFrequency = frequency;
}
/* Bandwidth/Sample rate change */
if( bandwidth != mTunerSampleRate )
{
int frequencyOffset = frequency - mTunerChannel.getFrequency();
if( mSineWaveGenerator == null )
{
mSineWaveGenerator =
new Oscillator( frequencyOffset, bandwidth );
}
else
{
mSineWaveGenerator.setSampleRate( bandwidth );
}
/* Get the original decimation filter output listener */
Listener<ComplexSample> listener = null;
/* Get new decimation filters */
mDecimationFilters = FilterFactory
.getDecimationFilters( bandwidth,
sCHANNEL_RATE,
sCHANNEL_PASS_FREQUENCY,
48, //dB attenuation
WindowType.HAMMING );
/* wire the filters together */
if( mDecimationFilters.length > 1 )
{
for( int x = 1; x < mDecimationFilters.length; x++ )
{
mDecimationFilters[ x - 1 ].setListener( mDecimationFilters[ x ] );
}
}
/* re-add the original output listener */
mDecimationFilters[ mDecimationFilters.length - 1 ]
.setListener( mBufferAssembler );
mTunerSampleRate = bandwidth;
}
}
public int getSampleRate() throws SourceException
{
return mTuner.getSampleRate();
}
public int getFrequency() throws SourceException
{
return mTuner.getFrequency();
}
public class DecimationProcessor implements Runnable
{
@Override
public void run()
{
List<Float[]> samplesList = new ArrayList<Float[]>();
if( mBuffer != null )
{
/* Limit to 10, so that after a garbage collect we don't
* induce wild swings as it tries to take tons of samples
* and process them, causing delays and buffer refill */
mBuffer.drainTo( samplesList, 10 );
for( Float[] samples: samplesList )
{
for( int x = 0; x < samples.length; x += 2 )
{
Float left = samples[ x ];
Float right = samples[ x + 1 ];
if( left != null && right != null )
{
/* perform frequency translation */
ComplexSample translated = ComplexSample.multiply(
mSineWaveGenerator.nextComplex(), left, right );
/* decimate the sample rate */
if( mDecimationFilters[ 0 ] != null )
{
mDecimationFilters[ 0 ].receive( translated );
}
}
}
}
samplesList.clear();
}
}
}
}

View File

@@ -0,0 +1,260 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
import javax.usb.UsbDeviceDescriptor;
public enum TunerClass
{
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" ),
COMPRO_VIDEOMATE_U650F( TunerType.ELONICS_E4000, "185B", "0650", "Compro", "Videomate U620F" ),
COMPRO_VIDEOMATE_U680F( TunerType.ELONICS_E4000, "185B", "0680", "Compro", "Videomate U620F" ),
DEXATEK_LOGILINK_VG002A( TunerType.FCI_FC2580, "1D19", "1101", "Dexatek", "Logilink VG0002A" ),
DEXATEK_DIGIVOX_MINI_II_REV3( TunerType.FCI_FC2580, "1D19", "1102", "Dexatek", "MSI Digivox Mini II v3.0" ),
DEXATEK_5217_DVBT( TunerType.FCI_FC2580, "1D19", "1103", "Dexatek", "5217 DVB-T" ),
ETTUS_USRP_B100( TunerType.ETTUS_VARIOUS, "2500", "0002", "Ettus Research", "USRP B100" ),
FUNCUBE_DONGLE_PRO( TunerType.FUNCUBE_DONGLE_PRO, "04D8", "FB56", "Hamlincrest", "Funcube Dongle Pro" ),
FUNCUBE_DONGLE_PRO_PLUS( TunerType.FUNCUBE_DONGLE_PRO_PLUS, "04D8", "FB31", "Hamlincrest", "Funcube Dongle Pro Plus" ),
GIGABYTE_GTU7300( TunerType.FITIPOWER_FC0012, "1B80", "D393", "Gigabyte", "GT-U7300" ),
GTEK_T803( TunerType.FITIPOWER_FC0012, "1F4D", "B803", "GTek", "T803" ),
LIFEVIEW_LV5T_DELUXE( TunerType.FITIPOWER_FC0012, "1F4D", "C803", "Liveview", "LV5T Deluxe" ),
MYGICA_TD312( TunerType.FITIPOWER_FC0012, "1F4D", "D286", "MyGica", "TD312" ),
PEAK_102569AGPK( TunerType.FITIPOWER_FC0012, "1B80", "D395", "Peak", "102569AGPK" ),
PROLECTRIX_DV107669( TunerType.FITIPOWER_FC0012, "1F4D", "D803", "Prolectrix", "DV107669" ),
SVEON_STV20( TunerType.FITIPOWER_FC0012, "1B80", "D39D", "Sveon", "STV20 DVB-T USB & FM" ),
TERRATEC_CINERGY_T_REV1( TunerType.FITIPOWER_FC0012, "0CCD", "00A9", "Terratec", "Cinergy T R1" ),
TERRATEC_CINERGY_T_REV3( TunerType.ELONICS_E4000, "0CCD", "00D3", "Terratec", "Cinergy T R3" ),
TERRATEC_NOXON_REV1_B3( TunerType.FITIPOWER_FC0013, "0CCD", "00B3", "Terratec", "NOXON R1 (B3)" ),
TERRATEC_NOXON_REV1_B4( TunerType.FITIPOWER_FC0013, "0CCD", "00B4", "Terratec", "NOXON R1 (B4)" ),
TERRATEC_NOXON_REV1_B7( TunerType.FITIPOWER_FC0013, "0CCD", "00B7", "Terratec", "NOXON R1 (B7)" ),
TERRATEC_NOXON_REV1_C6( TunerType.FITIPOWER_FC0013, "0CCD", "00C6", "Terratec", "NOXON R1 (C6)" ),
TERRATEC_NOXON_REV2( TunerType.ELONICS_E4000, "0CCD", "00E0", "Terratec", "NOXON R2" ),
TERRATEC_T_STICK_PLUS( TunerType.ELONICS_E4000, "0CCD", "00D7", "Terratec", "T Stick Plus" ),
TWINTECH_UT40( TunerType.FITIPOWER_FC0013, "1B80", "D3A4", "Twintech", "UT-40" ),
ZAAPA_ZTMINDVBZP( TunerType.FITIPOWER_FC0012, "1B80", "D398", "Zaapa", "ZT-MINDVBZP" ),
UNKNOWN( TunerType.UNKNOWN, "0", "0", "Unknown Manufacturer", "Unknown Device" );
private TunerType mTunerType;
private String mVendorID;
private String mDeviceID;
private String mVendorDescription;
private String mDeviceDescription;
private TunerClass( TunerType tunerType,
String vendorID,
String deviceID,
String vendorDescription,
String deviceDescription )
{
mTunerType = tunerType;
mVendorID = vendorID;
mDeviceID = deviceID;
mVendorDescription = vendorDescription;
mDeviceDescription = deviceDescription;
}
public String toString()
{
return "USB" +
" Tuner:" + mTunerType.toString() +
" Vendor:" + mVendorDescription +
" Device:" + mDeviceDescription +
" Address:" + mVendorID + ":" + mDeviceID;
}
public String getVendorDeviceLabel()
{
return mVendorDescription + " " + mDeviceDescription;
}
public TunerType getTunerClass()
{
return mTunerType;
}
public static TunerClass valueOf( UsbDeviceDescriptor descriptor )
{
return valueOf( descriptor.idVendor(), descriptor.idProduct() );
}
public static TunerClass valueOf( short vendor, short product )
{
TunerClass retVal = TunerClass.UNKNOWN;
//Cast the short to integer so that we can switch on unsigned numbers
int vendorID = vendor & 0xFFFF;
int productID = product & 0xFFFF;
switch( vendorID )
{
case 1240: //04DA
if( productID == 64305 ) //FB31
{
retVal = FUNCUBE_DONGLE_PRO_PLUS;
}
else if( productID == 64342 ) //FB56
{
retVal = FUNCUBE_DONGLE_PRO;
}
break;
case 3034: //0BDA
if( productID == 10290 ) //2832
{
retVal = GENERIC_2832;
}
else if( productID == 10296 ) //2838
{
retVal = GENERIC_2838;
}
break;
case 6235: //185B
if( productID == 1568 ) //0620
{
retVal = COMPRO_VIDEOMATE_U620F;
}
else if( productID == 1616 ) //0650
{
retVal = COMPRO_VIDEOMATE_U650F;
}
else if( productID == 1664 ) //0680
{
retVal = COMPRO_VIDEOMATE_U680F;
}
break;
case 7040: //1B80
if( productID == 54163 ) //D393
{
retVal = GIGABYTE_GTU7300;
}
else if( productID == 54165 ) //D395
{
retVal = PEAK_102569AGPK;
}
else if( productID == 54168 ) //D398
{
retVal = ZAAPA_ZTMINDVBZP;
}
else if( productID == 54173 ) //D39D
{
retVal = SVEON_STV20;
}
else if( productID == 54180 ) //D3A4
{
retVal = TWINTECH_UT40;
}
break;
case 7449: //1D19
if( productID == 4353 ) //1101
{
retVal = DEXATEK_LOGILINK_VG002A;
}
else if( productID == 4354 ) //1102
{
retVal = DEXATEK_DIGIVOX_MINI_II_REV3;
}
else if( productID == 4355 ) //1103
{
retVal = DEXATEK_5217_DVBT;
}
break;
case 8013: //1F4D
if( productID == 47107 ) //B803
{
retVal = GTEK_T803;
}
else if( productID == 51203 ) //C803
{
retVal = LIFEVIEW_LV5T_DELUXE;
}
else if( productID == 53894 ) //D286
{
retVal = MYGICA_TD312;
}
else if( productID == 55299 ) //D803
{
retVal = PROLECTRIX_DV107669;
}
break;
case 3277: //0CCD
if( productID == 169 ) //00A9
{
retVal = TERRATEC_CINERGY_T_REV1;
}
else if( productID == 179 ) //00B3
{
retVal = TERRATEC_NOXON_REV1_B3;
}
else if( productID == 180 ) //00B4
{
retVal = TERRATEC_NOXON_REV1_B4;
}
else if( productID == 181 ) //00B5
{
retVal = TERRATEC_NOXON_REV1_B7;
}
else if( productID == 198 ) //00C6
{
retVal = TERRATEC_NOXON_REV1_C6;
}
else if( productID == 211 ) //00D3
{
retVal = TERRATEC_CINERGY_T_REV3;
}
else if( productID == 215 ) //00D7
{
retVal = TERRATEC_T_STICK_PLUS;
}
else if( productID == 224) //00E0
{
retVal = TERRATEC_NOXON_REV2;
}
break;
case 9472: //2500
if( productID == 2 ) //0002
{
retVal = ETTUS_USRP_B100;
}
break;
default:
}
return retVal;
}
public String getVendorID()
{
return mVendorID;
}
public String getDeviceID()
{
return mDeviceID;
}
public String getVendorDescription()
{
return mVendorDescription;
}
public String getDeviceDescription()
{
return mDeviceDescription;
}
}

View File

@@ -0,0 +1,65 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;
/**
* Abstract class to hold a named configuration for a specific type of tuner
*/
@XmlType( name = "tuner_configuration" )
public abstract class TunerConfiguration
{
protected String mName;
/**
* Default constructor to support JAXB
*/
public TunerConfiguration()
{
this( "Unknown" );
}
public String toString()
{
return mName;
}
/**
* Normal constructor
*/
public TunerConfiguration( String name )
{
mName = name;
}
@XmlAttribute( name = "name" )
public String getName()
{
return mName;
}
public void setName( String name )
{
mName = name;
}
@XmlAttribute( name = "tuner_type" )
public abstract TunerType getTunerType();
}

View File

@@ -0,0 +1,64 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
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;
}
}

View File

@@ -0,0 +1,250 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.concurrent.RejectedExecutionException;
import log.Log;
import source.SourceException;
import source.tuner.FrequencyController.Tunable;
import controller.ThreadPoolManager;
public abstract class TunerController implements Tunable
{
/* List of currently tuned channels being served to demod channels */
protected ArrayList<TunerChannel> mTunedChannels =
new ArrayList<TunerChannel>();
protected FrequencyController mFrequencyController;
/**
* Abstract tuner controller class. The tuner controller manages frequency
* bandwidth and currently tuned channels that are being fed samples from
* the tuner.
*
* @param minimumFrequency - minimum uncorrected tunable frequency
* @param maximumFrequency - maximum uncorrected tunable frequency
* @throws SourceException - for any issues related to constructing the
* class, tuning a frequency, or setting the bandwidth
*/
public TunerController( int minimumFrequency, int maximumFrequency )
throws SourceException
{
mFrequencyController = new FrequencyController( this,
minimumFrequency,
maximumFrequency,
0.0d );
}
/**
* Applies the settings in the tuner configuration
*/
public abstract void apply( TunerConfiguration config )
throws SourceException;
public int getBandwidth()
{
return mFrequencyController.getBandwidth();
}
/**
* Sets the center frequency of the local oscillator.
*
* @param frequency in hertz
* @throws SourceException - if the tuner has any issues
*/
public void setFrequency( int frequency ) throws SourceException
{
mFrequencyController.setFrequency( frequency );
}
/**
* Gets the center frequency of the local oscillator
*
* @return frequency in hertz
*/
public int getFrequency()
{
return mFrequencyController.getFrequency();
}
public double getFrequencyCorrection()
{
return mFrequencyController.getFrequencyCorrection();
}
public void setFrequencyCorrection( double correction ) throws SourceException
{
mFrequencyController.setFrequencyCorrection( correction );
}
public int getMinFrequency()
{
return mFrequencyController.getMinimumFrequency();
}
public int getMaxFrequency()
{
return mFrequencyController.getMaximumFrequency();
}
/**
* Indicates if the tuner can accomodate this new channel frequency and
* bandwidth, along with all of the existing tuned channels currently in
* place.
*/
public boolean canTuneChannel( TunerChannel channel )
{
//Make sure we're within the tunable frequency range of this tuner
if( getMinFrequency() < channel.getMinFrequency() &&
getMaxFrequency() > channel.getMaxFrequency() )
{
//If this is the first lock, then we're good
if( mTunedChannels.isEmpty() )
{
return true;
}
else
{
//Sort the existing locks and get the min/max locked frequencies
Collections.sort( mTunedChannels );
int minLockedFrequency = mTunedChannels.get( 0 ).getMinFrequency();
int maxLockedFrequency = mTunedChannels
.get( mTunedChannels.size() - 1 ).getMaxFrequency();
//Requested channel is higher than min locked frequency
if( minLockedFrequency <= channel.getMinFrequency() &&
( channel.getMaxFrequency() - minLockedFrequency ) <= getBandwidth() )
{
return true;
}
//Requested channel is lower than the max locked frequency
else if( channel.getMaxFrequency() <= maxLockedFrequency &&
( maxLockedFrequency - channel.getMinFrequency() ) <= getBandwidth() )
{
return true;
}
}
}
return false;
}
public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager,
Tuner tuner, TunerChannel tunerChannel )
throws RejectedExecutionException,
SourceException
{
TunerChannelSource source = null;
if( canTuneChannel( tunerChannel ) )
{
mTunedChannels.add( tunerChannel );
updateLOFrequency();
source = new TunerChannelSource( threadPoolManager, tuner, tunerChannel );
}
return source;
}
public void releaseChannel( TunerChannelSource tunerChannelSource )
{
if( tunerChannelSource != null )
{
mTunedChannels.remove( tunerChannelSource.getTunerChannel() );
}
else
{
Log.error( "Tuner Controller - couldn't find the tuned channel "
+ "to release it" );
}
}
/**
* Sets the Local Oscillator frequency to the middle of the currently
* locked frequency range, adjusting the left/right of a channel, if the
* middle falls within the locked range of any of the channels. Note: this
* will fail to set the correct frequency if multiple overlapping channel
* bandwidths are locked in the exact middle of the total locked channel
* frequency range.
*
* @throws SourceException
*/
public void updateLOFrequency() throws SourceException
{
Collections.sort( mTunedChannels );
int minLockedFrequency = mTunedChannels.get( 0 ).getMinFrequency();
int maxLockedFrequency = mTunedChannels
.get( mTunedChannels.size() - 1 ).getMaxFrequency();
int middle = minLockedFrequency +
( ( maxLockedFrequency - minLockedFrequency ) / 2 );
int middleMin = middle - 10000;
int middleMax = middle + 10000;
Iterator<TunerChannel> it = mTunedChannels.iterator();
while( it.hasNext() )
{
TunerChannel lock = it.next();
//If a locked channel overlaps our middle frequency lockout, adjust to
//the left or the right of that channel, whichever is closer
if( lock.getMinFrequency() < middleMax && middleMin < lock.getMaxFrequency() )
{
if( middleMax - lock.getMinFrequency() < lock.getMaxFrequency() - middleMin )
{
middle = lock.getMinFrequency() - 10000;
}
else
{
middle = lock.getMaxFrequency() + 10000;
}
}
}
mFrequencyController.setFrequency( middle );
}
/**
* Sets the listener to be notified any time that the tuner changes frequency
* or bandwidth/sample rate.
*
* Note: this is normally used by the Tuner. Any additional listeners can
* be registered on the tuner.
*/
public void addListener( FrequencyChangeListener listener )
{
mFrequencyController.addListener( listener );
}
/**
* Removes the frequency change listener
*/
public void removeListener( FrequencyChangeListener listener )
{
mFrequencyController.removeListener( listener );
}
}

View File

@@ -0,0 +1,57 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
import gui.control.JFrequencyControl;
import source.SourceEditor;
import source.config.SourceConfigTuner;
import source.config.SourceConfiguration;
public class TunerEditor extends SourceEditor
{
private static final long serialVersionUID = 1L;
private JFrequencyControl mFrequencyControl;
public TunerEditor( SourceConfiguration config )
{
super( config );
initGUI();
}
public void reset()
{
mFrequencyControl.setFrequency(
((SourceConfigTuner)mConfig).getFrequency(), false );
}
public void save()
{
((SourceConfigTuner)mConfig).setFrequency( mFrequencyControl.getFrequency() );
}
private void initGUI()
{
mFrequencyControl = new JFrequencyControl();
mFrequencyControl.setFrequency(
((SourceConfigTuner)mConfig).getFrequency(), false );
add( mFrequencyControl );
}
}

View File

@@ -0,0 +1,35 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
import controller.BaseNode;
public class TunerGroupNode extends BaseNode
{
private static final long serialVersionUID = 1L;
public TunerGroupNode()
{
super( null );
}
public String toString()
{
return "Tuners [" + this.getChildCount() + "]";
}
}

View File

@@ -0,0 +1,547 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.RejectedExecutionException;
import javax.usb.UsbDevice;
import javax.usb.UsbException;
import javax.usb.UsbHostManager;
import javax.usb.UsbHub;
import javax.usb.UsbServices;
import log.Log;
import org.usb4java.Context;
import org.usb4java.DeviceList;
import org.usb4java.LibUsb;
import org.usb4java.LibUsbException;
import source.Source;
import source.SourceException;
import source.config.SourceConfigTuner;
import source.mixer.MixerManager;
import source.tuner.ettus.B100Tuner;
import source.tuner.fcd.FCDTuner;
import source.tuner.fcd.proV1.FCD1TunerController;
import source.tuner.fcd.proplusV2.FCD2TunerController;
import source.tuner.rtl.RTL2832Tuner;
import source.tuner.rtl.RTL2832TunerController;
import source.tuner.rtl.e4k.E4KTunerController;
import source.tuner.rtl.r820t.R820TTunerController;
import source.tuner.usb.USBTunerDevice;
import controller.ResourceManager;
import controller.channel.ProcessingChain;
public class TunerManager
{
private static final String sLOADED = "\t[LOADED]\t";
private static final String sNOT_LOADED = "\t[NOT LOADED]\t";
private ResourceManager mResourceManager;
private ArrayList<Tuner> mTuners = new ArrayList<Tuner>();
public TunerManager( ResourceManager resourceManager )
{
mResourceManager = resourceManager;
loadTuners();
}
/**
* Iterates current tuners to get a tuner channel source for the frequency
* specified in the channel config's source config object
*/
public Source getSource( ProcessingChain channel )
{
TunerChannelSource retVal = null;
if( channel.getChannel().getSourceConfiguration()
instanceof SourceConfigTuner )
{
TunerChannel tunerChannel = channel.getChannel().getTunerChannel();
Iterator<Tuner> it = mTuners.iterator();
Tuner tuner;
while( it.hasNext() && retVal == null )
{
tuner = it.next();
try
{
retVal = tuner.getChannel(
mResourceManager.getThreadPoolManager(), tunerChannel );
}
catch ( RejectedExecutionException ree )
{
Log.error( "TunerManager - couldn't provide tuner channel "
+ "source - " + ree.getLocalizedMessage() );
}
catch ( SourceException e )
{
Log.error( "TunerManager - error obtaining channel " +
"from tuner [" + tuner.getName() + "] - "
+ e.getLocalizedMessage() );
}
}
}
return retVal;
}
/**
* Get list of current tuners
*/
public List<Tuner> getTuners()
{
return mTuners;
}
/**
* Loads all USB tuners and USB/Mixer tuner devices
*/
private void loadTuners()
{
mTuners.clear();
//Get list of attached USB tuner devices
ArrayList<USBTunerDevice> devices = getUSBTunerDevices();
StringBuilder sb = new StringBuilder();
sb.append( "Tuner Manager - discovered [" + devices.size() +
"] USB tuner devices\n" );
for( USBTunerDevice device: devices )
{
String status = sNOT_LOADED;
String name = "Unknown";
String reason = "No device driver class available";
TunerClass tunerClass = device.getTunerClass();
switch( tunerClass )
{
case FUNCUBE_DONGLE_PRO:
//Locate the matching mixer tuner dataline
MixerTunerDataLine fcd1Dataline =
getMixerTunerDataLine( tunerClass.getTunerClass() );
if( fcd1Dataline != null )
{
FCD1TunerController fcd1Controller;
try
{
fcd1Controller = new FCD1TunerController( device );
FCDTuner fcd1Tuner =
new FCDTuner( fcd1Dataline, fcd1Controller );
TunerConfiguration config =
getTunerConfiguration( fcd1Tuner );
if( config != null )
{
fcd1Tuner.apply( config );
}
mTuners.add( fcd1Tuner );
name = fcd1Tuner.getName();
status = sLOADED;
reason = null;
}
catch ( SourceException e )
{
reason = "Controller Source Exception: " +
e.getLocalizedMessage();
}
}
else
{
reason = "Couldn't locate matching mixer/sound card device";
}
break;
case FUNCUBE_DONGLE_PRO_PLUS:
//Locate the matching mixer tuner dataline
MixerTunerDataLine fcd2Dataline =
getMixerTunerDataLine( tunerClass.getTunerClass() );
if( fcd2Dataline != null )
{
FCD2TunerController fcd2Controller;
try
{
fcd2Controller = new FCD2TunerController( device );
FCDTuner fcd2Tuner =
new FCDTuner( fcd2Dataline, fcd2Controller );
TunerConfiguration config =
getTunerConfiguration( fcd2Tuner );
if( config != null )
{
fcd2Tuner.apply( config );
}
mTuners.add( fcd2Tuner );
status = sLOADED;
name = fcd2Tuner.getName();
reason = null;
}
catch ( SourceException e )
{
reason = "Controller Source Exception: " +
e.getLocalizedMessage();
}
}
else
{
reason = "Couldn't locate matching mixer/sound card device";
}
break;
case GENERIC_2832:
case COMPRO_VIDEOMATE_U620F:
case COMPRO_VIDEOMATE_U650F:
case COMPRO_VIDEOMATE_U680F:
case DEXATEK_5217_DVBT:
case DEXATEK_DIGIVOX_MINI_II_REV3:
case DEXATEK_LOGILINK_VG002A:
case GENERIC_2838:
case GIGABYTE_GTU7300:
case GTEK_T803:
case LIFEVIEW_LV5T_DELUXE:
case MYGICA_TD312:
case PEAK_102569AGPK:
case PROLECTRIX_DV107669:
case SVEON_STV20:
case TERRATEC_CINERGY_T_REV1:
case TERRATEC_CINERGY_T_REV3:
case TERRATEC_NOXON_REV1_B3:
case TERRATEC_NOXON_REV1_B4:
case TERRATEC_NOXON_REV1_B7:
case TERRATEC_NOXON_REV1_C6:
case TERRATEC_NOXON_REV2:
case TERRATEC_T_STICK_PLUS:
case TWINTECH_UT40:
case ZAAPA_ZTMINDVBZP:
TunerType tunerType = tunerClass.getTunerClass();
if( tunerType == TunerType.RTL2832_VARIOUS )
{
tunerType = RTL2832TunerController.identifyTunerType( device );
}
switch( tunerType )
{
case ELONICS_E4000:
try
{
E4KTunerController controller =
new E4KTunerController( device );
controller.init();
RTL2832Tuner rtlTuner =
new RTL2832Tuner( tunerClass, controller );
TunerConfiguration config =
getTunerConfiguration( rtlTuner );
if( config != null )
{
rtlTuner.apply( config );
}
mTuners.add( rtlTuner );
status = sLOADED;
name = rtlTuner.getName();
reason = null;
}
catch( SourceException se )
{
status = sNOT_LOADED;
reason = "Error constructing E4K tuner "
+ "controller - " + se.getLocalizedMessage();
}
break;
case RAFAELMICRO_R820T:
// try
// {
// R820TTunerController controller =
// new R820TTunerController( device );
//
// controller.init();
//
// RTL2832Tuner rtlTuner =
// new RTL2832Tuner( tunerClass, controller );
//
//
// TunerConfiguration config =
// getTunerConfiguration( rtlTuner );
//
// if( config != null )
// {
// rtlTuner.apply( config );
// }
//
// mTuners.add( rtlTuner );
// status = sLOADED;
// name = rtlTuner.getName();
// reason = null;
// }
// catch( SourceException se )
// {
// status = sNOT_LOADED;
// reason = "Error constructing E4K tuner "
// + "controller - " + se.getLocalizedMessage();
// }
// break;
case FITIPOWER_FC0012:
case FITIPOWER_FC0013:
case RAFAELMICRO_R828D:
case UNKNOWN:
default:
status = sNOT_LOADED;
reason = "SDRTRunk doesn't currently support RTL2832 "
+ "Dongle with [" + tunerType.toString() +
"] tuner";
break;
}
break;
case ETTUS_USRP_B100:
mTuners.add( new B100Tuner( device ) );
status = sLOADED;
reason = null;
break;
case UNKNOWN:
default:
break;
}
/**
* Log the tuner loading status
*/
sb.append( status );
sb.append( String.format( "%04X",
device.getDevice().getUsbDeviceDescriptor().idVendor() ) );
sb.append( ":" );
sb.append( String.format( "%04X",
device.getDevice().getUsbDeviceDescriptor().idProduct() ) );
sb.append( " " );
// sb.append( device.getTunerType().getVendorDescription() );
// sb.append( " " );
// sb.append( device.getTunerType().getDeviceDescription() );
sb.append( name );
if( reason != null )
{
sb.append( " - " );
sb.append( reason );
}
sb.append( "\n" );
}
Log.header( "Configuring USB Tuners" );
Log.info( sb.toString() );
}
/**
* Get all USB tuner devices
*/
private ArrayList<USBTunerDevice> getUSBTunerDevices()
{
StringBuilder sb = new StringBuilder();
List<UsbDevice> devices = getUSBDevices();
sb.append( "TunerManager - discovered [" + devices.size() +
"] attached usb devices\n" );
ArrayList<USBTunerDevice> tuners = new ArrayList<USBTunerDevice>();
for (UsbDevice device : devices )
{
String status = "\t[NOT RECOGNIZED]\t";
TunerClass type = TunerClass.valueOf( device.getUsbDeviceDescriptor() );
if( type != TunerClass.UNKNOWN )
{
status = "\t[RECOGNIZED] \t";
tuners.add( new USBTunerDevice( device, type) );
}
sb.append( status );
sb.append( device.toString() );
if( type != TunerClass.UNKNOWN )
{
sb.append( " " );
sb.append( type.getVendorDescription() );
sb.append( " " );
sb.append( type.getDeviceDescription() );
}
sb.append( "\n" );
}
Log.header( "USB Device Discovery" );
Log.info( sb.toString() );
return tuners;
}
/**
* Gets all USB devices from the root hub downward, recursively, or
* returns empty list if none are discovered
*/
public static List<UsbDevice> getUSBDevices()
{
ArrayList<UsbDevice> devices = new ArrayList<UsbDevice>();
try
{
UsbServices services = UsbHostManager.getUsbServices();
devices = getUsbChildDevices( services.getRootUsbHub() );
}
catch( SecurityException e )
{
Log.error( "TunerManager - security exception while getting "
+ "USB devices" );
}
catch( UsbException e )
{
Log.error( "TunerManager - usb exception while getting USB "
+ "devices - " + e.getLocalizedMessage() );
}
return devices;
}
/**
* Get (recursively) all usb child devices below the usb hub
*/
public static ArrayList<UsbDevice> getUsbChildDevices( UsbHub hub )
{
ArrayList<UsbDevice> devices = new ArrayList<UsbDevice>();
for ( UsbDevice device : (List<UsbDevice>) hub.getAttachedUsbDevices() )
{
devices.add( device );
if ( device.isUsbHub() )
{
devices.addAll( getUsbChildDevices( (UsbHub)device ) );
}
}
return devices;
}
/**
* 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<TunerConfiguration> configs = mResourceManager
.getSettingsManager().getTunerConfigurations( tuner.getTunerType() );
TunerConfiguration config = null;
if( selected != null )
{
for( TunerConfiguration tunerConfig: configs )
{
if( tunerConfig.getName().contentEquals( selected.getTunerConfigurationName() ) )
{
config = tunerConfig;
}
}
}
else
{
config = configs.get( 0 );
}
//If we're still null at this point, create a default config
if( config == null )
{
config = mResourceManager.getSettingsManager()
.addNewTunerConfiguration( tuner.getTunerType(), "Default" );
}
//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.
*
* Note: this method is not currently able to align multiple tuner mixer
* data lines of the same tuner type. If you have multiple Funcube Dongle
* tuners of the same TYPE, there is no guarantee that you will get the
* correct mixer.
*
* @param tunerClass
* @return
*/
private MixerTunerDataLine getMixerTunerDataLine( TunerType tunerClass )
{
Collection<MixerTunerDataLine> datalines =
MixerManager.getInstance().getMixerTunerDataLines();
for( MixerTunerDataLine mixerTDL: datalines )
{
if( mixerTDL.getMixerTunerType().getTunerClass() == tunerClass )
{
return mixerTDL;
}
}
return null;
}
}

View File

@@ -0,0 +1,127 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
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 log.Log;
import source.SourceException;
import spectrum.SpectrumFrame;
import controller.BaseNode;
public class TunerNode extends BaseNode implements FrequencyChangeListener
{
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 int mFrequency;
private int mBandwidth;
public TunerNode( Tuner tuner )
{
super( tuner ); //does this make it a super tuner?
tuner.addListener( this );
try
{
mFrequency = tuner.getFrequency();
mBandwidth = tuner.getSampleRate();
}
catch ( SourceException e )
{
Log.error( "TunerNode - error reading frequency and sample rate "
+ "from tuner - " + e.getLocalizedMessage() );
}
}
@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)mBandwidth / 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 )
{
SpectrumFrame frame =
new SpectrumFrame( getModel().getResourceManager(),
getTuner() );
}
} );
retVal.add( newSpectrumItem );
return retVal;
}
@Override
public void frequencyChanged( int frequency, int bandwidth )
{
mFrequency = frequency;
mBandwidth = bandwidth;
getModel().nodeChanged( TunerNode.this );
}
}

View File

@@ -0,0 +1,23 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
public interface TunerSelectionListener
{
public void tunerSelected( Tuner tuner );
}

View File

@@ -0,0 +1,46 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner;
public enum TunerType
{
ELONICS_E4000( "E4000" ),
ETTUS_WBX( "WBX" ),
ETTUS_VARIOUS( "Ettus Tuner" ),
FCI_FC2580( "FC2580" ),
FITIPOWER_FC0012( "FC0012" ),
FITIPOWER_FC0013( "FC0013" ),
FUNCUBE_DONGLE_PRO( "Funcube Dongle Pro" ),
FUNCUBE_DONGLE_PRO_PLUS( "Funcube Dongle Pro Plus" ),
RAFAELMICRO_R820T( "R820T" ),
RAFAELMICRO_R828D( "R828D" ),
RTL2832_VARIOUS( "Generic" ),
UNKNOWN( "Unknown" );
private String mLabel;
private TunerType( String label )
{
mLabel = label;
}
public String getLabel()
{
return mLabel;
}
}

View File

@@ -0,0 +1,106 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.ettus;
import java.util.concurrent.RejectedExecutionException;
import javax.swing.JPanel;
import log.Log;
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 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;
}
@Override
public TunerClass getTunerClass()
{
return TunerClass.ETTUS_USRP_B100;
}
@Override
public TunerType getTunerType()
{
return TunerType.ETTUS_WBX;
}
@Override
public JPanel getEditor( ResourceManager resourceManager )
{
return new B100TunerEditorPanel( this, resourceManager );
}
@Override
public void apply( TunerConfiguration config ) throws SourceException
{
Log.error( "B100 Tuner - can't apply tuner configuration" );
}
@Override
public int getSampleRate()
{
return 0;
}
@Override
public int getFrequency() throws SourceException
{
return 0;
}
@Override
public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager,
TunerChannel channel ) throws RejectedExecutionException, SourceException
{
//TODO: pass this call to the tuner controller
return null;
}
@Override
public void releaseChannel( TunerChannelSource source )
{
//TODO: pass this call to the tuner controller
}
@Override
public String getUniqueID()
{
// TODO Auto-generated method stub
return null;
}
}

View File

@@ -0,0 +1,50 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.ettus;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import net.miginfocom.swing.MigLayout;
import controller.ResourceManager;
public class B100TunerEditorPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private B100Tuner mTuner;
private ResourceManager mResourceManager;
public B100TunerEditorPanel( B100Tuner tuner, ResourceManager resourceManager )
{
mTuner = tuner;
mResourceManager = resourceManager;
init();
}
private void init()
{
setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]" ) );
setBorder( BorderFactory.createTitledBorder( "Ettus B100 Tuner" ) );
add( new JLabel( "Note: SDRTrunk can only recognize the B100 at this time. It cannot use this tuner") );
}
}

View File

@@ -0,0 +1,163 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.fcd;
public enum FCDCommand
{
/**
* Note: the javaxUSB and usb4Java libraries are slightly different than
* the signal9 HID libraries used in C, so you will see variations in the
* byte array length and indexes, when comparing this setup to the qthid
* source code.
*/
//Bootloader Mode Commands
BL_QUERY( (byte) 0x01, 64 ), //01 Decimal
BL_RESET( (byte) 0x08, 2 ), //08
BL_ERASE( (byte) 0x19, 2 ), //24
BL_SET_BYTE_ADDR( (byte) 0x1A, 6 ), //25
BL_GET_BYTE_ADDR_RANGE( (byte) 0x1B, 6 ), //26
BL_WRITE_FLASH_BLOCK( (byte) 0x1C, 52 ), //27
BL_READ_FLASH_BLOCK( (byte) 0x1D, 48 ), //28
//Application Mode Commands
APP_SET_FREQUENCY_KHZ( (byte)0x64, 5 ), //100
APP_SET_FREQUENCY_HZ( (byte)0x65, 6 ), //101
APP_GET_FREQUENCY_HZ( (byte)0x66, 6 ), //102
APP_GET_IF_RSSI( (byte) 0x68, 3 ), //104
APP_GET_PLL_LOCKED( (byte) 0x69, 3 ), //105
APP_SET_DC_CORRECTION( (byte) 0x6A, 5 ), //106
APP_GET_DC_CORRECTION( (byte) 0x6B, 6 ), //107
APP_SET_IQ_CORRECTION( (byte) 0x6C, 5 ), //108
APP_GET_IQ_CORRECTION( (byte) 0x6D, 6 ), //109
APP_SET_LNA_GAIN( (byte)0x6E, 2 ), //110
APP_SET_LNA_ENHANCE( (byte)0x6F, 2 ), //111
APP_SET_BAND( (byte)0x70, 2 ), //112
APP_SET_RF_FILTER( (byte)0x71, 2 ), //113
APP_SET_MIXER_GAIN( (byte)0x72, 2 ), //114
APP_SET_BIAS_CURRENT( (byte)0x73, 2 ), //115
APP_SET_MIXER_FILTER( (byte)0x74, 2 ), //116
APP_SET_IF_GAIN1( (byte)0x75, 2 ), //117
APP_SET_IF_GAIN_MODE( (byte)0x76, 2 ), //118
APP_SET_IF_RC_FILTER( (byte)0x77, 2 ), //119
APP_SET_IF_GAIN2( (byte)0x78, 2 ), //120
APP_SET_IF_GAIN3( (byte)0x79, 2 ), //121
APP_SET_IF_FILTER( (byte)0x7A, 2 ), //122
APP_SET_IF_GAIN4( (byte)0x7B, 2 ), //123
APP_SET_IF_GAIN5( (byte)0x7C, 2 ), //124
APP_SET_IF_GAIN6( (byte)0x7D, 2 ), //125
APP_SET_BIAS_TEE( (byte)0x7E, 2 ), //126
APP_GET_LNA_GAIN( (byte)0x96, 3 ), //150
APP_GET_LNA_ENHANCE( (byte)0x97, 3 ), //151
APP_GET_BAND( (byte)0x98, 3 ), //152
APP_GET_RF_FILTER( (byte)0x99, 3 ), //153
APP_GET_MIXER_GAIN( (byte)0x9A, 3 ), //154
APP_GET_BIAS_CURRENT( (byte)0x9B, 3 ), //155
APP_GET_MIXER_FILTER( (byte)0x9C, 3 ), //156
APP_GET_IF_GAIN1( (byte)0x9D, 3 ), //157
APP_GET_IF_GAIN_MODE( (byte)0x9E, 3 ), //158
APP_GET_IF_RC_FILTER( (byte)0x9F, 3 ), //159
APP_GET_IF_GAIN2( (byte)0xA0, 3 ), //160
APP_GET_IF_GAIN3( (byte)0xA1, 3 ), //161
APP_GET_IF_FILTER( (byte)0xA2, 3 ), //162
APP_GET_IF_GAIN4( (byte)0xA3, 3 ), //163
APP_GET_IF_GAIN5( (byte)0xA4, 3 ), //164
APP_GET_IF_GAIN6( (byte)0xA5, 3 ), //165
APP_GET_BIAS_TEE( (byte)0xA6, 3 ), //166
APP_SEND_I2C_BYTE( (byte)0xC8, 2 ), //200
APP_RECV_I2C_BYTE( (byte)0xC9, 3 ), //201
APP_RESET( (byte)0xFF, 1 ); //255
/**
* Single-byte value to use for the command
*/
private byte mCommandByte;
/**
* Required array length
*/
private int mArrayLength;
private FCDCommand( byte value, int responseLength )
{
mCommandByte = value;
mArrayLength = responseLength;
}
/**
* Returns a byte array of the specified length with the command byte value
* set in index position zero. Fill in additional command arguments prior
* to sending to the device
*/
public byte[] getRequestTemplate()
{
byte[] retVal = new byte[ 64 ];
retVal[ 0 ] = mCommandByte;
retVal[ 1 ] = (byte)0x0;
return retVal;
}
/**
* Returns an empty byte array of the correct length to receive the response
* from the associated command
*/
public byte[] getResponseTemplate()
{
return new byte[ 64 ];
}
/**
* Byte value of the command
*/
public byte getCommand()
{
return mCommandByte;
}
/**
* Total array length required to send the command or receive the response.
*
* Index 0: command is echoed back
* Index 1: 0-fail, 1-success
*/
public int getArrayLength()
{
return mArrayLength;
}
/**
* Checks the response array to verify the pass/fail value that is placed
* in array index position 1, in responses from the device
*/
public static boolean checkResponse( FCDCommand command, byte[] response )
{
boolean valid = false;
if( command != null && response != null )
{
valid = response[ 0 ] == command.getCommand() &&
response[ 1 ] == 1;
}
return valid;
}
}

View File

@@ -0,0 +1,53 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.fcd;
public enum FCDModel
{
FUNCUBE_DONGLE_PRO( "FunCube Dongle Pro" ),
FUNCUBE_DONGLE_PRO_PLUS( "FunCube Dongle Pro Plus"),
FUNCUBE_UNKNOWN( "Unknown" );
private String mLabel;
private FCDModel( String label )
{
mLabel = label;
}
public String getLabel()
{
return mLabel;
}
public static FCDModel getFCD( String board )
{
FCDModel retVal = FUNCUBE_UNKNOWN;
if( board.equalsIgnoreCase( "Brd 1.1" ) )
{
retVal = FUNCUBE_DONGLE_PRO;
}
else if( board.equalsIgnoreCase( "Brd 2.0" ) )
{
retVal = FUNCUBE_DONGLE_PRO_PLUS;
}
return retVal;
}
}

View File

@@ -0,0 +1,125 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.fcd;
import java.util.concurrent.RejectedExecutionException;
import javax.swing.JPanel;
import sample.Listener;
import source.SourceException;
import source.mixer.ShortAdapter;
import source.tuner.FrequencyChangeListener;
import source.tuner.MixerTuner;
import source.tuner.MixerTunerDataLine;
import source.tuner.TunerChannel;
import source.tuner.TunerChannelSource;
import source.tuner.TunerClass;
import source.tuner.TunerConfiguration;
import source.tuner.TunerType;
import controller.ResourceManager;
import controller.ThreadPoolManager;
public class FCDTuner extends MixerTuner
{
private FCDTunerController mController;
public FCDTuner( MixerTunerDataLine mixerTDL,
FCDTunerController controller )
{
super( controller.getConfiguration().toString(),
mixerTDL,
new ShortAdapter() );
mController = controller;
mController.addListener( (FrequencyChangeListener)this );
}
public FCDTunerController getController()
{
return mController;
}
@Override
public TunerClass getTunerClass()
{
return mController.getTunerClass();
}
@Override
public TunerType getTunerType()
{
return mController.getTunerType();
}
@Override
public String getUniqueID()
{
return mController.getAddress();
}
@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 int getFrequency() throws SourceException
{
return mController.getFrequency();
}
@Override
public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager,
TunerChannel tunerChannel ) throws RejectedExecutionException,
SourceException
{
return mController.getChannel( threadPoolManager, this, tunerChannel );
}
/**
* Releases the tuned channel so that the tuner controller can tune to
* other frequencies as needed.
*/
@Override
public void releaseChannel( TunerChannelSource source )
{
if( source != null )
{
/* Unregister for receiving samples */
removeListener( (Listener<Float[]>)source );
/* Tell the controller to release the channel and cleanup */
/* This will release the channel as a frequency change listener */
mController.releaseChannel( source );
}
}
}

View File

@@ -0,0 +1,497 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.fcd;
import javax.swing.JPanel;
import javax.usb.UsbClaimException;
import javax.usb.UsbConfiguration;
import javax.usb.UsbDevice;
import javax.usb.UsbEndpoint;
import javax.usb.UsbException;
import javax.usb.UsbInterface;
import javax.usb.UsbInterfacePolicy;
import javax.usb.UsbPipe;
import log.Log;
import source.SourceException;
import source.tuner.TunerClass;
import source.tuner.TunerConfiguration;
import source.tuner.TunerController;
import source.tuner.TunerType;
import source.tuner.fcd.proV1.FCD1TunerController.Block;
import source.tuner.usb.USBTunerDevice;
import controller.ResourceManager;
public abstract class FCDTunerController extends TunerController
{
public final static int sINT_NULL_VALUE = -1;
public final static long sLONG_NULL_VALUE = -1l;
public final static double sDOUBLE_NULL_VALUE = -1.0D;
public final static byte sFCD_INTERFACE = (byte)0x2;
public final static byte sFCD_ENDPOINT_IN = (byte)0x82;
public final static byte sFCD_ENDPOINT_OUT = (byte)0x2;
public final static boolean sFCD_FORCE_CLAIM_HID_INTERFACE = true;
private UsbDevice mFuncube;
private UsbConfiguration mUsbConfiguration;
private UsbInterface mFuncubeHID;
private UsbPipe mPipeIn;
private UsbPipe mPipeOut;
private FCDConfiguration mConfiguration = new FCDConfiguration();
private int mSampleRate;
public FCDTunerController( USBTunerDevice device,
int minTunableFrequency,
int maxTunableFrequency ) throws SourceException
{
super( minTunableFrequency, maxTunableFrequency );
mFuncube = device.getDevice();
mUsbConfiguration = mFuncube.getActiveUsbConfiguration();
mFuncubeHID = mUsbConfiguration.getUsbInterface( sFCD_INTERFACE );
}
public abstract int getCurrentSampleRate() throws SourceException;
/**
* Returns the tuner class. Since this is a generic container for both
* types of funcube, we let the controller self identify the class and type
*
* @return - tuner class
*/
public abstract TunerClass getTunerClass();
/**
* Returns the tuner type. Since this is a generic container for both
* types of funcube, we let the controller self identify the class and type
*
* @return - tuner type
*/
public abstract TunerType getTunerType();
/**
* FCD editor panel
*/
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 applied tuner configuration or null if one hasn't yet been
* applied
*/
public abstract TunerConfiguration getTunerConfiguration();
/**
* USB address
*/
public String getAddress()
{
return mFuncube.toString();
}
/**
* Set fcd interface mode
*/
public void setFCDMode( Mode mode ) throws UsbException, UsbClaimException
{
byte[] response = null;
switch( mode )
{
case APPLICATION:
response = send( FCDCommand.BL_QUERY.getRequestTemplate(),
FCDCommand.BL_QUERY.getResponseTemplate() );
break;
case BOOTLOADER:
response = send( FCDCommand.APP_RESET.getRequestTemplate(),
FCDCommand.APP_RESET.getResponseTemplate() );
break;
case ERROR:
case UNKNOWN:
break;
}
if( response != null )
{
mConfiguration.set( response );
}
else
{
mConfiguration.setModeUnknown();
}
}
/**
* Sets the actual (uncorrected) device frequency
*/
public void setTunedFrequency( int frequency ) throws SourceException
{
try
{
send( FCDCommand.APP_SET_FREQUENCY_HZ, frequency );
}
catch( Exception e )
{
throw new SourceException( "Couldn't set FCD Local " +
"Oscillator Frequency [" + frequency + "]", e );
}
}
/**
* Gets the actual (uncorrected) device frequency
*/
public int getTunedFrequency() throws SourceException
{
try
{
return (int)send( FCDCommand.APP_GET_FREQUENCY_HZ );
}
catch( Exception e )
{
throw new SourceException( "FCDTunerController - "
+ "couldn't get LO frequency", e );
}
}
/**
* Returns the FCD device configuration
*/
public FCDConfiguration getConfiguration()
{
return mConfiguration;
}
/**
* Claims the FCD USB HID interface. If another application currently has
* the interface claimed, the sFCD_FORCE_CLAIM_HID_INTERFACE setting
* will dictate if the interface is forcibly claimed from the other
* application
*/
private boolean claim() throws UsbException, UsbClaimException
{
boolean claimed = false;
if( !mFuncubeHID.isClaimed() )
{
mFuncubeHID.claim( new UsbInterfacePolicy()
{
@Override
public boolean forceClaim( UsbInterface arg0 )
{
return sFCD_FORCE_CLAIM_HID_INTERFACE;
}
} );
claimed = true;
}
else
{
Log.info( "Attempted to claim the Funcube HID interface, but it's in use by another program" );
}
return claimed;
}
/**
* Releases the claimed USB (FCD) interface
*/
private void release()
{
mPipeIn = null;
mPipeOut = null;
try
{
mFuncubeHID.release();
}
catch ( Exception e )
{
Log.error( "Exception thrown while releasing the funcube HID interface" + e.getLocalizedMessage() );
}
}
/**
* Convenience wrapper for commands that don't take arguments
*/
public long send( FCDCommand command ) throws UsbException, UsbClaimException
{
return send( command, sLONG_NULL_VALUE );
}
/**
* Sends the command, optionally placing the argument value parsed into
* little endian bytes in the request array. Returns any integer value that
* part of the response, or sINT_NULL_VALUE (-1) if it fails.
*
* Note: we use a long return value to avoid any issues with the MSB of a
* 32-bit response value being misinterpreted as the integer sign bit.
*
* @param command
* @param commandArgument - command argument. Use -1 for no-argument commands
* @return - response long value, or -1 if fails
* @throws UsbException
* @throws UsbClaimException
*/
public long send( FCDCommand command, long commandArgument ) throws UsbException,
UsbClaimException
{
long retVal = sLONG_NULL_VALUE;
byte[] request = command.getRequestTemplate();
if( commandArgument != sLONG_NULL_VALUE )
{
//Parse the value argument into the request byte indices, according to command length
for( int x = 1; x < command.getArrayLength(); x++ )
{
//Little endian
request[ x ] = (byte)( 0xFF &
Long.rotateRight( commandArgument, ( x - 1 ) * 8 ) );
}
}
byte[] response = send( request, command.getResponseTemplate() );
if( response != null )
{
retVal = parseResponse( command, response );
}
return retVal;
}
/**
* Sends the request byte array to the device and returns the response array
* filled with the response bytes from the device, or returns null if
* something failed along the way
*/
private byte[] send( byte[] request, byte[] response )
throws UsbException, UsbClaimException
{
byte[] retVal = null;
if( mPipeIn != null && mPipeOut != null )
{
mPipeOut.syncSubmit( request );
mPipeIn.syncSubmit( response );
retVal = response;
}
return retVal;
}
/**
* Parses a multi-byte integer response value from a byte array that is
* 2 or more bytes long
*/
private long parseResponse( FCDCommand command, byte[] response )
{
long retVal = 0;
//Parse the value argument into the request byte indices, according to command length
for( int x = 2; x < command.getArrayLength(); x++ )
{
retVal += Long.rotateLeft( ( response[ x ] & 0xFF ), ( x - 2 ) * 8 );
}
return retVal;
}
protected void open() throws UsbException, UsbClaimException
{
if( claim() )
{
//Get the USB endpoints
UsbEndpoint endpointIn =
mFuncubeHID.getUsbEndpoint( sFCD_ENDPOINT_IN );
UsbEndpoint endpointOut =
mFuncubeHID.getUsbEndpoint( sFCD_ENDPOINT_OUT );
//Setup the pipes
if( endpointIn != null && endpointOut != null )
{
mPipeOut = endpointOut.getUsbPipe();
mPipeOut.open();
mPipeIn = endpointIn.getUsbPipe();
mPipeIn.open();
setFCDMode( Mode.APPLICATION );
}
else
{
Log.error( "FCD Pro Tuner Controller - couldn't gain access " +
"to the USB in/out endpoints or pipes." );
}
}
else
{
Log.error( "FCD Pro Tuner Controller - couldn't claim the USB " +
"interface. Check to see if another program is using it." );
}
}
protected void close()
{
release();
}
public class FCDConfiguration
{
private String mConfig;
private Mode mMode;
public FCDConfiguration()
{
mConfig = null;
mMode = Mode.UNKNOWN;
}
private void setModeUnknown()
{
mConfig = null;
mMode = Mode.UNKNOWN;
}
public void set( byte[] data )
{
if( FCDCommand.checkResponse( FCDCommand.BL_QUERY, data ) ||
FCDCommand.checkResponse( FCDCommand.APP_RESET, data ) )
{
mConfig = new String( data );
mMode = Mode.getMode( mConfig );
}
else
{
mConfig = null;
mMode = Mode.ERROR;
}
}
public Mode getMode()
{
return mMode;
}
public FCDModel getModel()
{
FCDModel retVal = FCDModel.FUNCUBE_UNKNOWN;
switch( mMode )
{
case APPLICATION:
retVal = FCDModel.getFCD( mConfig.substring( 15, 22 ) );
break;
case BOOTLOADER:
case UNKNOWN:
case ERROR:
break;
}
return retVal;
}
public Block getBandBlocking()
{
Block retVal = Block.UNKNOWN;
switch( mMode )
{
case APPLICATION:
retVal = Block.getBlock( mConfig.substring( 23, 33 ).trim() );
break;
case BOOTLOADER:
case UNKNOWN:
case ERROR:
break;
}
return retVal;
}
public String getFirmware()
{
String retVal = null;
switch( mMode )
{
case APPLICATION:
retVal = mConfig.substring( 9, 14 );
break;
case BOOTLOADER:
case UNKNOWN:
case ERROR:
break;
}
return retVal;
}
public String toString()
{
return getModel().getLabel();
}
}
public enum Mode
{
APPLICATION,
BOOTLOADER,
ERROR,
UNKNOWN;
public static Mode getMode( String config )
{
Mode retVal = UNKNOWN;
if( config == null )
{
retVal = ERROR;
}
else
{
if( config.length() >= 8 )
{
String mode = config.substring( 2, 8 ).trim();
if( mode.equalsIgnoreCase( "FCDAPP" ) )
{
retVal = APPLICATION;
}
else if( mode.equalsIgnoreCase( "FCDBL" ) )
{
retVal = BOOTLOADER;
}
}
}
return retVal;
}
}
}

View File

@@ -0,0 +1,48 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.fcd;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import source.tuner.rtl.RTL2832TunerController.Descriptor;
import net.miginfocom.swing.MigLayout;
public class FCDTunerDetailsPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private JLabel mAddress;
private JLabel mCellBlock;
private JLabel mFirmware;
public FCDTunerDetailsPanel( FCDTunerController controller )
{
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) );
add( new JLabel( "USB Address:" ) );
add( new JLabel( controller.getAddress() ) );
add( new JLabel( "Cellular Band:" ) );
add( new JLabel( controller.getConfiguration().getBandBlocking()
.getLabel() ) );
add( new JLabel( "Firmware:" ) );
add( new JLabel( controller.getConfiguration().getFirmware() ) );
}
}

View File

@@ -0,0 +1,145 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.fcd.proV1;
import javax.xml.bind.annotation.XmlAttribute;
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;
public class FCD1TunerConfiguration extends TunerConfiguration
{
private double mFrequencyCorrection = 22.0d;
private double mInphaseDCCorrection = 0.0d;
private double mQuadratureDCCorrection = 0.0d;
private double mPhaseCorrection = 0.0d;
private double mGainCorrection = 0.0d;
private LNAGain mLNAGain = LNAGain.LNA_GAIN_PLUS_20_0;
private LNAEnhance mLNAEnhance = LNAEnhance.LNA_ENHANCE_OFF;
private MixerGain mMixerGain = MixerGain.MIXER_GAIN_PLUS_12_0;
/**
* Default constructor for JAXB
*/
public FCD1TunerConfiguration()
{
this( "Default" );
}
public FCD1TunerConfiguration( String name )
{
super( name );
}
@Override
public TunerType getTunerType()
{
return TunerType.FUNCUBE_DONGLE_PRO;
}
public LNAGain getLNAGain()
{
return mLNAGain;
}
public void setLNAGain( LNAGain gain )
{
mLNAGain = gain;
}
@XmlAttribute( name = "lna_gain" )
public LNAEnhance getLNAEnhance()
{
return mLNAEnhance;
}
public void setLNAEnhance( LNAEnhance enhance )
{
mLNAEnhance = enhance;
}
@XmlAttribute( name = "mixer_gain" )
public MixerGain getMixerGain()
{
return mMixerGain;
}
public void setMixerGain( MixerGain gain )
{
mMixerGain = gain;
}
@XmlAttribute( name = "frequency_correction" )
public double getFrequencyCorrection()
{
return mFrequencyCorrection;
}
public void setFrequencyCorrection( double value )
{
mFrequencyCorrection = value;
}
@XmlAttribute( name = "inphase_dc_correction" )
public double getInphaseDCCorrection()
{
return mInphaseDCCorrection;
}
public void setInphaseDCCorrection( double value )
{
mInphaseDCCorrection = value;
}
@XmlAttribute( name = "quadrature_dc_correction" )
public double getQuadratureDCCorrection()
{
return mQuadratureDCCorrection;
}
public void setQuadratureDCCorrection( double value )
{
mQuadratureDCCorrection = value;
}
@XmlAttribute( name = "phase_correction" )
public double getPhaseCorrection()
{
return mPhaseCorrection;
}
public void setPhaseCorrection( double value )
{
mPhaseCorrection = value;
}
@XmlAttribute( name = "gain_correction" )
public double getGainCorrection()
{
return mGainCorrection;
}
public void setGainCorrection( double value )
{
mGainCorrection = value;
}
}

View File

@@ -0,0 +1,667 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.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.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.UsbClaimException;
import javax.usb.UsbException;
import com.jidesoft.swing.JideTabbedPane;
import log.Log;
import net.miginfocom.swing.MigLayout;
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 controller.ResourceManager;
public class FCD1TunerConfigurationPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private ResourceManager mResourceManager;
private FCD1TunerController mController;
private FCD1TunerConfiguration mSelectedConfig;
private JButton mNewConfiguration;
private JButton mDeleteConfiguration;
private JComboBox<FCD1TunerConfiguration> mComboConfigurations;
private JComboBox<LNAGain> mComboLNAGain;
private JComboBox<LNAEnhance> mComboLNAEnhance;
private JComboBox<MixerGain> 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<FCD1TunerConfiguration>();
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>( 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() + "]" );
Log.error( "FuncubeDonglePro Controller - usb claim "
+ "exception while applying LNA gain value [" +
gain.toString() + "] - " +
e1.getLocalizedMessage() );
}
catch ( UsbException e1 )
{
JOptionPane.showMessageDialog(
FCD1TunerConfigurationPanel.this,
"FCD Pro Tuner Controller - FCD Controller "
+ "cannot apply LNA gain [" + gain.toString() + "]" );
Log.error( "FuncubeDonglePro Controller - "
+ "exception while applying LNA gain value [" +
gain.toString() + "] - " +
e1.getLocalizedMessage() );
}
save();
}
} );
gainPanel.add( new JLabel( "LNA:" ) );
gainPanel.add( mComboLNAGain, "growx,push" );
/**
* LNA Enhance
*/
mComboLNAEnhance = new JComboBox<LNAEnhance>( 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() + "]" );
Log.error( "FuncubeDonglePro Controller - usb claim "
+ "exception while applying LNA enhance value [" +
enhance.toString() + "] - " +
e1.getLocalizedMessage() );
}
catch ( UsbException e1 )
{
JOptionPane.showMessageDialog(
FCD1TunerConfigurationPanel.this,
"FCD Pro Tuner Controller - FCD Controller "
+ "cannot apply LNA enhance [" + enhance.toString() + "]" );
Log.error( "FuncubeDonglePro Controller - "
+ "exception while applying LNA enhance value [" +
enhance.toString() + "] - " +
e1.getLocalizedMessage() );
}
save();
}
} );
gainPanel.add( new JLabel( "LNA Enhance:" ) );
gainPanel.add( mComboLNAEnhance, "growx,push" );
/**
* Mixer Gain
*/
mComboMixerGain = new JComboBox<MixerGain>( 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() + "]" );
Log.error( "FuncubeDonglePro Controller - usb claim "
+ "exception while applying Mixer gain value [" +
gain.toString() + "] - " +
e1.getLocalizedMessage() );
}
catch ( UsbException e1 )
{
JOptionPane.showMessageDialog(
FCD1TunerConfigurationPanel.this,
"FCD Pro Tuner Controller - FCD Controller "
+ "cannot apply Mixer gain [" + gain.toString() + "]" );
Log.error( "FuncubeDonglePro Controller - "
+ "exception while applying Mixer gain value [" +
gain.toString() + "] - " +
e1.getLocalizedMessage() );
}
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() );
Log.error( "FuncubeDonglePro Controller - couldn't apply "
+ "frequency correction value: " + value +
e1.getLocalizedMessage() );
}
}
} );
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.getAddress(), config );
}
catch ( SourceException e1 )
{
JOptionPane.showMessageDialog(
FCD1TunerConfigurationPanel.this,
"FCD Pro Tuner Controller - couldn't "
+ "apply the tuner configuration settings - " +
e1.getLocalizedMessage() );
Log.error( "FuncubeDonglePro Controller - couldn't apply "
+ "config [" + config.getName() + "] - " +
e1.getLocalizedMessage() );
}
}
private ComboBoxModel<FCD1TunerConfiguration> getModel()
{
ArrayList<TunerConfiguration> configs =
mResourceManager.getSettingsManager()
.getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO );
DefaultComboBoxModel<FCD1TunerConfiguration> model =
new DefaultComboBoxModel<FCD1TunerConfiguration>();
for( TunerConfiguration config: configs )
{
model.addElement( (FCD1TunerConfiguration)config );
}
return model;
}
@SuppressWarnings( "unused" )
private FCD1TunerConfiguration getNamedConfiguration( String name )
{
ArrayList<TunerConfiguration> 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 + "]" );
Log.error( "FuncubeDonglePro Controller - usb claim "
+ "exception while applying "
+ mCorrectionComponent.toString()
+ " correction value [" + value + "] - " +
e1.getLocalizedMessage() );
}
catch ( UsbException e1 )
{
JOptionPane.showMessageDialog(
FCD1TunerConfigurationPanel.this,
"FCD Pro Tuner Controller - USB error from FCD "
+ "Controller while applying "
+ mCorrectionComponent.toString() +
" correction value [" + value + "]" );
Log.error( "FuncubeDonglePro Controller - usb "
+ "exception while applying "
+ mCorrectionComponent.toString()
+ " correction value [" + value + "] - " +
e1.getLocalizedMessage() );
}
}
} );
}
}
}

View File

@@ -0,0 +1,698 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.fcd.proV1;
import javax.swing.JPanel;
import javax.usb.UsbClaimException;
import javax.usb.UsbException;
import log.Log;
import source.SourceException;
import source.tuner.TunerClass;
import source.tuner.TunerConfiguration;
import source.tuner.TunerType;
import source.tuner.fcd.FCDCommand;
import source.tuner.fcd.FCDTuner;
import source.tuner.fcd.FCDTunerController;
import source.tuner.usb.USBTunerDevice;
import controller.ResourceManager;
public class FCD1TunerController extends FCDTunerController
{
public static final int sMINIMUM_TUNABLE_FREQUENCY = 64000000;
public static final int sMAXIMUM_TUNABLE_FREQUENCY = 1700000000;
public static final int sSAMPLE_RATE = 96000;
private double mDCCorrectionInPhase = 0.0;
private double mDCCorrectionQuadrature = 0.0;
private double mPhaseCorrection = 0.0;
private double mGainCorrection = 0.0;
private LNAGain mLNAGain;
private LNAEnhance mLNAEnhance;
private MixerGain mMixerGain;
private FCD1TunerConfiguration mTunerConfiguration;
private FCD1TunerEditorPanel mEditor;
public FCD1TunerController( USBTunerDevice device )
throws SourceException
{
super( device,
sMINIMUM_TUNABLE_FREQUENCY,
sMAXIMUM_TUNABLE_FREQUENCY );
mFrequencyController.setBandwidth( sSAMPLE_RATE );
try
{
open();
setFCDMode( Mode.APPLICATION );
getPhaseAndGainCorrection();
getDCIQCorrection();
getLNAGainSetting();
getLNAEnhanceSetting();
getMixerGainSetting();
send( FCDCommand.APP_SET_MIXER_GAIN, 1 );
}
catch( Exception e )
{
e.printStackTrace();
throw new SourceException( "FCDTunerController error " +
"during construction", e );
}
}
public int getCurrentSampleRate()
{
return sSAMPLE_RATE;
}
@Override
public TunerClass getTunerClass()
{
return TunerClass.FUNCUBE_DONGLE_PRO;
}
@Override
public TunerType getTunerType()
{
return TunerType.FUNCUBE_DONGLE_PRO;
}
/**
* Get the tuner configuration
*
* @return current config or null if one hasn't been applied yet
*/
public FCD1TunerConfiguration getTunerConfiguration()
{
return mTunerConfiguration;
}
@Override
public void apply( TunerConfiguration tunerConfig )
throws SourceException
{
FCD1TunerConfiguration config = (FCD1TunerConfiguration)tunerConfig;
try
{
setFrequencyCorrection( config.getFrequencyCorrection() );
setLNAGain( config.getLNAGain() );
setLNAEnhance( config.getLNAEnhance() );
setMixerGain( config.getMixerGain() );
setDCCorrectionInPhase( config.getInphaseDCCorrection() );
setDCCorrectionQuadrature( config.getQuadratureDCCorrection() );
setGainCorrection( config.getGainCorrection() );
setPhaseCorrection( config.getPhaseCorrection() );
//If we get to here without exception, store this config
mTunerConfiguration = config;
}
catch( Exception e)
{
//Re-cast any usb exceptions as source exceptions
throw new SourceException( "FCD 1 Controller encountered an "
+ "exception while applying tuner config - " +
e.getLocalizedMessage() );
}
}
@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
*/
private void getLNAGainSetting() throws UsbClaimException, UsbException
{
int gain = (int)send( FCDCommand.APP_GET_LNA_GAIN );
Log.info( "FCD1: lna gain setting is:" + gain );
mLNAGain = LNAGain.valueOf( gain );
}
/**
* @return - current lna gain setting
*/
public LNAGain getLNAGain()
{
return mLNAGain;
}
/**
* Sets lna gain for the controller
* @param gain
* @throws UsbException
* @throws UsbClaimException
*/
public void setLNAGain( LNAGain gain )
throws UsbException, UsbClaimException
{
send( FCDCommand.APP_SET_LNA_GAIN, gain.getSetting() );
}
/**
* @return - current phase correction setting
*/
public double getPhaseCorrection()
{
return mPhaseCorrection;
}
/**
* Sets the phase correction
* @param value - new phase correction
* @throws IllegalArgumentException for value outside limits (0.0 <> 1.0)
* @throws UsbClaimException - if cannot claim the FCD HID controller
* @throws UsbException - if there was a usb error
*/
public void setPhaseCorrection( double value )
throws UsbClaimException, UsbException
{
if( value < -1.0 || value > 1.0 )
{
throw new IllegalArgumentException( "Phase correction value [" +
value + "] outside limits ( -1.0 <> 1.0 )" );
}
mPhaseCorrection = value;
setPhaseAndGainCorrection();
}
/**
* @return current gain correction setting
*/
public double getGainCorrection()
{
return mGainCorrection;
}
/**
*
* @param value - gain correction value
* @throws IllegalArgumentException for value outside limits (0.0 <> 1.0)
* @throws UsbClaimException - if cannot claim the FCD HID controller
* @throws UsbException - if there was a usb error
*/
public void setGainCorrection( double value )
throws UsbClaimException, UsbException
{
if( value < -1.0 || value > 1.0 )
{
throw new IllegalArgumentException( "Gain correction value [" +
value + "] outside limits ( -1.0 <> 1.0 )" );
}
mGainCorrection = value;
setPhaseAndGainCorrection();
}
/**
* Sends the phase and gain correction values to FCD.
*
* The interface expects an argument with the SIGNED phase value in the first
* 16 bits and the UNSIGNED gain value in the second 16 bits, both in
* big-endian format. Since we're sending the argument in java little endian
* format, we place the phase in the high-order bits and the gain in the
* low-order bits, so that the send(command) function will reorder the bytes
* into big endian format, and place the arguments correctly.
*
* Gain correction values range -1.0 to 0.0 to 1.0 and applied to the dongle
* as values 0 to 65534
*
* Phase correction values range -1.0 to 0.0 to 1.0 and applied to the
* dongle as signed values -32768 to 32767
*/
private void setPhaseAndGainCorrection() throws UsbClaimException,
UsbException
{
//UNSIGNED short gain value - masked into a long to avoid sign-extension
long gain = (long)( ( 1.0 + mGainCorrection ) * Short.MAX_VALUE );
//Left shift gain to place value in upper 16 bits
long longGain = Long.rotateLeft( ( 0xFFFF & gain ), 16 );
//SIGNED short phase value
short phase = (short)( mPhaseCorrection * Short.MAX_VALUE );
//Merge the results
long correction = longGain | phase;
send( FCDCommand.APP_SET_IQ_CORRECTION, correction );
}
/**
* Retrieves the stored phase and gain correction values from the FCD.
*
* Note: testing shows that when the dongle is unplugged, any stored phase
* and gain correction values are reset. Reading the dongle after a fresh
* plugin shows the values reset.
*
* @throws UsbClaimException
* @throws UsbException
*/
private void getPhaseAndGainCorrection() throws UsbClaimException,
UsbException
{
long correction = send( FCDCommand.APP_GET_IQ_CORRECTION );
/**
* Gain: mask the upper 16 phase bits and right shift to get the
* unsigned short value and then divide by 32768 to get the double
* value. We place the unsigned short value in an int to avoid sign
* issues.
*/
int gain = (int)( Long.rotateRight( correction & 0xFFFF0000, 16 ) );
mGainCorrection = ( (double)gain / (double)Short.MAX_VALUE ) - 1.0;
/**
* Phase: mask the lower 16 bits and divide by 32768 to get the double
* value
*/
short phase = (short)( correction & 0x0000FFFF );
mPhaseCorrection = ( (double)phase / (double)Short.MAX_VALUE );
Log.info( "FCD1: correction:" + correction + " gain:" + gain + " phase:" + phase );
}
/**
* @return current DC correction for the I (inphase) component
*/
public double getDCCorrectionInPhase()
{
return mDCCorrectionInPhase;
}
/**
* Sets DC correction for the I (inphase) component and sends the new value
* to the FCD controller
* @param value - new DC correction value for the inphase component
* @throws IllegalArgumentException for values outside limits (-1.0 <> 1.0)
* @throws UsbClaimException - if cannot claim the FCD HID controller
* @throws UsbException - if there was a usb error
*/
public void setDCCorrectionInPhase( double value )
throws UsbClaimException, UsbException
{
if( value < -1.0 || value > 1.0 )
{
throw new IllegalArgumentException( "DC inphase correction value "
+ "[" + value + "] outside limits ( 0.0 <> 1.0 )" );
}
mDCCorrectionInPhase = value;
setDCIQCorrection();
}
/**
* @return current DC correction for the Q (quadrature) component
*/
public double getDCCorrectionQuadrature()
{
return mDCCorrectionQuadrature;
}
/**
* Sets the DC correction for the Q (quadrature) component and send the
* new value to the FCD controller
* @param value - new DC correction value for the quadrature component
* @throws IllegalArgumentException for values outside limits (-1.0 <> 1.0)
* @throws UsbClaimException - if cannot claim the FCD HID controller
* @throws UsbException - if there was a usb error
*/
public void setDCCorrectionQuadrature( double value )
throws UsbClaimException, UsbException
{
if( value < -1.0 || value > 1.0 )
{
throw new IllegalArgumentException( "DC quadrature correction "
+ "value [" + value + "] outside limits ( 0.0 <> 1.0 )" );
}
mDCCorrectionQuadrature = value;
setDCIQCorrection();
}
/**
* Sends the I/Q DC offset correction values to FCD.
*
* The interface expects an argument with the signed inphase value in the
* first 16 bits and the signed quadrature value in the second 16 bits,
* both in big-endian format.
*
* Since we're sending the argument in java little endian format, we place
* the quadrature in the high-order bits and the inphase in the low-order
* bits, so that the send(command) function will reorder the bytes into big
* endian format, and place the arguments correctly.
*
* Both Inphase and Quadrature correction values range -1.0 to 0.0 to 1.0
* and are applied to the dongle as signed short values -32768 to 0 to 32767
*/
private void setDCIQCorrection() throws UsbClaimException, UsbException
{
//I & Q DC offset correction values are signed short values
short inphase = (short)( mDCCorrectionInPhase * Short.MAX_VALUE );
short quadrature = (short)( mDCCorrectionQuadrature * Short.MAX_VALUE );
//Mask the shorts into longs to preserve the sign bit and prepare
//for merging to a single 32-bit value
long maskedInphase = inphase & 0x0000FFFF;
long maskedQuadrature = quadrature & 0x0000FFFF;
//Left shift quadrature to place value in upper 16 bits
long shiftedQuadrature = Long.rotateLeft( maskedQuadrature, 16 );
//Merge the results
long correction = shiftedQuadrature | maskedInphase;
// Log.info( "FCD1: setting iq correction," +
// " inphase:" + inphase +
// " masked inphase:" + maskedInphase +
// " quad:" + quadrature +
// " masked quad:" + maskedQuadrature +
// " shifted quadrature:" + shiftedQuadrature +
// " correction:" + correction );
send( FCDCommand.APP_SET_DC_CORRECTION, correction );
}
private void getDCIQCorrection() throws UsbClaimException,
UsbException
{
long correction = send( FCDCommand.APP_GET_DC_CORRECTION );
/**
* Quadrature: mask the upper 16 bits and divide by 32768 to get the
* stored quadrature value. Cast the 16-bit value to a short to get
* the signed short value
*/
long shiftedQuadrature = correction & 0xFFFF0000;
long quadrature = Long.rotateRight( shiftedQuadrature, 16 );
mDCCorrectionQuadrature = ( (short)quadrature / (double)Short.MAX_VALUE );
/**
* InPhase: mask the lower 16 bits to get the short
* value and then divide by 32768 to get the stored inphase value.
* Cast the 16-bit value to a short to get the signed short value
*/
long inphase = correction & 0x0000FFFF;
mDCCorrectionInPhase = ( (short)inphase / (double)Short.MAX_VALUE );
// Log.info( "FCD1:" +
// " correction:" + correction +
// " Q:" + quadrature +
// " I:" + inphase +
// " QCorr:" + mDCCorrectionQuadrature +
// " ICorr:" + mDCCorrectionInPhase );
}
private void getLNAEnhanceSetting() throws UsbClaimException, UsbException
{
int enhance = (int)send( FCDCommand.APP_GET_LNA_ENHANCE );
Log.info( "FCD1: lna enhance setting is: " + enhance );
mLNAEnhance = LNAEnhance.valueOf( enhance );
}
public LNAEnhance getLNAEnhance()
{
return mLNAEnhance;
}
public void setLNAEnhance( LNAEnhance enhance )
throws UsbClaimException, UsbException
{
send( FCDCommand.APP_SET_LNA_ENHANCE, enhance.getSetting() );
}
private void getMixerGainSetting() throws UsbClaimException, UsbException
{
int gain = (int)send( FCDCommand.APP_GET_MIXER_GAIN );
Log.info( "FCD1: mixer gain setting is:" + gain );
mMixerGain = MixerGain.valueOf( gain );
}
public MixerGain getMixerGain()
{
return mMixerGain;
}
public void setMixerGain( MixerGain gain ) throws UsbClaimException, UsbException
{
send( FCDCommand.APP_SET_MIXER_GAIN, gain.getSetting() );
}
public enum Block
{
CELLULAR_BAND_BLOCKED( "Blocked" ),
NO_BAND_BLOCK( "Unblocked" ),
UNKNOWN( "Unknown" );
private String mLabel;
private Block( String label )
{
mLabel = label;
}
public String getLabel()
{
return mLabel;
}
public static Block getBlock( String block )
{
Block retVal = UNKNOWN;
if( block.equalsIgnoreCase( "No blk" ) )
{
retVal = NO_BAND_BLOCK;
}
else if( block.equalsIgnoreCase( "Cell blk" ) )
{
retVal = CELLULAR_BAND_BLOCKED;
}
return retVal;
}
}
/**
* LNA Gain values suppported by the FCD Pro 1.0
*/
public enum LNAGain
{
LNA_GAIN_MINUS_5_0( 0, "-5.0 dB" ),
LNA_GAIN_MINUS_2_5( 1, "-2.5 dB" ),
LNA_GAIN_PLUS_0_0( 4, "0.0 dB" ),
LNA_GAIN_PLUS_2_5( 5, "2.5 dB" ),
LNA_GAIN_PLUS_5_0( 6, "5.0 dB" ),
LNA_GAIN_PLUS_7_5( 7, "7.5 dB" ),
LNA_GAIN_PLUS_10_0( 8, "10.0 dB" ),
LNA_GAIN_PLUS_12_5( 9, "12.5 dB" ),
LNA_GAIN_PLUS_15_0( 10, "15.0 dB" ),
LNA_GAIN_PLUS_17_5( 11, "17.5 dB" ),
LNA_GAIN_PLUS_20_0( 12, "20.0 dB" ),
LNA_GAIN_PLUS_25_0( 13, "25.0 dB" ),
LNA_GAIN_PLUS_30_0( 14, "30.0 dB" );
private int mSetting;
private String mLabel;
private LNAGain( int setting, String label )
{
mSetting = setting;
mLabel = label;
}
public static LNAGain valueOf( int setting )
{
switch( setting )
{
case 0:
return LNA_GAIN_MINUS_5_0;
case 1:
return LNA_GAIN_MINUS_2_5;
case 4:
return LNA_GAIN_PLUS_0_0;
case 5:
return LNA_GAIN_PLUS_2_5;
case 6:
return LNA_GAIN_PLUS_5_0;
case 7:
return LNA_GAIN_PLUS_7_5;
case 8:
return LNA_GAIN_PLUS_10_0;
case 9:
return LNA_GAIN_PLUS_12_5;
case 10:
return LNA_GAIN_PLUS_15_0;
case 11:
return LNA_GAIN_PLUS_17_5;
case 12:
return LNA_GAIN_PLUS_20_0;
case 13:
return LNA_GAIN_PLUS_25_0;
case 14:
return LNA_GAIN_PLUS_30_0;
default:
throw new IllegalArgumentException( "FCD 1.0 Tuner "
+ "Controller - unrecognized LNA gain setting "
+ "value [" + setting + "]" );
}
}
public int getSetting()
{
return mSetting;
}
public String getLabel()
{
return mLabel;
}
public String toString()
{
return getLabel();
}
}
/**
* LNA Enhance values suppported by the FCD Pro 1.0
*/
public enum LNAEnhance
{
LNA_ENHANCE_OFF( 0, "Off" ),
LNA_ENHANCE_0( 1, "0" ),
LNA_ENHANCE_1( 3, "1" ),
LNA_ENHANCE_2( 5, "2" ),
LNA_ENHANCE_3( 7, "3" );
private int mSetting;
private String mLabel;
private LNAEnhance( int setting, String label )
{
mSetting = setting;
mLabel = label;
}
public static LNAEnhance valueOf( int setting )
{
switch( setting )
{
case 0:
return LNA_ENHANCE_OFF;
case 1:
return LNA_ENHANCE_0;
case 3:
return LNA_ENHANCE_1;
case 5:
return LNA_ENHANCE_2;
case 7:
return LNA_ENHANCE_3;
default:
throw new IllegalArgumentException( "FCD 1.0 Tuner "
+ "Controller - unrecognized LNA enhance setting "
+ "value [" + setting + "]" );
}
}
public int getSetting()
{
return mSetting;
}
public String getLabel()
{
return mLabel;
}
public String toString()
{
return getLabel();
}
}
/**
* Mixer Gain values suppported by the FCD Pro 1.0
*/
public enum MixerGain
{
MIXER_GAIN_PLUS_4_0( 0, "4.0 dB" ),
MIXER_GAIN_PLUS_12_0( 1, "12.0 dB" );
private int mSetting;
private String mLabel;
private MixerGain( int setting, String label )
{
mSetting = setting;
mLabel = label;
}
public static MixerGain valueOf( int setting )
{
switch( setting )
{
case 0:
return MIXER_GAIN_PLUS_4_0;
case 1:
return MIXER_GAIN_PLUS_12_0;
default:
throw new IllegalArgumentException( "FCD 1.0 Tuner "
+ "Controller - unrecognized mixer gain setting "
+ "value [" + setting + "]" );
}
}
public int getSetting()
{
return mSetting;
}
public String getLabel()
{
return mLabel;
}
public String toString()
{
return getLabel();
}
}
}

View File

@@ -0,0 +1,102 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.fcd.proV1;
import java.awt.Color;
import gui.control.JFrequencyControl;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import com.jidesoft.swing.JideTabbedPane;
import log.Log;
import net.miginfocom.swing.MigLayout;
import source.SourceException;
import source.tuner.FrequencyChangeListener;
import source.tuner.fcd.FCDTuner;
import source.tuner.fcd.FCDTunerDetailsPanel;
import controller.ResourceManager;
public class FCD1TunerEditorPanel extends JPanel implements FrequencyChangeListener
{
private static final long serialVersionUID = 1L;
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( int frequency, int bandwidth )
{
//Disregard the bandwidth value, which shouldn't be changing
try
{
mController.setFrequency( frequency );
}
catch ( SourceException e )
{
Log.error( "FCTProController - error setting frequency [" +
frequency + "] - " + e.getLocalizedMessage() );
}
}
}

View File

@@ -0,0 +1,82 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.fcd.proplusV2;
import javax.xml.bind.annotation.XmlAttribute;
import source.tuner.TunerConfiguration;
import source.tuner.TunerType;
public class FCD2TunerConfiguration extends TunerConfiguration
{
private boolean mGainLNA = true;
private boolean mGainMixer = true;
private double mFrequencyCorrection = -2.2d;
/**
* Default constructor for JAXB
*/
public FCD2TunerConfiguration()
{
this( "Default" );
}
public FCD2TunerConfiguration( String name )
{
super( name );
}
@Override
public TunerType getTunerType()
{
return TunerType.FUNCUBE_DONGLE_PRO_PLUS;
}
@XmlAttribute( name = "lna_gain" )
public boolean getGainLNA()
{
return mGainLNA;
}
public void setGainLNA( boolean gain )
{
mGainLNA = gain;
}
@XmlAttribute( name = "mixer_gain" )
public boolean getGainMixer()
{
return mGainMixer;
}
public void setGainMixer( boolean gain )
{
mGainMixer = gain;
}
@XmlAttribute( name = "frequency_correction" )
public double getFrequencyCorrection()
{
return mFrequencyCorrection;
}
public void setFrequencyCorrection( double value )
{
mFrequencyCorrection = value;
}
}

View File

@@ -0,0 +1,377 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.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.BorderFactory;
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 log.Log;
import net.miginfocom.swing.MigLayout;
import source.SourceException;
import source.tuner.TunerConfiguration;
import source.tuner.TunerConfigurationAssignment;
import source.tuner.TunerType;
import controller.ResourceManager;
public class FCD2TunerConfigurationPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private ResourceManager mResourceManager;
private FCD2TunerController mController;
private FCD2TunerConfiguration mSelectedConfig;
private JButton mNewConfiguration;
private JButton mDeleteConfiguration;
private JComboBox<FCD2TunerConfiguration> 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<FCD2TunerConfiguration>();
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() );
Log.error( "FuncubeDongleProPlus Controller - couldn't apply "
+ "frequency correction value: " + value +
e1.getLocalizedMessage() );
}
}
} );
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.getAddress() );
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.getAddress(), 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.getAddress(), config );
}
catch ( SourceException e1 )
{
JOptionPane.showMessageDialog(
FCD2TunerConfigurationPanel.this,
"FCD Pro Plus Tuner Controller - couldn't "
+ "apply the tuner configuration settings - " +
e1.getLocalizedMessage() );
Log.error( "FuncubeDongleProPlus Controller - couldn't apply "
+ "config [" + config.getName() + "] - " +
e1.getLocalizedMessage() );
}
}
private ComboBoxModel<FCD2TunerConfiguration> getModel()
{
ArrayList<TunerConfiguration> configs =
mResourceManager.getSettingsManager()
.getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO_PLUS );
DefaultComboBoxModel<FCD2TunerConfiguration> model =
new DefaultComboBoxModel<FCD2TunerConfiguration>();
for( TunerConfiguration config: configs )
{
model.addElement( (FCD2TunerConfiguration)config );
}
return model;
}
private FCD2TunerConfiguration getNamedConfiguration( String name )
{
ArrayList<TunerConfiguration> 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();
}
}

View File

@@ -0,0 +1,367 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.fcd.proplusV2;
import javax.swing.JPanel;
import javax.usb.UsbClaimException;
import javax.usb.UsbException;
import log.Log;
import source.SourceException;
import source.tuner.TunerClass;
import source.tuner.TunerConfiguration;
import source.tuner.TunerType;
import source.tuner.fcd.FCDCommand;
import source.tuner.fcd.FCDTuner;
import source.tuner.fcd.FCDTunerController;
import source.tuner.usb.USBTunerDevice;
import controller.ResourceManager;
public class FCD2TunerController extends FCDTunerController
{
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 FCD2TunerController( USBTunerDevice device )
throws SourceException
{
super( device,
sMINIMUM_TUNABLE_FREQUENCY,
sMAXIMUM_TUNABLE_FREQUENCY );
mFrequencyController.setBandwidth( sSAMPLE_RATE );
try
{
open();
setFCDMode( Mode.APPLICATION );
}
catch( Exception e )
{
throw new SourceException( "FCDTunerController error " +
"during construction", e );
}
}
public int getCurrentSampleRate()
{
return sSAMPLE_RATE;
}
@Override
public TunerClass getTunerClass()
{
return TunerClass.FUNCUBE_DONGLE_PRO_PLUS;
}
@Override
public TunerType getTunerType()
{
return TunerType.FUNCUBE_DONGLE_PRO_PLUS;
}
public JPanel getEditor( FCDTuner tuner, ResourceManager resourceManager )
{
if( mEditor == null )
{
mEditor = new FCD2TunerEditorPanel( tuner, resourceManager );
}
return mEditor;
}
@Override
public void apply( TunerConfiguration config ) throws SourceException
{
if( config instanceof FCD2TunerConfiguration )
{
FCD2TunerConfiguration plusConfig =
(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 ( UsbClaimException e )
{
throw new SourceException( "Exception while setting LNA Gain", e );
}
catch ( UsbException e )
{
throw new SourceException( "Exception 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 ( UsbClaimException e )
{
throw new SourceException( "Exception while setting mixer gain", e );
}
catch ( UsbException e )
{
throw new SourceException( "Exception while setting mixer gain", e );
}
mTunerConfiguration = plusConfig;
}
}
@Override
public TunerConfiguration getTunerConfiguration()
{
return mTunerConfiguration;
}
public int getDCCorrection()
{
int dcCorrection = -999;
try
{
dcCorrection = (int)send( FCDCommand.APP_GET_DC_CORRECTION );
}
catch ( UsbClaimException e )
{
Log.error( "FCTTunerController - couldn't claim funcube HID to"
+ " get dc correction value" );
}
catch ( UsbException e )
{
Log.error( "FCTTunerController - error getting dc correction "
+ "value - " + e.getLocalizedMessage() );
}
return dcCorrection;
}
public void setDCCorrection( int value )
{
try
{
send( FCDCommand.APP_SET_DC_CORRECTION, value );
}
catch ( UsbClaimException e )
{
Log.error( "FCDTunerController - error claiming usb hid while "
+ "trying to set dc correction to [" + value + "]" );
}
catch ( UsbException e )
{
Log.error( "FCDTunerController - error while "
+ "trying to set dc correction to [" + value + "] - " +
e.getLocalizedMessage() );
}
}
public int getIQCorrection()
{
int iqCorrection = -999;
try
{
iqCorrection = (int)send( FCDCommand.APP_GET_IQ_CORRECTION );
}
catch ( UsbClaimException e )
{
Log.error( "FCTTunerController - couldn't claim funcube HID to"
+ " get iq correction value" );
}
catch ( UsbException e )
{
Log.error( "FCTTunerController - error getting iq correction "
+ "value - " + e.getLocalizedMessage() );
}
return iqCorrection;
}
public void setIQCorrection( int value )
{
try
{
send( FCDCommand.APP_SET_IQ_CORRECTION, value );
}
catch ( UsbClaimException e )
{
Log.error( "FCDTunerController - error claiming usb hid while "
+ "trying to set iq correction to [" + value + "]" );
}
catch ( UsbException e )
{
Log.error( "FCDTunerController - error while "
+ "trying to set iq correction to [" + value + "] - " +
e.getLocalizedMessage() );
}
}
public enum Block
{
CELLULAR_BAND_BLOCKED( "Blocked" ),
NO_BAND_BLOCK( "Unblocked" ),
UNKNOWN( "Unknown" );
private String mLabel;
private Block( String label )
{
mLabel = label;
}
public String getLabel()
{
return mLabel;
}
public static Block getBlock( String block )
{
Block retVal = UNKNOWN;
if( block.equalsIgnoreCase( "No blk" ) )
{
retVal = NO_BAND_BLOCK;
}
else if( block.equalsIgnoreCase( "Cell blk" ) )
{
retVal = CELLULAR_BAND_BLOCKED;
}
return retVal;
}
}
public enum TunerRFFilter
{
TRF_0_4,
TRF_4_8,
TRF_8_16,
TRF_16_32,
TRF_32_75,
TRF_75_125,
TRF_125_250,
TRF_145,
TRF_410_875,
TRF_435,
TRF_875_2000,
UNKNOWN;
public static TunerRFFilter get( int value )
{
TunerRFFilter retVal = UNKNOWN;
if( 0 <= value && value <= 10 )
{
retVal = values()[ value ];
}
return retVal;
}
}
public enum TunerIFFilter
{
TIF_200KHZ,
TIF_300KHZ,
TIF_600KHZ,
TIF_1536KHZ,
TIF_5MHZ,
TIF_6MHZ,
TIF_7MHZ,
TIF_8MHZ,
UNKNOWN;
public static TunerIFFilter get( int value )
{
TunerIFFilter retVal = UNKNOWN;
if( 0 <= value && value <= 7 )
{
retVal = values()[ value ];
}
return retVal;
}
}
public enum LNAGain
{
LNA_GAIN_MINUS_5_0( 0, "-5.0 dB" ),
LNA_GAIN_MINUS_2_5( 1, "-2.5 dB" ),
LNA_GAIN_PLUS_0_0( 4, "0.0 dB" ),
LNA_GAIN_PLUS_2_5( 5, "2.5 dB" ),
LNA_GAIN_PLUS_5_0( 6, "5.0 dB" ),
LNA_GAIN_PLUS_7_5( 7, "7.5 dB" ),
LNA_GAIN_PLUS_10_0( 8, "10.0 dB" ),
LNA_GAIN_PLUS_12_5( 9, "12.5 dB" ),
LNA_GAIN_PLUS_15_0( 10, "15.0 dB" ),
LNA_GAIN_PLUS_17_5( 11, "17.5 dB" ),
LNA_GAIN_PLUS_20_0( 12, "20.0 dB" ),
LNA_GAIN_PLUS_25_0( 13, "25.0 dB" ),
LNA_GAIN_PLUS_30_0( 14, "30.0 dB" );
private int mSetting;
private String mLabel;
private LNAGain( int setting, String label )
{
mSetting = setting;
mLabel = label;
}
public int getSetting()
{
return mSetting;
}
public String getLabel()
{
return mLabel;
}
public String toString()
{
return getLabel();
}
}
}

View File

@@ -0,0 +1,101 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.fcd.proplusV2;
import java.awt.Color;
import gui.control.JFrequencyControl;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import com.jidesoft.swing.JideTabbedPane;
import log.Log;
import net.miginfocom.swing.MigLayout;
import source.SourceException;
import source.tuner.FrequencyChangeListener;
import source.tuner.fcd.FCDTuner;
import source.tuner.fcd.FCDTunerDetailsPanel;
import controller.ResourceManager;
public class FCD2TunerEditorPanel extends JPanel implements FrequencyChangeListener
{
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( int frequency, int bandwidth )
{
//Disregard the bandwidth, which shouldn't be changing
try
{
mController.setFrequency( frequency );
}
catch ( SourceException e )
{
Log.error( "FCTProController - error setting frequency [" +
frequency + "] - " + e.getLocalizedMessage() );
}
}
}

View File

@@ -0,0 +1,76 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.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" ) ;
}
}
}

View File

@@ -0,0 +1,59 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.rtl;
import source.Source.SampleType;
import source.mixer.SampleAdapter;
public class RTL2832SampleAdapter extends SampleAdapter
{
private final static float[] mLOOKUP_VALUES;
/**
* Creates a static lookup table that converts the 8-bit valued range
* from 0 - 255 into scaled float values of -128 to 0 to 127
*/
static
{
mLOOKUP_VALUES = new float[ 256 ];
float scale = (float)( 1.0f / 127.0f );
for( int x = 0; x < 256; x++ )
{
mLOOKUP_VALUES[ x ] = (float)( x - 128 ) * scale;
}
mLOOKUP_VALUES[ 128 ] = 0.0000001f;
}
@Override
public Float[] convert( byte[] samples )
{
Float[] convertedSamples = new Float[ samples.length ];
int pointer = 0;
for( byte sample: samples )
{
/* Convert byte value into float from the lookup table */
convertedSamples[ pointer++ ] = mLOOKUP_VALUES[ ( sample & 0xFF ) ];
}
return convertedSamples;
}
}

View File

@@ -0,0 +1,168 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.rtl;
import java.util.concurrent.RejectedExecutionException;
import javax.swing.JPanel;
import javax.usb.UsbException;
import log.Log;
import sample.Listener;
import source.SourceException;
import source.tuner.FrequencyChangeListener;
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.rtl.RTL2832TunerController.SampleRate;
import controller.ResourceManager;
import controller.ThreadPoolManager;
public class RTL2832Tuner extends Tuner
{
private TunerClass mTunerClass;
protected RTL2832TunerController mController;
public RTL2832Tuner( TunerClass tunerClass,
RTL2832TunerController controller )
throws SourceException
{
super( tunerClass.getVendorDeviceLabel() + "/" +
controller.getTunerType().getLabel() + " #" +
controller.getUniqueID() );
mTunerClass = tunerClass;
mController = controller;
mController.addListener( (FrequencyChangeListener)this );
}
public RTL2832TunerController getController()
{
return mController;
}
@Override
public String getUniqueID()
{
return mController.getUniqueID();
}
@Override
public TunerClass getTunerClass()
{
return mTunerClass;
}
@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 )
{
Log.error( "RTL2832 Tuner - couldn't get sample rate - " +
e.getLocalizedMessage() );
}
return 0;
}
public void setSampleRate( SampleRate sampleRate ) throws SourceException
{
try
{
mController.setSampleRate( sampleRate );
}
catch( UsbException e )
{
throw new SourceException( "RTL2832 Tuner - error setting "
+ "sample rate", e );
}
}
@Override
public int getFrequency() throws SourceException
{
return mController.getFrequency();
}
public void setFrequency( int frequency ) throws SourceException
{
mController.setFrequency( frequency );
}
@Override
public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager,
TunerChannel channel )
throws RejectedExecutionException,
SourceException
{
return mController.getChannel( threadPoolManager, this, channel );
}
/**
* Releases the tuned channel so that the tuner controller can tune to
* other frequencies as needed.
*/
@Override
public void releaseChannel( TunerChannelSource source )
{
/* Unregister for receiving samples */
removeListener( (Listener<Float[]>)source );
/* Tell the controller to release the channel and cleanup */
if( source != null )
{
mController.releaseChannel( source );
}
}
@Override
public void addListener( Listener<Float[]> listener )
{
mController.addListener( listener );
}
@Override
public void removeListener( Listener<Float[]> listener )
{
mController.removeListener( listener );
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.rtl;
public enum RTL2832TunerType
{
E4000,
FC0012,
FC0013,
R820T,
R828D,
UNKNOWN;
}

View File

@@ -0,0 +1,124 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.rtl.e4k;
import javax.xml.bind.annotation.XmlAttribute;
import source.tuner.TunerConfiguration;
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;
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_200MHZ;
/**
* Default constructor for JAXB
*/
public E4KTunerConfiguration()
{
this( "Default" );
}
public E4KTunerConfiguration( String name )
{
super( name );
}
@Override
public TunerType getTunerType()
{
return TunerType.ELONICS_E4000;
}
@XmlAttribute( name="master_gain" )
public E4KGain getMasterGain()
{
return mMasterGain;
}
public void setMasterGain( E4KGain gain )
{
mMasterGain = gain;
}
@XmlAttribute( name="mixer_gain" )
public E4KMixerGain getMixerGain()
{
return mMixerGain;
}
public void setMixerGain( E4KMixerGain mixerGain )
{
mMixerGain = mixerGain;
}
@XmlAttribute( name="lna_gain" )
public E4KLNAGain getLNAGain()
{
return mLNAGain;
}
public void setLNAGain( E4KLNAGain lnaGain )
{
mLNAGain = lnaGain;
}
@XmlAttribute( name="enhance_gain" )
public E4KEnhanceGain getEnhanceGain()
{
return mEnhanceGain;
}
public void setEnhanceGain( E4KEnhanceGain enhanceGain )
{
mEnhanceGain = enhanceGain;
}
@XmlAttribute( name="frequency_correction" )
public double getFrequencyCorrection()
{
return mFrequencyCorrection;
}
public void setFrequencyCorrection( double value )
{
mFrequencyCorrection = value;
}
@XmlAttribute( name="sample_rate" )
public SampleRate getSampleRate()
{
return mSampleRate;
}
public void setSampleRate( SampleRate sampleRate )
{
mSampleRate = sampleRate;
}
}

View File

@@ -0,0 +1,688 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.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 com.jidesoft.swing.JideTabbedPane;
import log.Log;
import net.miginfocom.swing.MigLayout;
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 ResourceManager mResourceManager;
private E4KTunerController mController;
private E4KTunerConfiguration mSelectedConfig;
private JButton mNewConfiguration;
private JButton mDeleteConfiguration;
private JComboBox<E4KTunerConfiguration> mComboConfigurations;
private JTextField mName;
private JSpinner mFrequencyCorrection;
private JSpinner mSampleRateCorrection;
private JComboBox<E4KGain> mComboMasterGain;
private JComboBox<E4KMixerGain> mComboMixerGain;
private JComboBox<E4KLNAGain> mComboLNAGain;
private JComboBox<E4KEnhanceGain> mComboEnhanceGain;
private JComboBox<SampleRate> 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<E4KTunerConfiguration>();
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() );
Log.error( "E4K Tuner Controller - couldn't apply "
+ "frequency correction value: " + value +
e1.getLocalizedMessage() );
}
}
} );
}
} );
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 | UsbException eSampleRate )
{
JOptionPane.showMessageDialog(
E4KTunerConfigurationPanel.this,
"E4K Tuner Controller - couldn't apply the sample "
+ "rate setting [" + sampleRate.getLabel() + "] " +
eSampleRate.getLocalizedMessage() );
Log.error( "E4K Tuner Controller - couldn't apply sample "
+ "rate setting [" + sampleRate.getLabel() + "] " +
eSampleRate.getLocalizedMessage() );
}
}
} );
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>( 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() );
Log.error( "E4K Tuner Controller - couldn't apply "
+ "gain setting - " + e.getLocalizedMessage() );
}
}
} );
mComboMasterGain.setToolTipText( "<html>Select <b>AUTOMATIC</b> for auto "
+ "gain, <b>MANUAL</b> to enable<br> independent control of "
+ "<i>Mixer</i>, <i>LNA</i> and <i>Enhance</i> gain<br>"
+ "settings, or one of the individual gain settings for<br>"
+ "semi-manual gain control</html>" );
gainPanel.add( new JLabel( "Master" ) );
gainPanel.add( mComboMasterGain );
/* Mixer Gain Control */
mComboMixerGain = new JComboBox<E4KMixerGain>( 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() );
Log.error( "E4K Tuner Controller - couldn't apply mixer "
+ "gain setting - " + e.getLocalizedMessage() );
}
}
} );
}
} );
mComboMixerGain.setToolTipText( "<html>Mixer Gain. Set master gain "
+ "to <b>MASTER</b> to enable adjustment</html>" );
mComboMixerGain.setEnabled( false );
gainPanel.add( new JLabel( "Mixer" ) );
gainPanel.add( mComboMixerGain, "wrap" );
/* LNA Gain Control */
mComboLNAGain = new JComboBox<E4KLNAGain>( 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() );
Log.error( "E4K Tuner Controller - couldn't apply LNA "
+ "gain setting - " + e.getLocalizedMessage() );
}
}
} );
mComboLNAGain.setToolTipText( "<html>LNA Gain. Set master gain "
+ "to <b>MANUAL</b> to enable adjustment</html>" );
mComboLNAGain.setEnabled( false );
gainPanel.add( new JLabel( "LNA" ) );
gainPanel.add( mComboLNAGain );
/* Enhance Gain Control */
mComboEnhanceGain = new JComboBox<E4KEnhanceGain>( 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() );
Log.error( "E4K Tuner Controller - couldn't apply enhance "
+ "gain setting - " + e.getLocalizedMessage() );
}
}
} );
mComboEnhanceGain.setToolTipText( "<html>Enhance Gain. Set master gain "
+ "to <b>MANUAL</b> to enable adjustment</html>" );
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() );
Log.error( "E4K Tuner Controller - couldn't apply "
+ "config [" + config.getName() + "] - " +
e1.getLocalizedMessage() );
}
}
/**
* 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<E4KTunerConfiguration> getModel()
{
ArrayList<TunerConfiguration> configs =
mResourceManager.getSettingsManager()
.getTunerConfigurations( TunerType.ELONICS_E4000 );
DefaultComboBoxModel<E4KTunerConfiguration> model =
new DefaultComboBoxModel<E4KTunerConfiguration>();
for( TunerConfiguration config: configs )
{
model.addElement( (E4KTunerConfiguration)config );
}
return model;
}
/**
* Retrieves a specific (named) tuner configuration
*/
private E4KTunerConfiguration getNamedConfiguration( String name )
{
ArrayList<TunerConfiguration> 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();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,111 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.rtl.e4k;
import gui.control.JFrequencyControl;
import java.awt.Color;
import java.awt.EventQueue;
import javax.swing.BorderFactory;
import javax.swing.JLabel;
import javax.swing.JPanel;
import com.jidesoft.swing.JideTabbedPane;
import log.Log;
import net.miginfocom.swing.MigLayout;
import source.SourceComponentEditor;
import source.SourceException;
import source.tuner.FrequencyChangeListener;
import source.tuner.rtl.RTL2832InfoPanel;
import controller.ResourceManager;
public class E4KTunerEditorPanel extends JPanel implements FrequencyChangeListener
{
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( final int frequency, int bandwidth )
{
EventQueue.invokeLater( new Runnable()
{
@Override
public void run()
{
try
{
mController.setFrequency( frequency );
}
catch ( SourceException e )
{
Log.error( "E4KTunerController - error setting frequency [" +
frequency + "] - " + e.getLocalizedMessage() );
}
}
} );
}
}

View File

@@ -0,0 +1,123 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.rtl.r820t;
import javax.xml.bind.annotation.XmlAttribute;
import source.tuner.TunerConfiguration;
import source.tuner.TunerType;
import source.tuner.rtl.RTL2832TunerController.SampleRate;
import source.tuner.rtl.r820t.R820TTunerController.R820TEnhanceGain;
import source.tuner.rtl.r820t.R820TTunerController.R820TGain;
import source.tuner.rtl.r820t.R820TTunerController.R820TLNAGain;
import source.tuner.rtl.r820t.R820TTunerController.R820TMixerGain;
public class R820TTunerConfiguration extends TunerConfiguration
{
private R820TGain mMasterGain = R820TGain.AUTOMATIC;
private R820TMixerGain mMixerGain = R820TMixerGain.AUTOMATIC;
private R820TLNAGain mLNAGain = R820TLNAGain.AUTOMATIC;
private R820TEnhanceGain mEnhanceGain = R820TEnhanceGain.AUTOMATIC;
private double mFrequencyCorrection = -12.0d;
private SampleRate mSampleRate = SampleRate.RATE_1_200MHZ;
/**
* Default constructor for JAXB
*/
public R820TTunerConfiguration()
{
this( "Default" );
}
public R820TTunerConfiguration( String name )
{
super( name );
}
@Override
public TunerType getTunerType()
{
return TunerType.ELONICS_E4000;
}
@XmlAttribute( name = "master_gain" )
public R820TGain getMasterGain()
{
return mMasterGain;
}
public void setMasterGain( R820TGain gain )
{
mMasterGain = gain;
}
@XmlAttribute( name = "mixer_gain" )
public R820TMixerGain getMixerGain()
{
return mMixerGain;
}
public void setMixerGain( R820TMixerGain mixerGain )
{
mMixerGain = mixerGain;
}
@XmlAttribute( name = "lna_gain" )
public R820TLNAGain getLNAGain()
{
return mLNAGain;
}
public void setLNAGain( R820TLNAGain lnaGain )
{
mLNAGain = lnaGain;
}
@XmlAttribute( name = "enhance_gain" )
public R820TEnhanceGain getEnhanceGain()
{
return mEnhanceGain;
}
public void setEnhanceGain( R820TEnhanceGain enhanceGain )
{
mEnhanceGain = enhanceGain;
}
@XmlAttribute( name = "frequency_correction" )
public double getFrequencyCorrection()
{
return mFrequencyCorrection;
}
public void setFrequencyCorrection( double value )
{
mFrequencyCorrection = value;
}
@XmlAttribute( name = "sample_rate" )
public SampleRate getSampleRate()
{
return mSampleRate;
}
public void setSampleRate( SampleRate sampleRate )
{
mSampleRate = sampleRate;
}
}

View File

@@ -0,0 +1,619 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.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 log.Log;
import net.miginfocom.swing.MigLayout;
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.R820TEnhanceGain;
import source.tuner.rtl.r820t.R820TTunerController.R820TGain;
import source.tuner.rtl.r820t.R820TTunerController.R820TLNAGain;
import source.tuner.rtl.r820t.R820TTunerController.R820TMixerGain;
import controller.ResourceManager;
public class R820TTunerConfigurationPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private ResourceManager mResourceManager;
private R820TTunerController mController;
private R820TTunerConfiguration mSelectedConfig;
private JButton mNewConfiguration;
private JButton mDeleteConfiguration;
private JComboBox<R820TTunerConfiguration> mComboConfigurations;
private JTextField mName;
private JSpinner mFrequencyCorrection;
private JComboBox<R820TGain> mComboMasterGain;
private JComboBox<R820TMixerGain> mComboMixerGain;
private JComboBox<R820TLNAGain> mComboLNAGain;
private JComboBox<R820TEnhanceGain> mComboEnhanceGain;
private JComboBox<SampleRate> mComboSampleRate;
public R820TTunerConfigurationPanel( ResourceManager resourceManager,
R820TTunerController controller )
{
mResourceManager = resourceManager;
mController = controller;
init();
}
/**
* Initializes the gui components
*/
private void init()
{
setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]" ) );
setBorder( BorderFactory.createTitledBorder( "Tuner Configuration" ) );
/**
* Tuner configuration selector
*/
mComboConfigurations = new JComboBox<R820TTunerConfiguration>();
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( mComboConfigurations, "span 2,wrap" );
/**
* 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( mName );
add( new JLabel( "Name" ), "wrap" );
/**
* 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,
"E4K Tuner Controller - couldn't apply "
+ "frequency correction value: " + value +
e1.getLocalizedMessage() );
Log.error( "E4K Tuner Controller - couldn't apply "
+ "frequency correction value: " + value +
e1.getLocalizedMessage() );
}
}
} );
}
} );
add( mFrequencyCorrection );
add( new JLabel( "Frequency Correction (ppm)" ), "wrap" );
/**
* 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 | UsbException eSampleRate )
{
JOptionPane.showMessageDialog(
R820TTunerConfigurationPanel.this,
"E4K Tuner Controller - couldn't apply the sample "
+ "rate setting [" + sampleRate.getLabel() + "] " +
eSampleRate.getLocalizedMessage() );
Log.error( "E4K Tuner Controller - couldn't apply sample "
+ "rate setting [" + sampleRate.getLabel() + "] " +
eSampleRate.getLocalizedMessage() );
}
}
} );
add( mComboSampleRate );
add( new JLabel( "Sample Rate" ), "wrap" );
/**
* 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>( 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(
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(
R820TTunerConfigurationPanel.this,
"E4K Tuner Controller - couldn't apply the gain "
+ "setting - " + e.getLocalizedMessage() );
Log.error( "E4K Tuner Controller - couldn't apply "
+ "gain setting - " + e.getLocalizedMessage() );
}
}
} );
mComboMasterGain.setToolTipText( "<html>Select <b>AUTOMATIC</b> for auto "
+ "gain, <b>MANUAL</b> to enable<br> independent control of "
+ "<i>Mixer</i>, <i>LNA</i> and <i>Enhance</i> gain<br>"
+ "settings, or one of the individual gain settings for<br>"
+ "semi-manual gain control</html>" );
gainPanel.add( mComboMasterGain );
gainPanel.add( new JLabel( "Master" ), "wrap" );
/* Mixer Gain Control */
mComboMixerGain = new JComboBox<R820TMixerGain>( 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( mComboMixerGain.isEnabled() )
{
mController.setMixerGain( mixerGain, true );
}
mSelectedConfig.setMixerGain( mixerGain );
save();
}
catch ( UsbException e )
{
JOptionPane.showMessageDialog(
R820TTunerConfigurationPanel.this,
"E4K Tuner Controller - couldn't apply the mixer "
+ "gain setting - " + e.getLocalizedMessage() );
Log.error( "E4K Tuner Controller - couldn't apply mixer "
+ "gain setting - " + e.getLocalizedMessage() );
}
}
} );
}
} );
mComboMixerGain.setToolTipText( "<html>Mixer Gain. Set master gain "
+ "to <b>MASTER</b> to enable adjustment</html>" );
mComboMixerGain.setEnabled( false );
gainPanel.add( mComboMixerGain );
gainPanel.add( new JLabel( "Mixer" ), "wrap" );
/* LNA Gain Control */
mComboLNAGain = new JComboBox<R820TLNAGain>( R820TLNAGain.values() );
mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() );
mComboLNAGain.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent arg0 )
{
try
{
R820TLNAGain lnaGain = (R820TLNAGain)mComboLNAGain.getSelectedItem();
if( mComboLNAGain.isEnabled() )
{
mController.setLNAGain( lnaGain, true );
}
mSelectedConfig.setLNAGain( lnaGain );
save();
}
catch ( UsbException e )
{
JOptionPane.showMessageDialog(
R820TTunerConfigurationPanel.this,
"E4K Tuner Controller - couldn't apply the LNA "
+ "gain setting - " + e.getLocalizedMessage() );
Log.error( "E4K Tuner Controller - couldn't apply LNA "
+ "gain setting - " + e.getLocalizedMessage() );
}
}
} );
mComboLNAGain.setToolTipText( "<html>LNA Gain. Set master gain "
+ "to <b>MANUAL</b> to enable adjustment</html>" );
mComboLNAGain.setEnabled( false );
gainPanel.add( mComboLNAGain );
gainPanel.add( new JLabel( "LNA" ), "wrap" );
/* Enhance Gain Control */
mComboEnhanceGain = new JComboBox<R820TEnhanceGain>( R820TEnhanceGain.values() );
mComboEnhanceGain.setSelectedItem( mSelectedConfig.getEnhanceGain() );
mComboEnhanceGain.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent arg0 )
{
try
{
R820TEnhanceGain enhanceGain =
(R820TEnhanceGain)mComboEnhanceGain.getSelectedItem();
if( mComboEnhanceGain.isEnabled() )
{
mController.setEnhanceGain( enhanceGain, true );
}
mSelectedConfig.setEnhanceGain( enhanceGain );
save();
}
catch ( UsbException e )
{
JOptionPane.showMessageDialog(
R820TTunerConfigurationPanel.this,
"E4K Tuner Controller - couldn't apply the enhance "
+ "gain setting - " + e.getLocalizedMessage() );
Log.error( "E4K Tuner Controller - couldn't apply enhance "
+ "gain setting - " + e.getLocalizedMessage() );
}
}
} );
mComboEnhanceGain.setToolTipText( "<html>Enhance Gain. Set master gain "
+ "to <b>MANUAL</b> to enable adjustment</html>" );
mComboEnhanceGain.setEnabled( false );
gainPanel.add( mComboEnhanceGain );
gainPanel.add( new JLabel( "Enhance" ), "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.ELONICS_E4000,
"New Configuration" );
mComboConfigurations.setModel( getModel() );
mComboConfigurations.setSelectedItem( config );
repaint();
}
} );
add( mNewConfiguration );
/**
* 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, "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() );
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(
R820TTunerConfigurationPanel.this,
"E4K Tuner Controller - couldn't "
+ "apply the tuner configuration settings - " +
e1.getLocalizedMessage() );
Log.error( "E4K Tuner Controller - couldn't apply "
+ "config [" + config.getName() + "] - " +
e1.getLocalizedMessage() );
}
}
/**
* 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<R820TTunerConfiguration> getModel()
{
ArrayList<TunerConfiguration> configs =
mResourceManager.getSettingsManager()
.getTunerConfigurations( TunerType.RAFAELMICRO_R820T );
DefaultComboBoxModel<R820TTunerConfiguration> model =
new DefaultComboBoxModel<R820TTunerConfiguration>();
for( TunerConfiguration config: configs )
{
model.addElement( (R820TTunerConfiguration)config );
}
return model;
}
/**
* Retrieves a specific (named) tuner configuration
*/
private R820TTunerConfiguration getNamedConfiguration( String name )
{
ArrayList<TunerConfiguration> 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();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,93 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.rtl.r820t;
import gui.control.JFrequencyControl;
import java.awt.EventQueue;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import log.Log;
import net.miginfocom.swing.MigLayout;
import source.SourceException;
import source.tuner.FrequencyChangeListener;
import controller.ResourceManager;
public class R820TTunerEditorPanel extends JPanel implements FrequencyChangeListener
{
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( "", "[grow,fill]", "[grow,fill]" ) );
setBorder( BorderFactory.createTitledBorder( "Rafael Micro R820T Tuner" ) );
mFrequencyControl = new JFrequencyControl();
mFrequencyControl.addListener( this );
mFrequencyControl.setFrequency( mController.getFrequency(), false );
add( mFrequencyControl, "wrap" );
add( new R820TTunerConfigurationPanel( mResourceManager, mController ),
"wrap" );
}
/**
* Frequency change listener for the frequency control gui component to
* apply end-user requested frequency changes against the tuner
*/
@Override
public void frequencyChanged( final int frequency, int bandwidth )
{
EventQueue.invokeLater( new Runnable()
{
@Override
public void run()
{
try
{
mController.setFrequency( frequency );
}
catch ( SourceException e )
{
Log.error( "E4KTunerController - error setting frequency [" +
frequency + "] - " + e.getLocalizedMessage() );
}
}
} );
}
}

View File

@@ -0,0 +1,44 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.usb;
import javax.usb.UsbDevice;
import source.tuner.TunerClass;
public class USBTunerDevice
{
private UsbDevice mDevice;
private TunerClass mTunerClass;
public USBTunerDevice( UsbDevice device, TunerClass tunerClass )
{
mDevice = device;
mTunerClass = tunerClass;
}
public UsbDevice getDevice()
{
return mDevice;
}
public TunerClass getTunerClass()
{
return mTunerClass;
}
}

View File

@@ -0,0 +1,114 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.tuner.usb;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.usb.UsbConfiguration;
import javax.usb.UsbDevice;
import javax.usb.UsbDisconnectedException;
import javax.usb.UsbEndpoint;
import javax.usb.UsbException;
import javax.usb.UsbHostManager;
import javax.usb.UsbHub;
import javax.usb.UsbInterface;
import javax.usb.UsbServices;
public class USBUtils
{
/**
* Returns a List of currently connected usb devices
*/
public static List<UsbDevice> getDevices()
throws SecurityException, UsbException, UnsupportedEncodingException
{
ArrayList<UsbDevice> devices = new ArrayList<UsbDevice>();
UsbServices services = UsbHostManager.getUsbServices();
UsbHub root = services.getRootUsbHub();
devices.addAll( getHubDevices( root ) );
return devices;
}
/**
* Returns a list of devices attached to the hub
*/
public static List<UsbDevice> getHubDevices( UsbHub hub )
throws UnsupportedEncodingException, UsbException
{
ArrayList<UsbDevice> devices = new ArrayList<UsbDevice>();
@SuppressWarnings( "unchecked" )
List<UsbDevice> children = hub.getAttachedUsbDevices();
Iterator<UsbDevice> it = children.iterator();
while( it.hasNext() )
{
UsbDevice child = it.next();
if( child.isUsbHub() )
{
devices.addAll( getHubDevices( (UsbHub)child ) );
}
else
{
devices.add( child );
}
}
return devices;
}
public static String getDeviceDetails( UsbDevice device )
throws UsbException, UnsupportedEncodingException, UsbDisconnectedException
{
StringBuilder sb = new StringBuilder();
sb.append( device.getUsbDeviceDescriptor().toString() + "\n\n" );
for( Object configObject: device.getUsbConfigurations() )
{
UsbConfiguration config = (UsbConfiguration)configObject;
sb.append( config.getUsbConfigurationDescriptor().toString() + "\n\n" );
for( Object interfaceObject: config.getUsbInterfaces() )
{
UsbInterface iface = (UsbInterface)interfaceObject;
sb.append( iface.getUsbInterfaceDescriptor().toString() + "\n\n" );
for( Object endpointObject: iface.getUsbEndpoints() )
{
UsbEndpoint endpoint = (UsbEndpoint)endpointObject;
sb.append( endpoint.getUsbEndpointDescriptor().toString() + "\n\n" );
}
}
}
return sb.toString();
}
}

View File

@@ -0,0 +1,300 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.wave;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import sample.Listener;
import sample.complex.ComplexSample;
public class ComplexWaveSource extends WaveSource
{
private boolean mRunning = false;
private boolean mLoop = false;
private AudioInputStream mInputStream = null;
private int mBytesPerFrame = 0;
private int mFrequency = 0;
private byte[] mBuffer;
private int mBufferPointer = 0;
private int mBufferLength;
private ShortBuffer mBufferWrapper;
private static int BUFFER_SAMPLE_SIZE = 2000;
private ArrayList<Listener<ComplexSample>> mListeners =
new ArrayList<Listener<ComplexSample>>();
/**
* Single channel (mono) wave file playback source with optional looping
* for continous playback.
*
* Playback can be invoked manually with the next() and next(x) methods to
* get sample(s).
*
* Plaback can be run automatically by invoking the start() and stop()
* methods.
*
* Registered float sample listener(s) will receive any samples produced
* by the manual or automatic methods.
*
* @param loop - true for looping
*
* @throws FileNotFoundException if filename is not found
*/
public ComplexWaveSource( File file, boolean loop ) throws IOException
{
super( file, SampleType.COMPLEX );
mLoop = loop;
}
@Override
public int getSampleRate()
{
return (int)mInputStream.getFormat().getSampleRate();
}
/**
* Returns the frequency set for this file. Normally returns zero, but
* the value can be set with setFrequency() method.
*/
public int getFrequency()
{
return mFrequency;
}
/**
* Changes the value returned from getFrequency() for this source.
*/
public void setFrequency( int frequency )
{
mFrequency = frequency;
}
/**
* Closes the audio input stream
* @throws IOException
*/
public void close() throws IOException
{
if( mInputStream != null )
{
mInputStream.close();
}
mInputStream = null;
}
public void open() throws IOException
{
try
{
mInputStream = AudioSystem.getAudioInputStream( getFile() );
}
catch( UnsupportedAudioFileException e )
{
throw new IOException( "MonoWaveSource - unsupported audio file "
+ "exception - ", e );
}
mBytesPerFrame = mInputStream.getFormat().getFrameSize();
if( mBytesPerFrame != 2 || mInputStream.getFormat().getChannels() != 2 )
{
throw new IOException( "Unsupported Wave Format - requires two "
+ "channels with 16-bit samples" );
}
mBuffer = new byte[ mBytesPerFrame * BUFFER_SAMPLE_SIZE ];
readBuffer();
}
/**
* Reads a sample from the wave file and broadcasts the sample to all
* registered float listeners.
*
* @return - true if data was read or false if no more data. If loop is
* set to true, then this method will continue to produce samples as long
* as there are not IO exceptions.
*
* @throws IOException if unable to read data from the wave file
*/
/**
* Reads the next buffer from the wave file.
* @throws IOException
*/
public void readBuffer() throws IOException
{
if( mInputStream != null )
{
/* reset the buffer pointer and fill the buffer with zeros */
mBufferPointer = 0;
mBufferLength = 0;
Arrays.fill( mBuffer, (byte)0 );
while( mBufferLength == 0 )
{
boolean reset = false;
/* Fill the buffer with samples from the file */
mBufferLength = (int)( mInputStream.read( mBuffer ) /
mBytesPerFrame );
if( reset && mBufferLength == 0 )
{
return;
}
/* If we get a partial buffer, reset the file for the next read */
if( mBufferLength < mBuffer.length && mLoop )
{
close();
open();
reset = true;
}
}
if( mBufferLength > 0 )
{
mBufferWrapper = ByteBuffer.wrap( mBuffer ).order(
ByteOrder.LITTLE_ENDIAN ).asShortBuffer();
}
else
{
mBufferWrapper = null;
}
}
}
private boolean next( boolean broadcast ) throws IOException
{
boolean success = false;
if( mBufferPointer >= mBufferLength )
{
readBuffer();
}
if( mBufferLength != 0 &&
mBufferWrapper != null &&
mBufferWrapper.hasRemaining() )
{
short i = mBufferWrapper.get();
short q = mBufferWrapper.get();
mBufferPointer++;
incrementCurrentLocation( 1, false );
if( broadcast )
{
send( new ComplexSample( (float)i, (float)q ) );
}
success = true;
}
return success;
}
public boolean next() throws IOException
{
return next( true );
}
/**
* Reads the next (count) samples from the wave file and broadcasts them
* to the registered listeners.
* @param count - number of samples to broadcast
* @return - true if successful
* @throws IOException for any IO issues
*/
public boolean next( int count ) throws IOException
{
for( int x = 0; x < count; x++ )
{
next();
}
return true;
}
@Override
public void jumpTo( long index ) throws IOException
{
if( index < mCurrentPosition )
{
close();
open();
}
while( mCurrentPosition < index )
{
next( false );
}
incrementCurrentLocation( 0, true );
}
/**
* Broadcasts a sample to the registered listeners
*/
private void send( ComplexSample sample )
{
Iterator<Listener<ComplexSample>> it = mListeners.iterator();
while( it.hasNext() )
{
it.next().receive( sample );
}
}
/**
* Adds a new listener to receive samples as they are read from the wave file
*/
public void addListener( Listener<ComplexSample> listener )
{
mListeners.add( listener );
}
public void removeListener( Listener<ComplexSample> listener )
{
mListeners.remove( listener );
}
@Override
public void dispose()
{
/* Method not implemented */
}
}

View File

@@ -0,0 +1,290 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.wave;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.Arrays;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import sample.Listener;
public class FloatWaveSource extends WaveSource
{
private boolean mRunning = false;
private boolean mLoop = false;
private AudioInputStream mInputStream = null;
private int mBytesPerFrame = 0;
private int mFrequency = 0;
private byte[] mBuffer;
private int mBufferPointer = 0;
private int mBufferLength;
private ShortBuffer mBufferWrapper;
private Listener<Float> mListener;
private static int BUFFER_SAMPLE_SIZE = 2000;
/**
* Single channel (mono) wave file playback source with optional looping
* for continous playback.
*
* Playback can be invoked manually with the next() and next(x) methods to
* get sample(s).
*
* Plaback can be run automatically by invoking the start() and stop()
* methods.
*
* Registered float sample listener(s) will receive any samples produced
* by the manual or automatic methods.
*
* @param filename - wave filename
*
* @param loop - true for looping
*
* @throws FileNotFoundException if filename is not found
*/
public FloatWaveSource( File file, boolean loop ) throws IOException
{
super( file, SampleType.FLOAT );
mLoop = loop;
}
@Override
public int getSampleRate()
{
return (int)mInputStream.getFormat().getSampleRate();
}
/**
* Returns the frequency set for this file. Normally returns zero, but
* the value can be set with setFrequency() method.
*/
public int getFrequency()
{
return mFrequency;
}
/**
* Changes the value returned from getFrequency() for this source.
*/
public void setFrequency( int frequency )
{
mFrequency = frequency;
}
/**
* Closes the audio input stream
* @throws IOException
*/
public void close() throws IOException
{
if( mInputStream != null )
{
mInputStream.close();
}
mInputStream = null;
}
public void open() throws IOException
{
if( mFile == null || !mFile.isFile() )
{
throw new IOException( "Empty or null file" );
}
try
{
mInputStream = AudioSystem.getAudioInputStream( getFile() );
}
catch( UnsupportedAudioFileException e )
{
throw new IOException( "MonoWaveSource - unsupported audio file "
+ "exception - ", e );
}
mBytesPerFrame = mInputStream.getFormat().getFrameSize();
if( mBytesPerFrame != 2 || mInputStream.getFormat().getChannels() != 1 )
{
throw new IOException( "Unsupported Wave Format - requires mono "
+ "channel with 16-bit samples" );
}
mBuffer = new byte[ mBytesPerFrame * BUFFER_SAMPLE_SIZE ];
readBuffer();
}
/**
* Reads a sample from the wave file and broadcasts the sample to all
* registered float listeners.
*
* @return - true if data was read or false if no more data. If loop is
* set to true, then this method will continue to produce samples as long
* as there are not IO exceptions.
*
* @throws IOException if unable to read data from the wave file
*/
/**
* Reads the next buffer from the wave file.
* @throws IOException
*/
public void readBuffer() throws IOException
{
if( mInputStream != null )
{
/* reset the buffer pointer and fill the buffer with zeros */
mBufferPointer = 0;
mBufferLength = 0;
Arrays.fill( mBuffer, (byte)0 );
while( mBufferLength == 0 )
{
boolean reset = false;
/* Fill the buffer with samples from the file */
mBufferLength = (int)( mInputStream.read( mBuffer ) /
mBytesPerFrame );
if( reset && mBufferLength == 0 )
{
return;
}
/* If we get a partial buffer, reset the file for the next read */
if( mBufferLength < mBuffer.length && mLoop )
{
close();
open();
reset = true;
}
}
if( mBufferLength > 0 )
{
mBufferWrapper = ByteBuffer.wrap( mBuffer ).order(
ByteOrder.LITTLE_ENDIAN ).asShortBuffer();
}
else
{
mBufferWrapper = null;
}
}
}
private boolean next( boolean broadcast ) throws IOException
{
boolean success = false;
if( mBufferPointer >= mBufferLength )
{
readBuffer();
}
if( mBufferLength != 0 &&
mBufferWrapper != null &&
mBufferWrapper.hasRemaining() )
{
short sample = mBufferWrapper.get();
mBufferPointer++;
incrementCurrentLocation( 1, false );
if( broadcast && mListener != null )
{
/* Convert the short into a float value */
mListener.receive( (float)sample );
}
success = true;
}
return success;
}
public boolean next() throws IOException
{
return next( true );
}
/**
* Reads the next (count) samples from the wave file and broadcasts them
* to the registered listeners.
* @param count - number of samples to broadcast
* @return - true if successful
* @throws IOException for any IO issues
*/
public boolean next( int count ) throws IOException
{
for( int x = 0; x < count; x++ )
{
next();
}
return true;
}
@Override
public void jumpTo( long index ) throws IOException
{
if( index < mCurrentPosition )
{
close();
open();
}
while( mCurrentPosition < index )
{
next( false );
}
incrementCurrentLocation( 0, true );
}
/**
* Adds a new listener to receive samples as they are read from the wave file
*/
public void setListener( Listener<Float> listener )
{
mListener = listener;
}
public void removeListener( Listener<Float> listener )
{
mListener = null;
}
@Override
public void dispose()
{
mListener = null;
}
}

View File

@@ -0,0 +1,76 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package source.wave;
import java.io.File;
import java.io.IOException;
import source.Source;
public abstract class WaveSource extends Source
{
protected File mFile;
protected long mCurrentPosition = 0;
protected PositionListener mListener;
public WaveSource( File file, SampleType sampleType )
{
super( sampleType.toString() + " Wave Source", sampleType );
mFile = file;
}
public File getFile()
{
return mFile;
}
public long getCurrentPosition()
{
return mCurrentPosition;
}
public void incrementCurrentLocation( long value, boolean reset )
{
mCurrentPosition += value;
if( mListener != null )
{
mListener.positionUpdated( mCurrentPosition, reset );
}
}
public void addListener( PositionListener listener )
{
mListener = listener;
}
public abstract void open() throws IOException;
public abstract void close() throws IOException;
public abstract boolean next() throws IOException;
public abstract boolean next( int count ) throws IOException;
public abstract void jumpTo( long index ) throws IOException;
public interface PositionListener
{
public void positionUpdated( long position, boolean reset );
}
}