diff --git a/.classpath b/.classpath
index 413e9ff0..4039eba0 100644
--- a/.classpath
+++ b/.classpath
@@ -1,27 +1,28 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/controller/channel/ProcessingChain.java b/src/controller/channel/ProcessingChain.java
index b76bf591..9631d871 100644
--- a/src/controller/channel/ProcessingChain.java
+++ b/src/controller/channel/ProcessingChain.java
@@ -404,6 +404,7 @@ public class ProcessingChain implements Listener
}
if( mSource instanceof TunerChannelSource &&
+ mChannel.getDecodeConfiguration().supportsAFC() &&
mChannel.getDecodeConfiguration().isAFCEnabled() )
{
TunerChannelSource tcs = (TunerChannelSource)mSource;
diff --git a/src/controller/state/ChannelStateList.java b/src/controller/state/ChannelStateList.java
index 5d976bd2..8ed1566e 100644
--- a/src/controller/state/ChannelStateList.java
+++ b/src/controller/state/ChannelStateList.java
@@ -39,6 +39,8 @@ import controller.channel.Channel;
import controller.channel.Channel.ChannelType;
import controller.channel.ChannelEvent;
import controller.channel.ChannelEventListener;
+import decode.am.AMChannelState;
+import decode.am.AMPanel;
import decode.fleetsync2.FleetsyncChannelState;
import decode.fleetsync2.FleetsyncPanel;
import decode.ltrnet.LTRNetChannelState;
@@ -139,8 +141,12 @@ public class ChannelStateList extends JPanel implements ChannelEventListener
ChannelState state = channel.getProcessingChain().getChannelState();
ChannelStatePanel panel = null;
-
- if( state instanceof NBFMChannelState )
+
+ if( state instanceof AMChannelState )
+ {
+ panel = new AMPanel( channel );
+ }
+ else if( state instanceof NBFMChannelState )
{
NBFMChannelState convState =
(NBFMChannelState)state;
diff --git a/src/decode/DecodeEditor.java b/src/decode/DecodeEditor.java
index 99a3dac2..9825d225 100644
--- a/src/decode/DecodeEditor.java
+++ b/src/decode/DecodeEditor.java
@@ -37,18 +37,21 @@ public class DecodeEditor extends Editor
{
mConfig = config;
- mAFC = new JCheckBox( "Automatic Frequency Control" );
- mAFC.setSelected( mConfig.isAFCEnabled() );
- mAFC.addActionListener( new ActionListener()
+ if( config.supportsAFC() )
{
- @Override
- public void actionPerformed( ActionEvent arg0 )
- {
- mConfig.setAFC( mAFC.isSelected() );
- }
- } );
+ mAFC = new JCheckBox( "Automatic Frequency Control" );
+ mAFC.setSelected( mConfig.isAFCEnabled() );
+ mAFC.addActionListener( new ActionListener()
+ {
+ @Override
+ public void actionPerformed( ActionEvent arg0 )
+ {
+ mConfig.setAFC( mAFC.isSelected() );
+ }
+ } );
- add( mAFC, "wrap" );
+ add( mAFC, "wrap" );
+ }
}
public DecodeConfiguration getConfig()
diff --git a/src/decode/DecoderFactory.java b/src/decode/DecoderFactory.java
index 9673541d..62a07178 100644
--- a/src/decode/DecoderFactory.java
+++ b/src/decode/DecoderFactory.java
@@ -22,6 +22,8 @@ import alias.AliasList;
import controller.channel.ChannelMap;
import controller.channel.ProcessingChain;
import controller.state.ChannelState;
+import decode.am.AMChannelState;
+import decode.am.AMDecoder;
import decode.config.AuxDecodeConfiguration;
import decode.config.DecodeConfigLTRNet;
import decode.config.DecodeConfigLTRStandard;
@@ -62,6 +64,9 @@ public class DecoderFactory
switch( config.getDecoderType() )
{
+ case AM:
+ retVal = new AMDecoder( sampleType );
+ break;
case NBFM:
retVal = new NBFMConventionalDecoder( sampleType );
break;
@@ -128,12 +133,11 @@ public class DecoderFactory
switch( decodeConfig.getDecoderType() )
{
+ case AM:
+ retVal = new AMChannelState( chain, aliasList );
+ break;
case NBFM:
retVal = new NBFMChannelState( chain, aliasList );
-
- DecodeConfigNBFM conv =
- (DecodeConfigNBFM)decodeConfig;
-
break;
case LTR_STANDARD:
retVal = new LTRChannelState( chain, aliasList );
diff --git a/src/decode/DecoderType.java b/src/decode/DecoderType.java
index 3695bada..d72c8680 100644
--- a/src/decode/DecoderType.java
+++ b/src/decode/DecoderType.java
@@ -25,6 +25,7 @@ import java.util.EnumSet;
*/
public enum DecoderType
{
+ AM( "AM", "AM", "images/am", 10000 ),
FLEETSYNC2( "Fleetsync II", "Fsync2", "images/fm", 12500 ),
LTR_STANDARD( "LTR-Standard", "LTR", "images/ltr_standard", 10000 ),
LTR_NET( "LTR-Net", "LTR-Net", "images/ltr_net", 10000 ),
@@ -53,7 +54,8 @@ public enum DecoderType
*/
public static EnumSet getAvailableDecoders()
{
- return EnumSet.of( DecoderType.LTR_NET,
+ return EnumSet.of( DecoderType.AM,
+ DecoderType.LTR_NET,
DecoderType.LTR_STANDARD,
DecoderType.MPT1327,
DecoderType.NBFM,
diff --git a/src/decode/am/AMChannelState.java b/src/decode/am/AMChannelState.java
new file mode 100644
index 00000000..15a26a04
--- /dev/null
+++ b/src/decode/am/AMChannelState.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * SDR Trunk
+ * Copyright (C) 2014 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ ******************************************************************************/
+package decode.am;
+
+import alias.AliasList;
+import audio.SquelchListener;
+import audio.SquelchListener.SquelchState;
+import controller.channel.ProcessingChain;
+import controller.state.AuxChannelState;
+import controller.state.ChannelState;
+
+/**
+ * Conventional channel state - this class doesn't do anything at the moment,
+ * it is a place holder for future expansion for the conventional decoder
+ */
+public class AMChannelState extends ChannelState
+{
+ public AMChannelState( ProcessingChain channel, AliasList aliasList )
+ {
+ super( channel, aliasList );
+ }
+
+ /**
+ * Make the ConventionalChannelState always unsquelched
+ */
+ public void addListener( SquelchListener listener )
+ {
+ super.addListener( listener );
+
+ super.setSquelchState( SquelchState.UNSQUELCH );
+ }
+
+ @Override
+ public void setSquelchState( SquelchState state )
+ {
+ //do nothing ... we want the squelch state always unsquelched (for now)
+ }
+ @Override
+ public String getActivitySummary()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( "Activity Summary\n" );
+ sb.append( "\tDecoder:\tAM\n\n" );
+
+ for( AuxChannelState state: mAuxChannelStates )
+ {
+ sb.append( state.getActivitySummary() );
+ sb.append( "\n\n" );
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/src/decode/am/AMDecoder.java b/src/decode/am/AMDecoder.java
new file mode 100644
index 00000000..6cf33587
--- /dev/null
+++ b/src/decode/am/AMDecoder.java
@@ -0,0 +1,68 @@
+package decode.am;
+
+import sample.Listener;
+import source.Source.SampleType;
+import decode.Decoder;
+import decode.DecoderType;
+import dsp.am.AMDemodulator;
+import dsp.filter.DCRemovalFilter2;
+
+public class AMDecoder extends Decoder
+{
+ /**
+ * This value determines how quickly the DC remove filter responds to
+ * changes in frequency.
+ */
+ private static final double DC_REMOVAL_RATIO = 0.000003;
+
+ private AMDemodulator mDemodulator;
+ private DCRemovalFilter2 mDCRemovalFilter;
+
+ public AMDecoder( SampleType sampleType )
+ {
+ super( sampleType );
+
+ /**
+ * Only setup a demod chain if we're receiving complex samples. If
+ * we're receiving demodulated samples, they'll be handled the same
+ * was as we handle the output of the demodulator.
+ */
+ if( mSourceSampleType == SampleType.COMPLEX )
+ {
+ /**
+ * The Decoder super class is both a Complex listener and a float
+ * listener. So, we add the demod to listen to the incoming
+ * quadrature samples, and we wire the output of the demod right
+ * back to this class, so we can receive the demodulated output
+ * to process
+ */
+ mDemodulator = new AMDemodulator();
+
+ this.addComplexListener( mDemodulator );
+
+ /**
+ * Remove the DC component that is present when we're mistuned
+ */
+ mDCRemovalFilter = new DCRemovalFilter2( DC_REMOVAL_RATIO );
+ mDemodulator.addListener( mDCRemovalFilter );
+
+ /**
+ * Route the demodulated, filtered samples back to this class to send
+ * to all registered listeners
+ */
+ mDCRemovalFilter.setListener( this.getFloatReceiver() );
+ }
+ }
+
+ @Override
+ public DecoderType getType()
+ {
+ return DecoderType.AM;
+ }
+
+ @Override
+ public void addUnfilteredFloatListener( Listener listener )
+ {
+ mDemodulator.addListener( listener );
+ }
+}
diff --git a/src/decode/am/AMEditor.java b/src/decode/am/AMEditor.java
new file mode 100644
index 00000000..6380a97f
--- /dev/null
+++ b/src/decode/am/AMEditor.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * SDR Trunk
+ * Copyright (C) 2014 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ ******************************************************************************/
+package decode.am;
+
+import decode.DecodeEditor;
+import decode.config.DecodeConfiguration;
+
+public class AMEditor extends DecodeEditor
+{
+ private static final long serialVersionUID = 1L;
+
+ public AMEditor( DecodeConfiguration config )
+ {
+ super( config );
+ }
+}
diff --git a/src/decode/am/AMPanel.java b/src/decode/am/AMPanel.java
new file mode 100644
index 00000000..3842b807
--- /dev/null
+++ b/src/decode/am/AMPanel.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * SDR Trunk
+ * Copyright (C) 2014 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ ******************************************************************************/
+package decode.am;
+
+import java.awt.EventQueue;
+
+import javax.swing.JLabel;
+
+import settings.ColorSetting;
+import settings.Setting;
+import controller.channel.Channel;
+import controller.state.ChannelState.ChangedAttribute;
+import controller.state.ChannelStatePanel;
+
+public class AMPanel extends ChannelStatePanel
+{
+ private static final long serialVersionUID = 1L;
+
+ private JLabel mStateLabel;
+ private JLabel mSourceLabel;
+ private JLabel mChannelLabel;
+
+ private JLabel mProtocol = new JLabel( "AM" );
+
+ public AMPanel( Channel channel )
+ {
+ super( channel );
+
+ init();
+ }
+
+ public void init()
+ {
+ mStateLabel = new JLabel( mChannel.getProcessingChain().
+ getChannelState().getState().getDisplayValue() );
+ mStateLabel.setFont( mFontDecoder );
+ mStateLabel.setForeground( mColorLabelDecoder );
+
+ mSourceLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() );
+ mSourceLabel.setFont( mFontDetails );
+ mSourceLabel.setForeground( mColorLabelDetails );
+
+ mChannelLabel = new JLabel( mChannel.getChannelDisplayName() );
+ mChannelLabel.setFont( mFontDetails );
+ mChannelLabel.setForeground( mColorLabelDetails );
+
+ mProtocol.setFont( mFontDecoder );
+ mProtocol.setForeground( mColorLabelDecoder );
+
+ add( mStateLabel );
+ add( mSourceLabel );
+ add( mChannelLabel, "wrap" );
+
+ add( mProtocol, "wrap" );
+ }
+
+ @Override
+ public void receive( final ChangedAttribute changedAttribute )
+ {
+ EventQueue.invokeLater( new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ switch( changedAttribute )
+ {
+ case CHANNEL_STATE:
+ mStateLabel.setText( mChannel.getProcessingChain().
+ getChannelState().getState().getDisplayValue() );
+ break;
+ case SOURCE:
+ mSourceLabel.setText( mChannel.getSourceConfiguration()
+ .getDescription() );
+ break;
+ case CHANNEL_NAME:
+ case SITE_NAME:
+ case SYSTEM_NAME:
+ mChannelLabel.setText( mChannel.getChannelDisplayName() );
+ break;
+ }
+
+ repaint();
+ }
+ });
+ }
+
+ @Override
+ public void settingChanged( Setting setting )
+ {
+ super.settingChanged( setting );
+
+ if( setting instanceof ColorSetting )
+ {
+ ColorSetting colorSetting = (ColorSetting)setting;
+
+ switch( colorSetting.getColorSettingName() )
+ {
+ case CHANNEL_STATE_LABEL_DECODER:
+ if( mStateLabel != null )
+ {
+ mStateLabel.setForeground( mColorLabelDecoder );
+ }
+ if( mProtocol != null )
+ {
+ mProtocol.setForeground( mColorLabelDecoder );
+ }
+ break;
+ case CHANNEL_STATE_LABEL_DETAILS:
+ if( mSourceLabel != null )
+ {
+ mSourceLabel.setForeground( mColorLabelDetails );
+ }
+ if( mChannelLabel != null )
+ {
+ mChannelLabel.setForeground( mColorLabelDetails );
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/src/decode/config/DecodeConfigAM.java b/src/decode/config/DecodeConfigAM.java
new file mode 100644
index 00000000..1ba26b68
--- /dev/null
+++ b/src/decode/config/DecodeConfigAM.java
@@ -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
+ ******************************************************************************/
+package decode.config;
+
+import decode.DecoderType;
+
+public class DecodeConfigAM extends DecodeConfiguration
+{
+ public DecodeConfigAM()
+ {
+ super( DecoderType.AM );
+
+ setAFC( false );
+ }
+
+ public boolean supportsAFC()
+ {
+ return false;
+ }
+}
diff --git a/src/decode/config/DecodeConfigFactory.java b/src/decode/config/DecodeConfigFactory.java
index 723d5ffd..9bbc7d2f 100644
--- a/src/decode/config/DecodeConfigFactory.java
+++ b/src/decode/config/DecodeConfigFactory.java
@@ -33,6 +33,9 @@ public class DecodeConfigFactory
switch( decoder )
{
+ case AM:
+ retVal = new DecodeConfigAM();
+ break;
case NBFM:
retVal = new DecodeConfigNBFM();
break;
diff --git a/src/decode/config/DecodeConfiguration.java b/src/decode/config/DecodeConfiguration.java
index b4cc4db1..e99e2ea9 100644
--- a/src/decode/config/DecodeConfiguration.java
+++ b/src/decode/config/DecodeConfiguration.java
@@ -24,7 +24,8 @@ import javax.xml.bind.annotation.XmlSeeAlso;
import controller.config.Configuration;
import decode.DecoderType;
-@XmlSeeAlso( { DecodeConfigNBFM.class,
+@XmlSeeAlso( { DecodeConfigAM.class,
+ DecodeConfigNBFM.class,
DecodeConfigLTRNet.class,
DecodeConfigLTRStandard.class,
DecodeConfigMPT1327.class,
@@ -65,4 +66,9 @@ public abstract class DecodeConfiguration extends Configuration
{
mAFCEnabled = enabled;
}
+
+ public boolean supportsAFC()
+ {
+ return true;
+ }
}
diff --git a/src/dsp/am/AMDemodulator.java b/src/dsp/am/AMDemodulator.java
new file mode 100644
index 00000000..f70fd318
--- /dev/null
+++ b/src/dsp/am/AMDemodulator.java
@@ -0,0 +1,62 @@
+package dsp.am;
+
+import sample.Broadcaster;
+import sample.Listener;
+import sample.complex.ComplexSample;
+import dsp.filter.ComplexFIRFilter;
+import dsp.filter.FilterFactory;
+import dsp.filter.Window.WindowType;
+
+public class AMDemodulator implements Listener
+{
+ private Broadcaster mBroadcaster = new Broadcaster();
+ private ComplexFIRFilter mIQFilter;
+
+ public AMDemodulator()
+ {
+ this( FilterFactory.getLowPass( 48000, 2000, 73, WindowType.HAMMING ),
+ 1000.0 );
+ }
+
+ public AMDemodulator( double[] iqFilter, double iqGain )
+ {
+ mIQFilter = new ComplexFIRFilter( iqFilter, iqGain );
+
+ mIQFilter.setListener( new Demodulator() );
+ }
+
+ @Override
+ public void receive( ComplexSample sample )
+ {
+ mIQFilter.receive( sample );
+ }
+
+ /**
+ * Adds a listener to receive demodulated samples
+ */
+ public void addListener( Listener listener )
+ {
+ mBroadcaster.addListener( listener );
+ }
+
+ /**
+ * Removes a listener from receiving demodulated samples
+ */
+ public void removeListener( Listener listener )
+ {
+ mBroadcaster.removeListener( listener );
+ }
+
+ public class Demodulator implements Listener
+ {
+ @Override
+ public void receive( ComplexSample sample )
+ {
+ float demodulated = (float)Math.abs(
+ Math.sqrt( ( sample.real() * sample.real() ) +
+ ( sample.imaginery() * sample.imaginery() ) ) );
+
+ mBroadcaster.broadcast( demodulated * 30.0f );
+ }
+ }
+}
diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java
index a1c229ba..5c99989c 100644
--- a/src/gui/SDRTrunk.java
+++ b/src/gui/SDRTrunk.java
@@ -17,7 +17,6 @@
******************************************************************************/
package gui;
-
import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.EventQueue;
@@ -29,7 +28,6 @@ import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.Date;
import javax.imageio.ImageIO;
import javax.swing.JFrame;