diff --git a/imports/jmbe-interface-0.2.0.jar b/imports/jmbe-interface-0.2.1.jar similarity index 77% rename from imports/jmbe-interface-0.2.0.jar rename to imports/jmbe-interface-0.2.1.jar index 85add8bd..9963638a 100644 Binary files a/imports/jmbe-interface-0.2.0.jar and b/imports/jmbe-interface-0.2.1.jar differ diff --git a/src/.directory b/src/.directory new file mode 100644 index 00000000..a931c4fe --- /dev/null +++ b/src/.directory @@ -0,0 +1,4 @@ +[Dolphin] +Timestamp=2015,9,1,6,42,59 +Version=3 +ViewMode=1 diff --git a/src/alias/AliasList.java b/src/alias/AliasList.java index 3ff0c546..1c97bd25 100644 --- a/src/alias/AliasList.java +++ b/src/alias/AliasList.java @@ -23,6 +23,7 @@ import java.util.HashMap; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; +import javax.xml.bind.annotation.XmlTransient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -488,4 +489,57 @@ public class AliasList implements Comparable { return getName().compareTo( otherAliasList.getName() ); } + + public Alias getAlias( String value, MetadataType type ) + { + switch( type ) + { + case ESN: + return getESNAlias( value ); + case FLEETSYNC: + return getFleetsyncAlias( value ); + case MDC1200: + return getMDC1200Alias( value ); + case MOBILE_ID: + return getMobileIDNumberAlias( value ); + case MPT1327: + return getMPT1327Alias( value ); + case SITE_ID: + return getSiteID( value ); + case STATUS: + if( value != null ) + { + try + { + return getStatus( Integer.valueOf( value ) ); + } + catch( Exception e ) + { + //do nothing, we couldn't parse the int value + } + } + break; + case FROM_TALKGROUP: + case TO: + return getTalkgroupAlias( value ); + case UNIQUE_ID: + if( value != null ) + { + try + { + return getUniqueID( Integer.valueOf( value ) ); + } + catch( Exception e ) + { + //do nothing, we couldn't parse the int value + } + } + break; + default: + break; + } + + return null; + } + } diff --git a/src/alias/Metadata.java b/src/alias/Metadata.java new file mode 100644 index 00000000..e1976183 --- /dev/null +++ b/src/alias/Metadata.java @@ -0,0 +1,102 @@ +package alias; + +public class Metadata +{ + private MetadataType mMetadataType; + private String mValue; + private Alias mValueAlias; + private boolean mTemporal; + + /** + * Constucts a metadata object with the temporal settings. Temporal + * indicates that this metadata will be removed at the end of a reset + * event, or call end event. + */ + public Metadata( MetadataType metadataType, String value, Alias alias, boolean temporal ) + { + mMetadataType = metadataType; + mValue = value; + mValueAlias = alias; + mTemporal = temporal; + } + + /** + * Constucts a metadata object with the temporal settings. Temporal + * indicates that this metadata will be removed at the end of a reset + * event, or call end event. + */ + public Metadata( MetadataType metadataType, String value, boolean temporal ) + { + this( metadataType, value, null, temporal ); + } + + /** + * Constucts a non-temporal metadata object + */ + public Metadata( MetadataType metadataType, String value, Alias alias ) + { + this( metadataType, value, alias, false ); + } + + /** + * Constucts a non-temporal metadata object + */ + public Metadata( MetadataType metadataType, String value ) + { + this( metadataType, value, null, false ); + } + + public MetadataType getMetadataType() + { + return mMetadataType; + } + + public String getKey() + { + return mMetadataType.getLabel(); + } + + public String getValue() + { + return mValue; + } + + public Alias getAlias() + { + return mValueAlias; + } + + public boolean hasAlias() + { + return mValueAlias != null; + } + + /** + * Indicates that this piece of metadata is only valid for the duration of + * a call event and should be removed during a reset + */ + public boolean isTemporal() + { + return mTemporal; + } + + public boolean isReset() + { + return false; + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append( mMetadataType.getLabel() ); + sb.append( ":" ); + sb.append( mValue ); + sb.append( " [" ); + sb.append( mMetadataType.name() ); + sb.append( "] temporal:" ); + sb.append( mTemporal ); + + return sb.toString(); + } +} diff --git a/src/alias/MetadataReset.java b/src/alias/MetadataReset.java new file mode 100644 index 00000000..8857ec4d --- /dev/null +++ b/src/alias/MetadataReset.java @@ -0,0 +1,14 @@ +package alias; + +public class MetadataReset extends Metadata +{ + public MetadataReset() + { + super( MetadataType.TEMPORAL_RESET, "reset", true ); + } + + public boolean isReset() + { + return true; + } +} diff --git a/src/alias/MetadataType.java b/src/alias/MetadataType.java new file mode 100644 index 00000000..e5426763 --- /dev/null +++ b/src/alias/MetadataType.java @@ -0,0 +1,38 @@ +package alias; + +public enum MetadataType +{ + CHANNEL( "Channel" ), + DETAILS( "Details" ), + ESN( "ESN" ), + FLEETSYNC( "Fleetsync" ), + FREQUENCY( "Frequency" ), + FROM_TALKGROUP( "From" ), + MDC1200( "MDC-1200" ), + MOBILE_ID( "Mobile ID" ), + MPT1327( "MPT-1327" ), + PROTOCOL( "Protocol" ), + SITE_ID( "Site ID" ), + STATUS( "Status" ), + TO( "To" ), + UNIQUE_ID( "Unique ID" ), + + TEMPORAL_RESET( "Reset" ); + + private String mLabel; + + private MetadataType( String label ) + { + mLabel = label; + } + + public String getLabel() + { + return mLabel; + } + + public String toString() + { + return mLabel; + } +} diff --git a/src/alias/action/AliasActionManager.java b/src/alias/action/AliasActionManager.java index 87918777..007902e4 100644 --- a/src/alias/action/AliasActionManager.java +++ b/src/alias/action/AliasActionManager.java @@ -6,10 +6,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import message.Message; +import module.decode.p25.P25_LSMDecoder; import sample.Listener; import alias.Alias; import controller.ResourceManager; -import decode.p25.P25Decoder; /** * Manages all alias action events. Each received message is interrogated for diff --git a/src/audio/AudioChannelPanel.java b/src/audio/AudioChannelPanel.java new file mode 100644 index 00000000..8197842a --- /dev/null +++ b/src/audio/AudioChannelPanel.java @@ -0,0 +1,356 @@ +package audio; + +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import javax.sound.sampled.BooleanControl; +import javax.sound.sampled.FloatControl; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import settings.ColorSetting; +import settings.Setting; +import settings.SettingChangeListener; +import settings.SettingsManager; +import settings.ColorSetting.ColorSettingName; +import alias.Alias; +import alias.Metadata; +import alias.MetadataType; +import audio.AudioEvent.Type; +import audio.metadata.AudioMetadata; + +public class AudioChannelPanel extends JPanel + implements Listener, SettingChangeListener +{ + private static final long serialVersionUID = 1L; + + private static final Logger mLog = LoggerFactory.getLogger( AudioChannelPanel.class ); + + private Font mFont = new Font( Font.MONOSPACED, Font.PLAIN, 12 ); + private Color mLabelColor; + private Color mDetailsColor; + + private SettingsManager mSettingsManager; + private IAudioController mController; + private String mChannel; + + private JLabel mChannelLabel; + private JLabel mChannelName; + private JLabel mMutedLabel; + + private JLabel mFromLabel = new JLabel( "From:" ); + private JLabel mFrom = new JLabel( "" ); + private JLabel mFromAlias = new JLabel( "" ); + + private JLabel mToLabel = new JLabel( "To:" ); + private JLabel mTo = new JLabel( "" ); + private JLabel mToAlias = new JLabel( "" ); + + private boolean mConfigured = false; + + private BooleanControl mMuteControl; + private FloatControl mGainControl; + + public AudioChannelPanel( SettingsManager settingsManager, + IAudioController controller, + String channel ) + { + mSettingsManager = settingsManager; + mSettingsManager.addListener( this ); + + mLabelColor = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_LABEL_DECODER ).getColor(); + mDetailsColor = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ).getColor(); + + mController = controller; + mChannel = channel; + + try + { + mMuteControl = mController.getMuteControl( mChannel ); + } + catch ( AudioException e ) + { + mLog.error( "Couldn't obtain mute control from audio channel [" + + mChannel + "]", e ); + } + + try + { + mGainControl = mController.getGainControl( mChannel ); + } + catch ( AudioException e ) + { + mLog.error( "Couldn't obtain gain control from audio channel [" + + mChannel + "]", e ); + } + + try + { + mController.addAudioEventListener( mChannel, this ); + } + catch( AudioException e ) + { + mLog.error( "Couldn't register as audio event listener on channel [" + + mChannel + "]", e ); + } + + mController.setAudioMetadataListener( mChannel, new AudioMetadataProcessor() ); + + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0", "[right][][grow,fill]", "[grow,fill]0[]0[]" ) ); + setBackground( Color.BLACK ); + + addMouseListener( new MouseSelectionListener() ); + + mChannelLabel = new JLabel( "Channel:" ); + mChannelLabel.setFont( mFont ); + mChannelLabel.setForeground( mDetailsColor ); + + add( mChannelLabel ); + + mChannelName = new JLabel( mChannel ); + mChannelName.setFont( mFont ); + mChannelName.setForeground( mDetailsColor ); + + add( mChannelName ); + + mMutedLabel = new JLabel( "" ); + mMutedLabel.setFont( mFont ); + mMutedLabel.setForeground( Color.RED ); + + add( mMutedLabel, "wrap" ); + + mFromLabel.setFont( mFont ); + mFromLabel.setForeground( mLabelColor ); + add( mFromLabel ); + + mFrom.setFont( mFont ); + mFrom.setForeground( mLabelColor ); + add( mFrom ); + + mFromAlias.setFont( mFont ); + mFromAlias.setForeground( mLabelColor ); + add( mFromAlias,"wrap" ); + + mToLabel.setFont( mFont ); + mToLabel.setForeground( mLabelColor ); + add( mToLabel ); + + mTo.setFont( mFont ); + mTo.setForeground( mLabelColor ); + add( mTo ); + + mToAlias.setFont( mFont ); + mToAlias.setForeground( mLabelColor ); + add( mToAlias ); + } + + private void updateMuteState() + { + if( mMuteControl != null ) + { + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + mMutedLabel.setText( mMuteControl.getValue() ? "Muted" : "" ); + } + } ); + } + } + + @Override + public void receive( final AudioEvent audioEvent ) + { + if( audioEvent.getType() == Type.AUDIO_STOPPED ) + { + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + resetLabels(); + } + } ); + } + } + + /** + * Resets the from and to labels. Note: this does not happen on the swing + * event thread. Only invoke from the swing thread. + */ + private void resetLabels() + { + mFrom.setText( "" ); + updateAlias( mFromAlias, null ); + + mTo.setText( "" ); + updateAlias( mToAlias, null ); + + mConfigured = false; + } + + /** + * Updates the alias label with text and icon from the alias. Note: this + * does not occur on the Swing event thread -- wrap any calls to this + * method with an event thread call. + */ + private void updateAlias( JLabel label, Alias alias ) + { + if( alias != null ) + { + label.setText( alias.getName() ); + + String icon = alias.getIconName(); + + if( icon != null ) + { + label.setIcon( mSettingsManager.getImageIcon( icon, 12 ) ); + } + else + { + label.setIcon( null ); + } + } + else + { + label.setText( "" ); + label.setIcon( null ); + } + } + + public class MouseSelectionListener implements MouseListener + { + @Override + public void mouseClicked( MouseEvent event ) + { + JPopupMenu popup = new JPopupMenu(); + + if( mMuteControl != null ) + { + JMenuItem mute = new JMenuItem( mMuteControl.getValue() ? + "Unmute Channel: " + mChannel : "Mute Channel: " + mChannel ); + + mute.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + mMuteControl.setValue( !mMuteControl.getValue() ); + updateMuteState(); + } + } ); + popup.add( mute ); + } + + popup.show( event.getComponent(), event.getX(), event.getY() ); + } + + @Override + public void mousePressed( MouseEvent e ) {} + @Override + public void mouseReleased( MouseEvent e ) {} + @Override + public void mouseEntered( MouseEvent e ) {} + @Override + public void mouseExited( MouseEvent e ) {} + } + + + /** + * Processes audio metadata to update this panel's display values + */ + public class AudioMetadataProcessor implements Listener + { + @Override + public void receive( AudioMetadata audioMetadata ) + { + if( !mConfigured || audioMetadata.isUpdated() ) + { + final Metadata from = audioMetadata.getMetadata( MetadataType.FROM_TALKGROUP ); + + final Metadata to = audioMetadata.getMetadata( MetadataType.TO ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + mFrom.setText( from.getValue() ); + updateAlias( mFromAlias, from.getAlias() ); + + mTo.setText( to.getValue() ); + updateAlias( mToAlias, to.getAlias() ); + } + } ); + + mConfigured = true; + } + } + } + + + @Override + public void settingChanged( Setting setting ) + { + if( setting instanceof ColorSetting ) + { + ColorSetting colorSetting = (ColorSetting)setting; + + switch( colorSetting.getColorSettingName() ) + { + case CHANNEL_STATE_LABEL_DECODER: + if( mFrom != null ) + { + mFrom.setForeground( mLabelColor ); + } + if( mFromAlias != null ) + { + mFromAlias.setForeground( mLabelColor ); + } + if( mTo != null ) + { + mTo.setForeground( mLabelColor ); + } + if( mToAlias != null ) + { + mToAlias.setForeground( mLabelColor ); + } + break; + case CHANNEL_STATE_LABEL_DETAILS: + if( mFromLabel != null ) + { + mFromLabel.setForeground( mDetailsColor ); + } + if( mToLabel != null ) + { + mToLabel.setForeground( mDetailsColor ); + } + break; + } + } + } + + @Override + public void settingDeleted( Setting setting ) {} +} diff --git a/src/audio/AudioEvent.java b/src/audio/AudioEvent.java new file mode 100644 index 00000000..e82bbf14 --- /dev/null +++ b/src/audio/AudioEvent.java @@ -0,0 +1,30 @@ +package audio; + +public class AudioEvent +{ + private Type mType; + private String mChannel; + + public AudioEvent( Type event, String channel ) + { + mType = event; + mChannel = channel; + } + + public Type getType() + { + return mType; + } + + public String getChannel() + { + return mChannel; + } + + public enum Type + { + AUDIO_STARTED, + AUDIO_STOPPED, + AUDIO_CONFIGURATION_CHANGED; + } +} diff --git a/src/audio/AudioException.java b/src/audio/AudioException.java new file mode 100644 index 00000000..365b605c --- /dev/null +++ b/src/audio/AudioException.java @@ -0,0 +1,16 @@ +package audio; + +public class AudioException extends Exception +{ + private static final long serialVersionUID = 1L; + + public AudioException( String description ) + { + super( description ); + } + + public AudioException( String description, Throwable throwable ) + { + super( description, throwable ); + } +} diff --git a/src/audio/AudioManager.java b/src/audio/AudioManager.java new file mode 100644 index 00000000..09d7dc2f --- /dev/null +++ b/src/audio/AudioManager.java @@ -0,0 +1,379 @@ +package audio; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.LinkedTransferQueue; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import javax.sound.sampled.BooleanControl; +import javax.sound.sampled.FloatControl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import audio.metadata.AudioMetadata; +import audio.output.AudioOutput; +import audio.output.MonoAudioOutput; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; + +public class AudioManager implements Listener, IAudioController +{ + private static final Logger mLog = LoggerFactory.getLogger( AudioManager.class ); + + public static final int AUDIO_TIMEOUT = 2000; //2 seconds + public static final int NO_SOURCE = 0; + + private LinkedTransferQueue mAudioPacketQueue = new LinkedTransferQueue<>(); + private Map mChannelAssignments = new HashMap<>(); + private int mLowestPrioritySource; + + private List mAvailableAudioOutputs = new ArrayList<>(); + private Map mChannelMap = new HashMap<>(); + private List mChannelNames = new ArrayList<>(); + private AudioOutput mAudioOutput; + private boolean mMuted = false; + + private Listener mConfigurationChangeListener; + + private ThreadPoolManager mThreadPoolManager; + private ScheduledFuture mProcessingTask; + + /** + * Handles all audio produced by the decoding channels and routes audio + * packets to any combination of outputs based on any alias audio routing + * options specified by the user. + */ + public AudioManager( ThreadPoolManager manager ) + { + mThreadPoolManager = manager; + + mAudioOutput = new MonoAudioOutput(); + mAvailableAudioOutputs.add( mAudioOutput ); + mChannelNames.add( mAudioOutput.getChannelName() ); + mChannelMap.put( mAudioOutput.getChannelName(), mAudioOutput ); + + mProcessingTask = mThreadPoolManager.scheduleFixedRate( + ThreadType.AUDIO_PROCESSING, new AudioPacketProcessor(), 15, + TimeUnit.MILLISECONDS ); + } + + public void dispose() + { + if( mThreadPoolManager != null && mProcessingTask != null ) + { + mThreadPoolManager.cancel( mProcessingTask ); + } + + mAudioPacketQueue.clear(); + + mThreadPoolManager = null; + mProcessingTask = null; + + mAudioOutput.dispose(); + } + + @Override + public synchronized void receive( AudioPacket packet ) + { + mAudioPacketQueue.add( packet ); + } + + public class AudioPacketProcessor implements Runnable + { + @Override + public void run() + { + if( !mMuted ) + { + + } + removeCompletedChannelAssignments(); + + if( mAudioPacketQueue != null ) + { + List packets = new ArrayList(); + + mAudioPacketQueue.drainTo( packets ); + + for( AudioPacket packet: packets ) + { + int source = packet.getMetadata().getSource(); + + /* Existing channel assignment */ + if( mChannelAssignments.containsKey( source ) ) + { + mChannelAssignments.get( source ).getAudioOutput() + .receive( packet ); + } + /* Start a new channel assignment */ + else if( !mAvailableAudioOutputs.isEmpty() ) + { + AudioOutput channel = mAvailableAudioOutputs.remove( 0 ); + + mChannelAssignments.put( packet.getMetadata().getSource(), + new AudioChannelAssignment( channel, packet.getMetadata().getPriority() ) ); + + updateLowestPriorityAssignment(); + + channel.receive( packet ); + } + /* Override the lowest priority assignment */ + else + { + if( mLowestPrioritySource > -1 ) + { + int priority = packet.getMetadata().getPriority(); + + if( packet.getMetadata().isSelected() || + priority < mChannelAssignments + .get( mLowestPrioritySource ).getPriority() ) + { + AudioChannelAssignment assignment = + mChannelAssignments.remove( mLowestPrioritySource ); + + if( assignment != null ) + { + assignment.setPriority( priority ); + + int overrideSource = packet.getMetadata().getSource(); + + mChannelAssignments.put( overrideSource, assignment ); + + mLowestPrioritySource = overrideSource; + + assignment.getAudioOutput().receive( packet ); + } + else + { + mLowestPrioritySource = -1; + } + } + } + } + } + } + } + } + + /** + * Checks each audio channel assignment and removes any completed channels. + */ + private void removeCompletedChannelAssignments() + { + if( !mChannelAssignments.isEmpty() ) + { + Iterator> it = + mChannelAssignments.entrySet().iterator(); + + Entry entry; + + boolean removed = false; + + while( it.hasNext() ) + { + entry = it.next(); + + if( entry.getValue().isComplete() ) + { + mAvailableAudioOutputs.add( entry.getValue().getAudioOutput() ); + entry.getValue().dispose(); + it.remove(); + removed = true; + } + } + + if( removed ) + { + updateLowestPriorityAssignment(); + } + } + } + + /** + * Identifies the lowest priority channel assignment ... the one that can + * be overridden with a higher priority call + */ + private void updateLowestPriorityAssignment() + { + int lowestPriority = -1; + + mLowestPrioritySource = -1; + + for( int source: mChannelAssignments.keySet() ) + { + AudioChannelAssignment assignment = mChannelAssignments.get( source ); + + if( assignment.getPriority() > lowestPriority ) + { + lowestPriority = assignment.getPriority(); + + mLowestPrioritySource = source; + } + } + } + + public class AudioChannelAssignment implements Listener + { + private static final long CALL_END_DELAY = 2000; //2 seconds + + private AudioOutput mAudioOutput; + private int mPriority; + private long mAudioStoppedTime = Long.MAX_VALUE; + + public AudioChannelAssignment( AudioOutput audioOutput, int priority ) + { + mAudioOutput = audioOutput; + mPriority = priority; + + mAudioOutput.addAudioEventListener( this ); + } + + public boolean isComplete() + { + return mAudioStoppedTime != Long.MAX_VALUE && + mAudioStoppedTime + CALL_END_DELAY < System.currentTimeMillis(); + } + + public void dispose() + { + mAudioOutput.removeAudioEventListener( this ); + mAudioOutput = null; + } + + public AudioOutput getAudioOutput() + { + return mAudioOutput; + } + + public int getPriority() + { + return mPriority; + } + + public void setPriority( int priority ) + { + mPriority = priority; + } + + @Override + public void receive( AudioEvent event ) + { + switch( event.getType() ) + { + case AUDIO_STARTED: + mAudioStoppedTime = Long.MAX_VALUE; + break; + case AUDIO_STOPPED: + mAudioStoppedTime = System.currentTimeMillis(); + break; + default: + break; + } + } + } + + @Override + public List getAudioChannels() + { + return mChannelNames; + } + + @Override + public BooleanControl getMuteControl( String channel ) + throws AudioException + { + if( mChannelMap.containsKey( channel ) ) + { + return mChannelMap.get( channel ).getMuteControl(); + } + + throw new AudioException( "Channel [" + channel + "] is not a valid channel" ); + } + + @Override + public boolean hasMuteControl( String channel ) + { + return mChannelMap.containsKey( channel ) && + mChannelMap.get( channel ) != null && + mChannelMap.get( channel ).getMuteControl() != null; + } + + @Override + public FloatControl getGainControl( String channel ) throws AudioException + { + if( mChannelMap.containsKey( channel ) ) + { + return mChannelMap.get( channel ).getGainControl(); + } + + throw new AudioException( "Channel [" + channel + "] is not a valid channel" ); + } + + @Override + public boolean hasGainControl( String channel ) + { + return mChannelMap.containsKey( channel ) && + mChannelMap.get( channel ) != null && + mChannelMap.get( channel ).getGainControl() != null; + } + + /** + * Sets the listener to receive audio events, specifically, audio + * configuration change events + */ + @Override + public void setConfigurationChangeListener( Listener listener ) + { + mConfigurationChangeListener = listener; + } + + @Override + public void addAudioEventListener( String channel, Listener listener ) + throws AudioException + { + if( mChannelMap.containsKey( channel ) ) + { + mChannelMap.get( channel ).addAudioEventListener( listener ); + } + else + { + throw new AudioException( "Channel [" + channel + "] is not a valid " + + "channel. Audio event listener was not registered" ); + } + } + + @Override + public void removeAudioEventListener( String channel, Listener listener ) + { + if( mChannelMap.containsKey( channel ) ) + { + mChannelMap.get( channel ).removeAudioEventListener( listener ); + } + } + + @Override + public void setAudioMetadataListener( String channel, + Listener listener ) + { + if( mChannelMap.containsKey( channel ) ) + { + mChannelMap.get( channel ).setAudioMetadataListener( listener ); + } + } + + @Override + public void removeAudioMetadataListener( String channel ) + { + if( mChannelMap.containsKey( channel ) ) + { + mChannelMap.get( channel ).removeAudioMetadataListener(); + } + } +} diff --git a/src/audio/AudioModule.java b/src/audio/AudioModule.java new file mode 100644 index 00000000..b0e1a1d3 --- /dev/null +++ b/src/audio/AudioModule.java @@ -0,0 +1,191 @@ +package audio; + +import module.Module; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; +import alias.Metadata; +import audio.metadata.AudioMetadata; +import audio.metadata.IMetadataListener; +import audio.squelch.ISquelchStateListener; +import audio.squelch.SquelchState; +import controller.channel.ChannelEvent; +import controller.channel.IChannelEventListener; +import dsp.filter.Filters; +import dsp.filter.dc.AveragingDCRemovalFilter_RB; +import dsp.filter.dc.DCRemovalFilter_RB; +import dsp.filter.fir.real.RealFIRFilter_R_R; + +/** + * Provides packaging of demodulated audio sample buffers into audio packets for + * broadcast to registered audio packet listeners. Includes audio packet + * metadata in constructed audio packets. + * + * Incorporates audio squelch state listener to control if audio packets are + * broadcast or ignored. + */ +public class AudioModule extends Module implements IAudioPacketProvider, + IChannelEventListener, + IMetadataListener, + IRealBufferListener, + ISquelchStateListener, + Listener +{ + protected static final Logger mLog = LoggerFactory.getLogger( AudioModule.class ); + + /* Determines responsiveness of DC filter to frequency changes */ + private static final float DC_REMOVAL_RATIO = 0.000003f; + + /* Provides a unique identifier for this audio module instance to use as a + * source identifier for all audio packets */ + private static int UNIQUE_ID = 0; + private int mSourceID; + + private AudioMetadata mAudioMetadata; + + private ChannelEventListener mChannelEventListener = new ChannelEventListener(); + + private SquelchStateListener mSquelchStateListener = new SquelchStateListener(); + private SquelchState mSquelchState = SquelchState.SQUELCH; + + private RealFIRFilter_R_R mAudioFilter = new RealFIRFilter_R_R( + Filters.FIR_BANDPASS_AUDIO_48KHZ.getCoefficients(), 2.0f ); + + private DCRemovalFilter_RB mDCRemovalFilter; + + private Listener mAudioPacketListener; + + private boolean mRemoveDC = false; + + public AudioModule() + { + this( false ); + } + + public AudioModule( boolean removeDC ) + { + mSourceID = ++UNIQUE_ID; + + mAudioMetadata = new AudioMetadata( mSourceID ); + + mRemoveDC = removeDC; + + if( mRemoveDC ) + { + mDCRemovalFilter = new AveragingDCRemovalFilter_RB( DC_REMOVAL_RATIO ); + mDCRemovalFilter.setListener( this ); + } + } + + @Override + public void dispose() + { + mSquelchStateListener = null; + mAudioPacketListener = null; + } + + @Override + public void init() + { + mAudioMetadata.reset(); + } + + /** + * Processes demodulated audio samples into audio packets with current audio + * metadata and sends to the registered listener + */ + @Override + public void receive( RealBuffer buffer ) + { + if( mAudioPacketListener != null && mSquelchState == SquelchState.UNSQUELCH ) + { + float[] audio = buffer.getSamples(); + + mAudioFilter.filter( audio ); + + AudioPacket packet = new AudioPacket( audio, mAudioMetadata.copyOf() ); + + mAudioPacketListener.receive( packet ); + } + } + + @Override + public Listener getRealBufferListener() + { + if( mRemoveDC ) + { + return mDCRemovalFilter; + } + + return this; + } + + @Override + public Listener getMetadataListener() + { + return mAudioMetadata; + } + + @Override + public void setAudioPacketListener( Listener listener ) + { + mAudioPacketListener = listener; + } + + @Override + public void removeAudioPacketListener() + { + mAudioPacketListener = null; + } + + @Override + public Listener getChannelEventListener() + { + return mChannelEventListener; + } + + @Override + public Listener getSquelchStateListener() + { + return mSquelchStateListener; + } + + /** + * Wrapper for channel event listener. Responds to channel state reset + * events to remove/cleanup current audio metadata + */ + public class ChannelEventListener implements Listener + { + @Override + public void receive( ChannelEvent event ) + { + switch( event.getEvent() ) + { + case CHANNEL_STATE_RESET: + mAudioMetadata.reset(); + break; + case CHANGE_SELECTED: + mAudioMetadata.setSelected( event.getChannel().isSelected() ); + break; + default: + break; + } + } + } + + /** + * Wrapper for squelch state listener + */ + public class SquelchStateListener implements Listener + { + @Override + public void receive( SquelchState state ) + { + mSquelchState = state; + } + } +} diff --git a/src/audio/AudioOutputImpl.java b/src/audio/AudioOutputImpl.java deleted file mode 100644 index 78d26ad0..00000000 --- a/src/audio/AudioOutputImpl.java +++ /dev/null @@ -1,124 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package audio; - -import java.nio.ByteBuffer; - -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.LineUnavailableException; -import javax.sound.sampled.SourceDataLine; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.real.RealSampleListener; -import audio.inverted.AudioAdapter; -import audio.inverted.AudioType; -import audio.inverted.IAudioTypeListener; -import buffer.FloatSampleBufferAssembler; - -public class AudioOutputImpl implements RealSampleListener, - SquelchListener, IAudioTypeListener, IAudioOutput -{ - private final static Logger mLog = - LoggerFactory.getLogger( AudioOutputImpl.class ); - private static final int sAUDIO_BLOCK_SIZE = 24000; - - private static final AudioFormat sAUDIO_FORMAT = - new AudioFormat( 48000, //SampleRate - 16, //Sample Size - 1, //Channels - true, //Signed - false ); //Little Endian - - private AudioAdapter mAdapter = new AudioAdapter( 48000 ); - - private FloatSampleBufferAssembler mBuffer = - new FloatSampleBufferAssembler( sAUDIO_BLOCK_SIZE ); - private SourceDataLine mOutput; - - private boolean mEnabled = false; - private SquelchState mSquelchState = SquelchState.SQUELCH; - - public AudioOutputImpl( String channelName ) - { - try - { - mOutput = AudioSystem.getSourceDataLine( sAUDIO_FORMAT ); - mOutput.open( sAUDIO_FORMAT, (int)( sAUDIO_FORMAT.getSampleRate() ) ); - mOutput.start(); - } - catch ( LineUnavailableException e ) - { - mLog.error( "AudioOutput - couldn't open audio speakers " - + "for playback", e ); - } - } - - @Override - public void setSquelch( SquelchState state ) - { - mSquelchState = state; - } - - @Override - public void setAudioPlaybackEnabled( boolean enabled ) - { - mEnabled = enabled; - } - - @Override - public void dispose() - { - mOutput.stop(); - mOutput.close(); - mOutput = null; - } - - @Override - public void receive( float sample ) - { - float outputSample = mAdapter.get( sample ); - - if( mEnabled && mSquelchState == SquelchState.UNSQUELCH ) - { - mBuffer.put( outputSample ); - } - else - { - /* Use 0-valued samples when we're disabled or squelched */ - mBuffer.put( 0.0f ); - } - - if( !mBuffer.hasRemaining() ) - { - ByteBuffer buffer = mBuffer.get(); - - mOutput.write( buffer.array(), 0, buffer.array().length ); - - mBuffer.reset(); - } - } - - @Override - public void setAudioType( AudioType type ) - { - mAdapter.setAudioType( type ); - } -} diff --git a/src/audio/AudioOutputManager.java b/src/audio/AudioOutputManager.java deleted file mode 100644 index b85d7134..00000000 --- a/src/audio/AudioOutputManager.java +++ /dev/null @@ -1,25 +0,0 @@ -package audio; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class AudioOutputManager -{ - private final static Logger mLog = - LoggerFactory.getLogger( AudioOutputManager.class ); - - /** - * Handles all audio produced by the decoding channels and routes audio - * packets to any combination of outputs based on any alias audio routing - * options specified by the user. - */ - public AudioOutputManager() - { - - } - - public static void main( String[] args ) - { - - } -} diff --git a/src/audio/AudioPacket.java b/src/audio/AudioPacket.java index 17de2efa..2989fa59 100644 --- a/src/audio/AudioPacket.java +++ b/src/audio/AudioPacket.java @@ -1,29 +1,34 @@ package audio; +import audio.metadata.AudioMetadata; + public class AudioPacket { - private String mSource; private Type mType; - private byte[] mAudioData; - private int mPriority; + private float[] mAudioData; + private AudioMetadata mAudioMetadata; - public AudioPacket( String source, Type type ) + public AudioPacket( Type type ) { - mSource = source; mType = type; } - public AudioPacket( String source, byte[] audio, int priority ) + public AudioPacket( float[] audio, AudioMetadata metadata ) { - this( source, Type.AUDIO ); + this( Type.AUDIO ); mAudioData = audio; - mPriority = priority; + mAudioMetadata = metadata; } - public String getSource() + public AudioMetadata getMetadata() { - return mSource; + return mAudioMetadata; + } + + public void setMetadata( AudioMetadata metadata ) + { + mAudioMetadata = metadata; } public Type getType() @@ -31,16 +36,11 @@ public class AudioPacket return mType; } - public byte[] getAudioData() + public float[] getAudioData() { return mAudioData; } - public int getPriority() - { - return mPriority; - } - public enum Type { AUDIO, diff --git a/src/audio/AudioPacketRateCounter.java b/src/audio/AudioPacketRateCounter.java new file mode 100644 index 00000000..cb888ab5 --- /dev/null +++ b/src/audio/AudioPacketRateCounter.java @@ -0,0 +1,56 @@ +package audio; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; + +public class AudioPacketRateCounter implements Listener +{ + private final static Logger mLog = + LoggerFactory.getLogger( AudioPacketRateCounter.class ); + + private Listener mListener; + private long mCount; + private long mStartTime = -1; + + private String mName; + + public AudioPacketRateCounter() + { + mName = "UNNAMED"; + } + + public AudioPacketRateCounter( String name ) + { + mName = name; + } + + @Override + public void receive( AudioPacket packet ) + { + if( mStartTime == -1 ) + { + mStartTime = System.currentTimeMillis(); + } + + mCount += packet.getAudioData().length; + + long elapsed = System.currentTimeMillis() - mStartTime; + + if( elapsed != 0 ) + { + mLog.debug( "[" + mName + "] Audio Rate: " + ( mCount / elapsed ) + " kHz" ); + } + + if( mListener != null ) + { + mListener.receive( packet ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } +} diff --git a/src/audio/AudioPanel.java b/src/audio/AudioPanel.java new file mode 100644 index 00000000..f62b733b --- /dev/null +++ b/src/audio/AudioPanel.java @@ -0,0 +1,120 @@ +package audio; + +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.sound.sampled.BooleanControl; +import javax.swing.JButton; +import javax.swing.JPanel; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import settings.SettingsManager; + +public class AudioPanel extends JPanel implements Listener +{ + private static final long serialVersionUID = 1L; + + private static final Logger mLog = LoggerFactory.getLogger( AudioPanel.class ); + + private SettingsManager mSettingsManager; + private IAudioController mController; + private MuteButton mMuteButton; + + public AudioPanel( SettingsManager settingsManager, IAudioController controller ) + { + mSettingsManager = settingsManager; + mController = controller; + + mMuteButton = new MuteButton( mController ); + + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]") ); + setBackground( Color.BLACK ); + + add( mMuteButton, "span" ); + + for( String channel: mController.getAudioChannels() ) + { + add( new AudioChannelPanel( mSettingsManager, mController, channel ), "span" ); + } + } + + public int getPreferredHeight() + { + return mMuteButton.getHeight() + + ( mController.getAudioChannels().size() * 20 ); + } + + @Override + public void receive( AudioEvent event ) + { + if( event.getType() == AudioEvent.Type.AUDIO_CONFIGURATION_CHANGED ) + { + mLog.debug( "Audio configuration change detected" ); + } + } + + public class MuteButton extends JButton + { + private IAudioController mController; + + private boolean mMuted = false; + + public MuteButton( IAudioController controller ) + { + super( "Mute Audio" ); + + setBackground( Color.GREEN ); + + mController = controller; + + addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + mMuted = !mMuted; + + for( String channel: mController.getAudioChannels() ) + { + try + { + BooleanControl control = mController.getMuteControl( channel ); + + if( control != null ) + { + control.setValue( mMuted ); + } + } + catch ( AudioException e1 ) + { + mLog.error( "Couldn't obtain mute control for " + + "channel [" + channel + "] from audio manager", e1 ); + } + } + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + MuteButton.this.setText( ( mMuted ? "Unmute Audio" : "Mute Audio" ) ); + MuteButton.this.setBackground( ( mMuted ? Color.RED : Color.GREEN ) ); + } + } ); + } + } ); + } + } +} diff --git a/src/audio/IAudioController.java b/src/audio/IAudioController.java new file mode 100644 index 00000000..eeb324e6 --- /dev/null +++ b/src/audio/IAudioController.java @@ -0,0 +1,36 @@ +package audio; + +import java.util.List; + +import javax.sound.sampled.BooleanControl; +import javax.sound.sampled.FloatControl; + +import sample.Listener; +import audio.metadata.AudioMetadata; + +/** + * Interface for controlling one or more audio channels + */ +public interface IAudioController +{ + public List getAudioChannels(); + + public BooleanControl getMuteControl( String channel ) throws AudioException; + + public boolean hasMuteControl( String channel ); + + public FloatControl getGainControl( String channel ) throws AudioException; + + public boolean hasGainControl( String channel ); + + public void addAudioEventListener( String channel, Listener listener ) + throws AudioException; + + public void removeAudioEventListener( String channel, Listener listener ); + + public void setAudioMetadataListener( String channel, Listener listener ); + + public void removeAudioMetadataListener( String channel ); + + public void setConfigurationChangeListener( Listener listener ); +} diff --git a/src/audio/IAudioOutput.java b/src/audio/IAudioOutput.java deleted file mode 100644 index 790f45ee..00000000 --- a/src/audio/IAudioOutput.java +++ /dev/null @@ -1,29 +0,0 @@ -package audio; - -import audio.SquelchListener.SquelchState; -import audio.inverted.AudioType; - -public interface IAudioOutput -{ - /** - * Squelch / Unsquelch state for this channel - */ - public abstract void setSquelch( SquelchState state ); - - /** - * Audio playback defines if this channel is currently playing on the - * system speakers. - */ - public abstract void setAudioPlaybackEnabled( boolean enabled ); - - /** - * Dispose of any system resources for this audio output - */ - public abstract void dispose(); - - /** - * Sets the audio to normal or inverted output - */ - public abstract void setAudioType( AudioType type ); - -} \ No newline at end of file diff --git a/src/audio/IAudioPacketProvider.java b/src/audio/IAudioPacketProvider.java new file mode 100644 index 00000000..47f4b0be --- /dev/null +++ b/src/audio/IAudioPacketProvider.java @@ -0,0 +1,9 @@ +package audio; + +import sample.Listener; + +public interface IAudioPacketProvider +{ + public void setAudioPacketListener( Listener listener ); + public void removeAudioPacketListener(); +} diff --git a/src/audio/inverted/AudioAdapter.java b/src/audio/inverted/AudioAdapter.java index 2d07a4e8..3c4b737b 100644 --- a/src/audio/inverted/AudioAdapter.java +++ b/src/audio/inverted/AudioAdapter.java @@ -88,7 +88,9 @@ public class AudioAdapter default: retVal = (float)( mPostInversionFilter.get( mAudioHighPassFilter.get( sample ) * - mSineWaveGenerator.nextFloat() ) ); + mSineWaveGenerator.getFloat() ) ); + + mSineWaveGenerator.rotate(); break; } diff --git a/src/audio/inverted/AudioInverter.java b/src/audio/inverted/AudioInverter.java index 26408556..c3b22236 100644 --- a/src/audio/inverted/AudioInverter.java +++ b/src/audio/inverted/AudioInverter.java @@ -38,7 +38,7 @@ public class AudioInverter implements RealSampleListener mSineWaveGenerator = new Oscillator( inversionFrequency, sampleRate ); mPostInversionLowPassFilter = new FloatFIRFilter( - Filters.FIRLP_55TAP_48000FS_3000FC.getCoefficients(), 1.04 ); + Filters.FIRLP_55TAP_48000FS_3000FC.getCoefficients(), 1.04f ); mPostInversionLowPassFilter.setListener( new FilteredSampleProcessor() ); } @@ -61,7 +61,9 @@ public class AudioInverter implements RealSampleListener if( mPostInversionLowPassFilter != null ) { mPostInversionLowPassFilter.receive( - sample * mSineWaveGenerator.nextFloat() ); + sample * mSineWaveGenerator.getFloat() ); + + mSineWaveGenerator.rotate(); } } diff --git a/src/audio/metadata/AudioMetadata.java b/src/audio/metadata/AudioMetadata.java new file mode 100644 index 00000000..a75da2fe --- /dev/null +++ b/src/audio/metadata/AudioMetadata.java @@ -0,0 +1,180 @@ +package audio.metadata; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import alias.Metadata; +import alias.MetadataType; + +public class AudioMetadata implements Listener +{ + protected static final Logger mLog = LoggerFactory.getLogger( AudioMetadata.class ); + + private int mSource; + private int mPriority = 99; + private boolean mSelected = false; + private List mMetadata = new ArrayList<>(); + private boolean mUpdated = false; + + public AudioMetadata( int source ) + { + mSource = source; + } + + public AudioMetadata copyOf() + { + AudioMetadata copy = new AudioMetadata( mSource ); + + copy.mPriority = mPriority; + copy.mSelected = mSelected; + copy.mMetadata.addAll( mMetadata ); + + if( mUpdated ) + { + copy.mUpdated = true; + + mUpdated = false; + } + + return copy; + } + + /** + * Removes any accumulated temporal metadata and returns to a default set. + */ + public void reset() + { + Iterator it = mMetadata.iterator(); + + while( it.hasNext() ) + { + if( it.next().isTemporal() ) + { + it.remove(); + } + } + + mUpdated = true; + } + + /** + * Indicates if this audio metadata contains updated information. The flag + * will only be set when new data is added and will be reset by a copyOf() + * method invocation. + */ + public boolean isUpdated() + { + return mUpdated; + } + + /** + * Adds the metadata. Any temporal metadata will be removed when the + * dispose() method is invoked. + */ + @Override + public void receive( Metadata metadata ) + { + if( metadata.isReset() ) + { + reset(); + } + else + { + mMetadata.add( metadata ); + + if( metadata.hasAlias() ) + { + //TODO: assign priority from alias here + } + } + + mUpdated = true; + } + + /** + * Returns the complete set of metadata + */ + public List getMetadata() + { + return mMetadata; + } + + /** + * Returns the first metadata object matching the type, or null if the + * metadata type is not found. + */ + public Metadata getMetadata( MetadataType type ) + { + for( Metadata metadata: mMetadata ) + { + if( metadata.getMetadataType() == type ) + { + return metadata; + } + } + + return null; + } + + /** + * Source channel ID + */ + public void setSource( int source ) + { + mSource = source; + + mUpdated = true; + } + + public int getSource() + { + return mSource; + } + + /** + * Sets the priority of this audio packet in the range of 1 - Max Integer value + * + * @param priority (1 - Max Int) + */ + public void setPriority( int priority ) + { + assert( 1 <= priority && priority <= Integer.MAX_VALUE ); + + mPriority = priority; + + mUpdated = true; + } + + /** + * Returns the priority of this audio packet or a priority of -1 if the + * audio source is currently selected, so that the priority of this packet + * is higher (lower?) than any other audio source + * + * @return - priority of audio packet where lower numbers are higher priority + */ + public int getPriority() + { + return mSelected ? -1 : mPriority; + } + + /** + * Selected audio channel. A selected channel will override the priority + * level so that the audio can be listened to immediately. + */ + public void setSelected( boolean selected ) + { + mSelected = selected; + + mUpdated = true; + } + + public boolean isSelected() + { + return mSelected; + } +} diff --git a/src/audio/metadata/IMetadataListener.java b/src/audio/metadata/IMetadataListener.java new file mode 100644 index 00000000..a4672515 --- /dev/null +++ b/src/audio/metadata/IMetadataListener.java @@ -0,0 +1,9 @@ +package audio.metadata; + +import sample.Listener; +import alias.Metadata; + +public interface IMetadataListener +{ + public Listener getMetadataListener(); +} diff --git a/src/audio/metadata/IMetadataProvider.java b/src/audio/metadata/IMetadataProvider.java new file mode 100644 index 00000000..afb2ebdc --- /dev/null +++ b/src/audio/metadata/IMetadataProvider.java @@ -0,0 +1,10 @@ +package audio.metadata; + +import sample.Listener; +import alias.Metadata; + +public interface IMetadataProvider +{ + public void setMetadataListener( Listener listener ); + public void removeMetadataListener(); +} diff --git a/src/audio/output/AudioOutput.java b/src/audio/output/AudioOutput.java new file mode 100644 index 00000000..9b81abf3 --- /dev/null +++ b/src/audio/output/AudioOutput.java @@ -0,0 +1,84 @@ +package audio.output; + +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.sound.sampled.BooleanControl; +import javax.sound.sampled.FloatControl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Broadcaster; +import sample.Listener; +import audio.AudioEvent; +import audio.AudioPacket; +import audio.metadata.AudioMetadata; + +public abstract class AudioOutput implements Listener +{ + private final static Logger mLog = LoggerFactory.getLogger( AudioOutput.class ); + + private AtomicBoolean mSquelched = new AtomicBoolean( false ); + private Listener mAudioMetadataListener; + protected Broadcaster mAudioEventBroadcaster = new Broadcaster<>(); + + public void dispose() + { + mAudioEventBroadcaster.dispose(); + mAudioEventBroadcaster = null; + mAudioMetadataListener = null; + } + + public abstract String getChannelName(); + + public abstract BooleanControl getMuteControl(); + public abstract boolean hasMuteControl(); + + public abstract FloatControl getGainControl(); + public abstract boolean hasGainControl(); + + protected void broadcast( AudioMetadata metadata ) + { + if( mAudioMetadataListener != null ) + { + mAudioMetadataListener.receive( metadata ); + } + } + + public void setSquelched( boolean squelched ) + { + mSquelched.set( squelched ); + } + + public boolean isSquelched() + { + return mSquelched.get(); + } + + /** + * Registers a single listener to receive audio start and audio stop events + */ + public void addAudioEventListener( Listener listener ) + { + mAudioEventBroadcaster.addListener( listener ); + } + + public void removeAudioEventListener( Listener listener ) + { + mAudioEventBroadcaster.removeListener( listener ); + } + + /** + * Registers a single listener to receive the audio metadata from each + * audio packet + */ + public void setAudioMetadataListener( Listener listener ) + { + mAudioMetadataListener = listener; + } + + public void removeAudioMetadataListener() + { + mAudioMetadataListener = null; + } +} diff --git a/src/audio/MonoAudioOutput.java b/src/audio/output/MonoAudioOutput.java similarity index 60% rename from src/audio/MonoAudioOutput.java rename to src/audio/output/MonoAudioOutput.java index 2148509f..eb16ddb3 100644 --- a/src/audio/MonoAudioOutput.java +++ b/src/audio/output/MonoAudioOutput.java @@ -1,5 +1,8 @@ -package audio; +package audio.output; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; @@ -9,20 +12,25 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.BooleanControl; +import javax.sound.sampled.FloatControl; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Listener; +import audio.AudioEvent; +import audio.AudioFormats; +import audio.AudioPacket; import audio.AudioPacket.Type; +import controller.NamingThreadFactory; /** * Audio output with automatic flow control based on the availability of * audio data packets. */ -public class MonoAudioOutput implements Listener +public class MonoAudioOutput extends AudioOutput { private final static Logger mLog = LoggerFactory.getLogger( MonoAudioOutput.class ); @@ -43,15 +51,24 @@ public class MonoAudioOutput implements Listener private ScheduledExecutorService mExecutorService; private SourceDataLine mOutput; + private FloatControl mGainControl; + private BooleanControl mMuteControl; private boolean mCanProcessAudio; - private AtomicBoolean mMuted = new AtomicBoolean( true ); - private AtomicBoolean mSquelched = new AtomicBoolean( false ); - + private long mLastBufferReceived; + + private AudioEvent mAudioStartEvent; + private AudioEvent mAudioStopEvent; + public MonoAudioOutput() { - try + super(); + + mAudioStartEvent = new AudioEvent( AudioEvent.Type.AUDIO_STARTED, getChannelName() ); + mAudioStopEvent = new AudioEvent( AudioEvent.Type.AUDIO_STOPPED, getChannelName() ); + + try { mOutput = AudioSystem.getSourceDataLine( AudioFormats.PCM_SIGNED_48KHZ_16BITS ); @@ -62,7 +79,14 @@ public class MonoAudioOutput implements Listener mCanProcessAudio = true; - mExecutorService = Executors.newSingleThreadScheduledExecutor(); + mGainControl = (FloatControl)mOutput.getControl( + FloatControl.Type.MASTER_GAIN ); + + mMuteControl = (BooleanControl)mOutput.getControl( + BooleanControl.Type.MUTE ); + + mExecutorService = Executors.newSingleThreadScheduledExecutor( + new NamingThreadFactory( "audio (mono) output" ) ); /* Run the queue processor task every 40 milliseconds */ mExecutorService.scheduleAtFixedRate( new QueueProcessor(), @@ -76,19 +100,24 @@ public class MonoAudioOutput implements Listener } } - public void setMuted( boolean muted ) + @Override + public String getChannelName() { - mMuted.set( muted ); + return "Mono"; } - public void setSquelched( boolean squelched ) + /** + * Timestamp when most recent buffer was received + */ + public long getLastBufferReceived() { - mSquelched.set( squelched ); + return mLastBufferReceived; } - - + public void dispose() { + super.dispose(); + mCanProcessAudio = false; mBuffer.clear(); @@ -109,10 +138,13 @@ public class MonoAudioOutput implements Listener { if( mCanProcessAudio ) { + mLastBufferReceived = System.currentTimeMillis(); + mBuffer.add( packet ); } } + public class QueueProcessor implements Runnable { private AtomicBoolean mProcessing = new AtomicBoolean(); @@ -131,23 +163,39 @@ public class MonoAudioOutput implements Listener List packets = new ArrayList(); mBuffer.drainTo( packets ); - + if( packets.size() > 0 ) { for( AudioPacket packet: packets ) { if( mCanProcessAudio ) { - if( mMuted.get() || mSquelched.get() ) + if( isSquelched() ) { checkStop(); } else if( packet.getType() == Type.AUDIO ) { - mOutput.write( packet.getAudioData(), 0, - packet.getAudioData().length ); + float[] samples = packet.getAudioData(); + + /* Little-endian byte buffer */ + ByteBuffer buffer = + ByteBuffer.allocate( samples.length * 2 ) + .order( ByteOrder.LITTLE_ENDIAN ); + + ShortBuffer shortBuffer = buffer.asShortBuffer(); + + for( float sample: samples ) + { + shortBuffer.put( (short)( sample * Short.MAX_VALUE ) ); + } + + mOutput.write( buffer.array(), 0, + buffer.array().length ); checkStart(); + + broadcast( packet.getMetadata() ); } } } @@ -163,21 +211,50 @@ public class MonoAudioOutput implements Listener private void checkStart() { - if( mCanProcessAudio && !mMuted.get() && !mSquelched.get() && + if( mCanProcessAudio && !isSquelched() && !mOutput.isRunning() && mOutput.available() <= START_THRESHOLD ) { mOutput.start(); + + mAudioEventBroadcaster.broadcast( mAudioStartEvent ); } } private void checkStop() { if( mCanProcessAudio && mOutput.isRunning() && - ( mOutput.available() >= STOP_THRESHOLD || mMuted.get() || mSquelched.get() ) ) + ( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) ) { mOutput.drain(); mOutput.stop(); + + mAudioEventBroadcaster.broadcast( mAudioStopEvent ); } } } + + + @Override + public BooleanControl getMuteControl() + { + return mMuteControl; + } + + @Override + public boolean hasMuteControl() + { + return mMuteControl != null; + } + + @Override + public FloatControl getGainControl() + { + return mGainControl; + } + + @Override + public boolean hasGainControl() + { + return mGainControl != null; + } } diff --git a/src/audio/squelch/ISquelchStateListener.java b/src/audio/squelch/ISquelchStateListener.java new file mode 100644 index 00000000..d5738f51 --- /dev/null +++ b/src/audio/squelch/ISquelchStateListener.java @@ -0,0 +1,8 @@ +package audio.squelch; + +import sample.Listener; + +public interface ISquelchStateListener +{ + public Listener getSquelchStateListener(); +} diff --git a/src/audio/squelch/ISquelchStateProvider.java b/src/audio/squelch/ISquelchStateProvider.java new file mode 100644 index 00000000..93b435f2 --- /dev/null +++ b/src/audio/squelch/ISquelchStateProvider.java @@ -0,0 +1,9 @@ +package audio.squelch; + +import sample.Listener; + +public interface ISquelchStateProvider +{ + public void setSquelchStateListener( Listener listener ); + public void removeSquelchStateListener(); +} diff --git a/src/audio/squelch/ManualSquelchController.java b/src/audio/squelch/ManualSquelchController.java new file mode 100644 index 00000000..cce0ed53 --- /dev/null +++ b/src/audio/squelch/ManualSquelchController.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ + +package audio.squelch; + + +/** + * Provides squelch control based on presense of decoded audio samples. + * + * NOTE: this class is not yet fully implemented with audio sample processing + * and currently only provides user driven squelch control + */ +public class ManualSquelchController extends SquelchController +{ + public ManualSquelchController() + { + mSquelchMode = SquelchMode.NONE; + } + + @Override + public void setSquelchMode( SquelchMode mode ) + { + //TODO: add functionality + } +} diff --git a/src/audio/squelch/SquelchController.java b/src/audio/squelch/SquelchController.java new file mode 100644 index 00000000..af4eaec9 --- /dev/null +++ b/src/audio/squelch/SquelchController.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ + +package audio.squelch; + +import sample.Broadcaster; +import sample.Listener; +import sample.real.RealBuffer; +import sample.real.RealSampleListener; +import sample.real.RealToRealBufferAssembler; + +/** + * Squelch control - receives single audio samples or real audio buffers and + * adjusts squelch state either based on the audio samples or based on external + * control inputs. + * + * Assembles received single audio samples into real sample buffers for + * consumption by an external audio processor, like the AudioPacketProcessor + * class. + */ +public abstract class SquelchController + implements Listener, RealSampleListener +{ + private Broadcaster mSquelchStateBroadcaster = new Broadcaster<>(); + private Listener mAudioListener; + protected SquelchState mSquelchState = SquelchState.UNSQUELCH; + protected SquelchMode mSquelchMode = SquelchMode.MANUAL; + + private RealToRealBufferAssembler mSampleAssembler = new RealToRealBufferAssembler( 512 ); + + public SquelchController() + { + mSampleAssembler.setListener( this ); + } + + @Override + public void receive( float sample ) + { + mSampleAssembler.receive( sample ); + } + + @Override + public void receive( RealBuffer buffer ) + { + if( mAudioListener != null ) + { + mAudioListener.receive( buffer ); + } + } + + public abstract void setSquelchMode( SquelchMode mode ); + + public SquelchMode getSquelchMode() + { + return mSquelchMode; + } + + public void setSquelchState( SquelchState state ) + { + mSquelchState = state; + } + + public SquelchState getSquelchState() + { + return mSquelchState; + } + + public boolean isSquelched() + { + switch( mSquelchMode ) + { + case AUTOMATIC: + case MANUAL: + return mSquelchState == SquelchState.SQUELCH; + case NONE: + return false; + default: + return false; + } + } + + public void addSquelchStateListener( Listener listener ) + { + if( listener != null ) + { + mSquelchStateBroadcaster.addListener( listener ); + + /* Send current squelch state to the listener */ + listener.receive( mSquelchState ); + } + } + + public void removeSquelchStateListener( Listener listener ) + { + mSquelchStateBroadcaster.removeListener( listener ); + } + + public void setAudioListener( Listener listener ) + { + mAudioListener = listener; + } + + public void removeAudioListener( Listener listener ) + { + mAudioListener = null; + } +} diff --git a/src/audio/squelch/SquelchMode.java b/src/audio/squelch/SquelchMode.java new file mode 100644 index 00000000..d5f09b9d --- /dev/null +++ b/src/audio/squelch/SquelchMode.java @@ -0,0 +1,25 @@ +package audio.squelch; + +/** + * Squelch state for a decoding channel. The state determines the handling of + * any audio packets processed by the channel state. + */ +public enum SquelchMode +{ + /** + * Automatic: decoded message activity dictates squelch state and determines + * if audio is passed + */ + AUTOMATIC, + + /** + * Manually applied squelch noise level threshold determines if audio is + * passed. + */ + MANUAL, + + /** + * No Squelch - no squelch is applied and all audio is passed + */ + NONE; +} diff --git a/src/audio/squelch/SquelchState.java b/src/audio/squelch/SquelchState.java new file mode 100644 index 00000000..1093eae8 --- /dev/null +++ b/src/audio/squelch/SquelchState.java @@ -0,0 +1,7 @@ +package audio.squelch; + +public enum SquelchState +{ + SQUELCH, + UNSQUELCH; +} diff --git a/src/decode/tait/Tait1200ChannelState.java b/src/audio/squelch/TrunkingSquelchController.java similarity index 50% rename from src/decode/tait/Tait1200ChannelState.java rename to src/audio/squelch/TrunkingSquelchController.java index 5f85b17d..18d50341 100644 --- a/src/decode/tait/Tait1200ChannelState.java +++ b/src/audio/squelch/TrunkingSquelchController.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,49 +15,41 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; -import message.Message; -import controller.state.AuxChannelState; -import controller.state.ChannelState; +package audio.squelch; -public class Tait1200ChannelState extends AuxChannelState +public class TrunkingSquelchController extends SquelchController { - private Tait1200ActivitySummary mActivitySummary; - - public Tait1200ChannelState( ChannelState parentState ) + private boolean mAllowSquelchOverride = true; + + public TrunkingSquelchController() { - super( parentState ); + } + + /** + * Provides control over audio stream as directed by an external trunking + * channel state. + * + * Override automatic squelch control by setting the squelch mode to none. + * + * Disable automatic control override by setting allowManualOverride to false; + * + * Note: this control does not respond to squelch mode manual. + */ + public TrunkingSquelchController( boolean allowSquelchOverride ) + { + mAllowSquelchOverride = allowSquelchOverride; - mActivitySummary = - new Tait1200ActivitySummary( parentState.getAliasList() ); + mSquelchMode = SquelchMode.AUTOMATIC; } - + @Override - public void reset() + public void setSquelchMode( SquelchMode mode ) { - } - - @Override - public void fade() - { - } - - @Override - public void receive( Message message ) - { - if( message instanceof Tait1200GPSMessage ) + if( mode == SquelchMode.AUTOMATIC || + ( mode == SquelchMode.NONE && mAllowSquelchOverride ) ) { - mActivitySummary.receive( message ); - -// mParentChannelState.receiveCallEvent( -// Tait1200CallEvent.getLJ1200Event( (Tait1200Message)message ) ); + mSquelchMode = mode; } - } - - @Override - public String getActivitySummary() - { - return mActivitySummary.getSummary(); - } + } } diff --git a/src/buffer/ComplexByteBuffer.java b/src/buffer/ComplexByteBuffer.java index 0fc4e95b..1abb25dc 100644 --- a/src/buffer/ComplexByteBuffer.java +++ b/src/buffer/ComplexByteBuffer.java @@ -20,14 +20,14 @@ package buffer; import java.nio.ByteBuffer; import java.util.Enumeration; -import sample.complex.ComplexSample; +import sample.complex.Complex; /** * Provides a complex sample enumeration wrapper around a byte array containing * interleaved byte-length I & Q sample values in the range of 0 - 255, converting * those samples into complex samples with value range of -128 to 0 to 127. */ -public class ComplexByteBuffer implements Enumeration +public class ComplexByteBuffer implements Enumeration { private static float[] mValues; @@ -81,11 +81,11 @@ public class ComplexByteBuffer implements Enumeration * method to ensure you do not read past the end of the buffer. */ @Override - public ComplexSample nextElement() + public Complex nextElement() { // return new ComplexSample( mValues[ ( mBytes[ mPointer++ ] & 0xFF ) ], // mValues[ ( mBytes[ mPointer++ ] & 0xFF ) ] ); - return new ComplexSample( mValues[ ( mBytes.get() & 0xFF ) ], + return new Complex( mValues[ ( mBytes.get() & 0xFF ) ], mValues[ ( mBytes.get() & 0xFF ) ] ); } } diff --git a/src/buffer/ComplexCircularBuffer.java b/src/buffer/ComplexCircularBuffer.java index d782ea31..f375a658 100644 --- a/src/buffer/ComplexCircularBuffer.java +++ b/src/buffer/ComplexCircularBuffer.java @@ -19,7 +19,7 @@ package buffer; import java.util.Arrays; -import sample.complex.ComplexSample; +import sample.complex.Complex; /** * Circular sample buffer - allocates a buffer and stores samples in a circular @@ -30,14 +30,14 @@ import sample.complex.ComplexSample; */ public class ComplexCircularBuffer { - ComplexSample[] mBuffer; + Complex[] mBuffer; int mBufferPointer = 0; public ComplexCircularBuffer( int size ) { - mBuffer = new ComplexSample[ size ]; + mBuffer = new Complex[ size ]; - Arrays.fill( mBuffer, new ComplexSample( 1.0f, 1.0f ) ); + Arrays.fill( mBuffer, new Complex( 1.0f, 1.0f ) ); } /** @@ -46,16 +46,16 @@ public class ComplexCircularBuffer * @param sample * @return */ - public ComplexSample get( ComplexSample sample ) + public Complex get( Complex sample ) { - ComplexSample retVal = mBuffer[ mBufferPointer ]; + Complex retVal = mBuffer[ mBufferPointer ]; put( sample ); return retVal; } - public void put( ComplexSample sample ) + public void put( Complex sample ) { mBuffer[ mBufferPointer ] = sample; @@ -67,9 +67,9 @@ public class ComplexCircularBuffer } } - public ComplexSample[] get( int count ) + public Complex[] get( int count ) { - ComplexSample[] samples = new ComplexSample[ count ]; + Complex[] samples = new Complex[ count ]; int tempCounter = mBufferPointer; diff --git a/src/buffer/ComplexSampleBufferAssembler.java b/src/buffer/ComplexSampleBufferAssembler.java index 8931d4d7..935696f1 100644 --- a/src/buffer/ComplexSampleBufferAssembler.java +++ b/src/buffer/ComplexSampleBufferAssembler.java @@ -21,7 +21,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class ComplexSampleBufferAssembler { @@ -41,7 +41,7 @@ public class ComplexSampleBufferAssembler * Load the sample's bytes, left then right, little endian, into the * backing byte array */ - public void put( ComplexSample sample ) + public void put( Complex sample ) { mByteBuffer.putShort( (short)( sample.left() * Short.MAX_VALUE ) ); mByteBuffer.putShort( (short)( sample.right() * Short.MAX_VALUE ) ); diff --git a/src/buffer/ComplexTappedCircularBuffer.java b/src/buffer/ComplexTappedCircularBuffer.java index 1fad24ef..214738d3 100644 --- a/src/buffer/ComplexTappedCircularBuffer.java +++ b/src/buffer/ComplexTappedCircularBuffer.java @@ -17,7 +17,7 @@ ******************************************************************************/ package buffer; -import sample.complex.ComplexSample; +import sample.complex.Complex; /** * Implements a FIFO circular buffer for complex samples that provides access @@ -26,17 +26,17 @@ import sample.complex.ComplexSample; */ public class ComplexTappedCircularBuffer { - private ComplexSample[] mBuffer; + private Complex[] mBuffer; private int mBufferPointer = 0; public ComplexTappedCircularBuffer( int length ) { - mBuffer = new ComplexSample[ length ]; + mBuffer = new Complex[ length ]; //Pre-load the buffer with 0-valued samples for( int x = 0; x < length; x++ ) { - mBuffer[ x ] = new ComplexSample( 0, 0 ); + mBuffer[ x ] = new Complex( 0, 0 ); } } @@ -44,7 +44,7 @@ public class ComplexTappedCircularBuffer * Adds the sample to the buffer and removes the oldest sample * @param sample */ - public void add( ComplexSample sample ) + public void add( Complex sample ) { mBuffer[ mBufferPointer++ ] = sample; @@ -68,7 +68,7 @@ public class ComplexTappedCircularBuffer * @throws ArrayIndexOutOfBoundsException if you attempt to access a sample * outside of the buffer size */ - public ComplexSample get( int index ) + public Complex get( int index ) { /* Since the samples are stored in a forward fashion, we have to access * the samples in reverse to comply with the access contract, where diff --git a/src/buffer/ShortComplexByteBuffer.java b/src/buffer/ShortComplexByteBuffer.java index 2a613797..2aff0558 100644 --- a/src/buffer/ShortComplexByteBuffer.java +++ b/src/buffer/ShortComplexByteBuffer.java @@ -21,9 +21,9 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Enumeration; -import sample.complex.ComplexSample; +import sample.complex.Complex; -public class ShortComplexByteBuffer implements Enumeration +public class ShortComplexByteBuffer implements Enumeration { private ByteBuffer mByteBuffer; @@ -45,9 +45,9 @@ public class ShortComplexByteBuffer implements Enumeration } @Override - public ComplexSample nextElement() + public Complex nextElement() { - return new ComplexSample( (float)mByteBuffer.getShort(), + return new Complex( (float)mByteBuffer.getShort(), (float)mByteBuffer.getShort() ); } } diff --git a/src/buffer/StereoByteBuffer.java b/src/buffer/StereoByteBuffer.java index 1d7688da..6e4e6db4 100644 --- a/src/buffer/StereoByteBuffer.java +++ b/src/buffer/StereoByteBuffer.java @@ -21,10 +21,10 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Enumeration; -import sample.complex.ComplexSample; +import sample.complex.Complex; import source.mixer.MixerChannel; -public class StereoByteBuffer implements Enumeration +public class StereoByteBuffer implements Enumeration { private ByteBuffer mByteBuffer; @@ -51,9 +51,9 @@ public class StereoByteBuffer implements Enumeration } @Override - public ComplexSample nextElement() + public Complex nextElement() { - return new ComplexSample( (float)mByteBuffer.getShort(), + return new Complex( (float)mByteBuffer.getShort(), (float)mByteBuffer.getShort() ); } } diff --git a/src/controller/ConfigurationControllerModel.java b/src/controller/ConfigurationControllerModel.java index bc24d0c2..8b20b272 100644 --- a/src/controller/ConfigurationControllerModel.java +++ b/src/controller/ConfigurationControllerModel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,6 +29,8 @@ import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreePath; import map.IconManager; +import module.decode.DecoderType; +import module.decode.config.DecodeConfigFactory; import playlist.PlaylistNode; import source.config.SourceConfigTuner; import source.recording.RecordingGroupNode; @@ -37,14 +39,11 @@ import source.tuner.TunerGroupNode; import source.tuner.TunerNode; import source.tuner.TunerSelectionListener; import controller.channel.Channel; -import controller.channel.ChannelEventListener; import controller.channel.ChannelNode; import controller.site.Site; import controller.system.System; import controller.system.SystemListNode; import controller.system.SystemNode; -import decode.DecoderType; -import decode.config.DecodeConfigFactory; public class ConfigurationControllerModel extends DefaultTreeModel { @@ -54,12 +53,8 @@ public class ConfigurationControllerModel extends DefaultTreeModel private ResourceManager mResourceManager; - private ArrayList mChannelConfigChangeListeners = - new ArrayList(); - private ArrayList mTunerSelectionListeners = new ArrayList(); - private PlaylistNode mPlaylistNode; private TunerGroupNode mTunerGroupNode; diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index a68cb667..f7dd9f19 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -21,7 +21,6 @@ import java.awt.Color; import java.awt.Component; import java.awt.Dimension; -import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; @@ -30,23 +29,24 @@ import javax.swing.event.ChangeListener; import map.MapPanel; import message.Message; +import module.decode.event.CallEventPanel; +import module.decode.event.MessageActivityPanel; +import module.decode.state.ChannelList; import net.miginfocom.swing.MigLayout; import sample.Listener; import spectrum.ChannelSpectrumPanel; +import audio.AudioPanel; import com.jidesoft.swing.JideSplitPane; import com.jidesoft.swing.JideTabbedPane; -import controller.activity.CallEventPanel; -import controller.activity.MessageActivityPanel; import controller.channel.ChannelManager; -import controller.state.ChannelStateList; public class ControllerPanel extends JPanel { private static final long serialVersionUID = 1L; - private ChannelStateList mChannelStateList; + private ChannelList mChannelStateList; private CallEventPanel mCallEventPanel = new CallEventPanel(); @@ -135,7 +135,8 @@ public class ControllerPanel extends JPanel /* Channel state list */ - mChannelStateList = new ChannelStateList( mResourceManager.getSettingsManager() ); + mChannelStateList = new ChannelList( mResourceManager.getPlaylistManager(), + mResourceManager.getSettingsManager() ); /* Register each of the components to receive channel events when the * channels are selected or change */ @@ -147,12 +148,20 @@ public class ControllerPanel extends JPanel JScrollPane channelStateListScroll = new JScrollPane(); channelStateListScroll.getViewport().setView( mChannelStateList ); - channelStateListScroll.setPreferredSize( new Dimension( 150, - (int)channelStateListScroll.getPreferredSize().getHeight() ) ); + channelStateListScroll.setPreferredSize( new Dimension( 200, 300 ) ); + AudioPanel audioPanel = new AudioPanel( mResourceManager.getSettingsManager(), + mResourceManager.getAudioManager() ); + audioPanel.setPreferredSize( new Dimension( 200, audioPanel.getPreferredHeight() ) ); + + JideSplitPane audioChannelListSplit = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT ); + audioChannelListSplit.setDividerSize( 5 ); + audioChannelListSplit.add( audioPanel ); + audioChannelListSplit.add( channelStateListScroll ); + JideSplitPane channelSplit = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); channelSplit.setDividerSize( 5 ); - channelSplit.add( channelStateListScroll ); + channelSplit.add( audioChannelListSplit ); channelSplit.add( mTabbedPane ); add( channelSplit ); diff --git a/src/controller/NamingThreadFactory.java b/src/controller/NamingThreadFactory.java new file mode 100644 index 00000000..ae3302b1 --- /dev/null +++ b/src/controller/NamingThreadFactory.java @@ -0,0 +1,48 @@ +package controller; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Thread factory that applies custom names to threads + */ +public class NamingThreadFactory implements ThreadFactory +{ + static final AtomicInteger poolNumber = new AtomicInteger(1); + + final ThreadGroup mThreadGroup; + + final AtomicInteger mThreadNumber = new AtomicInteger(1); + + final String mNamePrefix; + + public NamingThreadFactory( String prefix ) + { + SecurityManager s = System.getSecurityManager(); + + mThreadGroup = (s != null)? s.getThreadGroup() : + Thread.currentThread().getThreadGroup(); + + mNamePrefix = prefix + " pool-" + + poolNumber.getAndIncrement() + + "-thread-"; + } + + public Thread newThread( Runnable runnable ) + { + Thread thread = new Thread(mThreadGroup, runnable, + mNamePrefix + mThreadNumber.getAndIncrement(), 0 ); + + if( thread.isDaemon() ) + { + thread.setDaemon( false ); + } + + if( thread.getPriority() != Thread.NORM_PRIORITY ) + { + thread.setPriority( Thread.NORM_PRIORITY ); + } + + return thread; + } +} \ No newline at end of file diff --git a/src/controller/ResourceManager.java b/src/controller/ResourceManager.java index ee5c1b6f..15e14016 100644 --- a/src/controller/ResourceManager.java +++ b/src/controller/ResourceManager.java @@ -17,7 +17,6 @@ ******************************************************************************/ package controller; -import alias.action.AliasActionManager; import map.MapService; import playlist.PlaylistManager; import record.RecorderManager; @@ -25,6 +24,7 @@ import settings.SettingsManager; import source.SourceManager; import source.recording.RecordingSourceManager; import source.tuner.TunerManager; +import audio.AudioManager; import controller.channel.ChannelManager; import eventlog.EventLogManager; @@ -33,6 +33,7 @@ import eventlog.EventLogManager; */ public class ResourceManager { + private AudioManager mAudioManager; private ChannelManager mChannelManager; private EventLogManager mEventLogManager; private PlaylistManager mPlaylistManager; @@ -67,13 +68,24 @@ public class ResourceManager * ChannelManager requires a reference to the ResourceManager so that * channel processing chains can access system wide resources */ + mThreadPoolManager = new ThreadPoolManager(); + mAudioManager = new AudioManager( mThreadPoolManager ); mChannelManager = new ChannelManager( this ); mSourceManager = new SourceManager( this ); mRecordingSourceManager = new RecordingSourceManager( this ); mTunerManager = new TunerManager( this ); mController = new ConfigurationControllerModel( this ); mMapService = new MapService( this ); - mThreadPoolManager = new ThreadPoolManager(); + } + + public AudioManager getAudioManager() + { + return mAudioManager; + } + + public void setAudioManager( AudioManager audioManager ) + { + mAudioManager = audioManager; } public ConfigurationControllerModel getController() diff --git a/src/controller/ThreadPoolManager.java b/src/controller/ThreadPoolManager.java index 5af34435..b7d5bf41 100644 --- a/src/controller/ThreadPoolManager.java +++ b/src/controller/ThreadPoolManager.java @@ -19,13 +19,22 @@ package controller; import java.util.HashMap; import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ThreadPoolManager { + private final static Logger mLog = LoggerFactory.getLogger( ThreadPoolManager.class ); + private int THREAD_POOL_SIZE = 2; private int MAX_TASK_COUNT = 24; @@ -46,7 +55,8 @@ public class ThreadPoolManager { if( mExecutor == null ) { - mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE ); + mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE, + new NamingThreadFactory( "sdrtrunk" ) ); } if( mTasks.size() <= MAX_TASK_COUNT ) @@ -71,7 +81,8 @@ public class ThreadPoolManager { if( mExecutor == null ) { - mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE ); + mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE, + new NamingThreadFactory( "sdrtrunk" ) ); } if( mTasks.size() <= MAX_TASK_COUNT ) @@ -109,6 +120,7 @@ public class ThreadPoolManager public enum ThreadType { + AUDIO_PROCESSING, SOURCE_SAMPLE_PROCESSING, DECIMATION, DECODER; diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java index 106d507e..08af502d 100644 --- a/src/controller/channel/Channel.java +++ b/src/controller/channel/Channel.java @@ -17,17 +17,13 @@ ******************************************************************************/ package controller.channel; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JSeparator; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @@ -35,12 +31,23 @@ import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.bind.annotation.XmlTransient; import message.Message; +import module.Module; +import module.ProcessingChain; +import module.decode.DecoderFactory; +import module.decode.config.AuxDecodeConfiguration; +import module.decode.config.DecodeConfigFactory; +import module.decode.config.DecodeConfiguration; +import module.decode.event.CallEventModel; +import module.decode.event.MessageActivityModel; +import module.decode.state.ChannelState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import record.config.RecordConfiguration; import sample.Listener; +import source.Source; +import source.SourceException; import source.SourceType; import source.config.SourceConfigFactory; import source.config.SourceConfigRecording; @@ -48,19 +55,15 @@ import source.config.SourceConfigTuner; import source.config.SourceConfiguration; import source.tuner.TunerChannel; import source.tuner.TunerChannel.Type; -import source.tuner.frequency.AutomaticFrequencyControl; +import alias.AliasList; import controller.ResourceManager; -import controller.activity.ActivitySummaryFrame; import controller.channel.ChannelEvent.Event; import controller.config.Configuration; import controller.site.Site; -import controller.state.ChannelState; -import controller.state.ChannelState.State; import controller.system.System; -import decode.config.AuxDecodeConfiguration; -import decode.config.DecodeConfigFactory; -import decode.config.DecodeConfiguration; +import eventlog.MessageEventLogger; import eventlog.config.EventLogConfiguration; +import filter.FilterSet; @XmlSeeAlso( { Configuration.class } ) @XmlRootElement( name = "channel" ) @@ -68,15 +71,16 @@ public class Channel extends Configuration { private final static Logger mLog = LoggerFactory.getLogger( Channel.class ); + private static final boolean ENABLED = true; + private static final boolean DISABLED = false; + private static final boolean BROADCAST_CHANGE = true; + public enum ChannelType { STANDARD, TRAFFIC }; - private static final boolean ENABLED = true; - private static final boolean DISABLED = false; - private static final boolean BROADCAST_CHANGE = true; private CopyOnWriteArrayList mChannelListeners = new CopyOnWriteArrayList(); @@ -84,8 +88,7 @@ public class Channel extends Configuration private CopyOnWriteArrayList> mMessageListeners = new CopyOnWriteArrayList>(); - private HashMap mTrafficChannels = - new HashMap(); + private Map mTrafficChannels = new HashMap<>(); private DecodeConfiguration mDecodeConfiguration = DecodeConfigFactory.getDefaultDecodeConfiguration(); @@ -105,7 +108,15 @@ public class Channel extends Configuration private boolean mEnabled; private boolean mSelected; private ChannelType mChannelType; + + private CallEventModel mCallEventModel; + private MessageActivityModel mMessageActivityModel; + private MessageEventLogger mEventLogger; private ProcessingChain mProcessingChain; + + private ChannelState mChannelState; + private long mTrafficChannelTimeout = + DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS; /** * Constructs a new standard channel with a default name of "New Channel" @@ -135,34 +146,46 @@ public class Channel extends Configuration mChannelType = channelType; } - public ChannelType getChannelType() - { - return mChannelType; - } - /** * Indicates if the channel has Automatic or Direct Frequency Control */ public boolean hasFrequencyControl() { return mProcessingChain != null && - mProcessingChain.getDecoder().hasFrequencyCorrectionControl(); + mProcessingChain.hasFrequencyCorrectionControl(); } public long getFrequencyCorrection() { - if( mProcessingChain != null ) + if( hasFrequencyControl() ) { - return mProcessingChain.getDecoder().getFrequencyCorrection(); + return mProcessingChain.getFrequencyCorrectionControl().getErrorCorrection(); } return 0; } + /** + * Sets the channel state traffic channel call end timer to the specified + * value. + */ + public void setTrafficChannelTimeout( long milliseconds ) + { + mTrafficChannelTimeout = milliseconds; + + if( mChannelState != null ) + { + mChannelState.setTrafficChannelTimeout( milliseconds ); + } + } + /** * Sets the resource manager responsible for this channel and registers * the channel manager as a listener to this channel. Fires channel add * channel event to the newly added channel manager channel listener. + * + * Note: resource manager instance is applied after object construction + * since we construct the channel objects from the xml playlist */ public void setResourceManager( ResourceManager resourceManager ) { @@ -170,7 +193,7 @@ public class Channel extends Configuration /* Add system-wide channel listeners onto this channel */ addListeners( mResourceManager.getChannelManager().getChannelListeners() ); - + /* If we're enabled, fire enable changed event to get processing started */ if( mEnabled ) { @@ -200,7 +223,7 @@ public class Channel extends Configuration */ public boolean isProcessing() { - return mProcessingChain != null && mProcessingChain.isProcessing(); + return mProcessingChain != null && mProcessingChain.processing(); } /** @@ -216,100 +239,51 @@ public class Channel extends Configuration { mSelected = selected; - if( mProcessingChain != null && - mProcessingChain.isProcessing() && - mProcessingChain.getAudioOutput() != null ) - { - mProcessingChain.getAudioOutput().setAudioPlaybackEnabled( selected ); - } - fireChannelEvent( Event.CHANGE_SELECTED ); } - public JMenu getContextMenu() + @XmlTransient + public CallEventModel getCallEventModel() { - JMenu menu = new JMenu( mSite + "-" + mName ); - - if( mEnabled ) - { - JMenuItem disable = new JMenuItem( "Disable" ); - disable.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - setEnabled( DISABLED, BROADCAST_CHANGE ); - getResourceManager().getPlaylistManager().save(); - } - } ); - - menu.add( disable ); - - menu.add( new JSeparator() ); - - JMenuItem actySummaryItem = - new JMenuItem( "Activity Summary" ); - - actySummaryItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - if( mResourceManager != null ) - { - ProcessingChain chain = getProcessingChain(); - - if( chain != null ) - { - ChannelState state = chain.getChannelState(); - - if( state != null ) - { - String summary = state.getActivitySummary(); - - new ActivitySummaryFrame( summary, - mResourceManager.getController().getTree() ); - } - } - } - } - } ); - - menu.add( actySummaryItem ); - } - else - { - JMenuItem enable = new JMenuItem( "Enable" ); - enable.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - setEnabled( ENABLED, BROADCAST_CHANGE ); - getResourceManager().getPlaylistManager().save(); } - } ); - - menu.add( enable ); - } - - menu.add( new JSeparator() ); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - dispose(); - getResourceManager().getPlaylistManager().save(); } - } ); - - menu.add( deleteItem ); - - - return menu; + return mCallEventModel; } + /** + * Returns the MessageEventLogger for this processing chain + */ + @XmlTransient + public MessageEventLogger getEventLogger() + { + return mEventLogger; + } + + /** + * Sets the MessageEventLogger for this processing chain + */ + public void setEventLogger( MessageEventLogger eventLogger ) + { + mEventLogger = eventLogger; + } + + @XmlTransient + public MessageActivityModel getMessageActivityModel() + { + return mMessageActivityModel; + } + + @XmlTransient + public ChannelState getChannelState() + { + return mChannelState; + } + + @XmlTransient + public ChannelType getChannelType() + { + return mChannelType; + } + + /** * Orderly shutdown method when this channel is going to be deleted. */ @@ -393,6 +367,11 @@ public class Channel extends Configuration return mSystem; } + public boolean hasSystem() + { + return mSystem != null; + } + /** * Sets the owning system for this channel and optionally broadcasts a * system change channel event. @@ -415,6 +394,11 @@ public class Channel extends Configuration return mSite; } + public boolean hasSite() + { + return mSite != null; + } + /** * Sets the owning site for this channel and optionally broadcasts a site * change channel event. @@ -468,7 +452,14 @@ public class Channel extends Configuration if( fireChannelEvent ) { - fireChannelEvent( Event.CHANGE_ENABLED ); + if( enabled ) + { + fireChannelEvent( Event.CHANNEL_ENABLED ); + } + else + { + fireChannelEvent( Event.CHANNEL_DISABLED ); + } } } @@ -522,11 +513,6 @@ public class Channel extends Configuration public void setDecodeConfiguration( DecodeConfiguration config ) { mDecodeConfiguration = config; - - if( mProcessingChain != null && mProcessingChain.isRunning() ) - { - mProcessingChain.updateDecoder(); - } } /** @@ -544,11 +530,6 @@ public class Channel extends Configuration public void setAuxDecodeConfiguration( AuxDecodeConfiguration config ) { mAuxDecodeConfiguration = config; - - if( mProcessingChain != null && mProcessingChain.isRunning() ) - { - mProcessingChain.updateDecoder(); - } } /** @@ -581,11 +562,6 @@ public class Channel extends Configuration public void setSourceConfiguration( SourceConfiguration config ) { mSourceConfiguration = config; - - if( mProcessingChain != null && mProcessingChain.isRunning() ) - { - mProcessingChain.updateSource(); - } } /** @@ -618,11 +594,6 @@ public class Channel extends Configuration public void setEventLogConfiguration( EventLogConfiguration config ) { mEventLogConfiguration = config; - - if( mProcessingChain != null && mProcessingChain.isRunning() ) - { - mProcessingChain.updateEventLogging(); - } } /** @@ -655,11 +626,6 @@ public class Channel extends Configuration public void setRecordConfiguration( RecordConfiguration config ) { mRecordConfiguration = config; - - if( mProcessingChain != null && mProcessingChain.isRunning() ) - { - mProcessingChain.updateRecording(); - } } /** @@ -730,38 +696,124 @@ public class Channel extends Configuration */ private void start() { - if( mEnabled && mProcessingChain == null && mResourceManager != null ) + if( mEnabled && mResourceManager != null ) { - mProcessingChain = new ProcessingChain( this, mResourceManager ); + setupChannelState(); + + try + { + setup(); + + mProcessingChain.start(); + + fireChannelEvent( Event.CHANNEL_PROCESSING_STARTED ); + } + catch( SourceException se ) + { + mLog.error( "Error obtaining source for channel" ); + } + } + } + + private void setupChannelState() + { + if( mChannelState == null ) + { + mChannelState = new ChannelState( mResourceManager + .getThreadPoolManager(), mChannelType ); + + mChannelState.setTrafficChannelTimeout( mTrafficChannelTimeout ); + } + } + + /** + * Sets up the channel and prepares to commense processing. Sets up the + * processing chain and requests a source. + * + * @throws SourceException - if no source is available or if there is an + * error obtaining a source + */ + public void setup() throws SourceException + { + setupProcessingChain(); + + setupSource(); + + //TODO: Add logging + //TODO: Add recorder(s) + } + + private void setupProcessingChain() + { + if( mProcessingChain == null ) + { + mProcessingChain = new ProcessingChain( + mResourceManager.getThreadPoolManager() ); + + /* Get the optional alias list for the decode modules to use */ + AliasList aliasList = mResourceManager.getPlaylistManager() + .getPlayist().getAliasDirectory().getAliasList( mAliasListName ); + + /* Add the channel state as a module */ + mProcessingChain.addModule( mChannelState ); + + /* Processing Modules */ + List modules = DecoderFactory.getModules( mChannelType, + mResourceManager, mDecodeConfiguration, mAuxDecodeConfiguration, + aliasList ); + mProcessingChain.addModules( modules ); + + /* Get message filters for the set of processing modules */ + FilterSet messageFilter = DecoderFactory.getMessageFilters( modules ); + + /* Setup the message activity model and add message listeners */ + mMessageActivityModel = new MessageActivityModel( messageFilter ); + mProcessingChain.addMessageListener( mMessageActivityModel ); + + /* Add audio manager as listener for audio packets */ + mProcessingChain.addAudioPacketListener( + mResourceManager.getAudioManager() ); /* Add system-wide message listeners */ - mProcessingChain.addListeners( + mProcessingChain.addMessageListeners( mResourceManager.getChannelManager().getMessageListeners() ); - - /* Add individual message listeners */ - mProcessingChain.addListeners( mMessageListeners ); - - mProcessingChain.start(); - fireChannelEvent( Event.PROCESSING_STARTED ); - } + /* Add individual message listeners */ + mProcessingChain.addMessageListeners( mMessageListeners ); - /* If this is a traffic channel, override the call fade timeout before - * we set the state to call, so that it will auto-expire after the - * fade timeout and be auto deleted */ - if( mChannelType == ChannelType.TRAFFIC ) + /* Create the call event model to receive call events */ + mCallEventModel = new CallEventModel(); + mProcessingChain.addCallEventListener( mCallEventModel ); + } + } + + /** + * Attempts to obtain a source and apply it to the processing chain. + * + * @throws SourceException - if the processing chain is not setup or if + * there is an error produced while obtaining the source + */ + private void setupSource() throws SourceException + { + if( mProcessingChain != null ) { - if( mProcessingChain.isProcessing() ) + Source source = mResourceManager.getSourceManager() + .getSource( getSourceConfiguration(), getDecodeConfiguration() + .getDecoderType().getChannelBandwidth() ); + + if( source != null ) { - mProcessingChain.getChannelState().setCallFadeTimeout( 40000 ); - mProcessingChain.getChannelState().setState( State.CALL ); + mProcessingChain.setSource( source ); } else { - mProcessingChain.getChannelState().setCallFadeTimeout( 5000 ); - /* Channel state should already be no_tuner */ + throw new SourceException( "Couldn't obtain tuner channel source" ); } - + } + else + { + throw new SourceException( "Cannot obtain source for a null " + + "processing chain - setup the processing chain first" ); } } @@ -770,27 +822,14 @@ public class Channel extends Configuration */ private void stop() { - /* Stop and remove any traffic channels */ - for( Channel traffic: mTrafficChannels.values() ) - { - traffic.stop(); - traffic.dispose(); - } - - mTrafficChannels.clear(); - if( !mEnabled && mProcessingChain != null ) { mProcessingChain.stop(); - mProcessingChain.dispose(); - - mProcessingChain = null; - mSelected = false; } - fireChannelEvent( Event.PROCESSING_STOPPED ); + fireChannelEvent( Event.CHANNEL_PROCESSING_STOPPED ); } /** @@ -798,9 +837,18 @@ public class Channel extends Configuration */ public void fireChannelEvent( Event event ) { + ChannelEvent channelEvent = new ChannelEvent( this, event ); + for( ChannelEventListener listener: mChannelListeners ) { - listener.channelChanged( new ChannelEvent( this, event ) ); + listener.channelChanged( channelEvent ); + } + + /* Send event to processing chain so modules can respond to channel + * selection events */ + if( mProcessingChain != null ) + { + mProcessingChain.getChannelEventListener().receive( channelEvent ); } } @@ -813,7 +861,7 @@ public class Channel extends Configuration if( !mChannelListeners.contains( listener ) ) { mChannelListeners.add( listener ); - + listener.channelChanged( new ChannelEvent( this, Event.CHANNEL_ADDED ) ); } @@ -850,7 +898,7 @@ public class Channel extends Configuration if( isProcessing() ) { - mProcessingChain.addListener( listener ); + mProcessingChain.addMessageListener( listener ); } } @@ -858,9 +906,9 @@ public class Channel extends Configuration { mMessageListeners.remove( listener ); - if( isProcessing() ) + if( mProcessingChain != null ) { - mProcessingChain.removeListener( listener ); + mProcessingChain.removeMessageListener( listener ); } } @@ -870,13 +918,15 @@ public class Channel extends Configuration */ public boolean isWithin( long minimum, long maximum ) { - return getTunerChannel() != null && - getTunerChannel().isWithin( minimum, maximum ); + TunerChannel tunerChannel = getTunerChannel(); + + return tunerChannel != null && + tunerChannel.isWithin( minimum, maximum ); } - public void addTrafficChannel( int channelNumber, Channel channel ) + public void addTrafficChannel( String channelID, Channel channel ) { - mTrafficChannels.put( channelNumber, channel ); + mTrafficChannels.put( channelID, channel ); } public boolean hasTrafficChannel( Channel channel ) @@ -886,22 +936,11 @@ public class Channel extends Configuration public void removeTrafficChannel( Channel channel ) { - int keyToRemove = -1; + String name = channel.getChannelDisplayName(); - if( mTrafficChannels.values().contains( channel ) ) + if( mTrafficChannels.containsKey( name ) ) { - for( Integer channelNumber: mTrafficChannels.keySet() ) - { - if( mTrafficChannels.get( channelNumber ).equals( channel ) ) - { - keyToRemove = channelNumber; - } - } - } - - if( keyToRemove != -1 ) - { - mTrafficChannels.remove( keyToRemove ); + mTrafficChannels.remove( name ); } } @@ -910,8 +949,9 @@ public class Channel extends Configuration return Collections.unmodifiableCollection( mTrafficChannels.values() ); } - public boolean hasTrafficChannel( int channelNumber ) + public boolean hasTrafficChannel( String name ) { - return mTrafficChannels.containsKey( channelNumber ); + return mTrafficChannels.containsKey( name ); } + } diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index 082829a6..7f3d5bd0 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -35,6 +35,8 @@ import javax.swing.JTextField; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; +import module.decode.AuxDecodeComponentEditor; +import module.decode.DecodeComponentEditor; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; @@ -47,8 +49,6 @@ import alias.AliasList; import com.jidesoft.swing.JideTabbedPane; import controller.Editor; -import decode.AuxDecodeComponentEditor; -import decode.DecodeComponentEditor; import eventlog.EventLogComponentEditor; public class ChannelEditor extends Editor implements ActionListener @@ -99,7 +99,7 @@ public class ChannelEditor extends Editor implements ActionListener /** * ComboBox: Alias Lists */ - add( new JLabel( "Aliases:" ) ); + add( new JLabel( "Alias List:" ) ); mComboAliasLists = new JComboBox(); diff --git a/src/controller/channel/ChannelEvent.java b/src/controller/channel/ChannelEvent.java index df2fc15b..a8b88eab 100644 --- a/src/controller/channel/ChannelEvent.java +++ b/src/controller/channel/ChannelEvent.java @@ -34,19 +34,26 @@ public class ChannelEvent { CHANGE_ALIAS_LIST, CHANGE_DECODER, - CHANGE_ENABLED, CHANGE_EVENT_LOGGER, CHANGE_NAME, + CHANGE_SELECTED, CHANGE_SITE, CHANGE_RECORDER, - CHANGE_SELECTED, CHANGE_SOURCE, CHANGE_SYSTEM, - CHANNEL_ADDED, + CHANNEL_ADDED, CHANNEL_DELETED, + + CHANNEL_ENABLED, + CHANNEL_DISABLED, - PROCESSING_STARTED, - PROCESSING_STOPPED, + CHANNEL_PROCESSING_STARTED, + CHANNEL_PROCESSING_STOPPED, + + CHANNEL_STATE_RESET, + + TRAFFIC_CHANNEL_ADDED, + TRAFFIC_CHANNEL_DELETED; } } diff --git a/src/controller/channel/ChannelManager.java b/src/controller/channel/ChannelManager.java index ca475413..221b1253 100644 --- a/src/controller/channel/ChannelManager.java +++ b/src/controller/channel/ChannelManager.java @@ -17,8 +17,13 @@ ******************************************************************************/ package controller.channel; +import gui.SDRTrunk; + import java.util.ArrayList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import alias.action.AliasActionManager; import message.Message; import sample.Listener; @@ -26,6 +31,8 @@ import controller.ResourceManager; public class ChannelManager implements ChannelEventListener { + private final static Logger mLog = LoggerFactory.getLogger( ChannelManager.class ); + private ArrayList mChannels = new ArrayList(); private ArrayList mChannelListeners = @@ -56,13 +63,13 @@ public class ChannelManager implements ChannelEventListener { switch( event.getEvent() ) { - case CHANNEL_ADDED: + case CHANNEL_ENABLED: if( !mChannels.contains( event.getChannel() ) ) { mChannels.add( event.getChannel() ); } break; - case CHANNEL_DELETED: + case CHANNEL_DISABLED: mChannels.remove( event.getChannel() ); break; } diff --git a/src/controller/channel/ChannelNode.java b/src/controller/channel/ChannelNode.java index de687f37..4f19f59a 100644 --- a/src/controller/channel/ChannelNode.java +++ b/src/controller/channel/ChannelNode.java @@ -78,17 +78,17 @@ public class ChannelNode extends ConfigurableNode implements ChannelEventListene { /* Refresh the node for each of these events */ case CHANGE_DECODER: - case CHANGE_ENABLED: case CHANGE_NAME: case CHANGE_SELECTED: case CHANGE_SITE: case CHANGE_SYSTEM: - case PROCESSING_STARTED: - case PROCESSING_STOPPED: + case CHANNEL_ENABLED: + case CHANNEL_PROCESSING_STARTED: + case CHANNEL_PROCESSING_STOPPED: this.refresh(); break; /* We're being deleted, so cleanup */ - case CHANNEL_DELETED: + case CHANNEL_DISABLED: delete(); break; default: @@ -154,7 +154,7 @@ public class ChannelNode extends ConfigurableNode implements ChannelEventListene public JPopupMenu getContextMenu() { JPopupMenu popupMenu = new JPopupMenu( "Channel Menu" ); - popupMenu.add( getChannel().getContextMenu() ); +// popupMenu.add( getChannel().getContextMenu() ); return popupMenu; } diff --git a/src/controller/channel/ChannelSelectionListener.java b/src/controller/channel/ChannelSelectionListener.java index 9719ea6f..5bd1a8c9 100644 --- a/src/controller/channel/ChannelSelectionListener.java +++ b/src/controller/channel/ChannelSelectionListener.java @@ -17,7 +17,9 @@ ******************************************************************************/ package controller.channel; +import module.ProcessingChainOld; + public interface ChannelSelectionListener { - public void channelSelected( ProcessingChain channel ); + public void channelSelected( ProcessingChainOld channel ); } diff --git a/src/controller/channel/IChannelEventListener.java b/src/controller/channel/IChannelEventListener.java new file mode 100644 index 00000000..3ec41b53 --- /dev/null +++ b/src/controller/channel/IChannelEventListener.java @@ -0,0 +1,8 @@ +package controller.channel; + +import sample.Listener; + +public interface IChannelEventListener +{ + public Listener getChannelEventListener(); +} diff --git a/src/controller/channel/IChannelEventProvider.java b/src/controller/channel/IChannelEventProvider.java new file mode 100644 index 00000000..330e22ab --- /dev/null +++ b/src/controller/channel/IChannelEventProvider.java @@ -0,0 +1,9 @@ +package controller.channel; + +import sample.Listener; + +public interface IChannelEventProvider +{ + public void setChannelEventListener( Listener listener ); + public void removeChannelEventListener(); +} diff --git a/src/controller/channel/ChannelMap.java b/src/controller/channel/map/ChannelMap.java similarity index 98% rename from src/controller/channel/ChannelMap.java rename to src/controller/channel/map/ChannelMap.java index 60079530..3ec928a0 100644 --- a/src/controller/channel/ChannelMap.java +++ b/src/controller/channel/map/ChannelMap.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; +package controller.channel.map; import java.util.ArrayList; diff --git a/src/controller/channel/ChannelMapEditor.java b/src/controller/channel/map/ChannelMapEditor.java similarity index 97% rename from src/controller/channel/ChannelMapEditor.java rename to src/controller/channel/map/ChannelMapEditor.java index 727b79c0..11ede472 100644 --- a/src/controller/channel/ChannelMapEditor.java +++ b/src/controller/channel/map/ChannelMapEditor.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; +package controller.channel.map; import java.awt.Component; import java.awt.Dimension; @@ -80,7 +80,6 @@ public class ChannelMapEditor extends JPanel implements ActionListener @Override public void focusLost( FocusEvent arg0 ) { - mLog.info( "text field action event fired, new value:" + mNameText.getText() ); mChannelMapCopy.setName( mNameText.getText() ); } } ); @@ -208,7 +207,6 @@ public class ChannelMapEditor extends JPanel implements ActionListener @Override public void actionPerformed( ActionEvent e ) { - mLog.info( "Action Performed:" + e.getActionCommand() ); } @Override diff --git a/src/controller/channel/ChannelMapList.java b/src/controller/channel/map/ChannelMapList.java similarity index 98% rename from src/controller/channel/ChannelMapList.java rename to src/controller/channel/map/ChannelMapList.java index 38ba11ee..7a43b25b 100644 --- a/src/controller/channel/ChannelMapList.java +++ b/src/controller/channel/map/ChannelMapList.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; +package controller.channel.map; import java.util.ArrayList; diff --git a/src/controller/channel/ChannelMapListNode.java b/src/controller/channel/map/ChannelMapListNode.java similarity index 98% rename from src/controller/channel/ChannelMapListNode.java rename to src/controller/channel/map/ChannelMapListNode.java index 158b21d4..208e22a4 100644 --- a/src/controller/channel/ChannelMapListNode.java +++ b/src/controller/channel/map/ChannelMapListNode.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; +package controller.channel.map; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; diff --git a/src/controller/channel/ChannelMapModel.java b/src/controller/channel/map/ChannelMapModel.java similarity index 99% rename from src/controller/channel/ChannelMapModel.java rename to src/controller/channel/map/ChannelMapModel.java index 32237929..50da7dfe 100644 --- a/src/controller/channel/ChannelMapModel.java +++ b/src/controller/channel/map/ChannelMapModel.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; +package controller.channel.map; import javax.swing.table.AbstractTableModel; diff --git a/src/controller/channel/ChannelMapNode.java b/src/controller/channel/map/ChannelMapNode.java similarity index 98% rename from src/controller/channel/ChannelMapNode.java rename to src/controller/channel/map/ChannelMapNode.java index d9b68419..7a0712e0 100644 --- a/src/controller/channel/ChannelMapNode.java +++ b/src/controller/channel/map/ChannelMapNode.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; +package controller.channel.map; import java.awt.event.ActionEvent; diff --git a/src/controller/channel/ChannelRange.java b/src/controller/channel/map/ChannelRange.java similarity index 98% rename from src/controller/channel/ChannelRange.java rename to src/controller/channel/map/ChannelRange.java index edc861b1..a9814239 100644 --- a/src/controller/channel/ChannelRange.java +++ b/src/controller/channel/map/ChannelRange.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; +package controller.channel.map; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; diff --git a/src/controller/config/Configuration.java b/src/controller/config/Configuration.java index ff979c4a..8a46d301 100644 --- a/src/controller/config/Configuration.java +++ b/src/controller/config/Configuration.java @@ -20,10 +20,10 @@ package controller.config; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; +import module.decode.config.AuxDecodeConfiguration; +import module.decode.config.DecodeConfiguration; import record.config.RecordConfiguration; import source.config.SourceConfiguration; -import decode.config.AuxDecodeConfiguration; -import decode.config.DecodeConfiguration; import eventlog.config.EventLogConfiguration; @XmlSeeAlso( { AuxDecodeConfiguration.class, diff --git a/src/controller/state/AuxChannelState.java b/src/controller/state/AuxChannelState.java deleted file mode 100644 index 218c37cc..00000000 --- a/src/controller/state/AuxChannelState.java +++ /dev/null @@ -1,80 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package controller.state; - -import message.Message; -import sample.Broadcaster; -import sample.Listener; -import controller.state.ChannelState.ChangedAttribute; - -/** - * ChannelState for auxiliary decoders. - */ -public abstract class AuxChannelState implements Listener -{ - protected ChannelState mParentChannelState; - protected Broadcaster mChangeBroadcaster = - new Broadcaster(); - - public AuxChannelState( ChannelState parentState ) - { - mParentChannelState = parentState; - } - - /** - * Prepare for garbage collect - */ - public void dispose() - { - mChangeBroadcaster.dispose(); - mParentChannelState = null; - } - - /** - * Call fade - change display and await a call to reset() - */ - public abstract void fade(); - - /** - * Activity summary for this decoder - */ - public abstract String getActivitySummary(); - - /** - * Call end - cleanup the display - */ - public abstract void reset(); - - public void addListener( Listener listener ) - { - mChangeBroadcaster.addListener( listener ); - } - - public void removeListener( Listener listener ) - { - mChangeBroadcaster.removeListener( listener ); - } - - public void broadcastChange( ChangedAttribute attribute ) - { - if( mChangeBroadcaster != null ) - { - mChangeBroadcaster.broadcast( attribute ); - } - } -} diff --git a/src/controller/state/ChannelState.java b/src/controller/state/ChannelState.java deleted file mode 100644 index 3f395086..00000000 --- a/src/controller/state/ChannelState.java +++ /dev/null @@ -1,697 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package controller.state; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import javax.swing.JTable; - -import message.Message; -import message.MessageFilterFactory; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import properties.SystemProperties; -import sample.Broadcaster; -import sample.Listener; -import alias.AliasList; -import audio.SquelchListener; -import audio.SquelchListener.SquelchState; -import audio.inverted.AudioType; -import audio.inverted.IAudioTypeListener; -import controller.activity.CallEvent; -import controller.activity.CallEvent.CallEventType; -import controller.activity.CallEventAliasCellRenderer; -import controller.activity.CallEventModel; -import controller.activity.MessageActivityModel; -import controller.channel.Channel.ChannelType; -import controller.channel.ProcessingChain; -import filter.FilterSet; - -/** - * ChannelState provides a state machine for tracking a voice or data call - * through it's life cycle of CALL/DATA, FADE, and then END, and then back to - * an IDLE state. This class also provides squelch control for unsquelching - * sound to the speakers, and/or sound to an audio recorder - * - * State Descriptions: - * IDLE No voice or data call activity - * CALL/DATA A voice or data call has started, or is continuing - * CONTROL Control channel - * FADE The phase after a voice or data call when either an explicit - * call end has been received, or when no new signalling updates - * have been received, and the fade timer has expired. This phase - * allows for gui updates to signal to the user that the call is - * ended, while continuing to display the call details for the user - * END The phase after the voice or data call FADE, when the call is - * now over, and the gui should be reset to IDLE. - * - * When a call is started, invoke mTimerService.startFadeTimer() to start the - * call fade monitoring timer process that will automatically signal a call end - * once no more call signalling has been detected after the fade timeout has - * occurred, by invoking the fade() method. - * - * When a call ends, invoke the mTimerService.stopFadeTimer() and then invoke - * the mTimerService.startResetTimer(). Once the reset timeout occurs, - * the reset() method will be called, allowing the channel state to perform - * any reset actions, like clearing status and id labels on any view panels. - * - * AuxChannelState class allows for auxiliary decoders like Fleetsync to both - * have a state and leverage the state machine of the parent ChannelState. - * This allows a non-squelching channel state like for conventional fm to have - * an aux decoder like fleetsync, and allow the fleetsync auxchannelstate to - * leverage the call/end states of the parent conventional channelstate. - * - */ -public abstract class ChannelState implements Listener -{ - private final static Logger mLog = - LoggerFactory.getLogger( ChannelState.class ); - - private State mState = State.IDLE; - private Object mCallFadeLock = new Object(); - private Object mCallResetLock = new Object(); - private long mFadeTimeout = -1; - private long mResetTimeout = -1; - - protected long mCallFadeTimeout; - protected long mCallResetTimeout; - protected TimerService mTimerService = new TimerService(); - protected ArrayList mSquelchListeners = - new ArrayList(); - protected ArrayList mAuxChannelStates = - new ArrayList(); - protected Broadcaster mChangeBroadcaster = - new Broadcaster(); - protected IAudioTypeListener mAudioTypeListener; - protected AudioType mAudioType = AudioType.NORMAL; - - protected CallEventModel mCallEventModel = new CallEventModel(); - - protected JTable mCallEventTable; - - protected MessageActivityModel mMessageActivityModel; - protected JTable mMessageActivityTable; - - protected ProcessingChain mProcessingChain; - - protected AliasList mAliasList; - - protected CallEvent mCurrentCallEvent; - - public ChannelState( ProcessingChain processingChain, AliasList aliasList ) - { - mProcessingChain = processingChain; - mAliasList = aliasList; - - - /* Register to receive decoded messages from the processing chain */ - mProcessingChain.addListener( this ); - - SystemProperties props = SystemProperties.getInstance(); - mCallFadeTimeout = props.get( "call.fade.timeout", 2000 ); - mCallResetTimeout = props.get( "call.reset.timeout", 4000 ); - - /* Get a message filter for the primary decoder and aux decoders */ - FilterSet messageFilter = MessageFilterFactory - .getMessageFilter( mProcessingChain.getChannel() ); - - mMessageActivityModel = new MessageActivityModel( messageFilter ); - } - - public void setCurrentCallEvent( CallEvent callEvent ) - { - mCurrentCallEvent = callEvent; - } - - public CallEvent getCurrentCallEvent() - { - return mCurrentCallEvent; - } - - /** - * Allows setting the call event model to use for traffic channels, so that - * all traffic channels can share the parent control channel call event - * model. - * @param model - */ - public void setCallEventModel( CallEventModel model ) - { - mCallEventModel = model; - } - - public CallEventModel getCallEventModel() - { - return mCallEventModel; - } - - public void receiveCallEvent( CallEvent event ) - { - mCallEventModel.add( event ); - } - - public void dispose() - { - mTimerService.dispose(); - - mAuxChannelStates.clear(); - mSquelchListeners.clear(); - mChangeBroadcaster.dispose(); - mChangeBroadcaster = null; - - if( mProcessingChain.getChannel().getChannelType() == ChannelType.STANDARD ) - { - mCallEventModel.dispose(); - } - /* If we're a traffic channel, set the call event model to null so that - * we don't dispose the parent control channel's call event model */ - else - { - mCallEventModel = null; - } - - mMessageActivityModel.dispose(); - mMessageActivityModel = null; - - mAliasList = null; - mAudioTypeListener = null; - mCallEventModel = null; - mProcessingChain = null; - } - - /** - * Channel Activity Summary - implemented by the sub-class. - */ - public abstract String getActivitySummary(); - - public AliasList getAliasList() - { - return mAliasList; - } - - public boolean hasAliasList() - { - return mAliasList != null; - } - - public void broadcastChange( final ChangedAttribute attribute ) - { - mChangeBroadcaster.broadcast( attribute ); - } - - public void setCallFadeTimeout( long milliseconds ) - { - mCallFadeTimeout = milliseconds; - } - - public JTable getCallEventTable() - { - if( mCallEventTable == null ) - { - mCallEventTable = new JTable( mCallEventModel ); - mCallEventTable.setAutoCreateRowSorter( true ); - mCallEventTable.setAutoResizeMode( JTable.AUTO_RESIZE_LAST_COLUMN ); - - CallEventAliasCellRenderer renderer = - new CallEventAliasCellRenderer( mProcessingChain - .getResourceManager().getSettingsManager() ); - - mCallEventTable.getColumnModel().getColumn( 3 ) - .setCellRenderer( renderer ); - - mCallEventTable.getColumnModel().getColumn( 5 ) - .setCellRenderer( renderer ); - } - - return mCallEventTable; - } - - /** - * Returns the message activity model. - */ - public MessageActivityModel getMessageActivityModel() - { - return mMessageActivityModel; - } - - public void addAuxChannelState( AuxChannelState state ) - { - mAuxChannelStates.add( state ); - } - - public ArrayList getAuxChannelStates() - { - return mAuxChannelStates; - } - - public void receive( Message message ) - { - if( mMessageActivityModel != null ) - { - mMessageActivityModel.receive( message ); - } - - for( AuxChannelState state: mAuxChannelStates ) - { - if( state != null ) - { - state.receive( message ); - } - } - } - - public ProcessingChain getProcessingChain() - { - return mProcessingChain; - } - - public State getState() - { - return mState; - } - - public void setState( State state ) - { - /* Let all CALL state changes go through in order to update the fade - * timer, otherwise, don't update the state if it hasn't changed */ - if( mState != state || state == State.CALL || state == State.DATA || state == State.ENCRYPTED ) - { - if( mState.canChangeTo( state ) ) - { - mState = state; - - switch( state ) - { - case CONTROL: - setSquelchState( SquelchState.SQUELCH ); - break; - case CALL: - setSquelchState( SquelchState.UNSQUELCH ); - mTimerService.stopResetTimer(); - mTimerService.startFadeTimer(); - break; - case DATA: - mTimerService.stopResetTimer(); - mTimerService.startFadeTimer(); - break; - case ENCRYPTED: - setSquelchState( SquelchState.SQUELCH ); - mTimerService.stopResetTimer(); - mTimerService.startFadeTimer(); - case NO_TUNER: - mTimerService.stopResetTimer(); - if( getProcessingChain().getChannel() - .getChannelType() == ChannelType.TRAFFIC ) - { - mTimerService.startFadeTimer(); - } - break; - case FADE: - mTimerService.stopFadeTimer(); - mTimerService.startResetTimer(); - setSquelchState( SquelchState.SQUELCH ); - if( mAudioTypeListener != null ) - { - mAudioTypeListener.setAudioType( mAudioType ); - } - break; - case END: - mTimerService.stopResetTimer(); - mTimerService.stopFadeTimer(); - setSquelchState( SquelchState.SQUELCH ); - if( mAudioTypeListener != null ) - { - mAudioTypeListener.setAudioType( mAudioType ); - } - - /* If this is a traffic channel, stop it */ - if( mProcessingChain.getChannel() - .getChannelType() == ChannelType.TRAFFIC ) - { - mProcessingChain.getChannel().setEnabled( false ); - } - break; - case IDLE: - break; - default: - break; - } - - broadcastChange( ChangedAttribute.CHANNEL_STATE ); - } - else - { - mLog.error( "Channel State - can't change from [" + - mState.toString() + "] to [" + state.toString() + "]" ); - } - } - } - - /** - * End method to reset the channel state once a call has completely - * ended and now you want to return the channel state to idle - */ - protected void reset() - { - setState( State.END ); - - for( AuxChannelState state: mAuxChannelStates ) - { - state.reset(); - } - - //Reset the state to idle - setState( State.IDLE ); - } - - /** - * Method to perform any call fade actions. On fade, you should change the - * channel state to fading, and construct a call-reset timer to execute a few - * seconds later. This allows the view to signal to the user that the call - * has faded/ended, and display faded information to the user, until the - * cleanup() task is run. - */ - protected void fade( CallEventType type ) - { - for( AuxChannelState state: mAuxChannelStates ) - { - state.fade(); - } - - setState( State.FADE ); - } - - public void setListener( IAudioTypeListener listener ) - { - mAudioTypeListener = listener; - } - - public void addListener( SquelchListener listener ) - { - mSquelchListeners.add( listener ); - } - - public void removeListener( SquelchListener listener ) - { - mSquelchListeners.remove( listener ); - } - - protected void setSquelchState( SquelchState state ) - { - Iterator it = mSquelchListeners.iterator(); - - while( it.hasNext() ) - { - it.next().setSquelch( state ); - } - } - - /** - * Timer service - */ - public class TimerService extends ScheduledThreadPoolExecutor - { - private long mTimerMonitorRevisitRate = 100; //100 milliseconds - - private ScheduledFuture mTimerMonitor; - - public TimerService() - { - super( 1 ); - } - - public void dispose() - { - if( mTimerMonitor != null ) - { - mTimerMonitor.cancel( true ); - } - - this.shutdownNow(); - } - - private void checkMonitor() - { - boolean createTimer = false; - - synchronized( mCallFadeLock ) - { - if( mFadeTimeout != -1 ) - { - createTimer = true; - } - } - - synchronized( mCallResetLock ) - { - if( mResetTimeout != -1 ) - { - createTimer = true; - } - } - - if( createTimer ) - { - //Check to see if the timer is already running, if not, start it - - if( mTimerMonitor == null || mTimerMonitor.isDone() ) - { - scheduleAtFixedRate( new StateTimeoutMonitor(), - mTimerMonitorRevisitRate, - mTimerMonitorRevisitRate, - TimeUnit.MILLISECONDS ); - } - } - else - { - if( mTimerMonitor != null ) - { - mTimerMonitor.cancel( true ); - } - } - } - - public void startFadeTimer() - { - synchronized( mCallFadeLock ) - { - mFadeTimeout = System.currentTimeMillis() + mCallFadeTimeout; - } - - checkMonitor(); - } - - public void resetFadeTimer() - { - startFadeTimer(); - } - - public void stopFadeTimer() - { - synchronized( mCallFadeLock ) - { - mFadeTimeout = -1; - } - - checkMonitor(); - } - - public void startResetTimer() - { - synchronized( mCallResetLock ) - { - mResetTimeout = System.currentTimeMillis() + mCallResetTimeout; - } - - checkMonitor(); - } - - public void stopResetTimer() - { - synchronized( mCallResetLock ) - { - mResetTimeout = -1; - } - - checkMonitor(); - } - } - - public class StateTimeoutMonitor implements Runnable - { - @Override - public void run() - { - long now = System.currentTimeMillis(); - - synchronized( mCallFadeLock ) - { - if( mFadeTimeout != -1 && now >= mFadeTimeout ) - { - mFadeTimeout = -1; - - fade( CallEventType.CALL_TIMEOUT ); - } - } - - synchronized( mCallResetLock ) - { - if( mResetTimeout != -1 && now >= mResetTimeout ) - { - mResetTimeout = -1; - - reset(); - } - } - } - } - - - public enum State - { - IDLE( "IDLE" ) { - @Override - public boolean canChangeTo( State state ) - { - return true; - } - }, - CALL( "CALL" ) { - @Override - public boolean canChangeTo( State state ) - { - return state == CALL || - state == CONTROL || - state == DATA || - state == ENCRYPTED || - state == FADE; - } - }, - DATA( "DATA" ) { - @Override - public boolean canChangeTo( State state ) - { - return state == CALL || - state == CONTROL || - state == DATA || - state == State.ENCRYPTED || - state == FADE; - } - }, - ENCRYPTED( "ENCRYPTED" ) - { - @Override - public boolean canChangeTo( State state ) - { - return state == FADE; - } - }, - CONTROL( "CONTROL" ) - { - @Override - public boolean canChangeTo( State state ) - { - return true; - } - }, - FADE( "FADE" ) { - @Override - public boolean canChangeTo( State state ) - { - return state != FADE; //All states except fade allowed - } - }, - END( "END" ) { - @Override - public boolean canChangeTo( State state ) - { - return state != FADE; //Fade is only disallowed state - } - }, - NO_TUNER( "NO TUNER" ) { - @Override - public boolean canChangeTo( State state ) - { - return state == FADE || state == END; - } - }; - - private String mDisplayValue; - - private State( String displayValue ) - { - mDisplayValue = displayValue; - } - - public abstract boolean canChangeTo( State state ); - - public String getDisplayValue() - { - return mDisplayValue; - } - } - - /** - * Registers a listener to receive channel state attribute changes - */ - public void addListener( Listener listener ) - { - mChangeBroadcaster.addListener( listener ); - } - - /** - * Removes a listener from receiving channel state attribute changes - */ - public void removeListener( Listener listener ) - { - mChangeBroadcaster.removeListener( listener ); - } - - public enum ChangedAttribute - { - AUDIO_TYPE, - CHANNEL_NUMBER, - CHANNEL_NAME, - CHANNEL_SITE_NUMBER, - CHANNEL_SITE_NUMBER_ALIAS, - CHANNEL_STATE, - DESCRIPTION, - FROM_TALKGROUP, - FROM_TALKGROUP_ALIAS, - FROM_TALKGROUP_TYPE, - MESSAGE, - MESSAGE_TYPE, - NAC, - SITE, - SITE_ALIAS, - SITE_NAME, - SOURCE, - SYSTEM, - SYSTEM_NAME, - SQUELCH, - TO_TALKGROUP, - TO_TALKGROUP_ALIAS, - TO_TALKGROUP_TYPE, - WACN; - } -} diff --git a/src/controller/state/ChannelStatePanel.java b/src/controller/state/ChannelStatePanel.java deleted file mode 100644 index 4c8e6b3f..00000000 --- a/src/controller/state/ChannelStatePanel.java +++ /dev/null @@ -1,388 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package controller.state; - -import java.awt.Color; -import java.awt.EventQueue; -import java.awt.Font; -import java.awt.GradientPaint; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Paint; -import java.util.HashMap; - -import javax.swing.JMenu; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; -import sample.Listener; -import settings.ColorSetting; -import settings.ColorSetting.ColorSettingName; -import settings.Setting; -import settings.SettingChangeListener; -import settings.SettingsManager; -import controller.channel.Channel; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelState.State; - -public abstract class ChannelStatePanel extends JPanel - implements Listener, - SettingChangeListener -{ - private static final long serialVersionUID = 1L; - - protected Font mFontDetails = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); - protected Font mFontDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); - protected Font mFontAuxDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); - - protected Color mColorChannelBackground; - protected Color mColorChannelSelected; - protected Color mColorTopCall; - protected Color mColorMiddleCall; - protected Color mColorTopControl; - protected Color mColorMiddleControl; - protected Color mColorTopData; - protected Color mColorMiddleData; - protected Color mColorTopFade; - protected Color mColorMiddleFade; - protected Color mColorTopIdle; - protected Color mColorMiddleIdle; - protected Color mColorTopNoTuner; - protected Color mColorMiddleNoTuner; - protected Color mColorLabelDetails; - protected Color mColorLabelDecoder; - protected Color mColorLabelAuxDecoder; - - private HashMap mTrafficPanels = - new HashMap(); - - protected Channel mChannel; - - public ChannelStatePanel( Channel channel ) - { - mChannel = channel; - - /* Register to receive settings updates */ - getSettingsManager().addListener( this ); - - /* Register to receive channel updates */ - mChannel.getProcessingChain().getChannelState().addListener( this ); - - getColors(); - - setLayout( new MigLayout( "insets 3 2 2 2", "[grow,fill]", "[]0[]0[]") ); - } - - public SettingsManager getSettingsManager() - { - return mChannel.getResourceManager().getSettingsManager(); - } - - public void dispose() - { - mTrafficPanels.clear(); - mChannel = null; - } - - private void getColors() - { - mColorChannelBackground = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_BACKGROUND ).getColor(); - mColorChannelSelected = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL ).getColor(); - mColorTopCall = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL ).getColor(); - mColorMiddleCall = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL ).getColor(); - mColorTopControl = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL ).getColor(); - mColorMiddleControl = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL ).getColor(); - mColorTopData = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA ).getColor(); - mColorMiddleData = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA ).getColor(); - mColorTopFade = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE ).getColor(); - mColorMiddleFade = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE ).getColor(); - mColorTopIdle = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE ).getColor(); - mColorMiddleIdle = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE ).getColor(); - mColorTopNoTuner = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ).getColor(); - mColorMiddleNoTuner = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ).getColor(); - - mColorLabelDetails = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ).getColor(); - mColorLabelDecoder = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_LABEL_DECODER ).getColor(); - mColorLabelAuxDecoder = getSettingsManager().getColorSetting( - ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ).getColor(); - } - - public HashMap getTrafficPanels() - { - return mTrafficPanels; - } - - public JMenu getContextMenu() - { - return mChannel.getContextMenu(); - } - - public Channel getChannel() - { - return mChannel; - } - - public ChannelState getChannelState() - { - if( mChannel.getProcessingChain() != null ) - { - return mChannel.getProcessingChain().getChannelState(); - } - - return null; - } - - public boolean isSelected() - { - return mChannel.isSelected(); - } - - public void addPanel( final JPanel panel ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - add( panel, "span,wrap" ); - validate(); - } - } ); - } - - public void removePanel( final JPanel panel ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - remove( panel ); - - validate(); - - if( getParent() != null ) - { - getParent().validate(); - } - - if( panel instanceof ChannelStatePanel ) - { - ((ChannelStatePanel)panel).dispose(); - } - else if( panel instanceof AuxStatePanel ) - { - ((AuxStatePanel)panel).dispose(); - } - } - } ); - } - - public void trafficChannelAdded( Channel traffic ) - { - //Disregard -- sub class can override and implement - } - - public void trafficChannelDeleted( Channel traffic ) - { - ChannelStatePanel panelToRemove = - getTrafficPanels().remove( traffic ); - - removePanel( panelToRemove ); - } - - public void setSelected( boolean selected ) - { - for( ChannelStatePanel traffic: getTrafficPanels().values() ) - { - traffic.setSelected( false ); - } - - mChannel.setSelected( selected ); - - repaint(); - } - - private GradientPaint getGradient( Color top, Color middle ) - { - return new GradientPaint( 0f, -50.0f, top, - 0f, (float)getHeight() / 2.2f, middle, true ); - } - - @Override - protected void paintComponent(Graphics g) - { - super.paintComponent( g ); - - setBackground( mColorChannelBackground ); - - Graphics2D g2 = (Graphics2D)g.create(); - - Paint p = null; - - ChannelState channelState = getChannelState(); - - if( channelState != null ) - { - State state = channelState.getState(); - - if( state != null ) - { - switch( state ) - { - case CALL: - p = getGradient( mColorTopCall, mColorMiddleCall ); - break; - case CONTROL: - p = getGradient( mColorTopControl, mColorMiddleControl ); - break; - case DATA: - p = getGradient( mColorTopData, mColorMiddleData ); - break; - case FADE: - p = getGradient( mColorTopFade, mColorMiddleFade ); - break; - case NO_TUNER: - p = getGradient( mColorTopNoTuner, mColorMiddleNoTuner ); - break; - case END: - case IDLE: - default: - p = getGradient( mColorTopIdle, mColorMiddleIdle ); - break; - } - } - } - - g2.setPaint( p ); - g2.fillRect( 0, 0, getWidth(), getHeight() ); - - /* Draw bottom separator line */ - g2.setColor( Color.LIGHT_GRAY ); - g2.drawLine( 0, getHeight() - 1, getWidth(), getHeight() - 1 ); - - /* Draw channel selected box */ - if( mChannel.isSelected() ) - { - g2.setColor( mColorChannelSelected ); - g2.drawRect( 1, 1, getWidth() - 2, getHeight() - 2 ); - } - - g2.dispose(); - } - - @Override - public void settingChanged( Setting setting ) - { - if( setting instanceof ColorSetting ) - { - ColorSetting colorSetting = (ColorSetting)setting; - - switch( colorSetting.getColorSettingName() ) - { - case CHANNEL_STATE_BACKGROUND: - mColorChannelBackground = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_MIDDLE_CALL: - mColorMiddleCall = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_TOP_CALL: - mColorTopCall = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL: - mColorMiddleControl = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_TOP_CONTROL: - mColorTopControl = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_MIDDLE_DATA: - mColorMiddleData = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_TOP_DATA: - mColorTopData = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_MIDDLE_FADE: - mColorMiddleFade = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_TOP_FADE: - mColorTopFade = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_MIDDLE_IDLE: - mColorMiddleIdle = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_TOP_IDLE: - mColorTopIdle = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER: - mColorMiddleNoTuner = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_GRADIENT_TOP_NO_TUNER: - mColorTopNoTuner = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_LABEL_AUX_DECODER: - mColorLabelAuxDecoder = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_LABEL_DECODER: - mColorLabelDecoder = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_LABEL_DETAILS: - mColorLabelDetails = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_SELECTED_CHANNEL: - mColorChannelSelected = colorSetting.getColor(); - repaint(); - break; - } - } - } - - @Override - public void settingDeleted( Setting setting ) {} -} diff --git a/src/decode/DecodeEditorFactory.java b/src/decode/DecodeEditorFactory.java deleted file mode 100644 index 9d284a57..00000000 --- a/src/decode/DecodeEditorFactory.java +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode; - -import controller.channel.ChannelNode; -import decode.config.DecodeConfiguration; -import decode.ltrnet.LTRNetEditor; -import decode.ltrstandard.LTRStandardEditor; -import decode.mpt1327.MPT1327Editor; -import decode.nbfm.NBFMEditor; -import decode.p25.P25Editor; -import decode.passport.PassportEditor; - -public class DecodeEditorFactory -{ - public static DecodeEditor getPanel( DecodeConfiguration config, - ChannelNode channelNode ) - { - DecodeEditor configuredPanel; - - switch( config.getDecoderType() ) - { - case NBFM: - configuredPanel = new NBFMEditor( config ); - break; - case LTR_STANDARD: - configuredPanel = new LTRStandardEditor( config ); - break; - case LTR_NET: - configuredPanel = new LTRNetEditor( config ); - break; - case MPT1327: - configuredPanel = new MPT1327Editor( config, channelNode ); - break; - case PASSPORT: - configuredPanel = new PassportEditor( config ); - break; - case P25_PHASE1: - configuredPanel = new P25Editor( config ); - break; - default: - configuredPanel = new DecodeEditor( config ); - break; - } - - return configuredPanel; - } -} diff --git a/src/decode/Decoder.java b/src/decode/Decoder.java deleted file mode 100644 index 7fe365e4..00000000 --- a/src/decode/Decoder.java +++ /dev/null @@ -1,276 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode; - -import java.util.ArrayList; -import java.util.List; - -import message.Message; -import sample.Broadcaster; -import sample.Listener; -import sample.complex.ComplexSample; -import sample.real.RealSampleBroadcaster; -import sample.real.RealSampleListener; -import source.Source.SampleType; -import source.tuner.frequency.AutomaticFrequencyControl; -import source.tuner.frequency.FrequencyCorrectionControl; -import audio.IAudioOutput; -import decode.config.DecodeConfiguration; -import eventlog.MessageEventLogger; - -public abstract class Decoder implements Listener -{ - private MessageEventLogger mEventLogger; - private Broadcaster mMessageBroadcaster = new Broadcaster(); - private RealSampleBroadcaster mRealBroadcaster = new RealSampleBroadcaster(); - private Broadcaster mComplexBroadcaster = - new Broadcaster(); - - protected SampleType mSourceSampleType; - protected ArrayList mAuxiliaryDecoders = new ArrayList(); - - protected FrequencyCorrectionControl mFrequencyCorrection; - - /** - * Abstract decoder class. - * - * @param sampleType - complex (I/Q) or real (demodulated) sample type. - * Each decoder instance will be configured to receive either complex or - * real samples. When configured to receive complex samples, the - * decoder will implement a demodulator component and feed the output from - * that demodulator back onto itself via the real sample interface, so - * that any added auxiliary decoders will then receive the demodulated - * output stream. - */ - public Decoder( SampleType sampleType ) - { - mSourceSampleType = sampleType; - } - - public Decoder( DecodeConfiguration config, SampleType sampleType ) - { - this( sampleType ); - - if( config.supportsAFC() && config.isAFCEnabled() ) - { - mFrequencyCorrection = - new AutomaticFrequencyControl( config.getAFCMaximumCorrection() ); - - /* Register AFC to receive non-filtered demodulated audio samples */ - addUnfilteredRealSampleListener( - (RealSampleListener)mFrequencyCorrection ); - } - } - - /** - * Optional frequency correction control to provide frequency adjustments - * directed by the decoder. - */ - public FrequencyCorrectionControl getFrequencyCorrectionControl() - { - return mFrequencyCorrection; - } - - /** - * Indicates if a frequency correction controller exists for this decoder - */ - public boolean hasFrequencyCorrectionControl() - { - return mFrequencyCorrection != null; - } - - /** - * Current frequency correction setting or zero if no controller exists - */ - public int getFrequencyCorrection() - { - if( hasFrequencyCorrectionControl() ) - { - return mFrequencyCorrection.getErrorCorrection(); - } - - return 0; - } - - /** - * Returns a real (ie demodulated) sample listener interface for - * connecting this decoder to a real sample stream provider - */ - public RealSampleListener getRealReceiver() - { - return mRealBroadcaster; - } - - public abstract IAudioOutput getAudioOutput(); - - /** - * Returns a complex listener interface for connecting this decoder to a - * float stream provider - */ - public Listener getComplexReceiver() - { - return (Listener)mComplexBroadcaster; - } - - /** - * Returns the primary decoder type for this decoder - */ - public abstract DecoderType getType(); - - /** - * Cleanup method. Invoke this method after stop and before delete. - */ - public void dispose() - { - for( Decoder auxiliaryDecoder: mAuxiliaryDecoders ) - { - auxiliaryDecoder.dispose(); - } - - mAuxiliaryDecoders.clear(); - - mMessageBroadcaster.clear(); - mComplexBroadcaster.clear(); - mRealBroadcaster.clear(); - - if( mFrequencyCorrection != null ) - { - mFrequencyCorrection.dispose(); - } - } - - /** - * Adds the auxiliary decoder (piggyback) to this decoder. - * - * Note: we assume that the auxiliary decoder is designed to receive - * demodulated samples, thus we automatically register the aux decoder to - * receive the demodulated sample stream. - * - * Registers this decoder to receive the message output stream from the - * auxiliary decoder, so that those messages can be echoed and included in - * the consolidated message stream to all message listeners registered on - * the primary decoder. - */ - public void addAuxiliaryDecoder( Decoder decoder ) - { - mAuxiliaryDecoders.add( decoder ); - mRealBroadcaster.addListener( decoder.getRealReceiver() ); - decoder.addMessageListener( this ); - } - - /** - * Returns a list of all auxiliary decoders that have been added to this - * processing chain - */ - public List getAuxiliaryDecoders() - { - return mAuxiliaryDecoders; - } - - /** - * Sets the MessageEventLogger for this processing chain - */ - public void setEventLogger( MessageEventLogger eventLogger ) - { - mEventLogger = eventLogger; - } - - /** - * Returns the MessageEventLogger for this processing chain - */ - public MessageEventLogger getEventLogger() - { - return mEventLogger; - } - - /** - * Main receiver method for all demodulators to send their decoded messages - * so that they will be broadcast to all registered listeners - */ - @Override - public void receive( Message message ) - { - send( message ); - } - - /** - * Broadcasts the message to all registered listeners - */ - public void send( Message message ) - { - if( mMessageBroadcaster != null ) - { - mMessageBroadcaster.receive( message ); - } - } - - /** - * Adds a listener to receiving decoded messages from all attached decoders - */ - public void addMessageListener( Listener listener ) - { - mMessageBroadcaster.addListener( listener ); - } - - /** - * Removes the listener from receiving decoded messages from all attached - * decoders - */ - public void removeMessageListener( Listener listener ) - { - mMessageBroadcaster.removeListener( listener ); - } - - /** - * Adds a real sample listener to receive unfiltered demodulated samples - */ - public abstract void addUnfilteredRealSampleListener( RealSampleListener listener ); - - /** - * Adds a real sample listener to receive demodulated samples - */ - public void addRealSampleListener( RealSampleListener listener ) - { - mRealBroadcaster.addListener( listener ); - } - - /** - * Remove the real sample listener from receiving demodulated samples - */ - public void removeRealListener( RealSampleListener listener ) - { - mRealBroadcaster.removeListener( listener ); - } - - /** - * Adds a complex (I/Q) sample listener to receive copy of the inbound - * complex sample stream - */ - public void addComplexListener( Listener listener ) - { - mComplexBroadcaster.addListener( listener ); - } - - /** - * Removes the complex (I/Q) sample listener from receiving samples - */ - public void removeComplexListener( Listener listener ) - { - mComplexBroadcaster.removeListener( listener ); - } -} diff --git a/src/decode/DecoderFactory.java b/src/decode/DecoderFactory.java deleted file mode 100644 index da7db700..00000000 --- a/src/decode/DecoderFactory.java +++ /dev/null @@ -1,219 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode; - -import source.Source.SampleType; -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; -import decode.config.DecodeConfigMPT1327; -import decode.config.DecodeConfigP25Phase1; -import decode.config.DecodeConfiguration; -import decode.fleetsync2.Fleetsync2Decoder; -import decode.fleetsync2.FleetsyncChannelState; -import decode.lj1200.LJ1200ChannelState; -import decode.lj1200.LJ1200Decoder; -import decode.ltrnet.LTRNetChannelState; -import decode.ltrnet.LTRNetDecoder; -import decode.ltrstandard.LTRChannelState; -import decode.ltrstandard.LTRStandardDecoder; -import decode.mdc1200.MDCChannelState; -import decode.mdc1200.MDCDecoder; -import decode.mpt1327.MPT1327ChannelState; -import decode.mpt1327.MPT1327Decoder; -import decode.nbfm.NBFMChannelState; -import decode.nbfm.NBFMDecoder; -import decode.p25.P25ChannelState; -import decode.p25.P25Decoder; -import decode.passport.PassportChannelState; -import decode.passport.PassportDecoder; -import decode.tait.Tait1200ChannelState; -import decode.tait.Tait1200Decoder; - -public class DecoderFactory -{ - /** - * Returns a fully configured decoder with embedded auxiliary decoders - * @param channelConfig - * @return - */ - public static Decoder getDecoder( ProcessingChain chain, - SampleType sampleType, - AliasList aliasList ) - { - Decoder retVal = null; - - DecodeConfiguration config = - chain.getChannel().getDecodeConfiguration(); - - - switch( config.getDecoderType() ) - { - case AM: - retVal = new AMDecoder( sampleType ); - break; - case NBFM: - retVal = new NBFMDecoder( config, sampleType ); - break; - case LTR_STANDARD: - DecodeConfigLTRStandard ltrStandardConfig = - (DecodeConfigLTRStandard)config; - - retVal = new LTRStandardDecoder( config, sampleType, aliasList, - ltrStandardConfig.getMessageDirection() ); - break; - case LTR_NET: - DecodeConfigLTRNet ltrNetConfig = (DecodeConfigLTRNet)config; - - retVal = new LTRNetDecoder( config, sampleType, aliasList, - ltrNetConfig.getMessageDirection() ); - break; - case MPT1327: - DecodeConfigMPT1327 mptConfig = (DecodeConfigMPT1327)config; - - - retVal = new MPT1327Decoder( config, sampleType, aliasList, - mptConfig.getSync() ); - break; - case PASSPORT: - retVal = new PassportDecoder( config, sampleType, aliasList ); - break; - case P25_PHASE1: - DecodeConfigP25Phase1 p25Config = (DecodeConfigP25Phase1)config; - - retVal = new P25Decoder( chain.getResourceManager(), - sampleType, p25Config.getModulation(), aliasList ); - break; - default: - throw new IllegalArgumentException( - "Unknown decoder type [" + - config.getDecoderType().toString() + "]" ); - } - - if( retVal != null ) - { - AuxDecodeConfiguration auxConfig = - chain.getChannel().getAuxDecodeConfiguration(); - - for( DecoderType auxDecoder: auxConfig.getAuxDecoders() ) - { - switch( auxDecoder ) - { - case FLEETSYNC2: - retVal.addAuxiliaryDecoder( - new Fleetsync2Decoder( aliasList ) ); - break; - case MDC1200: - retVal.addAuxiliaryDecoder( - new MDCDecoder( aliasList ) ); - break; - case LJ_1200: - retVal.addAuxiliaryDecoder( - new LJ1200Decoder( aliasList ) ); - break; - case TAIT_1200: - retVal.addAuxiliaryDecoder( - new Tait1200Decoder( aliasList ) ); - break; - } - } - } - - return retVal; - } - - public static ChannelState getChannelStateNew( ProcessingChain chain, - AliasList aliasList ) - { - ChannelState retVal = null; - - DecodeConfiguration decodeConfig = - chain.getChannel().getDecodeConfiguration(); - - switch( decodeConfig.getDecoderType() ) - { - case AM: - retVal = new AMChannelState( chain, aliasList ); - break; - case NBFM: - retVal = new NBFMChannelState( chain, aliasList ); - break; - case LTR_STANDARD: - retVal = new LTRChannelState( chain, aliasList ); - break; - case LTR_NET: - retVal = new LTRNetChannelState( chain, aliasList ); - break; - case MPT1327: - DecodeConfigMPT1327 mpt = (DecodeConfigMPT1327)decodeConfig; - - String channelMapName = mpt.getChannelMapName(); - - ChannelMap map = chain.getResourceManager().getPlaylistManager() - .getPlayist().getChannelMapList() - .getChannelMap( channelMapName ); - - retVal = new MPT1327ChannelState( chain, aliasList, map ); - break; - case PASSPORT: - retVal = new PassportChannelState( chain, aliasList ); - break; - case P25_PHASE1: - retVal = new P25ChannelState( chain, aliasList ); - break; - default: - break; - } - - if( retVal != null ) - { - AuxDecodeConfiguration auxConfig = - chain.getChannel().getAuxDecodeConfiguration(); - - for( DecoderType auxDecoder: auxConfig.getAuxDecoders() ) - { - switch( auxDecoder ) - { - case FLEETSYNC2: - retVal.addAuxChannelState( - new FleetsyncChannelState( retVal ) ); - break; - case LJ_1200: - retVal.addAuxChannelState( - new LJ1200ChannelState( retVal ) ); - break; - case MDC1200: - retVal.addAuxChannelState( - new MDCChannelState( retVal ) ); - break; - case TAIT_1200: - retVal.addAuxChannelState( - new Tait1200ChannelState( retVal ) ); - } - } - } - - return retVal; - } -} diff --git a/src/decode/am/AMChannelState.java b/src/decode/am/AMChannelState.java deleted file mode 100644 index 15a26a04..00000000 --- a/src/decode/am/AMChannelState.java +++ /dev/null @@ -1,69 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package 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 deleted file mode 100644 index 12acac7d..00000000 --- a/src/decode/am/AMDecoder.java +++ /dev/null @@ -1,105 +0,0 @@ -package decode.am; - -import audio.AudioOutputImpl; -import audio.IAudioOutput; -import sample.real.RealSampleListener; -import source.Source.SampleType; -import decode.Decoder; -import decode.DecoderType; -import dsp.am.AMDemodulator; -import dsp.filter.ComplexFIRFilter; -import dsp.filter.DCRemovalFilter2; -import dsp.filter.FilterFactory; -import dsp.filter.FloatFIRFilter; -import dsp.filter.Window.WindowType; -import dsp.gain.ComplexAutomaticGainControl; -import dsp.gain.RealAutomaticGainControl; - -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.003; - - private ComplexFIRFilter mIQFilter; - private ComplexAutomaticGainControl mBasebandAGC = - new ComplexAutomaticGainControl(); - private AMDemodulator mDemodulator = new AMDemodulator(); - private DCRemovalFilter2 mDCRemovalFilter = - new DCRemovalFilter2( DC_REMOVAL_RATIO ); - private RealAutomaticGainControl mAudioAGC = new RealAutomaticGainControl(); - - private FloatFIRFilter mLowPassFilter; - - private AudioOutputImpl mAudioOutput = new AudioOutputImpl( "AM Decoder Audio Output" ); - - 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 ) - { - mIQFilter = new ComplexFIRFilter( - FilterFactory.getLowPass( 48000, 6500, 73, WindowType.HAMMING ), - 1.0 ); - /** - * 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 - */ - this.addComplexListener( mIQFilter ); - mIQFilter.setListener( mBasebandAGC ); - mBasebandAGC.setListener( mDemodulator ); - - - /** - * Remove the DC component that is present when we're mistuned - */ - mDemodulator.setListener( mDCRemovalFilter ); - - /** - * Route the demodulated, filtered samples back to this class to send - * to all registered listeners - */ - - mLowPassFilter = new FloatFIRFilter( - FilterFactory.getLowPass( 48000, 3000, 31, WindowType.COSINE ), 1.0 ); - - mDCRemovalFilter.setListener( mLowPassFilter ); - - mLowPassFilter.setListener( mAudioAGC ); - mAudioAGC.setListener( this.getRealReceiver() ); - - addRealSampleListener( mAudioOutput ); - } - } - - @Override - public IAudioOutput getAudioOutput() - { - return mAudioOutput; - } - - @Override - public DecoderType getType() - { - return DecoderType.AM; - } - - @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - throw new IllegalArgumentException( "cannot add real sample " - + "listener to AM demodulator - not implemented" ); - } - -} diff --git a/src/decode/am/AMPanel.java b/src/decode/am/AMPanel.java deleted file mode 100644 index 3842b807..00000000 --- a/src/decode/am/AMPanel.java +++ /dev/null @@ -1,136 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package 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/fleetsync2/FleetsyncActivitySummary.java b/src/decode/fleetsync2/FleetsyncActivitySummary.java deleted file mode 100644 index c1540651..00000000 --- a/src/decode/fleetsync2/FleetsyncActivitySummary.java +++ /dev/null @@ -1,171 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.fleetsync2; - -import java.util.Iterator; -import java.util.TreeSet; - -import message.Message; -import sample.Listener; -import alias.Alias; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; - -public class FleetsyncActivitySummary implements ActivitySummaryProvider, - Listener -{ - private AliasList mAliasList; - private TreeSet mIdents = new TreeSet(); - private TreeSet mEmergencyIdents = new TreeSet(); - - /** - * Compiles a summary of active talkgroups, unique radio ids encountered - * in the decoded messages. Talkgroups have to be heard a minimum of twice - * to be considered valid ... in order to weed out the invalid ones. - * - * Returns a textual summary of the activity - */ - public FleetsyncActivitySummary( AliasList list ) - { - mAliasList = list; - } - - public void dispose() - { - mIdents.clear(); - mEmergencyIdents.clear(); - } - - @Override - public void receive( Message message ) - { - if( message instanceof FleetsyncMessage ) - { - FleetsyncMessage fs = ((FleetsyncMessage)message); - - if( fs.isValid() ) - { - String from = fs.getFromID(); - mIdents.add( from ); - - String to = fs.getToID(); - - if( !to.startsWith( "xxx" )) - { - mIdents.add( to ); - } - } - - FleetsyncMessageType type = fs.getMessageType(); - - switch( type ) - { - case EMERGENCY: - case LONE_WORKER_EMERGENCY: - mEmergencyIdents.add( fs.getFromID() ); - break; - } - } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "=============================\n" ); - sb.append( "Decoder:\tFleetsync II\n\n" ); - sb.append( "Fleetsync Idents\n" ); - - if( mIdents.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mIdents.iterator(); - - while( it.hasNext() ) - { - String ident = it.next(); - - sb.append( " " ); - sb.append( formatIdent( ident ) ); - sb.append( " " ); - - Alias alias = mAliasList.getFleetsyncAlias( ident ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - sb.append( "\nFleetsync Emergency Activations\n" ); - - if( mEmergencyIdents.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mEmergencyIdents.iterator(); - - while( it.hasNext() ) - { - String ident = it.next(); - - sb.append( " " ); - sb.append( formatIdent( ident ) ); - sb.append( " " ); - - Alias alias = mAliasList.getFleetsyncAlias( ident ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - return sb.toString(); - } - - public static String formatIdent( String ident ) - { - StringBuilder sb = new StringBuilder(); - - if( ident.length() == 7 ) - { - sb.append( ident.substring( 0, 3 ) ); - sb.append( "-" ); - sb.append( ident.substring( 3, 7 ) ); - - return sb.toString(); - } - else - { - return ident; - } - } -} diff --git a/src/decode/fleetsync2/FleetsyncChannelState.java b/src/decode/fleetsync2/FleetsyncChannelState.java deleted file mode 100644 index afcfc228..00000000 --- a/src/decode/fleetsync2/FleetsyncChannelState.java +++ /dev/null @@ -1,193 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.fleetsync2; - -import message.Message; -import alias.Alias; -import controller.state.AuxChannelState; -import controller.state.ChannelState; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelState.State; - -public class FleetsyncChannelState extends AuxChannelState -{ - private String mFleetFrom; - private Alias mFleetFromAlias; - - private String mFleetTo; - private Alias mFleetToAlias; - - private String mMessage; - private String mMessageType; - - private FleetsyncActivitySummary mActivitySummary; - - public FleetsyncChannelState( ChannelState parentState ) - { - super( parentState ); - - mActivitySummary = - new FleetsyncActivitySummary( parentState.getAliasList() ); - } - - @Override - public void reset() - { - setFleetIDFrom( null ); - setFleetIDFromAlias( null ); - setFleetIDTo( null ); - setFleetIDToAlias( null ); - setMessage( null ); - setMessageType( null ); - } - - @Override - public void fade() - { - } - - @Override - public void receive( Message message ) - { - if( message instanceof FleetsyncMessage ) - { - mActivitySummary.receive( message ); - - FleetsyncMessage fleetsync = (FleetsyncMessage)message; - - setFleetIDFrom( fleetsync.getFromID() ); - setFleetIDFromAlias( fleetsync.getFromIDAlias() ); - - FleetsyncMessageType type = fleetsync.getMessageType(); - - if( type != FleetsyncMessageType.ANI ) - { - setFleetIDTo( fleetsync.getToID() ); - setFleetIDToAlias( fleetsync.getToIDAlias() ); - } - - setMessageType( type.getLabel() ); - - switch( type ) - { - case GPS: - setMessage( fleetsync.getGPSLocation() ); - break; - case STATUS: - StringBuilder sb = new StringBuilder(); - sb.append( fleetsync.getStatus() ); - - Alias status = fleetsync.getStatusAlias(); - - if( status != null ) - { - sb.append( "/" ); - sb.append( status.getName() ); - } - - setMessage( sb.toString() ); - break; - default: - break; - } - - /* Set the state to CALL on the parent channel state, so that the - * parent state timer can track turning off any updates we apply */ - mParentChannelState.setState( State.CALL ); - - FleetsyncCallEvent fsCallEvent = - FleetsyncCallEvent.getFleetsync2Event( fleetsync ); - - fsCallEvent.setAliasList( mParentChannelState.getAliasList() ); - - mParentChannelState.receiveCallEvent( fsCallEvent ); - } - } - - public String getFleetIDFrom() - { - return mFleetFrom; - } - - public void setFleetIDFrom( String from ) - { - mFleetFrom = from; - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - } - - public Alias getFleetIDFromAlias() - { - return mFleetFromAlias; - } - - public void setFleetIDFromAlias( Alias alias ) - { - mFleetFromAlias = alias; - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - } - - public String getFleetIDTo() - { - return mFleetTo; - } - - public void setFleetIDTo( String to ) - { - mFleetTo = to; - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - } - - public Alias getFleetIDToAlias() - { - return mFleetToAlias; - } - - public void setFleetIDToAlias( Alias alias ) - { - mFleetToAlias = alias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - - public String getMessage() - { - return mMessage; - } - - public void setMessage( String message ) - { - mMessage = message; - broadcastChange( ChangedAttribute.MESSAGE ); - } - - public String getMessageType() - { - return mMessageType; - } - - public void setMessageType( String type ) - { - mMessageType = type; - broadcastChange( ChangedAttribute.MESSAGE_TYPE ); - } - - @Override - public String getActivitySummary() - { - return mActivitySummary.getSummary(); - } -} diff --git a/src/decode/lj1200/LJ1200ChannelState.java b/src/decode/lj1200/LJ1200ChannelState.java deleted file mode 100644 index 8bd626dc..00000000 --- a/src/decode/lj1200/LJ1200ChannelState.java +++ /dev/null @@ -1,67 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.lj1200; - -import message.Message; -import controller.state.AuxChannelState; -import controller.state.ChannelState; - -public class LJ1200ChannelState extends AuxChannelState -{ - private LJ1200ActivitySummary mActivitySummary; - - public LJ1200ChannelState( ChannelState parentState ) - { - super( parentState ); - - mActivitySummary = - new LJ1200ActivitySummary( parentState.getAliasList() ); - } - - @Override - public void reset() - { - } - - @Override - public void fade() - { - } - - @Override - public void receive( Message message ) - { - if( message instanceof LJ1200Message ) - { - mActivitySummary.receive( message ); - - mParentChannelState.receiveCallEvent( - LJ1200CallEvent.getLJ1200Event( (LJ1200Message)message ) ); - } - else if( message instanceof LJ1200TransponderMessage ) - { - mActivitySummary.receive( message ); - } - } - - @Override - public String getActivitySummary() - { - return mActivitySummary.getSummary(); - } -} diff --git a/src/decode/lj1200/LJ1200MessageFilter.java b/src/decode/lj1200/LJ1200MessageFilter.java deleted file mode 100644 index 0c9a313e..00000000 --- a/src/decode/lj1200/LJ1200MessageFilter.java +++ /dev/null @@ -1,36 +0,0 @@ -package decode.lj1200; - -import java.util.Collections; -import java.util.List; - -import message.Message; -import filter.Filter; -import filter.FilterElement; - -public class LJ1200MessageFilter extends Filter -{ - - public LJ1200MessageFilter() - { - super( "LJ-1200 Message Filter" ); - } - - @Override - public boolean passes( Message message ) - { - return mEnabled && canProcess( message ); - } - - @Override - public boolean canProcess( Message message ) - { - return message instanceof LJ1200Message || - message instanceof LJ1200TransponderMessage; - } - - @Override - public List> getFilterElements() - { - return Collections.EMPTY_LIST; - } -} diff --git a/src/decode/ltrnet/LTRNetActivitySummary.java b/src/decode/ltrnet/LTRNetActivitySummary.java deleted file mode 100644 index af4b0d85..00000000 --- a/src/decode/ltrnet/LTRNetActivitySummary.java +++ /dev/null @@ -1,404 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.ltrnet; - -import java.text.DecimalFormat; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.TreeSet; - -import message.Message; -import alias.Alias; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; - -public class LTRNetActivitySummary implements ActivitySummaryProvider -{ - private static final int sINT_NULL_VALUE = -1; - private DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000"); - - private AliasList mAliasList; - private HashSet mTalkgroupsFirstHeard = new HashSet(); - private TreeSet mTalkgroups = new TreeSet(); - private TreeSet mESNs = new TreeSet(); - private TreeSet mUniqueIDs = new TreeSet(); - private TreeSet mNeighborIDs = new TreeSet(); - private TreeSet mSiteIDs = new TreeSet(); - private HashMap mReceiveFrequencies = - new HashMap(); - private HashMap mTransmitFrequencies = - new HashMap(); - private int mMonitoredLCN; - - /** - * Compiles a summary of active talkgroups, unique radio ids encountered - * in the decoded messages. Talkgroups have to be heard a minimum of twice - * to be considered valid ... in order to weed out the invalid ones. - * - * Returns a textual summary of the activity - */ - public LTRNetActivitySummary( AliasList list ) - { - mAliasList = list; - } - - @Override - public void receive( Message message ) - { - if( message instanceof LTRNetOSWMessage ) - { - LTRNetOSWMessage ltr = ((LTRNetOSWMessage)message); - - if( ltr.isValid() ) - { - switch( ltr.getMessageType() ) - { - case CA_STRT: - case CA_ENDD: - String talkgroup = ltr.getTalkgroupID(); - - if( mTalkgroupsFirstHeard.contains( talkgroup ) ) - { - mTalkgroups.add( talkgroup ); - } - else - { - mTalkgroupsFirstHeard.add( talkgroup ); - } - break; - case SY_IDLE: - mMonitoredLCN = ltr.getChannel(); - break; - case MA_CHNH: - break; - case MA_CHNL: - break; - case FQ_RXHI: - mReceiveFrequencies.put( ltr.getHomeRepeater(), - ltr.getFrequency() ); - break; - case FQ_RXLO: - break; - case FQ_TXHI: - mTransmitFrequencies.put( ltr.getHomeRepeater(), - ltr.getFrequency() ); - break; - case FQ_TXLO: - break; - case ID_ESNH: - break; - case ID_ESNL: - break; - case ID_NBOR: - String neighborID = ltr.getNeighborID(); - - if( neighborID != null ) - { - mNeighborIDs.add( neighborID ); - } - break; - case ID_UNIQ: - int uniqueid = ltr.getRadioUniqueID(); - - if( uniqueid != sINT_NULL_VALUE ) - { - mUniqueIDs.add( uniqueid ); - } - - break; - case ID_SITE: - String siteID = ltr.getSiteID(); - - if( siteID != null ) - { - mSiteIDs.add( siteID ); - } - break; - case UN_KNWN: - break; - default: - break; - } - } - } - else if( message instanceof LTRNetISWMessage ) - { - LTRNetISWMessage ltr = ((LTRNetISWMessage)message); - - if( ltr.isValid() ) - { - switch( ltr.getMessageType() ) - { - case CA_STRT: - case CA_ENDD: - String talkgroup = ltr.getTalkgroupID(); - - if( mTalkgroupsFirstHeard.contains( talkgroup ) ) - { - mTalkgroups.add( talkgroup ); - } - else - { - mTalkgroupsFirstHeard.add( talkgroup ); - } - break; - case ID_ESNH: - case ID_ESNL: - String esn = ltr.getESN(); - - if( !esn.contains( "xxxx" ) ) - { - mESNs.add( ltr.getESN() ); - } - break; - case ID_UNIQ: - int uniqueid = ltr.getRadioUniqueID(); - - if( uniqueid != sINT_NULL_VALUE ) - { - mUniqueIDs.add( uniqueid ); - } - - break; - default: - break; - } - } - } - - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "Activity Summary\n" ); - sb.append( "Decoder:\tLTR-Net\n\n" ); - - if( mSiteIDs.isEmpty() ) - { - sb.append( "Site:\tUnknown\n" ); - } - else - { - Iterator it = mSiteIDs.iterator(); - - while( it.hasNext() ) - { - sb.append( "Site:\t" ); - - String siteID = it.next(); - - sb.append( siteID ); - - Alias siteAlias = mAliasList.getSiteID( String.valueOf( siteID ) ); - - if( siteAlias != null ) - { - sb.append( " " ); - sb.append( siteAlias.getName() ); - } - - sb.append( "\n" ); - } - } - - sb.append( "\nLCNs (transmit | receive)\n" ); - - if( mReceiveFrequencies.isEmpty() && mTransmitFrequencies.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - for( int x = 1; x < 21; x++ ) - { - double rcv = 0.0d; - - if( mReceiveFrequencies.containsKey( x ) ) - { - rcv = mReceiveFrequencies.get( x ); - } - - double xmt = 0.0d; - - if( mTransmitFrequencies.containsKey( x ) ) - { - xmt = mTransmitFrequencies.get( x ); - } - - if( rcv > 0.0d || xmt > 0.0d ) - { - if( x < 10 ) - { - sb.append( " " ); - } - - sb.append( x ); - sb.append( ": " ); - - if( xmt == -1.0d ) - { - sb.append( "---.-----" ); - } - else - { - sb.append( mDecimalFormatter.format( xmt ) ); - } - sb.append( " | " ); - - if( rcv == -1.0d ) - { - sb.append( "---.-----" ); - } - else - { - sb.append( mDecimalFormatter.format( rcv ) ); - } - - if( x == mMonitoredLCN ) - { - sb.append( " **" ); - } - - sb.append( "\n" ); - } - } - } - - sb.append( "\nTalkgroups\n" ); - - if( mTalkgroups.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mTalkgroups.iterator(); - - while( it.hasNext() ) - { - String tgid = it.next(); - - sb.append( " " ); - sb.append( tgid ); - sb.append( " " ); - - Alias alias = mAliasList.getTalkgroupAlias( tgid ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - sb.append( "\nRadio Unique IDs\n" ); - - if( mUniqueIDs.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mUniqueIDs.iterator(); - - while( it.hasNext() ) - { - int uid = it.next(); - - sb.append( " " ); - sb.append( uid ); - sb.append( " " ); - - Alias alias = mAliasList.getUniqueID( uid ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - sb.append( "\nESNs\n" ); - - if( mESNs.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mESNs.iterator(); - - while( it.hasNext() ) - { - String esn = it.next(); - - sb.append( " " ); - sb.append( esn ); - sb.append( " " ); - - Alias alias = mAliasList.getESNAlias( esn ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - sb.append( "\nNeighbor Sites\n" ); - - if( mNeighborIDs.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mNeighborIDs.iterator(); - - while( it.hasNext() ) - { - String neighbor = it.next(); - - sb.append( " " ); - sb.append( neighbor ); - sb.append( " " ); - - Alias alias = mAliasList.getSiteID( String.valueOf( neighbor ) ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - return sb.toString(); - } -} diff --git a/src/decode/ltrnet/LTRNetChannelState.java b/src/decode/ltrnet/LTRNetChannelState.java deleted file mode 100644 index 5ed71f21..00000000 --- a/src/decode/ltrnet/LTRNetChannelState.java +++ /dev/null @@ -1,433 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.ltrnet; - -import java.awt.EventQueue; -import java.util.HashMap; - -import message.Message; -import alias.Alias; -import alias.AliasList; -import controller.activity.CallEvent; -import controller.activity.CallEvent.CallEventType; -import controller.activity.CallEventModel; -import controller.channel.ProcessingChain; -import controller.state.AuxChannelState; -import controller.state.ChannelState; -import controller.state.ChannelState.State; - -public class LTRNetChannelState extends ChannelState -{ - private String mTalkgroup; - private Alias mTalkgroupAlias; - private String mDescription; - private int mChannelNumber; - private HashMap mActiveCalls = new HashMap(); - private LTRNetActivitySummary mActivitySummary; - - public LTRNetChannelState( ProcessingChain channel, AliasList aliasList ) - { - super( channel, aliasList ); - - mActivitySummary = new LTRNetActivitySummary( aliasList ); - } - - /** - * Intercept the fade event so that we can generate a call end event - */ - @Override - public void fade( final CallEventType type ) - { - /* - * We can receive multiple call tear-down messages -- only respond to - * the message that can change the state to fade - */ - if( getState().canChangeTo( State.FADE ) ) - { - LTRCallEvent current = getCurrentLTRCallEvent(); - - if( current != null ) - { - /* Close out the call event. If we only received 1 call - * message, then flag it as noise and remove the call event */ - if( current.isValid() ) - { - mCallEventModel.setEnd( current ); - } - else - { - mCallEventModel.remove( current ); - } - } - } - - setCurrentCallEvent( null ); - - mActiveCalls.clear(); - - super.fade( type ); - } - - public LTRCallEvent getCurrentLTRCallEvent() - { - CallEvent current = getCurrentCallEvent(); - - if( current != null ) - { - return (LTRCallEvent)current; - } - - return null; - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mActivitySummary.getSummary() ); - - for( AuxChannelState state: mAuxChannelStates ) - { - sb.append( state.getActivitySummary() ); - sb.append( "\n\n" ); - } - - return sb.toString(); - } - - @Override - public void receive( Message message ) - { - super.receive( message ); - - if( message instanceof LTRNetOSWMessage ) - { - mActivitySummary.receive( message ); - - LTRNetOSWMessage ltr = (LTRNetOSWMessage)message; - - Alias talkgroupAlias = ltr.getTalkgroupIDAlias(); - - switch( ltr.getMessageType() ) - { - case CA_STRT: - if( mChannelNumber == 0 ) - { - mChannelNumber = ltr.getChannel(); - } - - if( ltr.getChannel() == mChannelNumber ) - { - processCallMessage( ltr ); - - if( ltr.getGroup() == 253 ) - { - mDescription = "REGISTER"; - broadcastChange( ChangedAttribute.DESCRIPTION ); - } - else - { - mTalkgroup = ltr.getTalkgroupID(); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - if( talkgroupAlias != null ) - { - mTalkgroupAlias = talkgroupAlias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - } - } - else - { - //Call Detect - if( !mActiveCalls.containsKey( ltr.getChannel() ) || - !mActiveCalls.get( ltr.getChannel() ) - .contentEquals( ltr.getTalkgroupID() ) ) - { - mActiveCalls.put( ltr.getChannel(), - ltr.getTalkgroupID() ); - - mCallEventModel.add( - new LTRCallEvent.Builder( CallEventType.CALL_DETECT ) - .aliasList( mAliasList ) - .channel( String.valueOf( ltr.getChannel() ) ) - .to( ltr.getTalkgroupID() ) - .build() ); - } - } - break; - case CA_ENDD: - if( ltr.getGroup() != 253 ) - { - mTalkgroup = ltr.getTalkgroupID(); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - if( talkgroupAlias != null ) - { - mTalkgroupAlias = talkgroupAlias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - } - - fade( CallEventType.CALL_END ); - break; - case ID_UNIQ: - mDescription = "REGISTER UID"; - broadcastChange( ChangedAttribute.DESCRIPTION ); - - mTalkgroup = String.valueOf( ltr.getRadioUniqueID() ); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - mTalkgroupAlias = ltr.getRadioUniqueIDAlias(); - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - - final LTRCallEvent currentEvent = getCurrentLTRCallEvent(); - - if( currentEvent != null && - currentEvent.getCallEventType() == CallEventType.REGISTER ) - { - final String uid = String.valueOf( ltr.getRadioUniqueID() ); - - if( uid != null ) - { - mCallEventModel.setFromID( currentEvent, uid ); - } - } - else - { - mCallEventModel.add( - new LTRCallEvent.Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .details( "Radio Unique ID" ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .from( String.valueOf( ltr.getRadioUniqueID() ) ) - .build() ); - } - - setState( State.CALL ); - break; - case SY_IDLE: - mChannelNumber = ltr.getChannel(); - broadcastChange( ChangedAttribute.CHANNEL_NUMBER ); - break; - default: - break; - } - - } - else if( message instanceof LTRNetISWMessage ) - { - mActivitySummary.receive( message ); - - LTRNetISWMessage ltr = (LTRNetISWMessage)message; - - Alias talkgroupAlias = ltr.getTalkgroupIDAlias(); - - switch( ltr.getMessageType() ) - { - case CA_STRT: - processCallMessage( ltr ); - - if( ltr.getGroup() == 253 ) - { - mDescription = "REGISTER"; - broadcastChange( ChangedAttribute.DESCRIPTION ); - } - else - { - mTalkgroup = ltr.getTalkgroupID(); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - if( talkgroupAlias != null ) - { - mTalkgroupAlias = talkgroupAlias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - } - break; - case CA_ENDD: - mTalkgroup = ltr.getTalkgroupID(); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - if( talkgroupAlias != null ) - { - mTalkgroupAlias = talkgroupAlias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - fade( CallEventType.CALL_END ); - break; - case ID_UNIQ: - mDescription = "REGISTER UID"; - broadcastChange( ChangedAttribute.DESCRIPTION ); - - mTalkgroup = String.valueOf( ltr.getRadioUniqueID() ); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - mTalkgroupAlias = ltr.getRadioUniqueIDAlias(); - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - - if( getState() != State.DATA ) - { - LTRCallEvent event = - new LTRCallEvent.Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .channel( String.valueOf( ltr.getChannel() ) ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .to( ltr.getTalkgroupID() ) - .build(); - - mCallEventModel.add( event ); - - setCurrentCallEvent( event ); - - setState( State.DATA ); - } - - if( mCurrentCallEvent != null ) - { - mCurrentCallEvent.setFromID( - String.valueOf( ltr.getRadioUniqueID() ) ); - mCurrentCallEvent.setDetails( "Unique ID" ); - } - break; - case ID_ESNH: - case ID_ESNL: - mDescription = "ESN"; - broadcastChange( ChangedAttribute.DESCRIPTION ); - - mTalkgroup = ltr.getESN(); - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - mTalkgroupAlias = ltr.getESNAlias(); - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - - setState( State.DATA ); - - mCallEventModel.add( - new LTRCallEvent.Builder( CallEventType.REGISTER_ESN ) - .aliasList( mAliasList ) - .details( "ESN:" + ltr.getESN() ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .from( ltr.getESN() ) - .build() ); - break; - default: - break; - } - - } - } - - private void processCallMessage( LTRNetMessage message ) - { - LTRCallEvent current = getCurrentLTRCallEvent(); - - if( current == null || !current.addMessage( message ) ) - { - if( current != null ) - { - mCallEventModel.remove( current ); - } - - LTRCallEvent event; - - if( message.getGroup() == 253 ) - { - event = new LTRCallEvent.Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .channel( String.valueOf( message.getChannel() ) ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .to( message.getTalkgroupID() ) - .build(); - - setState( State.DATA ); - } - else - { - event = new LTRCallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( String.valueOf( message.getChannel() ) ) - .frequency( mProcessingChain.getChannel() - .getTunerChannel().getFrequency() ) - .to( message.getTalkgroupID() ) - .build(); - - setState( State.CALL ); - } - - mCallEventModel.add( event ); - - setCurrentCallEvent( event ); - } - else - { - if( getState() == State.CALL ) - { - setState( State.CALL ); - } - else if( getState() == State.DATA ) - { - setState( State.DATA ); - } - } - } - - public void reset() - { - mTalkgroup = null; - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - mTalkgroupAlias = null; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - - mDescription = null; - broadcastChange( ChangedAttribute.DESCRIPTION ); - - super.reset(); - } - - public String getToTalkgroup() - { - return mTalkgroup; - } - - public Alias getToTalkgroupAlias() - { - return mTalkgroupAlias; - } - - public String getDescription() - { - return mDescription; - } - - public boolean hasChannelNumber() - { - return mChannelNumber != 0; - } - - public int getChannelNumber() - { - return mChannelNumber; - } -} diff --git a/src/decode/ltrnet/LTRNetDecoder.java b/src/decode/ltrnet/LTRNetDecoder.java deleted file mode 100644 index 163573cc..00000000 --- a/src/decode/ltrnet/LTRNetDecoder.java +++ /dev/null @@ -1,175 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.ltrnet; - -import instrument.Instrumentable; -import instrument.tap.Tap; - -import java.util.ArrayList; -import java.util.List; - -import message.MessageDirection; -import sample.real.RealSampleListener; -import source.Source.SampleType; -import alias.AliasList; -import audio.AudioOutputImpl; -import audio.IAudioOutput; -import bits.MessageFramer; -import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; -import decode.config.DecodeConfiguration; -import dsp.filter.DCRemovalFilter2; -import dsp.fsk.LTRFSKDecoder; -import dsp.nbfm.FilteringNBFMDemodulator; - -public class LTRNetDecoder extends Decoder implements Instrumentable -{ - /** - * This value determines how quickly the DC remove filter responds to - * changes in frequency. - */ - private static final double sDC_REMOVAL_RATIO = 0.000003; - - public static final int sLTR_STANDARD_MESSAGE_LENGTH = 40; - private FilteringNBFMDemodulator mDemodulator; - private DCRemovalFilter2 mDCRemovalFilter; - private LTRFSKDecoder mLTRFSKDecoder; - private MessageFramer mLTRMessageFramer; - private LTRNetMessageProcessor mLTRMessageProcessor; - private AudioOutputImpl mAudioOutput = new AudioOutputImpl( "LTRNet Decoder Audio Output" ); - - private List mAvailableTaps; - - public LTRNetDecoder( DecodeConfiguration config, - SampleType sampleType, - AliasList aliasList, - MessageDirection direction ) - { - super( config, 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 FilteringNBFMDemodulator(); - this.addComplexListener( mDemodulator ); - - /** - * Remove the DC component that is present when we're mistuned - */ - mDCRemovalFilter = new DCRemovalFilter2( sDC_REMOVAL_RATIO ); - mDemodulator.addListener( mDCRemovalFilter ); - - mDCRemovalFilter.setListener( this.getRealReceiver() ); - } - - addRealSampleListener( mAudioOutput ); - - mLTRFSKDecoder = new LTRFSKDecoder(); - - /** - * The DC removal filter will impact performance of the LTRFSKDemod, - * so we get the samples straight from the NBFM demod - */ - if( mSourceSampleType == SampleType.COMPLEX ) - { - mDemodulator.addListener( mLTRFSKDecoder ); - } - else - { - this.addRealSampleListener( mLTRFSKDecoder ); - } - - if( direction == MessageDirection.OSW ) - { - mLTRMessageFramer = - new MessageFramer( SyncPattern.LTR_STANDARD_OSW.getPattern(), - sLTR_STANDARD_MESSAGE_LENGTH ); - } - else - { - mLTRMessageFramer = - new MessageFramer( SyncPattern.LTR_STANDARD_ISW.getPattern(), - sLTR_STANDARD_MESSAGE_LENGTH ); - } - - mLTRFSKDecoder.addListener( mLTRMessageFramer ); - - mLTRMessageProcessor = new LTRNetMessageProcessor( direction, aliasList ); - mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); - mLTRMessageProcessor.addMessageListener( this ); - } - - @Override - public DecoderType getType() - { - return DecoderType.LTR_NET; - } - - @Override - public List getTaps() - { - if( mAvailableTaps == null ) - { - mAvailableTaps = new ArrayList(); - - mAvailableTaps.addAll( mLTRFSKDecoder.getTaps() ); - } - - return mAvailableTaps; - } - - @Override - public void addTap( Tap tap ) - { - mLTRFSKDecoder.addTap( tap ); - } - - @Override - public void removeTap( Tap tap ) - { - mLTRFSKDecoder.removeTap( tap ); - } - - @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - if( mDemodulator != null ) - { - mDemodulator.addListener( listener ); - } - // TODO Auto-generated method stub - } - - @Override - public IAudioOutput getAudioOutput() - { - return mAudioOutput; - } -} diff --git a/src/decode/ltrnet/LTRNetMessageProcessor.java b/src/decode/ltrnet/LTRNetMessageProcessor.java deleted file mode 100644 index 7a7dcc19..00000000 --- a/src/decode/ltrnet/LTRNetMessageProcessor.java +++ /dev/null @@ -1,165 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.ltrnet; - -import java.util.HashMap; - -import message.Message; -import message.MessageDirection; -import message.MessageType; -import sample.Broadcaster; -import sample.Listener; -import alias.AliasList; -import bits.BinaryMessage; - -public class LTRNetMessageProcessor implements Listener -{ - private Broadcaster mBroadcaster = new Broadcaster(); - - private HashMap mReceiveHighAuxMessages = - new HashMap(); - private HashMap mReceiveLowAuxMessages = - new HashMap(); - - private HashMap mTransmitHighAuxMessages = - new HashMap(); - private HashMap mTransmitLowAuxMessages = - new HashMap(); - - private LTRNetISWMessage mESNHighMessage; - private LTRNetISWMessage mESNLowMessage; - - private MessageDirection mDirection; - private AliasList mAliasList; - - public LTRNetMessageProcessor( MessageDirection direction, - AliasList list ) - { - mDirection = direction; - mAliasList = list; - } - - @Override - public void receive( BinaryMessage buffer ) - { - LTRNetMessage message; - - if( mDirection == MessageDirection.OSW ) - { - message = new LTRNetOSWMessage( buffer, mAliasList ); - - if( message.isValid() ) - { - if( message.getMessageType() == MessageType.FQ_TXHI ) - { - mTransmitHighAuxMessages.put( message.getHomeRepeater(), - (LTRNetOSWMessage)message ); - - message.setAuxiliaryMessage( mTransmitLowAuxMessages.get( - message.getHomeRepeater() ) ); - } - else if( message.getMessageType() == MessageType.FQ_TXLO ) - { - mTransmitLowAuxMessages.put( message.getHomeRepeater(), - (LTRNetOSWMessage)message ); - - message.setAuxiliaryMessage( mTransmitHighAuxMessages.get( - message.getHomeRepeater() ) ); - } - else if( message.getMessageType() == MessageType.FQ_RXHI ) - { - mReceiveHighAuxMessages.put( message.getHomeRepeater(), - (LTRNetOSWMessage)message ); - - message.setAuxiliaryMessage( mReceiveLowAuxMessages.get( - message.getHomeRepeater() ) ); - } - else if( message.getMessageType() == MessageType.FQ_RXLO ) - { - mReceiveLowAuxMessages.put( message.getHomeRepeater(), - (LTRNetOSWMessage)message ); - message.setAuxiliaryMessage( mReceiveHighAuxMessages.get( - message.getHomeRepeater() ) ); - } - } - } - else - { - buffer.flip( 0, 40 ); - - message = new LTRNetISWMessage( buffer, mAliasList ); - - if( message.isValid() ) - { - /** - * Catch ESN High messages to marry up with ESN Low messages. Purge - * the ESN high message after 2 messages, so that it doesn't get - * paired with the wrong esn low message - */ - if( message.getMessageType() == MessageType.ID_ESNH ) - { - mESNHighMessage = (LTRNetISWMessage)message; - - if( mESNLowMessage != null ) - { - /* Check that the stored message is not older than - * 5 seconds */ - if( System.currentTimeMillis() - - mESNLowMessage.getTimeReceived() < 5000 ) - { - ((LTRNetISWMessage)message).setAuxiliaryMessage( mESNLowMessage ); - } - else - { - mESNLowMessage = null; - } - } - } - else if( message.getMessageType() == MessageType.ID_ESNL ) - { - mESNLowMessage = (LTRNetISWMessage)message; - - if( mESNHighMessage != null ) - { - /* Check that the stored message is not older than 5 seconds */ - if( System.currentTimeMillis() - mESNHighMessage.getTimeReceived() < 5000 ) - { - ((LTRNetISWMessage)message).setAuxiliaryMessage( mESNHighMessage ); - } - else - { - mESNHighMessage = null; - } - } - } - } - } - - mBroadcaster.receive( message ); - } - - public void addMessageListener( Listener listener ) - { - mBroadcaster.addListener( listener ); - } - - public void removeMessageListener( Listener listener ) - { - mBroadcaster.removeListener( listener ); - } -} diff --git a/src/decode/ltrstandard/LTRChannelState.java b/src/decode/ltrstandard/LTRChannelState.java deleted file mode 100644 index 5563b5bf..00000000 --- a/src/decode/ltrstandard/LTRChannelState.java +++ /dev/null @@ -1,240 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.ltrstandard; - -import java.util.HashMap; - -import message.Message; -import alias.Alias; -import alias.AliasList; -import controller.activity.CallEvent; -import controller.activity.CallEvent.CallEventType; -import controller.activity.CallEventModel; -import controller.channel.ProcessingChain; -import controller.state.AuxChannelState; -import controller.state.ChannelState; -import decode.ltrnet.LTRCallEvent; - -public class LTRChannelState extends ChannelState -{ - private String mTalkgroup; - private Alias mTalkgroupAlias; - private int mChannelNumber; - private HashMap mActiveCalls = new HashMap(); - private LTRStandardActivitySummary mActivitySummary; - - public LTRChannelState( ProcessingChain channel, AliasList aliasList ) - { - super( channel, aliasList ); - - mActivitySummary = new LTRStandardActivitySummary( aliasList ); - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mActivitySummary.getSummary() ); - - for( AuxChannelState state: mAuxChannelStates ) - { - sb.append( state.getActivitySummary() ); - sb.append( "\n\n" ); - } - - return sb.toString(); - } - - @Override - public void receive( Message message ) - { - super.receive( message ); - - if( message instanceof LTRStandardOSWMessage ) - { - mActivitySummary.receive( message ); - - LTRStandardOSWMessage ltr = (LTRStandardOSWMessage)message; - - switch( ltr.getMessageType() ) - { - /** - * Process Call Message - * - * States: - * 1) Call message with invalid LCN - * 2) State=IDLE/FADE, new CA_STRT - * 3) State=CALL, new CA_STRT, same talkgroup - * 4) State=CALL, new CA_STRT, different talkgroup - */ - case CA_STRT: - if( 0 < ltr.getChannel() && ltr.getChannel() <=20 ) - { - /* If we haven't captured the LCN yet ... */ - if( mChannelNumber == 0 ) - { - mChannelNumber = ltr.getChannel(); - broadcastChange( ChangedAttribute.CHANNEL_NUMBER ); - } - - /* If this call event is for this channel, then update - * talkgroup and alias */ - if( ltr.getChannel() == mChannelNumber ) - { - mTalkgroup = ltr.getTalkgroupID(); - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - - mTalkgroupAlias = ltr.getTalkgroupIDAlias(); - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - - LTRCallEvent current = getCurrentLTRCallEvent(); - - /* If we have an ongoing call, then add this message - * to the current event - if addMessage returns - * false, then we have a different talkgroup and a - * new call event, so close out the old one. */ - if( current != null && !current.addMessage( ltr ) ) - { - setState( State.FADE ); - } - - if( current == null ) - { - LTRCallEvent event = - new LTRCallEvent.Builder( CallEventType.CALL ) - .to( ltr.getTalkgroupID() ) - .aliasList( getAliasList() ) - .channel( String.valueOf( ltr.getChannel() ) ) - .frequency( ( ltr.getChannel() == mChannelNumber ) ? - mProcessingChain.getChannel() - .getTunerChannel().getFrequency() : 0 ) - .build(); - - mCallEventModel.add( event ); - - setCurrentCallEvent( event ); - } - - setState( State.CALL ); - } - } - break; - case CA_ENDD: - mTalkgroup = ltr.getTalkgroupID(); - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - - mTalkgroupAlias = ltr.getTalkgroupIDAlias(); - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - - fade( CallEventType.CALL_END ); - break; - case SY_IDLE: - int channel = ltr.getChannel(); - - if( 0 < channel && channel <= 20 && - channel == ltr.getHomeRepeater() && - channel == ltr.getFree() ) - { - mChannelNumber = channel; - broadcastChange( ChangedAttribute.CHANNEL_NUMBER ); - } - break; - default: - break; - } - } - } - - public LTRCallEvent getCurrentLTRCallEvent() - { - CallEvent current = getCurrentCallEvent(); - - if( current != null ) - { - return (LTRCallEvent)current; - } - - return null; - } - - /** - * Intercept the fade event so that we can generate a call end event - */ - @Override - public void fade( final CallEventType type ) - { - /* We can receive multiple call tear-down messages -- only respond to - * the message that can change the state to fade */ - if( getState().canChangeTo( State.FADE ) ) - { - mActiveCalls.clear(); - - LTRCallEvent current = getCurrentLTRCallEvent(); - - if( current != null ) - { - /* Close out the call event. If we only received 1 call - * message, then flag it as noise and remove the call event */ - if( current.isValid() ) - { - mCallEventModel.setEnd( current ); - } - else - { - mCallEventModel.remove( current ); - } - } - - setCurrentCallEvent( null ); - } - - super.fade( type ); - } - - public void reset() - { - mTalkgroup = null; - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - - mTalkgroupAlias = null; - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - - super.reset(); - } - - public String getTalkgroup() - { - return mTalkgroup; - } - - public Alias getTalkgroupAlias() - { - return mTalkgroupAlias; - } - - public boolean hasChannelNumber() - { - return mChannelNumber != 0; - } - - public int getChannelNumber() - { - return mChannelNumber; - } -} diff --git a/src/decode/ltrstandard/LTRStandardActivitySummary.java b/src/decode/ltrstandard/LTRStandardActivitySummary.java deleted file mode 100644 index cee0a23e..00000000 --- a/src/decode/ltrstandard/LTRStandardActivitySummary.java +++ /dev/null @@ -1,209 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.ltrstandard; - -import java.text.DecimalFormat; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Iterator; -import java.util.TreeSet; - -import message.Message; -import sample.Listener; -import alias.Alias; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; - -public class LTRStandardActivitySummary implements ActivitySummaryProvider, - Listener -{ - private static final int sINT_NULL_VALUE = -1; - private DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000"); - - private AliasList mAliasList; - private HashSet mTalkgroupsFirstHeard = new HashSet(); - private TreeSet mTalkgroups = new TreeSet(); - private TreeSet mActiveLCNs = new TreeSet(); - private int mMonitoredLCN; - - /** - * Compiles a summary of active talkgroups, unique radio ids encountered - * in the decoded messages. Talkgroups have to be heard a minimum of twice - * to be considered valid ... in order to weed out the invalid ones. - * - * Returns a textual summary of the activity - */ - public LTRStandardActivitySummary( AliasList list ) - { - mAliasList = list; - } - - @Override - public void receive( Message message ) - { - if( message instanceof LTRStandardOSWMessage ) - { - LTRStandardOSWMessage ltr = ((LTRStandardOSWMessage)message); - - if( ltr.isValid() ) - { - switch( ltr.getMessageType() ) - { - case CA_STRT: - case CA_ENDD: - int home = ltr.getHomeRepeater(); - - if( 0 < home && home <= 20 ) - { - String talkgroup = ltr.getTalkgroupID( false ); - - if( mTalkgroupsFirstHeard.contains( talkgroup ) ) - { - mTalkgroups.add( talkgroup ); - } - else - { - mTalkgroupsFirstHeard.add( talkgroup ); - } - } - - int channel = ltr.getChannel(); - - if( 0 < channel && channel <= 20 ) - { - if( channel == home ) - { - mMonitoredLCN = channel; - mActiveLCNs.add( channel ); - } - - int free = ltr.getFree(); - - if( 0 < free && free <= 20 ) - { - mActiveLCNs.add( free ); - } - } - - break; - case SY_IDLE: - int idleChannel = ltr.getChannel(); - - if( idleChannel == ltr.getHomeRepeater() && - idleChannel == ltr.getFree() ) - { - mMonitoredLCN = idleChannel; - mActiveLCNs.add( idleChannel ); - } - break; - case UN_KNWN: - default: - break; - } - } - } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "Activity Summary\n\n" ); - - sb.append( "Decoder:\tLTR-Standard\n" ); - - sb.append( "Monitored LCN: " ); - - if( mMonitoredLCN > 0 ) - { - sb.append( mMonitoredLCN ); - } - else - { - sb.append( "unknown" ); - } - - sb.append( "\n" ); - - Integer[] lcns = mActiveLCNs.toArray( new Integer[0] ); - - sb.append( "Active LCNs:\t" ); - - if( lcns.length > 0 ) - { - sb.append( Arrays.toString( lcns ) ); - } - else - { - sb.append( "none" ); - } - sb.append( "\n\n" ); - - sb.append( "Talkgroups\n" ); - - if( mTalkgroups.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mTalkgroups.iterator(); - - while( it.hasNext() ) - { - String tgid = it.next(); - - sb.append( " " ); - sb.append( formatTalkgroup( tgid ) ); - sb.append( " " ); - - Alias alias = mAliasList.getTalkgroupAlias( tgid ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - return sb.toString(); - } - - public static String formatTalkgroup( String talkgroup ) - { - StringBuilder sb = new StringBuilder(); - - if( talkgroup.length() == 6 ) - { - sb.append( talkgroup.substring( 0, 1 ) ); - sb.append( "-" ); - sb.append( talkgroup.substring( 1, 3 ) ); - sb.append( "-" ); - sb.append( talkgroup.substring( 3, 6 ) ); - - return sb.toString(); - } - else - { - return talkgroup; - } - } -} diff --git a/src/decode/ltrstandard/LTRStandardDecoder.java b/src/decode/ltrstandard/LTRStandardDecoder.java deleted file mode 100644 index 338e7003..00000000 --- a/src/decode/ltrstandard/LTRStandardDecoder.java +++ /dev/null @@ -1,141 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.ltrstandard; - -import message.MessageDirection; -import sample.real.RealSampleListener; -import source.Source.SampleType; -import alias.AliasList; -import audio.AudioOutputImpl; -import audio.IAudioOutput; -import bits.MessageFramer; -import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; -import decode.config.DecodeConfiguration; -import dsp.filter.DCRemovalFilter2; -import dsp.fsk.LTRFSKDecoder; -import dsp.nbfm.FilteringNBFMDemodulator; - -public class LTRStandardDecoder extends Decoder -{ - /** - * This value determines how quickly the DC remove filter responds to - * changes in frequency. - */ - private static final double sDC_REMOVAL_RATIO = 0.000003; - - public static final int sLTR_STANDARD_MESSAGE_LENGTH = 40; - private FilteringNBFMDemodulator mDemodulator; - private DCRemovalFilter2 mDCRemovalFilter; - private LTRFSKDecoder mLTRFSKDecoder; - private MessageFramer mLTRMessageFramer; - private LTRStandardMessageProcessor mLTRMessageProcessor; - private AudioOutputImpl mAudioOutput = new AudioOutputImpl( "LTR Standard Decoder Audio Output" ); - - public LTRStandardDecoder( DecodeConfiguration config, - SampleType sampleType, - AliasList aliasList, - MessageDirection direction ) - { - super( config, 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 Short - * 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 FilteringNBFMDemodulator(); - this.addComplexListener( mDemodulator ); - - /** - * Remove the DC component that is present when we're mistuned - */ - mDCRemovalFilter = new DCRemovalFilter2( sDC_REMOVAL_RATIO ); - mDemodulator.addListener( mDCRemovalFilter ); - - mDCRemovalFilter.setListener( this.getRealReceiver() ); - } - - addRealSampleListener( mAudioOutput ); - - mLTRFSKDecoder = new LTRFSKDecoder(); - - /** - * The DC removal filter will impact performance of the LTRFSKDemod, - * so we get the samples straight from the NBFM demod - */ - if( mSourceSampleType == SampleType.COMPLEX ) - { - mDemodulator.addListener( mLTRFSKDecoder ); - } - else - { - this.addRealSampleListener( mLTRFSKDecoder ); - } - - if( direction == MessageDirection.OSW ) - { - mLTRMessageFramer = - new MessageFramer( SyncPattern.LTR_STANDARD_OSW.getPattern(), - sLTR_STANDARD_MESSAGE_LENGTH ); - } - else - { - mLTRMessageFramer = - new MessageFramer( SyncPattern.LTR_STANDARD_ISW.getPattern(), - sLTR_STANDARD_MESSAGE_LENGTH ); - } - - mLTRFSKDecoder.addListener( mLTRMessageFramer ); - - mLTRMessageProcessor = new LTRStandardMessageProcessor( direction, aliasList ); - mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); - mLTRMessageProcessor.addMessageListener( this ); - } - - @Override - public DecoderType getType() - { - return DecoderType.LTR_STANDARD; - } - - @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - if( mDemodulator != null ) - { - mDemodulator.addListener( listener ); - } - } - - @Override - public IAudioOutput getAudioOutput() - { - return mAudioOutput; - } -} diff --git a/src/decode/mdc1200/MDCActivitySummary.java b/src/decode/mdc1200/MDCActivitySummary.java deleted file mode 100644 index f937ad91..00000000 --- a/src/decode/mdc1200/MDCActivitySummary.java +++ /dev/null @@ -1,131 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.mdc1200; - -import java.util.Iterator; -import java.util.TreeSet; - -import message.Message; -import sample.Listener; -import alias.Alias; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; - -public class MDCActivitySummary implements ActivitySummaryProvider, - Listener -{ - private AliasList mAliasList; - private TreeSet mIdents = new TreeSet(); - private TreeSet mEmergencyIdents = new TreeSet(); - - /** - * Compiles a summary of active talkgroups, unique radio ids encountered - * in the decoded messages. Talkgroups have to be heard a minimum of twice - * to be considered valid ... in order to weed out the invalid ones. - * - * Returns a textual summary of the activity - */ - public MDCActivitySummary( AliasList list ) - { - mAliasList = list; - } - - @Override - public void receive( Message message ) - { - if( message instanceof MDCMessage ) - { - MDCMessage mdc = ((MDCMessage)message); - - mIdents.add( mdc.getUnitID() ); - - if( mdc.isEmergency() ) - { - mEmergencyIdents.add( mdc.getUnitID() ); - } - } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "=============================\n" ); - sb.append( "Decoder:\tMDC-1200\n\n" ); - sb.append( "MDC-1200 Idents\n" ); - - if( mIdents.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mIdents.iterator(); - - while( it.hasNext() ) - { - String ident = it.next(); - - sb.append( " " ); - sb.append( ident ); - sb.append( " " ); - - Alias alias = mAliasList.getMDC1200Alias( ident ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - sb.append( "MDC-1200 Emergency Idents\n" ); - - if( mEmergencyIdents.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mEmergencyIdents.iterator(); - - while( it.hasNext() ) - { - String ident = it.next(); - - sb.append( " " ); - sb.append( ident ); - sb.append( " " ); - - Alias alias = mAliasList.getMDC1200Alias( ident ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - return sb.toString(); - } -} diff --git a/src/decode/mdc1200/MDCChannelState.java b/src/decode/mdc1200/MDCChannelState.java deleted file mode 100644 index b73c9d9a..00000000 --- a/src/decode/mdc1200/MDCChannelState.java +++ /dev/null @@ -1,183 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.mdc1200; - -import message.Message; -import alias.Alias; -import controller.state.AuxChannelState; -import controller.state.ChannelState; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelState.State; - -public class MDCChannelState extends AuxChannelState -{ - private String mFrom; - private Alias mFromAlias; - private String mTo; - private Alias mToAlias; - private String mMessage; - private String mMessageType; - - private MDCActivitySummary mActivitySummary; - - public MDCChannelState( ChannelState parentState ) - { - super( parentState ); - - mActivitySummary = new MDCActivitySummary( parentState.getAliasList() ); - } - - @Override - public void reset() - { - setFrom( null ); - setFromAlias( null ); - setTo( null); - setToAlias( null ); - setMessage( null ); - setMessageType( null ); - } - - @Override - public void fade() {} - - @Override - public void receive( Message message ) - { - if( message instanceof MDCMessage ) - { - mActivitySummary.receive( message ); - - MDCMessage mdc = (MDCMessage)message; - - setFrom( mdc.getFromID() ); - setFromAlias( mdc.getFromIDAlias() ); - - MDCMessageType type = mdc.getMessageType(); - - setMessageType( type.getLabel() ); - - StringBuilder sb = new StringBuilder(); - - switch( type ) - { - case ACKNOWLEDGE: - case ANI: - case EMERGENCY: - case PAGING: - case STATUS: - default: - sb.append( "OPCODE " ); - sb.append( String.valueOf( mdc.getOpcode() ) ); - - if( mdc.isBOT() ) - { - sb.append( " TYPE:BOT" ); - } - - if( mdc.isEOT() ) - { - sb.append( " TYPE:EOT" ); - } - break; - } - - setMessage( sb.toString() ); - - //Set the state to CALL on the parent channel state, so that the - //timer can track turning off any updates we apply - mParentChannelState.setState( State.CALL ); - - MDCCallEvent event = MDCCallEvent.getMDCCallEvent( mdc ); - event.setAliasList( mParentChannelState.getAliasList() ); - mParentChannelState.receiveCallEvent( event ); - } - } - - public String getFrom() - { - return mFrom; - } - - public void setFrom( String from ) - { - mFrom = from; - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - } - - public Alias getFromAlias() - { - return mFromAlias; - } - - public void setFromAlias( Alias alias ) - { - mFromAlias = alias; - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - } - - public String getTo() - { - return mTo; - } - - public void setTo( String to ) - { - mTo = to; - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - } - - public Alias getToAlias() - { - return mToAlias; - } - - public void setToAlias( Alias alias ) - { - mToAlias = alias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - - public String getMessage() - { - return mMessage; - } - - public void setMessage( String message ) - { - mMessage = message; - broadcastChange( ChangedAttribute.MESSAGE ); - } - - public String getMessageType() - { - return mMessageType; - } - - public void setMessageType( String type ) - { - mMessageType = type; - broadcastChange( ChangedAttribute.MESSAGE_TYPE ); - } - - @Override - public String getActivitySummary() - { - return mActivitySummary.getSummary(); - } -} diff --git a/src/decode/mpt1327/MPT1327ActivitySummary.java b/src/decode/mpt1327/MPT1327ActivitySummary.java deleted file mode 100644 index 018fd9f7..00000000 --- a/src/decode/mpt1327/MPT1327ActivitySummary.java +++ /dev/null @@ -1,159 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.mpt1327; - -import java.util.Iterator; -import java.util.TreeSet; - -import message.Message; -import sample.Listener; -import alias.Alias; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; - -public class MPT1327ActivitySummary implements ActivitySummaryProvider, - Listener -{ - private static final int sINT_NULL_VALUE = -1; - - private AliasList mAliasList; - private TreeSet mIdents = new TreeSet(); - private String mSite; - - /** - * Compiles a summary of active talkgroups, unique radio ids encountered - * in the decoded messages. Talkgroups have to be heard a minimum of twice - * to be considered valid ... in order to weed out the invalid ones. - * - * Returns a textual summary of the activity - */ - public MPT1327ActivitySummary( AliasList list ) - { - mAliasList = list; - } - - /** - * Cleanup method - */ - public void dispose() - { - mIdents.clear(); - } - - @Override - public void receive( Message message ) - { - if( message instanceof MPT1327Message ) - { - MPT1327Message mpt = ((MPT1327Message)message); - - if( mpt.isValid() ) - { - switch( mpt.getMessageType() ) - { - case ACK: - mIdents.add( mpt.getFromID() ); - break; - case ACKI: - mIdents.add( mpt.getFromID() ); - mIdents.add( mpt.getToID() ); - break; - case AHYC: - mIdents.add( mpt.getToID() ); - break; - case ALH: - mSite = mpt.getSiteID(); - break; - case GTC: - if( mpt.hasFromID() ) - { - mIdents.add( mpt.getFromID() ); - } - if( mpt.hasToID() ) - { - mIdents.add( mpt.getToID() ); - } - break; - } - } - } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "Activity Summary\n" ); - sb.append( "Decoder:\tMPT-1327\n" ); - sb.append( "Site:\t" ); - - if( mSite != null ) - { - sb.append( mSite ); - - if( mAliasList != null ) - { - Alias siteAlias = mAliasList.getSiteID( mSite ); - - if( siteAlias != null ) - { - sb.append( " " ); - sb.append( siteAlias.getName() ); - sb.append( "\n" ); - } - } - } - else - { - sb.append( "Unknown\n" ); - } - - sb.append( "\n" ); - - sb.append( "Talkgroups\n" ); - if( mIdents.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mIdents.iterator(); - - while( it.hasNext() ) - { - String ident = it.next(); - - sb.append( " " ); - sb.append( ident ); - sb.append( " " ); - - Alias alias = mAliasList.getMPT1327Alias( ident ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - return sb.toString(); - } -} diff --git a/src/decode/mpt1327/MPT1327ChannelState.java b/src/decode/mpt1327/MPT1327ChannelState.java deleted file mode 100644 index 5b3268ee..00000000 --- a/src/decode/mpt1327/MPT1327ChannelState.java +++ /dev/null @@ -1,422 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.mpt1327; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import message.Message; -import sample.Listener; -import source.config.SourceConfigTuner; -import alias.AliasList; -import audio.SquelchListener; -import audio.SquelchListener.SquelchState; -import controller.activity.CallEvent; -import controller.activity.CallEvent.CallEventType; -import controller.channel.Channel; -import controller.channel.Channel.ChannelType; -import controller.channel.ChannelMap; -import controller.channel.ProcessingChain; -import controller.state.AuxChannelState; -import controller.state.ChannelState; -import decode.config.DecodeConfigMPT1327; -import decode.mpt1327.MPT1327Message.IdentType; - -public class MPT1327ChannelState extends ChannelState -{ - private final static Logger mLog = LoggerFactory.getLogger( MPT1327ChannelState.class ); - - private String mSite; - private String mFromTalkgroup; - private String mToTalkgroup; - private int mChannelNumber; - private ChannelType mChannelType; - private ChannelMap mChannelMap; - private MPT1327ActivitySummary mActivitySummary; - - public MPT1327ChannelState( ProcessingChain processingChain, - AliasList aliasList, - ChannelMap channelMap ) - { - super( processingChain, aliasList ); - - mActivitySummary = new MPT1327ActivitySummary( aliasList ); - - mChannelMap = channelMap; - - mChannelType = processingChain.getChannel().getChannelType(); - } - - public void dispose() - { - super.dispose(); - - mActivitySummary.dispose(); - mActivitySummary = null; - mCurrentCallEvent = null; - } - - public void setCurrentCall( MPT1327CallEvent event ) - { - mCurrentCallEvent = event; - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mActivitySummary.getSummary() ); - - for( AuxChannelState state: mAuxChannelStates ) - { - sb.append( state.getActivitySummary() ); - sb.append( "\n\n" ); - } - - return sb.toString(); - } - - @Override - public void receive( Message message ) - { - super.receive( message ); - - if( message instanceof MPT1327Message ) - { - mActivitySummary.receive( message ); - - MPT1327Message mpt = (MPT1327Message)message; - - if( mpt.isValid() ) - { - switch( mpt.getMessageType() ) - { - case ACK: - IdentType identType = ( (MPT1327Message) message ).getIdent1Type(); - - if( identType == IdentType.REGI ) - { - mCallEventModel.add( - new MPT1327CallEvent.Builder( CallEventType.REGISTER ) - .aliasList( getProcessingChain().getAliasList() ) - .channel( String.valueOf( mChannelNumber ) ) - .details( "REGISTERED ON NETWORK" ) - .frequency( mChannelMap.getFrequency( mChannelNumber ) ) - .from( mpt.getToID() ) - .to( mpt.getFromID() ) - .build() ); - } - else - { - mCallEventModel.add( - new MPT1327CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( getProcessingChain().getAliasList() ) - .channel( String.valueOf( mChannelNumber ) ) - .details( "ACK " + identType.getLabel() ) - .frequency( mChannelMap.getFrequency( mChannelNumber ) ) - .from( mpt.getFromID() ) - .to( mpt.getToID() ) - .build() ); - } - break; - case AHYC: - mCallEventModel.add( - new MPT1327CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( getProcessingChain().getAliasList() ) - .channel( String.valueOf( mChannelNumber ) ) - .details( ( (MPT1327Message) message ).getRequestString() ) - .frequency( mChannelMap.getFrequency( mChannelNumber ) ) - .from( mpt.getFromID() ) - .to( mpt.getToID() ) - .build() ); - break; - case AHYQ: - mCallEventModel.add( - new MPT1327CallEvent.Builder( CallEventType.STATUS ) - .aliasList( getProcessingChain().getAliasList() ) - .channel( String.valueOf( mChannelNumber ) ) - .details( mpt.getStatusMessage() ) - .frequency( mChannelMap.getFrequency( mChannelNumber ) ) - .from( mpt.getFromID() ) - .to( mpt.getToID() ) - .build() ); - break; - case ALH: - String site = mpt.getSiteID(); - - if( mSite == null ) - { - mSite = site; - broadcastChange( ChangedAttribute.CHANNEL_SITE_NUMBER ); - } - else if( site != null && !site.contentEquals( mSite ) ) - { - mSite = site; - broadcastChange( ChangedAttribute.CHANNEL_SITE_NUMBER ); - } - - setState( State.CONTROL ); - break; - case GTC: - if( mpt.isValidCall() ) - { - int channelNumber = mpt.getChannel(); - - if( !getProcessingChain().getChannel() - .hasTrafficChannel( channelNumber ) ) - { - String aliasListName = mProcessingChain - .getChannel().getAliasListName(); - - Channel traffic = getTrafficChannel( channelNumber, - aliasListName ); - - /* Set the system and site to same as control channel */ - traffic.setSystem( getProcessingChain().getChannel() - .getSystem(), false ); - traffic.setSite( getProcessingChain().getChannel() - .getSite(), false ); - - /* Add the traffic channel to the parent control channel */ - getProcessingChain().getChannel() - .addTrafficChannel( channelNumber, traffic ); - - /* Start the traffic channel */ - traffic.setEnabled( true ); - - /* Set traffic channel state info */ - MPT1327ChannelState trafficState = - (MPT1327ChannelState)traffic - .getProcessingChain().getChannelState(); - - MPT1327ChannelState controlState = - (MPT1327ChannelState)getProcessingChain() - .getChannelState(); - - trafficState.setChannelMap( controlState.getChannelMap() ); - trafficState.setChannelNumber( channelNumber ); - trafficState.setFromTalkgroup( mpt.getFromID() ); - trafficState.setToTalkgroup( mpt.getToID() ); - - /* Add this control channel as message listener on the - * traffic channel so we can receive call tear-down - * messages */ - traffic.addListener( (Listener)this ); - - CallEventType type = traffic.isProcessing() ? - CallEventType.CALL : - CallEventType.CALL_NO_TUNER; - - /* - * Set the traffic channel's call event model to - * share the control channel's call event model - */ - traffic.getProcessingChain().getChannelState() - .setCallEventModel( mCallEventModel ); - - MPT1327CallEvent callStartEvent = - new MPT1327CallEvent.Builder( type ) - .aliasList( traffic.getProcessingChain().getAliasList() ) - .channel( String.valueOf( channelNumber ) ) - .frequency( mChannelMap.getFrequency( channelNumber ) ) - .from( mpt.getFromID() ) - .to( mpt.getToID() ) - .build(); - - traffic.getProcessingChain().getChannelState() - .setCurrentCallEvent( callStartEvent ); - - mCallEventModel.add( callStartEvent ); - } - } - break; - case CLEAR: - case MAINT: - if( mChannelType == ChannelType.TRAFFIC ) - { - fade( CallEventType.CALL_END ); - } - break; - case HEAD_PLUS1: - case HEAD_PLUS2: - case HEAD_PLUS3: - case HEAD_PLUS4: - mCallEventModel.add( - new MPT1327CallEvent.Builder( CallEventType.SDM ) - .aliasList( mAliasList ) - .details( mpt.getMessage() ) - .from( mpt.getFromID() ) - .to( mpt.getToID() ) - .build() ); - break; - } - } - } - } - - /** - * Intercept the fade event so that we can generate a call end event - */ - @Override - public void fade( final CallEventType type ) - { - /* - * We can receive multiple call tear-down messages -- only post a call - * end event for the one that can change the state to fade - */ - if( getState().canChangeTo( State.FADE ) ) - { - CallEvent current = getCurrentCallEvent(); - - if( current != null ) - { - mCallEventModel.setEnd( current ); - } - else - { - mCallEventModel.add( - new MPT1327CallEvent.Builder( type ) - .aliasList( getAliasList() ) - .channel( String.valueOf( mChannelNumber ) ) - .frequency( mChannelMap.getFrequency( mChannelNumber ) ) - .from( mFromTalkgroup ) - .to( mToTalkgroup ) - .build() ); - } - - setCurrentCall( null ); - } - - super.fade( type ); - } - - /** - * Make the ConventionalChannelState always unsquelched - */ - public void addListener( SquelchListener listener ) - { - super.addListener( listener ); - - super.setSquelchState( SquelchState.UNSQUELCH ); - } - - public void reset() - { - mFromTalkgroup = null; - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - mToTalkgroup = null; - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - super.reset(); - } - - public String getSite() - { - return mSite; - } - - public ChannelMap getChannelMap() - { - return mChannelMap; - } - - public void setChannelMap( ChannelMap channelMap ) - { - mChannelMap = channelMap; - } - - public String getFromTalkgroup() - { - return mFromTalkgroup; - } - - /** - * Set the talkgroup. This is used primarily for traffic channels since - * the talkgroup will already have been identified prior to the traffic - * channel being created. - */ - public void setFromTalkgroup( String fromTalkgroup ) - { - mFromTalkgroup = fromTalkgroup; - - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - } - - public String getToTalkgroup() - { - return mToTalkgroup; - } - - /** - * Set the talkgroup. This is used primarily for traffic channels since - * the talkgroup will already have been identified prior to the traffic - * channel being created. - */ - public void setToTalkgroup( String toTalkgroup ) - { - mToTalkgroup = toTalkgroup; - - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - } - - public int getChannelNumber() - { - return mChannelNumber; - } - /** - * Set the channel number. This is used primarily for traffic channels since - * the channel will already have been identified prior to the traffic - * channel being created. - */ - public void setChannelNumber( int channel ) - { - mChannelNumber = channel; - - broadcastChange( ChangedAttribute.CHANNEL_NUMBER ); - } - - public String getAliasListName() - { - return mProcessingChain.getChannel().getAliasListName(); - } - - private Channel getTrafficChannel( int channelNumber, String aliasListName ) - { - Channel traffic = new Channel( "Traffic Channel " + channelNumber, - ChannelType.TRAFFIC ); - - traffic.setResourceManager( getProcessingChain().getResourceManager() ); - - SourceConfigTuner source = new SourceConfigTuner(); - - long frequency = mChannelMap.getFrequency( channelNumber ); - - source.setFrequency( frequency ); - - traffic.setSourceConfiguration( source ); - - DecodeConfigMPT1327 decode = new DecodeConfigMPT1327(); - - traffic.setDecodeConfiguration( decode ); - - traffic.setAliasListName( aliasListName ); - - return traffic; - } -} diff --git a/src/decode/mpt1327/MPT1327Panel.java b/src/decode/mpt1327/MPT1327Panel.java deleted file mode 100644 index 9cb89284..00000000 --- a/src/decode/mpt1327/MPT1327Panel.java +++ /dev/null @@ -1,282 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.mpt1327; - -import java.awt.EventQueue; - -import javax.swing.ImageIcon; -import javax.swing.JLabel; - -import settings.ColorSetting; -import settings.Setting; -import alias.Alias; -import alias.AliasList; -import controller.channel.Channel; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelStatePanel; - -public class MPT1327Panel extends ChannelStatePanel -{ - private static final long serialVersionUID = 1L; - - private JLabel mProtocol; - private JLabel mSourceLabel; - private JLabel mChannelLabel; - - private JLabel mStateLabel; - private JLabel mTalkgroup; - private JLabel mTalkgroupAlias; - - private JLabel mSiteLabel; - private JLabel mSite; - private JLabel mSiteAliasLabel; - - private MPT1327ChannelState mState; - - private AliasList mAliasList; - - public MPT1327Panel( Channel channel ) - { - super( channel ); - mState = (MPT1327ChannelState)channel.getProcessingChain().getChannelState(); - mState.addListener( this ); - - mAliasList = mState.getAliasList(); - - init(); - } - - public void dispose() - { - super.dispose(); - mState = null; - mAliasList = null; - } - - public void init() - { - mProtocol = new JLabel( "MPT1327" ); - mProtocol.setFont( mFontDecoder ); - mProtocol.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 ); - - mStateLabel = new JLabel( mChannel.getProcessingChain(). - getChannelState().getState().getDisplayValue() ); - mStateLabel.setFont( mFontDecoder ); - mStateLabel.setForeground( mColorLabelDecoder ); - - mTalkgroup = new JLabel( mState.getFromTalkgroup() ); - mTalkgroup.setFont( mFontDecoder ); - mTalkgroup.setForeground( mColorLabelDecoder ); - - mTalkgroupAlias = new JLabel(); - mTalkgroupAlias.setFont( mFontDecoder ); - mTalkgroupAlias.setForeground( mColorLabelDecoder ); - - Alias alias = getTalkgroupAlias( mState.getFromTalkgroup() ); - - if( alias != null ) - { - mTalkgroupAlias.setText( alias.getName() ); - mTalkgroupAlias.setIcon( getIcon( alias ) ); - } - - mSiteLabel = new JLabel( "Site:" ); - mSiteLabel.setFont( mFontDetails ); - mSiteLabel.setForeground( mColorLabelDetails ); - - mSite = new JLabel( String.valueOf( mState.getSite() ) ); - mSite.setFont( mFontDecoder ); - mSite.setForeground( mColorLabelDecoder ); - - mSiteAliasLabel = new JLabel(); - mSiteAliasLabel.setFont( mFontDecoder ); - mSiteAliasLabel.setForeground( mColorLabelDecoder ); - - Alias siteAlias = getSiteAlias( mState.getSite() ); - - if( siteAlias != null ) - { - mSiteAliasLabel.setText( siteAlias.getName() ); - } - - add( mProtocol ); - add( mSourceLabel ); - add( mChannelLabel, "wrap" ); - - add( mStateLabel ); - add( mTalkgroup ); - add( mTalkgroupAlias, "wrap" ); - - add( mSiteLabel ); - add( mSite ); - add( mSiteAliasLabel, "wrap" ); - } - - @Override - public void receive( final ChangedAttribute changedAttribute ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - switch( changedAttribute ) - { - case CHANNEL_STATE: - mStateLabel.setText( mState.getState().getDisplayValue() ); - break; - case CHANNEL_SITE_NUMBER: - mSite.setText( String.valueOf( mState.getSite() ) ); - - Alias siteAlias = getSiteAlias( mState.getSite() ); - - if( siteAlias != null ) - { - mSiteAliasLabel.setText( siteAlias.getName() ); - } - repaint(); - break; - case FROM_TALKGROUP: - mTalkgroup.setText( mState.getFromTalkgroup() ); - - Alias alias = getTalkgroupAlias( mState.getFromTalkgroup() ); - - if( alias != null ) - { - mTalkgroupAlias.setText( alias.getName() ); - mTalkgroupAlias.setIcon( getIcon( alias ) ); - } - repaint(); - break; - } - } - } ); - } - - private ImageIcon getIcon( Alias alias ) - { - if( alias != null ) - { - String iconName = alias.getIconName(); - - if( iconName != null ) - { - return getSettingsManager().getImageIcon( iconName, 12 ); - } - } - - return null; - } - - private Alias getSiteAlias( String site ) - { - if( site != null && mAliasList != null ) - { - return mAliasList.getSiteID( site ); - } - - return null; - } - - private Alias getTalkgroupAlias( String talkgroup ) - { - if( talkgroup != null && mAliasList != null ) - { - return mAliasList.getTalkgroupAlias( talkgroup ); - } - - return null; - } - - @Override - public void trafficChannelAdded( Channel traffic ) - { - if( !getTrafficPanels().keySet().contains( traffic ) ) - { - MPT1327TrafficPanel panel = - new MPT1327TrafficPanel( traffic ); - - getTrafficPanels().put( traffic, panel ); - - addPanel( panel ); - } - } - - @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( mProtocol != null ) - { - mProtocol.setForeground( mColorLabelDecoder ); - } - if( mStateLabel != null ) - { - mStateLabel.setForeground( mColorLabelDecoder ); - } - if( mTalkgroup != null ) - { - mTalkgroup.setForeground( mColorLabelDecoder ); - } - if( mTalkgroupAlias != null ) - { - mTalkgroupAlias.setForeground( mColorLabelDecoder ); - } - if( mSite != null ) - { - mSite.setForeground( mColorLabelDecoder ); - } - if( mSiteAliasLabel != null ) - { - mSiteAliasLabel.setForeground( mColorLabelDecoder ); - } - break; - case CHANNEL_STATE_LABEL_DETAILS: - if( mSourceLabel != null ) - { - mSourceLabel.setForeground( mColorLabelDetails ); - } - if( mChannelLabel != null ) - { - mChannelLabel.setForeground( mColorLabelDetails ); - } - if( mSiteLabel != null ) - { - mSiteLabel.setForeground( mColorLabelDetails ); - } - break; - } - } - } -} diff --git a/src/decode/mpt1327/MPT1327TrafficPanel.java b/src/decode/mpt1327/MPT1327TrafficPanel.java deleted file mode 100644 index e2647d04..00000000 --- a/src/decode/mpt1327/MPT1327TrafficPanel.java +++ /dev/null @@ -1,258 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.mpt1327; - -import java.awt.EventQueue; - -import javax.swing.JLabel; - -import settings.ColorSetting; -import settings.Setting; -import alias.Alias; -import alias.AliasList; -import controller.channel.Channel; -import controller.channel.ProcessingChain; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelStatePanel; - -public class MPT1327TrafficPanel extends ChannelStatePanel -{ - private static final long serialVersionUID = 1L; - - private JLabel mProtocolLabel; - private JLabel mFrequencyLabel; - private JLabel mChannelLabel; - - private JLabel mStateLabel; - private JLabel mFromTalkgroupLabel; - private JLabel mFromTalkgroupAliasLabel; - - private JLabel mToLabel; - private JLabel mToTalkgroupLabel; - private JLabel mToTalkgroupAliasLabel; - - private AliasList mAliasList; - - public MPT1327TrafficPanel( Channel channel ) - { - super( channel ); - - mAliasList = channel.getProcessingChain().getChannelState().getAliasList(); - - init(); - } - - public void dispose() - { - super.dispose(); - mAliasList = null; - } - - public void init() - { - mProtocolLabel = new JLabel( "MPT1327" ); - mProtocolLabel.setFont( mFontDecoder ); - mProtocolLabel.setForeground( mColorLabelDecoder ); - - mFrequencyLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() ); - mFrequencyLabel.setFont( mFontDetails ); - mFrequencyLabel.setForeground( mColorLabelDetails ); - - MPT1327ChannelState state = - (MPT1327ChannelState)mChannel.getProcessingChain().getChannelState(); - int channelNumber = state.getChannelNumber(); - - mChannelLabel = new JLabel( "Channel " + channelNumber ); - mChannelLabel.setFont( mFontDetails ); - mChannelLabel.setForeground( mColorLabelDetails ); - - mStateLabel = new JLabel( mChannel.getProcessingChain(). - getChannelState().getState().getDisplayValue() ); - mStateLabel.setFont( mFontDecoder ); - mStateLabel.setForeground( mColorLabelDecoder ); - - mFromTalkgroupLabel = new JLabel( state.getFromTalkgroup() ); - mFromTalkgroupLabel.setFont( mFontDecoder ); - mFromTalkgroupLabel.setForeground( mColorLabelDecoder ); - - mFromTalkgroupAliasLabel = new JLabel(); - mFromTalkgroupAliasLabel.setFont( mFontDecoder ); - mFromTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); - - if( mAliasList != null ) - { - Alias alias = mAliasList.getMPT1327Alias( state.getFromTalkgroup() ); - - if( alias != null ) - { - mFromTalkgroupAliasLabel.setText( alias.getName() ); - - mFromTalkgroupAliasLabel.setIcon( getChannel() - .getResourceManager().getSettingsManager() - .getImageIcon( alias.getIconName(), 12 ) ); - } - } - - mToLabel = new JLabel( "TO:" ); - mToLabel.setFont( mFontDetails ); - mToLabel.setForeground( mColorLabelDetails ); - - mToTalkgroupLabel = new JLabel( state.getFromTalkgroup() ); - mToTalkgroupLabel.setFont( mFontDecoder ); - mToTalkgroupLabel.setForeground( mColorLabelDecoder ); - - mToTalkgroupAliasLabel = new JLabel(); - mToTalkgroupAliasLabel.setFont( mFontDecoder ); - mToTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); - - if( mAliasList != null ) - { - Alias alias = mAliasList.getMPT1327Alias( state.getFromTalkgroup() ); - - if( alias != null ) - { - mToTalkgroupAliasLabel.setText( alias.getName() ); - - mToTalkgroupAliasLabel.setIcon( getChannel() - .getResourceManager().getSettingsManager() - .getImageIcon( alias.getIconName(), 12 ) ); - } - } - - add( mProtocolLabel ); - add( mFrequencyLabel ); - add( mChannelLabel, "wrap" ); - - add( mStateLabel ); - add( mFromTalkgroupLabel ); - add( mFromTalkgroupAliasLabel, "wrap" ); - - add( mToLabel ); - add( mToTalkgroupLabel ); - add( mToTalkgroupAliasLabel, "wrap" ); - } - - @Override - public void receive( final ChangedAttribute changedAttribute ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - ProcessingChain chain = getChannel().getProcessingChain(); - - if( chain != null ) - { - final MPT1327ChannelState state = - (MPT1327ChannelState)chain.getChannelState(); - - if( state != null ) - { - switch( changedAttribute ) - { - case CHANNEL_STATE: - mStateLabel.setText( state.getState().getDisplayValue() ); - break; - case CHANNEL_NUMBER: - mChannelLabel.setText( "Channel " + state.getChannelNumber() ); - break; - case FROM_TALKGROUP: - String talkgroup = state.getFromTalkgroup(); - - mFromTalkgroupLabel.setText( talkgroup ); - - if( mAliasList != null ) - { - Alias alias = mAliasList.getMPT1327Alias( talkgroup ); - - if( alias != null ) - { - mFromTalkgroupAliasLabel.setText( alias.getName() ); - - String icon = alias.getIconName(); - - mFromTalkgroupAliasLabel.setIcon( getChannel() - .getResourceManager().getSettingsManager() - .getImageIcon( icon, 12 ) ); - } - } - break; - case TO_TALKGROUP: - String toTalkgroup = state.getToTalkgroup(); - - mToTalkgroupLabel.setText( toTalkgroup ); - - if( mAliasList != null ) - { - Alias alias = mAliasList.getMPT1327Alias( toTalkgroup ); - - if( alias != null ) - { - mToTalkgroupAliasLabel.setText( alias.getName() ); - - String icon = alias.getIconName(); - - if( icon != null ) - { - mToTalkgroupAliasLabel.setIcon( getChannel() - .getResourceManager().getSettingsManager() - .getImageIcon( icon, 12 ) ); - } - } - } - break; - default: - 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: - mProtocolLabel.setForeground( mColorLabelDecoder ); - mStateLabel.setForeground( mColorLabelDecoder ); - mFromTalkgroupLabel.setForeground( mColorLabelDecoder ); - mFromTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); - mToTalkgroupLabel.setForeground( mColorLabelDecoder ); - mToTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); - break; - case CHANNEL_STATE_LABEL_DETAILS: - mFrequencyLabel.setForeground( mColorLabelDetails ); - mChannelLabel.setForeground( mColorLabelDetails ); - mToLabel.setForeground( mColorLabelDetails ); - break; - } - } - } -} diff --git a/src/decode/nbfm/NBFMChannelState.java b/src/decode/nbfm/NBFMChannelState.java deleted file mode 100644 index fa4cd66a..00000000 --- a/src/decode/nbfm/NBFMChannelState.java +++ /dev/null @@ -1,69 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.nbfm; - -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 NBFMChannelState extends ChannelState -{ - public NBFMChannelState( 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:\tNBFM\n\n" ); - - for( AuxChannelState state: mAuxChannelStates ) - { - sb.append( state.getActivitySummary() ); - sb.append( "\n\n" ); - } - - return sb.toString(); - } -} diff --git a/src/decode/nbfm/NBFMDecoder.java b/src/decode/nbfm/NBFMDecoder.java deleted file mode 100644 index 5f8c1faa..00000000 --- a/src/decode/nbfm/NBFMDecoder.java +++ /dev/null @@ -1,99 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.nbfm; - -import sample.real.RealSampleListener; -import source.Source.SampleType; -import audio.AudioOutputImpl; -import audio.IAudioOutput; -import decode.Decoder; -import decode.DecoderType; -import decode.config.DecodeConfiguration; -import dsp.filter.DCRemovalFilter2; -import dsp.nbfm.FilteringNBFMDemodulator; - -public class NBFMDecoder extends Decoder -{ - /** - * This value determines how quickly the DC remove filter responds to - * changes in frequency. - */ - private static final double sDC_REMOVAL_RATIO = 0.000003; - - private FilteringNBFMDemodulator mDemodulator; - private DCRemovalFilter2 mDCRemovalFilter; - private AudioOutputImpl mAudioOutput = new AudioOutputImpl( "NBFM Decoder Audio Output" ); - - public NBFMDecoder( DecodeConfiguration config, SampleType sampleType ) - { - super( config, 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 FilteringNBFMDemodulator(); - this.addComplexListener( mDemodulator ); - - /** - * Remove the DC component that is present when we're mistuned - */ - mDCRemovalFilter = new DCRemovalFilter2( sDC_REMOVAL_RATIO ); - mDemodulator.addListener( mDCRemovalFilter ); - - /** - * Route the demodulated, filtered samples back to this class to send - * to all registered listeners - */ - mDCRemovalFilter.setListener( this.getRealReceiver() ); - } - - addRealSampleListener( mAudioOutput ); - } - - @Override - public DecoderType getType() - { - return DecoderType.NBFM; - } - - @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - if( mDemodulator != null ) - { - mDemodulator.addListener( listener ); - } - } - - @Override - public IAudioOutput getAudioOutput() - { - return mAudioOutput; - } -} diff --git a/src/decode/nbfm/NBFMPanel.java b/src/decode/nbfm/NBFMPanel.java deleted file mode 100644 index f1555a49..00000000 --- a/src/decode/nbfm/NBFMPanel.java +++ /dev/null @@ -1,136 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.nbfm; - -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 NBFMPanel extends ChannelStatePanel -{ - private static final long serialVersionUID = 1L; - - private JLabel mStateLabel; - private JLabel mSourceLabel; - private JLabel mChannelLabel; - - private JLabel mProtocol = new JLabel( "NBFM" ); - - public NBFMPanel( 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/p25/P25ActivitySummary.java b/src/decode/p25/P25ActivitySummary.java deleted file mode 100644 index 65ac26fc..00000000 --- a/src/decode/p25/P25ActivitySummary.java +++ /dev/null @@ -1,350 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.p25; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; - -import message.Message; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.Listener; -import alias.Alias; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.message.pdu.osp.control.NetworkStatusBroadcastExtended; -import decode.p25.message.pdu.osp.control.ProtectionParameterBroadcast; -import decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.message.tsbk.osp.control.AdjacentStatusBroadcast; -import decode.p25.message.tsbk.osp.control.IdentifierUpdate; -import decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast; -import decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast; -import decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.message.tsbk.osp.data.SNDCPDataChannelAnnouncementExplicit; -import decode.p25.reference.Service; - -public class P25ActivitySummary implements ActivitySummaryProvider, - Listener -{ - private static final int sINT_NULL_VALUE = -1; - - private final static Logger mLog = - LoggerFactory.getLogger( P25ActivitySummary.class ); - - private static final DecimalFormat mFrequencyFormatter = - new DecimalFormat( "0.000000" ); - - private AliasList mAliasList; - - private NetworkStatusBroadcast mNetworkStatus; - private NetworkStatusBroadcastExtended mNetworkStatusExtended; - private ProtectionParameterBroadcast mProtectionParameterBroadcast; - private RFSSStatusBroadcast mRFSSStatusMessage; - private RFSSStatusBroadcastExtended mRFSSStatusMessageExtended; - private SNDCPDataChannelAnnouncementExplicit mSNDCPDataChannel; - private HashMap mNeighborMap = - new HashMap(); - - private Set mSecondaryControlChannels = - new TreeSet(); - - private HashMap mBands = - new HashMap(); - - public P25ActivitySummary( AliasList list ) - { - mAliasList = list; - } - - /** - * Cleanup method - */ - public void dispose() - { - } - - @Override - public void receive( Message message ) - { - if( !message.isValid() ) - { - mLog.debug( "Got an invalid message!" ); - } - - if( message instanceof TSBKMessage ) - { - if( message instanceof AdjacentStatusBroadcast ) - { - AdjacentStatusBroadcast neighborMessage = - (AdjacentStatusBroadcast)message; - - mNeighborMap.put( neighborMessage.getUniqueID(), neighborMessage ); - } - else if( message instanceof IdentifierUpdate ) - { - IdentifierUpdate iu = (IdentifierUpdate)message; - - if( !mBands.containsKey( iu.getIdentifier() ) ) - { - mBands.put( iu.getIdentifier(), iu ); - } - } - else if( message instanceof NetworkStatusBroadcast ) - { - mNetworkStatus = (NetworkStatusBroadcast)message; - } - else if( message instanceof RFSSStatusBroadcast ) - { - mRFSSStatusMessage = (RFSSStatusBroadcast)message; - } - else if( message instanceof SecondaryControlChannelBroadcast ) - { - SecondaryControlChannelBroadcast sccb = - (SecondaryControlChannelBroadcast)message; - - if( sccb.getDownlinkFrequency1() > 0 ) - { - mSecondaryControlChannels.add( - (SecondaryControlChannelBroadcast)message ); - } - } - else if( message instanceof SNDCPDataChannelAnnouncementExplicit ) - { - mSNDCPDataChannel = (SNDCPDataChannelAnnouncementExplicit)message; - } - } - else if( message instanceof PDUMessage ) - { - if( message instanceof NetworkStatusBroadcastExtended ) - { - mNetworkStatusExtended = (NetworkStatusBroadcastExtended)message; - } - else if( message instanceof ProtectionParameterBroadcast ) - { - mProtectionParameterBroadcast = - (ProtectionParameterBroadcast)message; - } - else if( message instanceof RFSSStatusBroadcastExtended ) - { - mRFSSStatusMessageExtended = (RFSSStatusBroadcastExtended)message; - } - } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "Activity Summary\n" ); - sb.append( "Decoder:\tP25\n" ); - sb.append( "===================== THIS SITE ======================" ); - - if( mNetworkStatus != null ) - { - sb.append( "\nNAC:\t" + mNetworkStatus.getNAC() ); - sb.append( "\nWACN-SYS:\t" + mNetworkStatus.getWACN() ); - sb.append( "-" + mNetworkStatus.getSystemID() ); - sb.append( " [" ); - sb.append( mNetworkStatus.getNetworkCallsign() ); - sb.append( "]" ); - sb.append( "\nLRA:\t" + mNetworkStatus.getLocationRegistrationArea() ); - } - else if( mNetworkStatusExtended != null ) - { - sb.append( "\nNAC:\t" + mNetworkStatusExtended.getNAC() ); - sb.append( "\nWACN-SYS:\t" + mNetworkStatusExtended.getWACN() ); - sb.append( "-" + mNetworkStatusExtended.getSystemID() ); - sb.append( " [" ); - sb.append( mNetworkStatusExtended.getNetworkCallsign() ); - sb.append( "]" ); - sb.append( "\nLRA:\t" + mNetworkStatusExtended.getLocationRegistrationArea() ); - } - - String site = null; - - if( mRFSSStatusMessage != null ) - { - site = mRFSSStatusMessage.getRFSubsystemID() + "-" + mRFSSStatusMessage.getSiteID(); - } - else if( mRFSSStatusMessageExtended != null ) - { - site = mRFSSStatusMessageExtended.getRFSubsystemID() + "-" + - mRFSSStatusMessageExtended.getSiteID(); - } - - sb.append( "\nRFSS-SITE:\t" + site ); - - if( mAliasList != null ) - { - Alias siteAlias = mAliasList.getSiteID( site ); - - if( siteAlias != null ) - { - sb.append( " " + siteAlias.getName() ); - } - } - - if( mNetworkStatus != null ) - { - sb.append( "\nSERVICES:\t" + SystemService.toString( - mNetworkStatus.getSystemServiceClass() ) ); - sb.append( "\nPCCH:\tDNLINK " + mFrequencyFormatter.format( - (double)mNetworkStatus.getDownlinkFrequency() / 1E6d ) + - " [" + mNetworkStatus.getIdentifier() + "-" + - mNetworkStatus.getChannel() + "]\n" ); - sb.append( "\tUPLINK " + mFrequencyFormatter.format( - (double)mNetworkStatus.getUplinkFrequency() / 1E6d ) + " [" + - mNetworkStatus.getIdentifier() + "-" + - mNetworkStatus.getChannel() + "]\n" ); - } - else if( mNetworkStatusExtended != null ) - { - sb.append( "\nSERVICES:\t" + SystemService.toString( - mNetworkStatusExtended.getSystemServiceClass() ) ); - sb.append( "\nPCCH:\tDNLINK " + mFrequencyFormatter.format( - (double)mNetworkStatusExtended.getDownlinkFrequency() / 1E6d ) + - " [" + mNetworkStatusExtended.getTransmitIdentifier() + "-" + - mNetworkStatusExtended.getTransmitChannel() + "]\n" ); - sb.append( "\tUPLINK " + mFrequencyFormatter.format( - (double)mNetworkStatusExtended.getUplinkFrequency() / 1E6d ) + - " [" + mNetworkStatusExtended.getReceiveIdentifier() + "-" + - mNetworkStatusExtended.getReceiveChannel() + "]" ); - } - - if( mSecondaryControlChannels.isEmpty() ) - { - sb.append( "\nSCCH:\tNONE" ); - } - else - { - for( SecondaryControlChannelBroadcast sec: mSecondaryControlChannels ) - { - sb.append( "\nSCCH:\tDNLINK " + mFrequencyFormatter.format( - (double)sec.getDownlinkFrequency1() / 1E6d ) + - " [" + sec.getIdentifier1() + "-" + sec.getChannel1() + "]\n" ); - sb.append( "\tUPLINK " + mFrequencyFormatter.format( - (double)sec.getUplinkFrequency1() / 1E6d ) + " [" + - sec.getIdentifier1() + "-" + sec.getChannel1() + "]\n" ); - - if( sec.hasChannel2() ) - { - sb.append( "\nSCCH:\tDNLINK " + mFrequencyFormatter.format( - (double)sec.getDownlinkFrequency2() / 1E6d ) + - " [" + sec.getIdentifier2() + "-" + sec.getChannel2() + "]\n" ); - sb.append( "\tUPLINK " + mFrequencyFormatter.format( - (double)sec.getUplinkFrequency2() / 1E6d ) + " [" + - sec.getIdentifier2() + "-" + sec.getChannel2() + "]" ); - } - } - } - - if( mSNDCPDataChannel != null ) - { - sb.append( "\nSNDCP:" ); - sb.append( "\tDNLINK " + mFrequencyFormatter.format( - (double)mSNDCPDataChannel.getDownlinkFrequency()/1E6D ) + - " [" + mSNDCPDataChannel.getTransmitChannel() + "]" ); - sb.append( "\tUPLINK " + mFrequencyFormatter.format( - (double)mSNDCPDataChannel.getUplinkFrequency()/1E6D ) + - " [" + mSNDCPDataChannel.getReceiveChannel() + "]" ); - } - - if( mProtectionParameterBroadcast != null ) - { - sb.append( "\nENCRYPTION:" ); - sb.append( "\nTYPE:\t" + mProtectionParameterBroadcast - .getEncryptionType().name() ); - sb.append( "\nALGORITHM:\t" + mProtectionParameterBroadcast - .getAlgorithmID() ); - sb.append( "\nKEY:\t" + mProtectionParameterBroadcast.getKeyID() ); - sb.append( "\nINBOUND IV:\t" + mProtectionParameterBroadcast - .getInboundInitializationVector() ); - sb.append( "\nOUTBOUND IV:\t" + mProtectionParameterBroadcast - .getOutboundInitializationVector() ); - } - - List identifiers = new ArrayList( mBands.keySet() ); - Collections.sort( identifiers ); - - sb.append( "\nFREQUENCY BANDS:" ); - for( Integer id: identifiers ) - { - IBandIdentifier band = mBands.get( id ); - - sb.append( "\n\t" + id ); - sb.append( "- BASE: " + mFrequencyFormatter.format( - (double)band.getBaseFrequency() / 1E6d ) ); - sb.append( " CHANNEL SIZE: " + mFrequencyFormatter.format( - (double)band.getChannelSpacing() / 1E6d ) ); - sb.append( " UPLINK OFFSET: " + mFrequencyFormatter.format( - (double)band.getTransmitOffset() / 1E6D ) ); - } - - sb.append( "\n\n=================== NEIGHBORS ======================" ); - - if( mNeighborMap.isEmpty() ) - { - sb.append( "\n\tNONE\n" ); - sb.append( "\n----------------------------------------------------" ); - } - else - { - for( AdjacentStatusBroadcast neighbor: mNeighborMap.values() ) - { - sb.append( "\nNAC:\t" + neighbor.getNAC() ); - sb.append( "\nSYSTEM:\t" + neighbor.getSystemID() ); - sb.append( "\nLRA:\t" + neighbor.getLocationRegistrationArea() ); - - String neighborID = neighbor.getRFSS() + "-" + neighbor.getSiteID(); - sb.append( "\nRFSS-SITE:\t" + neighborID ); - - if( mAliasList != null ) - { - Alias siteAlias = mAliasList.getSiteID( neighborID ); - - if( siteAlias != null ) - { - sb.append( " " + siteAlias.getName() ); - } - } - - sb.append( "\nPCCH:\tDNLINK " + mFrequencyFormatter.format( - (double)neighbor.getDownlinkFrequency() / 1E6d ) + - " [" + neighbor.getIdentifier() + "-" + neighbor.getChannel() + "]" ); - sb.append( "\n\tUPLINK:" + mFrequencyFormatter.format( - (double)neighbor.getUplinkFrequency() / 1E6d ) + "\n" ); - sb.append( "\nSERVICES:\t" + Service.getServices( neighbor.getSystemServiceClass() ) ); - sb.append( "\n----------------------------------------------------" ); - } - } - - return sb.toString(); - } -} diff --git a/src/decode/p25/P25ChannelState.java b/src/decode/p25/P25ChannelState.java deleted file mode 100644 index 6a6831be..00000000 --- a/src/decode/p25/P25ChannelState.java +++ /dev/null @@ -1,3408 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.p25; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import message.Message; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.config.SourceConfigTuner; -import source.config.SourceConfiguration; -import alias.Alias; -import alias.AliasList; -import audio.SquelchListener; -import audio.SquelchListener.SquelchState; -import controller.ThreadPoolManager; -import controller.activity.CallEvent; -import controller.activity.CallEvent.CallEventType; -import controller.channel.ProcessingChain; -import controller.state.AuxChannelState; -import controller.state.ChannelState; -import decode.p25.audio.P25AudioOutput; -import decode.p25.message.P25Message; -import decode.p25.message.hdu.HDUMessage; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.message.ldu.LDUMessage; -import decode.p25.message.ldu.lc.CallTermination; -import decode.p25.message.ldu.lc.TelephoneInterconnectVoiceChannelUser; -import decode.p25.message.ldu.lc.UnitToUnitVoiceChannelUser; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.message.pdu.confirmed.PDUConfirmedMessage; -import decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextAccept; -import decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextReject; -import decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextRequest; -import decode.p25.message.pdu.confirmed.SNDCPDeactivateTDSContext; -import decode.p25.message.pdu.confirmed.SNDCPUserData; -import decode.p25.message.pdu.osp.control.AdjacentStatusBroadcastExtended; -import decode.p25.message.pdu.osp.control.CallAlertExtended; -import decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended; -import decode.p25.message.pdu.osp.control.GroupAffiliationResponseExtended; -import decode.p25.message.pdu.osp.control.MessageUpdateExtended; -import decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended; -import decode.p25.message.pdu.osp.control.RoamingAddressUpdateExtended; -import decode.p25.message.pdu.osp.control.StatusQueryExtended; -import decode.p25.message.pdu.osp.control.StatusUpdateExtended; -import decode.p25.message.pdu.osp.control.UnitRegistrationResponseExtended; -import decode.p25.message.pdu.osp.data.GroupDataChannelGrantExtended; -import decode.p25.message.pdu.osp.data.IndividualDataChannelGrantExtended; -import decode.p25.message.pdu.osp.voice.GroupVoiceChannelGrantExplicit; -import decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantExplicit; -import decode.p25.message.pdu.osp.voice.UnitToUnitAnswerRequestExplicit; -import decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantExtended; -import decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantUpdateExtended; -import decode.p25.message.tdu.lc.AdjacentSiteStatusBroadcast; -import decode.p25.message.tdu.lc.GroupVoiceChannelUpdate; -import decode.p25.message.tdu.lc.GroupVoiceChannelUpdateExplicit; -import decode.p25.message.tdu.lc.NetworkStatusBroadcast; -import decode.p25.message.tdu.lc.NetworkStatusBroadcastExplicit; -import decode.p25.message.tdu.lc.SecondaryControlChannelBroadcast; -import decode.p25.message.tdu.lc.SecondaryControlChannelBroadcastExplicit; -import decode.p25.message.tdu.lc.TDULinkControlMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.message.tsbk.osp.control.AcknowledgeResponse; -import decode.p25.message.tsbk.osp.control.AuthenticationCommand; -import decode.p25.message.tsbk.osp.control.CallAlert; -import decode.p25.message.tsbk.osp.control.DenyResponse; -import decode.p25.message.tsbk.osp.control.ExtendedFunctionCommand; -import decode.p25.message.tsbk.osp.control.GroupAffiliationQuery; -import decode.p25.message.tsbk.osp.control.GroupAffiliationResponse; -import decode.p25.message.tsbk.osp.control.LocationRegistrationResponse; -import decode.p25.message.tsbk.osp.control.MessageUpdate; -import decode.p25.message.tsbk.osp.control.ProtectionParameterUpdate; -import decode.p25.message.tsbk.osp.control.QueuedResponse; -import decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast; -import decode.p25.message.tsbk.osp.control.RadioUnitMonitorCommand; -import decode.p25.message.tsbk.osp.control.RoamingAddressCommand; -import decode.p25.message.tsbk.osp.control.StatusQuery; -import decode.p25.message.tsbk.osp.control.StatusUpdate; -import decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge; -import decode.p25.message.tsbk.osp.control.UnitRegistrationCommand; -import decode.p25.message.tsbk.osp.control.UnitRegistrationResponse; -import decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncement; -import decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncementExplicit; -import decode.p25.message.tsbk.osp.data.GroupDataChannelGrant; -import decode.p25.message.tsbk.osp.data.IndividualDataChannelGrant; -import decode.p25.message.tsbk.osp.data.SNDCPDataChannelGrant; -import decode.p25.message.tsbk.osp.data.SNDCPDataPageRequest; -import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant; -import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate; -import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit; -import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectAnswerRequest; -import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrant; -import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrantUpdate; -import decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest; -import decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrant; -import decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate; -import decode.p25.reference.IPProtocol; -import decode.p25.reference.Response; -import decode.p25.reference.Vendor; - -public class P25ChannelState extends ChannelState -{ - private final static Logger mLog = LoggerFactory.getLogger( P25ChannelState.class ); - - - private HashMap mRegistrations = new HashMap(); - private String mLastCommandEventID; - private String mLastPageEventID; - private String mLastQueryEventID; - private String mLastRegistrationEventID; - private String mLastResponseEventID; - - private ActiveCall mCurrentActiveCall; - private Map mCallDetects = new HashMap(); - - private P25ActivitySummary mActivitySummary; - private String mNAC; - private String mSystem; - private String mSite; - private String mSiteAlias; - private String mFromTalkgroup; - private Alias mFromAlias; - private String mToTalkgroup; - private Alias mToAlias; - - /* The currently tuned traffic channel - dynamically updated */ - private String mCurrentChannel = "CURRENT"; - private long mCurrentChannelFrequency = 0; - - private P25AudioOutput mAudioOutput; - - public P25ChannelState( ProcessingChain chain, AliasList aliasList ) - { - super( chain, aliasList ); - - mActivitySummary = new P25ActivitySummary( aliasList ); - - /* Get the channel frequency from the source config */ - SourceConfiguration source = chain.getChannel().getSourceConfiguration(); - - if( source instanceof SourceConfigTuner ) - { - mCurrentChannelFrequency = ((SourceConfigTuner)source).getFrequency(); - } - } - - public void setAudioOutput( P25AudioOutput audioOutput ) - { - mAudioOutput = audioOutput; - } - - public void addListener( SquelchListener listener ) - { - super.addListener( listener ); - - super.setSquelchState( SquelchState.UNSQUELCH ); - } - - /** - * Message processor. All messages are received and processed via this method. - */ - public void receive( Message message ) - { - super.receive( message ); - - if( message instanceof P25Message ) - { - updateNAC( ( (P25Message) message ).getNAC() ); - - mActivitySummary.receive( (P25Message)message ); - - if( message instanceof HDUMessage ) - { - /* Header Data Unit Message - preceeds voice LDUs */ - processHDU( (HDUMessage)message ); - } - if( message instanceof LDUMessage ) - { - /* Voice Vocoder messages */ - processLDU( (LDUMessage)message ); - } - else if( message instanceof PDUMessage ) - { - /* Packet Data Unit Messages */ - processPDU( (PDUMessage)message ); - } - else if( message instanceof TDULinkControlMessage ) - { - /* Terminator Data Unit with Link Control Message */ - processTDULC( (TDULinkControlMessage)message ); - } - else if( message instanceof TSBKMessage ) - { - /* Trunking Signalling Block Messages - indicates Control Channel */ - setState( State.CONTROL ); - - processTSBK( (TSBKMessage)message ); - } - } - } - - private void processHDU( HDUMessage hdu ) - { - State state = State.CALL; - - if( hdu.isValid() && hdu.isEncrypted() ) - { - state = State.ENCRYPTED; - } - - if( isActiveCall( mCurrentChannel, hdu.getToID() ) ) - { - updateCall( state, mCurrentChannel, null, hdu.getTalkgroupID() ); - } - else - { - StringBuilder sb = new StringBuilder(); - - CallEventType type = CallEventType.CALL; - - if( hdu.isValid() && hdu.isEncrypted() ) - { - type = CallEventType.ENCRYPTED_CALL; - - sb.append( "ENCRYPTED WITH " ); - sb.append( hdu.getEncryption().name() ); - sb.append( " KEY:" ); - sb.append( hdu.getKeyID() ); - } - - P25CallEvent event = new P25CallEvent.Builder( type ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .frequency( mCurrentChannelFrequency ) - .to( hdu.getToID() ) - .details( sb.toString() ) - .build(); - - addCall( event ); - } - } - - /** - * Processes Terminator Data Unit with Link Control messages that occur on - * the traffic channel - */ - private void processTDULC( TDULinkControlMessage tdulc ) - { - switch( tdulc.getOpcode() ) - { - case ADJACENT_SITE_STATUS_BROADCAST: - if( tdulc instanceof AdjacentSiteStatusBroadcast ) - { - AdjacentSiteStatusBroadcast assb = - (AdjacentSiteStatusBroadcast)tdulc; - - updateSystem( assb.getSystemID() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: - /* This message doesn't provide anything we need for channel state */ - break; - case CALL_ALERT: - if( tdulc instanceof decode.p25.message.tdu.lc.CallAlert ) - { - decode.p25.message.tdu.lc.CallAlert ca = - (decode.p25.message.tdu.lc.CallAlert)tdulc; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( ca.getSourceAddress() ) - .to( ca.getTargetAddress() ) - .details( "CALL ALERT" ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case CALL_TERMINATION_OR_CANCELLATION: - endAllCalls(); - break; - case CHANNEL_IDENTIFIER_UPDATE: - /* This message is handled by the P25MessageProcessor and - * inserted into any channels needing frequency band info */ - break; - case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT: - /* This message is handled by the P25MessageProcessor and - * inserted into any channels needing frequency band info */ - break; - case EXTENDED_FUNCTION_COMMAND: - if( tdulc instanceof decode.p25.message.tdu.lc.ExtendedFunctionCommand ) - { - decode.p25.message.tdu.lc.ExtendedFunctionCommand efc = - (decode.p25.message.tdu.lc.ExtendedFunctionCommand)tdulc; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( efc.getTargetAddress() ) - .details( "FUNCTION:" + efc.getExtendedFunction().getLabel() + - " ARG:" + efc.getArgument() ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case GROUP_AFFILIATION_QUERY: - if( tdulc instanceof decode.p25.message.tdu.lc.GroupAffiliationQuery ) - { - decode.p25.message.tdu.lc.GroupAffiliationQuery gaq = - (decode.p25.message.tdu.lc.GroupAffiliationQuery)tdulc; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .details( "GROUP AFFILIATION QUERY" ) - .from( gaq.getSourceAddress() ) - .to( gaq.getTargetAddress() ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case GROUP_VOICE_CHANNEL_UPDATE: - /* Reflects other call activity on the system: CALL DETECT */ - if( tdulc instanceof GroupVoiceChannelUpdate ) - { - GroupVoiceChannelUpdate gvcu = (GroupVoiceChannelUpdate)tdulc; - - if( isActiveCall( gvcu.getChannelA(), - gvcu.getGroupAddressA() ) ) - { - updateCall( State.CALL, - gvcu.getChannelA(), - gvcu.getGroupAddressA(), - gvcu.getGroupAddressB() ); - } - else - { - P25CallEvent gvcuEvent = - new P25CallEvent.Builder( CallEventType.CALL_DETECT ) - .aliasList( mAliasList ) - .channel( gvcu.getChannelA() ) - .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) - .frequency( gvcu.getDownlinkFrequencyA() ) - .from( gvcu.getGroupAddressA() ) - .to( gvcu.getGroupAddressB() ) - .build(); - - addCall( gvcuEvent ); - } - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: - /* Reflects other call activity on the system: CALL DETECT */ - - if( tdulc instanceof GroupVoiceChannelUpdateExplicit ) - { - GroupVoiceChannelUpdateExplicit gvcue = - (GroupVoiceChannelUpdateExplicit)tdulc; - if( isActiveCall( gvcue.getTransmitChannel(), - gvcue.getGroupAddress() ) ) - { - updateCall( State.CALL, - gvcue.getTransmitChannel(), - null, - gvcue.getGroupAddress() ); - } - else - { - P25CallEvent gvcueEvent = - new P25CallEvent.Builder( CallEventType.CALL_DETECT ) - .aliasList( mAliasList ) - .channel( gvcue.getTransmitChannel() ) - .details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) ) - .frequency( gvcue.getDownlinkFrequency() ) - .to( gvcue.getGroupAddress() ) - .build(); - - addCall( gvcueEvent ); - } - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case GROUP_VOICE_CHANNEL_USER: - if( tdulc instanceof decode.p25.message.tdu.lc.GroupVoiceChannelUser ) - { - decode.p25.message.tdu.lc.GroupVoiceChannelUser gvcuser = - (decode.p25.message.tdu.lc.GroupVoiceChannelUser)tdulc; - - if( isActiveCall( mCurrentChannel, gvcuser.getGroupAddress() ) ) - { - updateCall( State.CALL, - mCurrentChannel, - gvcuser.getSourceAddress(), - gvcuser.getGroupAddress() ); - } - else - { - P25CallEvent gvcuserEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( ( gvcuser.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcuser.isEmergency() ? " EMERGENCY" : "") ) - .frequency( mCurrentChannelFrequency ) - .from( gvcuser.getSourceAddress() ) - .to( gvcuser.getGroupAddress() ) - .build(); - - addCall( gvcuserEvent ); - } - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case MESSAGE_UPDATE: - if( tdulc instanceof decode.p25.message.tdu.lc.MessageUpdate ) - { - decode.p25.message.tdu.lc.MessageUpdate mu = - (decode.p25.message.tdu.lc.MessageUpdate)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.SDM ) - .aliasList( mAliasList ) - .from( mu.getSourceAddress() ) - .to( mu.getTargetAddress() ) - .details( "MSG: " + mu.getShortDataMessage() ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case NETWORK_STATUS_BROADCAST: - if( tdulc instanceof NetworkStatusBroadcast ) - { - updateSystem( ((NetworkStatusBroadcast)tdulc).getSystem() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case NETWORK_STATUS_BROADCAST_EXPLICIT: - if( tdulc instanceof NetworkStatusBroadcastExplicit ) - { - updateSystem( ((NetworkStatusBroadcastExplicit)tdulc).getSystem() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case PROTECTION_PARAMETER_BROADCAST: - if( tdulc instanceof decode.p25.message.tdu.lc.ProtectionParameterBroadcast ) - { - decode.p25.message.tdu.lc.ProtectionParameterBroadcast ppb = - (decode.p25.message.tdu.lc.ProtectionParameterBroadcast)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( ppb.getTargetAddress() ) - .details( "ENCRYPTION: " + - ppb.getEncryption().name() + " KEY:" + - ppb.getEncryptionKey() ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case RFSS_STATUS_BROADCAST: - if( tdulc instanceof decode.p25.message.tdu.lc.RFSSStatusBroadcast ) - { - decode.p25.message.tdu.lc.RFSSStatusBroadcast rfsssb = - (decode.p25.message.tdu.lc.RFSSStatusBroadcast)tdulc; - - updateSystem( rfsssb.getSystem() ); - - String site = rfsssb.getRFSubsystemID() + "-" + - rfsssb.getSiteID(); - - updateSite( site ); - -// if( mCurrentChannel == null || -// !mCurrentChannel.contentEquals( rfsssb.getChannel() ) ) -// { -// mCurrentChannel = rfsssb.getChannel(); -// mCurrentChannelFrequency = rfsssb.getDownlinkFrequency(); -// } - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case RFSS_STATUS_BROADCAST_EXPLICIT: - if( tdulc instanceof decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit ) - { - decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit rfsssbe = - (decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit)tdulc; - - String site = rfsssbe.getRFSubsystemID() + "-" + - rfsssbe.getSiteID(); - - updateSite( site ); - -// if( mCurrentChannel == null || -// !mCurrentChannel.contentEquals( rfsssbe.getTransmitChannel() ) ) -// { -// mCurrentChannel = rfsssbe.getTransmitChannel(); -// mCurrentChannelFrequency = rfsssbe.getDownlinkFrequency(); -// } - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST: - if( tdulc instanceof SecondaryControlChannelBroadcast ) - { - SecondaryControlChannelBroadcast sccb = - (SecondaryControlChannelBroadcast)tdulc; - - String site = sccb.getRFSubsystemID() + "-" + - sccb.getSiteID(); - - updateSite( site ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: - if( tdulc instanceof SecondaryControlChannelBroadcastExplicit ) - { - SecondaryControlChannelBroadcastExplicit sccb = - (SecondaryControlChannelBroadcastExplicit)tdulc; - - String site = sccb.getRFSubsystemID() + "-" + - sccb.getSiteID(); - - updateSite( site ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case STATUS_QUERY: - if( tdulc instanceof decode.p25.message.tdu.lc.StatusQuery ) - { - decode.p25.message.tdu.lc.StatusQuery sq = - (decode.p25.message.tdu.lc.StatusQuery)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .details( "STATUS QUERY" ) - .from( sq.getSourceAddress() ) - .to( sq.getTargetAddress() ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case STATUS_UPDATE: - if( tdulc instanceof decode.p25.message.tdu.lc.StatusUpdate ) - { - decode.p25.message.tdu.lc.StatusUpdate su = - (decode.p25.message.tdu.lc.StatusUpdate)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.STATUS ) - .aliasList( mAliasList ) - .details( "STATUS UNIT:" + su.getUnitStatus() + - " USER:" + su.getUserStatus() ) - .from( su.getSourceAddress() ) - .to( su.getTargetAddress() ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case SYSTEM_SERVICE_BROADCAST: - /* This message doesn't provide anything we need for channel state */ - break; - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - if( tdulc instanceof decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest ) - { - decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest tiar = - (decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( tiar.getTelephoneNumber() ) - .to( tiar.getTargetAddress() ) - .details( "TELEPHONE CALL ALERT" ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER: - if( tdulc instanceof decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser ) - { - decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser tivcu = - (decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser)tdulc; - - if( isActiveCall( mCurrentChannel, tivcu.getAddress() ) ) - { - updateCall( State.CALL, - mCurrentChannel, - null, - tivcu.getAddress() ); - } - else - { - P25CallEvent tivcuEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "TELEPHONE INTERCONNECT " + - ( tivcu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( tivcu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( mCurrentChannelFrequency ) - .from( null ) - .to( tivcu.getAddress() ) - .build(); - - addCall( tivcuEvent ); - } - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case UNIT_AUTHENTICATION_COMMAND: - if( tdulc instanceof decode.p25.message.tdu.lc.UnitAuthenticationCommand ) - { - decode.p25.message.tdu.lc.UnitAuthenticationCommand uac = - (decode.p25.message.tdu.lc.UnitAuthenticationCommand)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( uac.getCompleteTargetAddress() ) - .details( "AUTHENTICATE" ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case UNIT_REGISTRATION_COMMAND: - if( tdulc instanceof decode.p25.message.tdu.lc.UnitRegistrationCommand ) - { - decode.p25.message.tdu.lc.UnitRegistrationCommand urc = - (decode.p25.message.tdu.lc.UnitRegistrationCommand)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( urc.getCompleteTargetAddress() ) - .details( "REGISTER" ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case UNIT_TO_UNIT_ANSWER_REQUEST: - if( tdulc instanceof decode.p25.message.tdu.lc.UnitToUnitAnswerRequest ) - { - decode.p25.message.tdu.lc.UnitToUnitAnswerRequest uuar = - (decode.p25.message.tdu.lc.UnitToUnitAnswerRequest)tdulc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( uuar.getSourceAddress() ) - .to( uuar.getTargetAddress() ) - .details( "UNIT TO UNIT CALL ALERT" ) - .build() ); - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_USER: - if( tdulc instanceof decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser ) - { - decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser uuvcu = - (decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser)tdulc; - - if( isActiveCall( mCurrentChannel, uuvcu.getTargetAddress() ) ) - { - updateCall( State.CALL, - mCurrentChannel, - uuvcu.getSourceAddress(), - uuvcu.getTargetAddress() ); - } - else - { - P25CallEvent tivcuEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( ( uuvcu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( uuvcu.isEmergency() ? " EMERGENCY" : "" ) ) - .frequency( mCurrentChannelFrequency ) - .from( uuvcu.getSourceAddress() ) - .to( uuvcu.getTargetAddress() ) - .build(); - - addCall( tivcuEvent ); - } - } - else - { - logAlternateVendorMessage( tdulc ); - } - break; - default: - break; - } - } - - /** - * Log optional vendor format messages that we don't yet support so that we - * can understand those messages and eventually add support. - */ - private void logAlternateVendorMessage( P25Message p25 ) - { - if( p25 instanceof TDULinkControlMessage ) - { - TDULinkControlMessage tdulc = (TDULinkControlMessage)p25; - - mLog.debug( "TDULC - VENDOR [" + tdulc.getVendor() + - "] SPECIFIC FORMAT FOR " + tdulc.getVendorOpcode().getLabel() + - " " + tdulc.getBinaryMessage() ); - } - else if( p25 instanceof LDU1Message ) - { - LDU1Message ldu1 = (LDU1Message)p25; - - mLog.debug( "LDU1 - VENDOR [" + ldu1.getVendor() + - "] SPECIFIC FORMAT FOR " + ldu1.getVendorOpcode().getLabel() + - " " + ldu1.getBinaryMessage() ); - } - else if( p25 instanceof PDUMessage ) - { - PDUMessage pdu = (PDUMessage)p25; - - mLog.debug( "PDU - VENDOR [" + pdu.getVendor() + - "] SPECIFIC FORMAT FOR " + pdu.getVendorOpcode().getLabel() + - " " + pdu.getBinaryMessage() ); - } - else - { - mLog.debug( "P25 - ALTERNATE VENDOR MESSAGE [" + p25.getClass() + "]" ); - } - } - - private void processLDU( LDUMessage ldu ) - { - State state = getState(); - - /* Only change the state for valid ldus */ - if( ldu.isValid() ) - { - state = ldu.isEncrypted() ? State.ENCRYPTED : State.CALL; - } - - /* Send audio frames to the audio output */ - if( mAudioOutput != null && state != State.ENCRYPTED ) - { - mAudioOutput.receive( ldu ); - } - - if( ldu instanceof LDU1Message ) - { - switch( ((LDU1Message)ldu).getOpcode() ) - { - case ADJACENT_SITE_STATUS_BROADCAST: - if( ldu instanceof decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcast ) - { - decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcast assb = - (decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcast)ldu; - - updateSystem( assb.getSystemID() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( State.CALL, mCurrentChannel, null, null ); - break; - case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: - /* This message doesn't provide anything we need for channel state */ - updateCall( state, mCurrentChannel, null, null ); - break; - case CALL_ALERT: - if( ldu instanceof decode.p25.message.ldu.lc.CallAlert ) - { - decode.p25.message.ldu.lc.CallAlert ca = - (decode.p25.message.ldu.lc.CallAlert)ldu; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( ca.getSourceAddress() ) - .to( ca.getTargetAddress() ) - .details( "CALL ALERT" ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - updateCall( State.CALL, mCurrentChannel, null, null ); - break; - case CALL_TERMINATION_OR_CANCELLATION: - if( ldu instanceof CallTermination ) - { - endAllCalls(); - } - else - { - logAlternateVendorMessage( ldu ); - } - break; - case CHANNEL_IDENTIFIER_UPDATE: - /* This message is handled by the P25MessageProcessor and - * inserted into any channels needing frequency band info */ - updateCall( state, mCurrentChannel, null, null ); - break; - case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT: - /* This message is handled by the P25MessageProcessor and - * inserted into any channels needing frequency band info */ - updateCall( state, mCurrentChannel, null, null ); - break; - case EXTENDED_FUNCTION_COMMAND: - if( ldu instanceof decode.p25.message.ldu.lc.ExtendedFunctionCommand ) - { - decode.p25.message.ldu.lc.ExtendedFunctionCommand efc = - (decode.p25.message.ldu.lc.ExtendedFunctionCommand)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( efc.getTargetAddress() ) - .details( "FUNCTION:" + efc.getExtendedFunction().getLabel() + - " ARG:" + efc.getArgument() ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - updateCall( state, mCurrentChannel, null, null ); - break; - case GROUP_AFFILIATION_QUERY: - if( ldu instanceof decode.p25.message.ldu.lc.GroupAffiliationQuery ) - { - decode.p25.message.ldu.lc.GroupAffiliationQuery gaq = - (decode.p25.message.ldu.lc.GroupAffiliationQuery)ldu; - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .details( "GROUP AFFILIATION QUERY" ) - .from( gaq.getSourceAddress() ) - .to( gaq.getTargetAddress() ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - updateCall( state, mCurrentChannel, null, null ); - break; - case GROUP_VOICE_CHANNEL_UPDATE: - if( ldu instanceof decode.p25.message.ldu.lc.GroupVoiceChannelUpdate ) - { - decode.p25.message.ldu.lc.GroupVoiceChannelUpdate gvcu = - (decode.p25.message.ldu.lc.GroupVoiceChannelUpdate)ldu; - - if( isActiveCall( gvcu.getChannelA(), - gvcu.getGroupAddressA() ) ) - { - updateCall( state, - gvcu.getChannelA(), - gvcu.getGroupAddressA(), - gvcu.getGroupAddressB() ); - } - else - { - P25CallEvent gvcuEvent = - new P25CallEvent.Builder( CallEventType.CALL_DETECT ) - .aliasList( mAliasList ) - .channel( gvcu.getChannelA() ) - .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) - .frequency( gvcu.getDownlinkFrequencyA() ) - .from( gvcu.getGroupAddressA() ) - .to( gvcu.getGroupAddressB() ) - .build(); - - addCall( gvcuEvent ); - } - } - else - { - logAlternateVendorMessage( ldu ); - } - break; - case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: - if( ldu instanceof decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit ) - { - decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit gvcue = - (decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit)ldu; - - if( isActiveCall( gvcue.getTransmitChannel(), - gvcue.getGroupAddress() ) ) - { - updateCall( state, - gvcue.getTransmitChannel(), - null, - gvcue.getGroupAddress() ); - } - else - { - P25CallEvent gvcueEvent = - new P25CallEvent.Builder( CallEventType.CALL_DETECT ) - .aliasList( mAliasList ) - .channel( gvcue.getTransmitChannel() ) - .details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) ) - .frequency( gvcue.getDownlinkFrequency() ) - .to( gvcue.getGroupAddress() ) - .build(); - - addCall( gvcueEvent ); - } - } - else - { - logAlternateVendorMessage( ldu ); - } - break; - case GROUP_VOICE_CHANNEL_USER: - if( ldu instanceof decode.p25.message.ldu.lc.GroupVoiceChannelUser ) - { - decode.p25.message.ldu.lc.GroupVoiceChannelUser gvcuser = - (decode.p25.message.ldu.lc.GroupVoiceChannelUser)ldu; - - if( isActiveCall( mCurrentChannel, gvcuser.getGroupAddress() ) ) - { - updateCall( state, - mCurrentChannel, - gvcuser.getSourceAddress(), - gvcuser.getGroupAddress() ); - } - else - { - P25CallEvent gvcuserEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( ( gvcuser.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcuser.isEmergency() ? " EMERGENCY" : "") ) - .frequency( mCurrentChannelFrequency ) - .from( gvcuser.getSourceAddress() ) - .to( gvcuser.getGroupAddress() ) - .build(); - - addCall( gvcuserEvent ); - } - } - else - { - logAlternateVendorMessage( ldu ); - } - break; - case MESSAGE_UPDATE: - if( ldu instanceof decode.p25.message.ldu.lc.MessageUpdate ) - { - decode.p25.message.ldu.lc.MessageUpdate mu = - (decode.p25.message.ldu.lc.MessageUpdate)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.SDM ) - .aliasList( mAliasList ) - .from( mu.getSourceAddress() ) - .to( mu.getTargetAddress() ) - .details( "MSG: " + mu.getShortDataMessage() ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case NETWORK_STATUS_BROADCAST: - if( ldu instanceof decode.p25.message.ldu.lc.NetworkStatusBroadcast ) - { - updateSystem( ((decode.p25.message.ldu.lc.NetworkStatusBroadcast)ldu).getSystem() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case NETWORK_STATUS_BROADCAST_EXPLICIT: - if( ldu instanceof decode.p25.message.ldu.lc.NetworkStatusBroadcastExplicit ) - { - updateSystem( ((decode.p25.message.ldu.lc.NetworkStatusBroadcastExplicit)ldu).getSystem() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case PROTECTION_PARAMETER_BROADCAST: - if( ldu instanceof decode.p25.message.ldu.lc.ProtectionParameterBroadcast ) - { - decode.p25.message.ldu.lc.ProtectionParameterBroadcast ppb = - (decode.p25.message.ldu.lc.ProtectionParameterBroadcast)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( ppb.getTargetAddress() ) - .details( "ENCRYPTION: " + - ppb.getEncryption().name() + " KEY:" + - ppb.getEncryptionKey() ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case RFSS_STATUS_BROADCAST: - if( ldu instanceof decode.p25.message.ldu.lc.RFSSStatusBroadcast ) - { - decode.p25.message.ldu.lc.RFSSStatusBroadcast rfsssb = - (decode.p25.message.ldu.lc.RFSSStatusBroadcast)ldu; - - updateSystem( rfsssb.getSystem() ); - - String site = rfsssb.getRFSubsystemID() + "-" + - rfsssb.getSiteID(); - - updateSite( site ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case RFSS_STATUS_BROADCAST_EXPLICIT: - if( ldu instanceof decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit ) - { - decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit rfsssbe = - (decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit)ldu; - - String site = rfsssbe.getRFSubsystemID() + "-" + - rfsssbe.getSiteID(); - - updateSite( site ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST: - if( ldu instanceof decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast ) - { - decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast sccb = - (decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast)ldu; - - String site = sccb.getRFSubsystemID() + "-" + - sccb.getSiteID(); - - updateSite( site ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: - if( ldu instanceof decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit ) - { - decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit sccb = - (decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit)ldu; - - String site = sccb.getRFSubsystemID() + "-" + - sccb.getSiteID(); - - updateSite( site ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case STATUS_QUERY: - if( ldu instanceof decode.p25.message.ldu.lc.StatusQuery ) - { - decode.p25.message.ldu.lc.StatusQuery sq = - (decode.p25.message.ldu.lc.StatusQuery)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .details( "STATUS QUERY" ) - .from( sq.getSourceAddress() ) - .to( sq.getTargetAddress() ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case STATUS_UPDATE: - if( ldu instanceof decode.p25.message.ldu.lc.StatusUpdate ) - { - decode.p25.message.ldu.lc.StatusUpdate su = - (decode.p25.message.ldu.lc.StatusUpdate)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.STATUS ) - .aliasList( mAliasList ) - .details( "STATUS UNIT:" + su.getUnitStatus() + - " USER:" + su.getUserStatus() ) - .from( su.getSourceAddress() ) - .to( su.getTargetAddress() ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - - break; - case SYSTEM_SERVICE_BROADCAST: - /* This message doesn't provide anything we need for channel state */ - updateCall( state, mCurrentChannel, null, null ); - break; - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - if( ldu instanceof decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest ) - { - decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest tiar = - (decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( tiar.getTelephoneNumber() ) - .to( tiar.getTargetAddress() ) - .details( "TELEPHONE CALL ALERT" ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER: - if( ldu instanceof TelephoneInterconnectVoiceChannelUser ) - { - TelephoneInterconnectVoiceChannelUser tivcu = - (TelephoneInterconnectVoiceChannelUser)ldu; - - if( isActiveCall( mCurrentChannel, tivcu.getAddress() ) ) - { - updateCall( state, - mCurrentChannel, - null, - tivcu.getAddress() ); - } - else - { - P25CallEvent tivcuEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "TELEPHONE INTERCONNECT " + - ( tivcu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( tivcu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( mCurrentChannelFrequency ) - .from( null ) - .to( tivcu.getAddress() ) - .build(); - - addCall( tivcuEvent ); - } - } - else - { - logAlternateVendorMessage( ldu ); - } - break; - case UNIT_AUTHENTICATION_COMMAND: - if( ldu instanceof decode.p25.message.ldu.lc.UnitAuthenticationCommand ) - { - decode.p25.message.ldu.lc.UnitAuthenticationCommand uac = - (decode.p25.message.ldu.lc.UnitAuthenticationCommand)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( uac.getCompleteTargetAddress() ) - .details( "AUTHENTICATE" ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case UNIT_REGISTRATION_COMMAND: - if( ldu instanceof decode.p25.message.ldu.lc.UnitRegistrationCommand ) - { - decode.p25.message.ldu.lc.UnitRegistrationCommand urc = - (decode.p25.message.ldu.lc.UnitRegistrationCommand)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .to( urc.getCompleteTargetAddress() ) - .details( "REGISTER" ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case UNIT_TO_UNIT_ANSWER_REQUEST: - if( ldu instanceof decode.p25.message.ldu.lc.UnitToUnitAnswerRequest ) - { - decode.p25.message.ldu.lc.UnitToUnitAnswerRequest uuar = - (decode.p25.message.ldu.lc.UnitToUnitAnswerRequest)ldu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( uuar.getSourceAddress() ) - .to( uuar.getTargetAddress() ) - .details( "UNIT TO UNIT CALL ALERT" ) - .build() ); - } - else - { - logAlternateVendorMessage( ldu ); - } - - updateCall( state, mCurrentChannel, null, null ); - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_USER: - if( ldu instanceof UnitToUnitVoiceChannelUser ) - { - UnitToUnitVoiceChannelUser uuvcu = - (UnitToUnitVoiceChannelUser)ldu; - - if( isActiveCall( mCurrentChannel, uuvcu.getTargetAddress() ) ) - { - updateCall( state, - mCurrentChannel, - uuvcu.getSourceAddress(), - uuvcu.getTargetAddress() ); - } - else - { - P25CallEvent tivcuEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( ( uuvcu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( uuvcu.isEmergency() ? " EMERGENCY" : "" ) ) - .frequency( mCurrentChannelFrequency ) - .from( uuvcu.getSourceAddress() ) - .to( uuvcu.getTargetAddress() ) - .build(); - - addCall( tivcuEvent ); - } - } - else - { - logAlternateVendorMessage( ldu ); - } - break; - default: - break; - } - } - else - { - //LDU2 - updateCall( state, mCurrentChannel, null, null ); - } - } - - /** - * Process a Trunking Signalling Block message - */ - private void processTSBK( TSBKMessage tsbk ) - { - if( tsbk.getVendor() == Vendor.STANDARD ) - { - switch( tsbk.getOpcode() ) - { - case ACKNOWLEDGE_RESPONSE: - processTSBKResponse( tsbk ); - break; - case AUTHENTICATION_COMMAND: - processTSBKCommand( tsbk ); - break; - case CALL_ALERT: - processTSBKCall( tsbk ); - break; - case DENY_RESPONSE: - processTSBKResponse( tsbk ); - break; - case EXTENDED_FUNCTION_COMMAND: - processTSBKCommand( tsbk ); - break; - case GROUP_AFFILIATION_QUERY: - processTSBKQuery( tsbk ); - break; - case GROUP_AFFILIATION_RESPONSE: - processTSBKResponse( tsbk ); - break; - case GROUP_DATA_CHANNEL_ANNOUNCEMENT: - case GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: - case GROUP_DATA_CHANNEL_GRANT: - case GROUP_VOICE_CHANNEL_GRANT: - case GROUP_VOICE_CHANNEL_GRANT_UPDATE: - case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: - case INDIVIDUAL_DATA_CHANNEL_GRANT: - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - processTSBKCall( tsbk ); - break; - case LOCATION_REGISTRATION_RESPONSE: - case UNIT_DEREGISTRATION_ACKNOWLEDGE: - processTSBKResponse( tsbk ); - break; - case MESSAGE_UPDATE: - processTSBKMessage( tsbk ); - break; - case PROTECTION_PARAMETER_UPDATE: - processTSBKResponse( tsbk ); - break; - case QUEUED_RESPONSE: - processTSBKResponse( tsbk ); - break; - case RADIO_UNIT_MONITOR_COMMAND: - processTSBKCommand( tsbk ); - break; - case RFSS_STATUS_BROADCAST: - processTSBKRFSSStatus( (RFSSStatusBroadcast)tsbk ); - break; - case ROAMING_ADDRESS_COMMAND: - processTSBKCommand( tsbk ); - break; - case SNDCP_DATA_CHANNEL_GRANT: - processTSBKCall( tsbk ); - break; - case STATUS_QUERY: - processTSBKQuery( tsbk ); - break; - case STATUS_UPDATE: - processTSBKResponse( tsbk ); - break; - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - case UNIT_TO_UNIT_ANSWER_REQUEST: - processTSBKPage( tsbk ); - break; - case UNIT_REGISTRATION_COMMAND: - processTSBKCommand( tsbk ); - break; - case UNIT_REGISTRATION_RESPONSE: - processTSBKResponse( tsbk ); - break; - default: - break; - } - } - } - - /** - * Process a Packet Data Unit message - */ - private void processPDU( PDUMessage pdu ) - { - if( pdu instanceof PDUConfirmedMessage ) - { - PDUConfirmedMessage pduc = (PDUConfirmedMessage)pdu; - - switch( pduc.getPDUType() ) - { - case SN_ACTIVATE_TDS_CONTEXT_ACCEPT: - SNDCPActivateTDSContextAccept satca = - (SNDCPActivateTDSContextAccept)pduc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "ACTIVATE SNDCP USE IP:" + satca.getIPAddress() ) - .frequency( mCurrentChannelFrequency ) - .to( satca.getLogicalLinkID() ) - .build() ); - break; - case SN_ACTIVATE_TDS_CONTEXT_REJECT: - SNDCPActivateTDSContextReject satcr = - (SNDCPActivateTDSContextReject)pduc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "REJECT: SNDCP CONTEXT ACTIVATION " - + "REASON:" + satcr.getReason().getLabel() ) - .frequency( mCurrentChannelFrequency ) - .to( satcr.getLogicalLinkID() ) - .build() ); - break; - case SN_ACTIVATE_TDS_CONTEXT_REQUEST: - SNDCPActivateTDSContextRequest satcreq = - (SNDCPActivateTDSContextRequest)pduc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "REQUEST SNDCP USE IP:" + satcreq.getIPAddress() ) - .frequency( mCurrentChannelFrequency ) - .from( satcreq.getLogicalLinkID() ) - .build() ); - break; - case SN_DEACTIVATE_TDS_CONTEXT_ACCEPT: - SNDCPDeactivateTDSContext sdtca = - (SNDCPDeactivateTDSContext)pduc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "ACCEPT DEACTIVATE SNDCP CONTEXT" ) - .frequency( mCurrentChannelFrequency ) - .from( sdtca.getLogicalLinkID() ) - .build() ); - break; - case SN_DEACTIVATE_TDS_CONTEXT_REQUEST: - SNDCPDeactivateTDSContext sdtcreq = - (SNDCPDeactivateTDSContext)pduc; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "REQUEST DEACTIVATE SNDCP CONTEXT" ) - .frequency( mCurrentChannelFrequency ) - .from( sdtcreq.getLogicalLinkID() ) - .build() ); - break; - case SN_RF_CONFIRMED_DATA: - SNDCPUserData sud = (SNDCPUserData)pduc; - - StringBuilder sbFrom = new StringBuilder(); - StringBuilder sbTo = new StringBuilder(); - - sbFrom.append( sud.getSourceIPAddress() ); - sbTo.append( sud.getDestinationIPAddress() ); - - if( sud.getIPProtocol() == IPProtocol.UDP ) - { - sbFrom.append( ":" ); - sbFrom.append( sud.getUDPSourcePort() ); - sbTo.append( ":" ); - sbTo.append( sud.getUDPDestinationPort() ); - } - - P25CallEvent dataEvent = new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( mCurrentChannel ) - .details( "DATA: " + sud.getPayload() + - " RADIO IP:" + sbTo.toString() ) - .frequency( mCurrentChannelFrequency ) - .from( sbFrom.toString() ) - .to( pduc.getLogicalLinkID() ) - .build(); - - if( isActiveCall( mCurrentChannel, pduc.getLogicalLinkID() ) ) - { - updateCall( State.DATA, mCurrentChannel, sbFrom.toString(), pduc.getLogicalLinkID() ); - } - else - { - addCall( dataEvent ); - } - break; - default: - mLog.debug( "PDUC - Unrecognized Message: " + pduc.toString() ); - break; - } - } - else - { - switch( pdu.getOpcode() ) - { - case ADJACENT_STATUS_BROADCAST: - AdjacentStatusBroadcastExtended asbe = - (AdjacentStatusBroadcastExtended)pdu; - - updateSystem( asbe.getSystemID() ); - break; - case CALL_ALERT: - if( pdu instanceof CallAlertExtended ) - { - CallAlertExtended ca = (CallAlertExtended)pdu; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( ca.getWACN() + "-" + ca.getSystemID() + "-" + - ca.getSourceID() ) - .to( ca.getTargetAddress() ) - .build() ); - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case GROUP_AFFILIATION_QUERY: - if( pdu instanceof GroupAffiliationQueryExtended ) - { - GroupAffiliationQueryExtended gaqe = - (GroupAffiliationQueryExtended)pdu; - - if( mLastQueryEventID == null || !gaqe.getTargetAddress() - .contentEquals( mLastQueryEventID ) ) - { - mCallEventModel.add( new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .details( "GROUP AFFILIATION" ) - .from( gaqe.getWACN() + "-" + gaqe.getSystemID() + - "-" + gaqe.getSourceID() ) - .to( gaqe.getTargetAddress() ) - .build() ); - - mLastQueryEventID = gaqe.getToID(); - } - } - break; - case GROUP_AFFILIATION_RESPONSE: - if( pdu instanceof GroupAffiliationResponseExtended ) - { - GroupAffiliationResponseExtended gar = - (GroupAffiliationResponseExtended)pdu; - - if( mLastResponseEventID == null || !gar.getTargetAddress() - .contentEquals( mLastResponseEventID ) ) - { - mCallEventModel.add( new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .details( "AFFILIATION:" + gar.getResponse().name() + - " FOR GROUP:" + gar.getGroupWACN() + "-" + - gar.getGroupSystemID() + "-" + - gar.getGroupID() + " ANNOUNCEMENT GROUP:" + - gar.getAnnouncementGroupID() ) - .from( gar.getSourceWACN() + "-" + - gar.getSourceSystemID() + "-" + - gar.getSourceID() ) - .to( gar.getTargetAddress() ) - .build() ); - - mLastResponseEventID = gar.getTargetAddress(); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case GROUP_DATA_CHANNEL_GRANT: - if( pdu instanceof GroupDataChannelGrantExtended ) - { - GroupDataChannelGrantExtended gdcge = - (GroupDataChannelGrantExtended)pdu; - - if( isActiveCall( gdcge.getTransmitChannel(), gdcge.getGroupAddress() ) ) - { - updateCall( State.DATA, - gdcge.getTransmitChannel(), - gdcge.getSourceAddress(), - gdcge.getGroupAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( gdcge.getTransmitChannel() ) - .details( ( gdcge.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gdcge.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gdcge.getDownlinkFrequency() ) - .from( gdcge.getSourceAddress() ) - .to( gdcge.getGroupAddress() ) - .build(); - - addCall( event ); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case GROUP_VOICE_CHANNEL_GRANT: - if( pdu instanceof GroupVoiceChannelGrantExplicit ) - { - GroupVoiceChannelGrantExplicit gvcge = - (GroupVoiceChannelGrantExplicit)pdu; - - if( isActiveCall( gvcge.getTransmitChannel(), gvcge.getGroupAddress() ) ) - { - updateCall( State.CALL, - gvcge.getTransmitChannel(), - gvcge.getSourceAddress(), - gvcge.getGroupAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcge.getTransmitChannel() ) - .details( ( gvcge.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcge.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcge.getDownlinkFrequency() ) - .from( gvcge.getSourceAddress() ) - .to( gvcge.getGroupAddress() ) - .build(); - - addCall( event ); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case INDIVIDUAL_DATA_CHANNEL_GRANT: - if( pdu instanceof IndividualDataChannelGrantExtended ) - { - IndividualDataChannelGrantExtended idcge = - (IndividualDataChannelGrantExtended)pdu; - - if( isActiveCall( idcge.getTransmitChannel(), - idcge.getTargetAddress() ) ) - { - updateCall( State.DATA, - idcge.getTransmitChannel(), - idcge.getSourceAddress(), - idcge.getTargetAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( idcge.getTransmitChannel() ) - .details( ( idcge.isEncrypted() ? "ENCRYPTED" : "" ) + - ( idcge.isEmergency() ? " EMERGENCY" : "") ) - .frequency( idcge.getDownlinkFrequency() ) - .from( idcge.getSourceWACN() + "-" + - idcge.getSourceSystemID() + "-" + - idcge.getSourceAddress() ) - .to( idcge.getTargetAddress() ) - .build(); - - addCall( event ); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case MESSAGE_UPDATE: - if( pdu instanceof MessageUpdateExtended ) - { - MessageUpdateExtended mu = (MessageUpdateExtended)pdu; - - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.SDM ) - .aliasList( mAliasList ) - .details( "MESSAGE: " + mu.getMessage() ) - .from( mu.getSourceWACN() + "-" + mu.getSourceSystemID() + - "-" + mu.getSourceID() ) - .to( mu.getTargetAddress() ) - .build(); - - mCallEventModel.add( event ); - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case RFSS_STATUS_BROADCAST: - if( pdu instanceof RFSSStatusBroadcastExtended ) - { - RFSSStatusBroadcastExtended rsbe = (RFSSStatusBroadcastExtended)pdu; - - updateNAC( rsbe.getNAC() ); - updateSystem( rsbe.getSystemID() ); - updateSite( rsbe.getRFSubsystemID() + "-" + rsbe.getSiteID() ); - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case ROAMING_ADDRESS_UPDATE: - if( pdu instanceof RoamingAddressUpdateExtended ) - { - RoamingAddressUpdateExtended raue = - (RoamingAddressUpdateExtended)pdu; - - StringBuilder sb = new StringBuilder(); - sb.append( "ROAMING ADDRESS STACK A:" ); - sb.append( raue.getWACNA() + "-" + raue.getSystemIDA() ); - - if( raue.isFormat2() ) - { - sb.append( " B:" ); - sb.append( raue.getWACNB() + "-" + raue.getSystemIDB() ); - sb.append( " C:" ); - sb.append( raue.getWACNC() + "-" + raue.getSystemIDC() ); - sb.append( " D:" ); - sb.append( raue.getWACND() + "-" + raue.getSystemIDD() ); - } - - if( raue.isFormat3() ) - { - sb.append( " E:" ); - sb.append( raue.getWACNE() + "-" + raue.getSystemIDE() ); - sb.append( " F:" ); - sb.append( raue.getWACNF() + "-" + raue.getSystemIDF() ); - sb.append( " G:" ); - sb.append( raue.getWACNG() + "-" + raue.getSystemIDG() ); - } - - mCallEventModel.add( new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .details( sb.toString() ) - .from( raue.getSourceID() ) - .to( raue.getTargetAddress() ) - .build() ); - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case STATUS_QUERY: - if( pdu instanceof StatusQueryExtended ) - { - StatusQueryExtended sq = (StatusQueryExtended)pdu; - - if( mLastQueryEventID == null || !sq.getTargetAddress() - .contentEquals( mLastQueryEventID ) ) - { - mCallEventModel.add( new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .details( "STATUS QUERY" ) - .from( sq.getSourceWACN() + "-" + - sq.getSourceSystemID() + "-" + - sq.getSourceID() ) - .to( sq.getTargetAddress() ) - .build() ); - - mLastQueryEventID = sq.getToID(); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case STATUS_UPDATE: - if( pdu instanceof StatusUpdateExtended ) - { - StatusUpdateExtended su = (StatusUpdateExtended)pdu; - - if( mLastResponseEventID == null || !mLastResponseEventID - .contentEquals( su.getTargetAddress() ) ) - { - mCallEventModel.add( new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .details( "STATUS USER: " + su.getUserStatus() + - " UNIT: " + su.getUnitStatus() ) - .from( su.getSourceWACN() + "-" + - su.getSourceSystemID() + "-" + - su.getSourceID() ) - .to( su.getTargetAddress() ) - .build() ); - - mLastResponseEventID = su.getTargetAddress(); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - TelephoneInterconnectChannelGrantExplicit ticge = - (TelephoneInterconnectChannelGrantExplicit)pdu; - - if( isActiveCall( ticge.getTransmitChannel(), ticge.getAddress() )) - { - updateCall( State.CALL, - ticge.getTransmitChannel(), - ticge.getLogicalLinkID(), - ticge.getAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( ticge.getTransmitChannel() ) - .details( "TELEPHONE CALL " + - ( ticge.isEncrypted() ? "ENCRYPTED" : "" ) + - ( ticge.isEmergency() ? " EMERGENCY" : "") + - " CALL TIMER:" + ticge.getCallTimer() ) - .frequency( ticge.getDownlinkFrequency() ) - .from( ticge.getAddress() ) - .build(); - - addCall( event ); - } - break; - case UNIT_REGISTRATION_RESPONSE: - if( pdu instanceof UnitRegistrationResponseExtended ) - { - UnitRegistrationResponseExtended urr = - (UnitRegistrationResponseExtended)pdu; - - if( urr.getResponse() == Response.ACCEPT ) - { - mRegistrations.put( urr.getAssignedSourceAddress(), - System.currentTimeMillis() ); - } - - if( mLastRegistrationEventID == null || !mLastRegistrationEventID - .contentEquals( urr.getAssignedSourceAddress() ) ) - { - mCallEventModel.add( new P25CallEvent.Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .details( "REGISTRATION:" + urr.getResponse().name() + - " FOR EXTERNAL SYSTEM ADDRESS: " + - urr.getWACN() + "-" + - urr.getSystemID() + "-" + - urr.getSourceAddress() + - " SOURCE ID: " + urr.getSourceID() ) - .from( urr.getAssignedSourceAddress() ) - .build() ); - - mLastRegistrationEventID = urr.getAssignedSourceAddress(); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case UNIT_TO_UNIT_ANSWER_REQUEST: - if( pdu instanceof UnitToUnitAnswerRequestExplicit ) - { - UnitToUnitAnswerRequestExplicit utuare = - (UnitToUnitAnswerRequestExplicit)pdu; - - if( mLastPageEventID == null || !mLastPageEventID - .contentEquals( utuare.getTargetAddress() ) ) - { - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .details( ( utuare.isEmergency() ? "EMERGENCY" : "" ) ) - .from( utuare.getWACN() + "-" + - utuare.getSystemID() + "-" + - utuare.getSourceID() ) - .to( utuare.getTargetAddress() ) - .build() ); - - mLastPageEventID = utuare.getTargetAddress(); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - if( pdu instanceof UnitToUnitVoiceChannelGrantExtended ) - { - UnitToUnitVoiceChannelGrantExtended uuvcge = - (UnitToUnitVoiceChannelGrantExtended)pdu; - - if( isActiveCall( uuvcge.getTransmitChannel(), uuvcge.getTargetAddress() ) ) - { - updateCall( State.CALL, - uuvcge.getTransmitChannel(), - uuvcge.getSourceAddress(), - uuvcge.getTargetAddress() ); - } - else - { - P25CallEvent uuvcgeEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( uuvcge.getTransmitChannel() ) - .details( ( uuvcge.isEncrypted() ? "ENCRYPTED" : "" ) + - ( uuvcge.isEmergency() ? " EMERGENCY" : "") ) - .frequency( uuvcge.getDownlinkFrequency() ) - .from( uuvcge.getSourceWACN() + "-" + - uuvcge.getSourceSystemID() + "-" + - uuvcge.getSourceID() ) - .to( uuvcge.getTargetAddress() ) - .build(); - - addCall( uuvcgeEvent ); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - if( pdu instanceof UnitToUnitVoiceChannelGrantUpdateExtended ) - { - UnitToUnitVoiceChannelGrantUpdateExtended uuvcgue = - (UnitToUnitVoiceChannelGrantUpdateExtended)pdu; - - if( isActiveCall( uuvcgue.getTransmitChannel(), - uuvcgue.getTargetAddress() ) ) - { - updateCall( State.CALL, - uuvcgue.getTransmitChannel(), - uuvcgue.getSourceAddress(), - uuvcgue.getTargetAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( uuvcgue.getTransmitChannel() ) - .details( ( uuvcgue.isEncrypted() ? "ENCRYPTED" : "" ) + - ( uuvcgue.isEmergency() ? " EMERGENCY" : "") ) - .frequency( uuvcgue.getDownlinkFrequency() ) - .from( uuvcgue.getSourceWACN() + "-" + - uuvcgue.getSourceSystemID() + "-" + - uuvcgue.getSourceID() ) - .to( uuvcgue.getTargetAddress() ) - .build(); - - addCall( event ); - } - } - else - { - logAlternateVendorMessage( pdu ); - } - break; - default: - break; - } - } - } - - private void processTSBKCommand( TSBKMessage message ) - { - P25CallEvent event = null; - - switch( message.getOpcode() ) - { - case AUTHENTICATION_COMMAND: - AuthenticationCommand ac = (AuthenticationCommand)message; - - if( mLastCommandEventID == null || !mLastCommandEventID - .contentEquals( ac.getFullTargetID() )) - { - event = new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .details( "AUTHENTICATE" ) - .to( ac.getWACN() + "-" + ac.getSystemID() + "-" + - ac.getTargetID() ) - .build(); - - mLastCommandEventID = ac.getFullTargetID(); - } - break; - case EXTENDED_FUNCTION_COMMAND: - ExtendedFunctionCommand efc = (ExtendedFunctionCommand)message; - - if( mLastCommandEventID == null || !mLastCommandEventID - .contentEquals( efc.getTargetAddress() ) ) - { - event = new P25CallEvent.Builder( CallEventType.FUNCTION ) - .aliasList( mAliasList ) - .details( "EXTENDED FUNCTION: " + - efc.getExtendedFunction().getLabel() ) - .from( efc.getSourceAddress() ) - .to( efc.getTargetAddress() ) - .build(); - - mLastCommandEventID = efc.getTargetAddress(); - } - break; - case RADIO_UNIT_MONITOR_COMMAND: - RadioUnitMonitorCommand rumc = (RadioUnitMonitorCommand)message; - - if( mLastCommandEventID == null || !mLastCommandEventID - .contentEquals( rumc.getTargetAddress() ) ) - { - event = new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .details( "RADIO UNIT MONITOR" ) - .from( rumc.getSourceAddress() ) - .to( rumc.getTargetAddress() ) - .build(); - - mLastCommandEventID = rumc.getTargetAddress(); - } - break; - case ROAMING_ADDRESS_COMMAND: - RoamingAddressCommand rac = (RoamingAddressCommand)message; - - if( mLastCommandEventID == null || !mLastCommandEventID - .contentEquals( rac.getTargetID() ) ) - { - event = new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .details( rac.getStackOperation().name() + - " ROAMING ADDRESS " + rac.getWACN() + "-" + - rac.getSystemID() ) - .to( rac.getTargetID() ) - .build(); - - mLastCommandEventID = rac.getTargetID(); - } - break; - case UNIT_REGISTRATION_COMMAND: - UnitRegistrationCommand urc = (UnitRegistrationCommand)message; - - if( mLastCommandEventID == null || !mLastCommandEventID - .contentEquals( urc.getTargetAddress() ) ) - { - event = new P25CallEvent.Builder( CallEventType.COMMAND ) - .aliasList( mAliasList ) - .details( "REGISTER" ) - .from( urc.getSourceAddress() ) - .to( urc.getTargetAddress() ) - .build(); - - mLastCommandEventID = urc.getTargetAddress(); - } - break; - default: - break; - } - - if( event != null ) - { - mCallEventModel.add( event ); - } - } - - private void processTSBKMessage( TSBKMessage message ) - { - MessageUpdate mu = (MessageUpdate)message; - - CallEvent event = - new P25CallEvent.Builder( CallEventType.SDM ) - .aliasList( mAliasList ) - .details( "MESSAGE: " + mu.getMessage() ) - .from( mu.getSourceAddress() ) - .to( mu.getTargetAddress() ) - .build(); - - mCallEventModel.add( event ); - } - - private void processTSBKQuery( TSBKMessage message ) - { - CallEvent event = null; - - switch( message.getOpcode() ) - { - case GROUP_AFFILIATION_QUERY: - GroupAffiliationQuery gaq = (GroupAffiliationQuery)message; - - if( mLastQueryEventID == null || !mLastQueryEventID - .contentEquals( gaq.getTargetAddress() ) ) - { - event = new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .details( "GROUP AFFILIATION" ) - .from( gaq.getSourceAddress() ) - .to( gaq.getTargetAddress() ) - .build(); - } - break; - case STATUS_QUERY: - StatusQuery sq = (StatusQuery)message; - - if( mLastQueryEventID == null || !mLastQueryEventID - .contentEquals( sq.getTargetAddress() ) ) - { - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.QUERY ) - .aliasList( mAliasList ) - .details( "STATUS QUERY" ) - .from( sq.getSourceAddress() ) - .to( sq.getTargetAddress() ) - .build() ); - } - break; - default: - break; - } - - if( event != null ) - { - mCallEventModel.add( event ); - - mLastQueryEventID = event.getToID(); - } - } - - private void processTSBKResponse( TSBKMessage message ) - { - CallEvent event = null; - - switch( message.getOpcode() ) - { - case ACKNOWLEDGE_RESPONSE: - AcknowledgeResponse ar = (AcknowledgeResponse)message; - - if( mLastResponseEventID == null || !ar.getTargetAddress() - .contentEquals( mLastResponseEventID ) ) - { - event = new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .details( "ACKNOWLEDGE" ) - .to( ar.getTargetAddress() ) - .build(); - - if( ar.hasAdditionalInformation() ) - { - if( ar.hasExtendedAddress() ) - { - event.setToID( ar.getWACN() + "-" + ar.getSystemID() + "-" + - ar.getTargetAddress() ); - } - else - { - event.setFromID( ar.getFromID() ); - } - } - - mLastResponseEventID = ar.getTargetAddress(); - } - break; - case DENY_RESPONSE: - DenyResponse dr = (DenyResponse)message; - - if( mLastResponseEventID == null || !dr.getTargetAddress() - .contentEquals( mLastResponseEventID ) ) - { - event = new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .details( "DENY REASON: " + dr.getReason().name() + - " REQUESTED: " + dr.getServiceType().name() ) - .from( dr.getSourceAddress() ) - .to( dr.getTargetAddress() ) - .build(); - - mLastResponseEventID = dr.getTargetAddress(); - } - break; - case GROUP_AFFILIATION_RESPONSE: - GroupAffiliationResponse gar = (GroupAffiliationResponse)message; - - if( mLastResponseEventID == null || !gar.getTargetAddress() - .contentEquals( mLastResponseEventID ) ) - { - event = new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .details( "AFFILIATION:" + gar.getResponse().name() + - " FOR " + gar.getAffiliationScope() + - " GROUP:" + gar.getGroupAddress() + - " ANNOUNCEMENT GROUP:" + - gar.getAnnouncementGroupAddress() ) - .to( gar.getTargetAddress() ) - .build(); - - mLastResponseEventID = gar.getTargetAddress(); - } - break; - case LOCATION_REGISTRATION_RESPONSE: - LocationRegistrationResponse lrr = - (LocationRegistrationResponse)message; - - if( lrr.getResponse() == Response.ACCEPT ) - { - mRegistrations.put( lrr.getTargetAddress(), - System.currentTimeMillis() ); - } - - if( mLastRegistrationEventID == null || - !mLastRegistrationEventID.contentEquals( lrr.getTargetAddress() ) ) - { - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .details( "REGISTRATION:" + lrr.getResponse().name() + - " SITE: " + lrr.getRFSSID() + "-" + - lrr.getSiteID() ) - .from( lrr.getTargetAddress() ) - .to( lrr.getGroupAddress() ) - .build() ); - - mLastRegistrationEventID = lrr.getTargetAddress(); - } - break; - case PROTECTION_PARAMETER_UPDATE: - ProtectionParameterUpdate ppu = (ProtectionParameterUpdate)message; - - if( mLastResponseEventID == null || !ppu.getTargetAddress() - .contentEquals( mLastResponseEventID ) ) - { - event = new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .details( "USE ENCRYPTION ALGORITHM:" + - ppu.getAlgorithm().name() + " KEY:" + - ppu.getKeyID() ) - .to( ppu.getTargetAddress() ) - .build(); - - mLastResponseEventID = ppu.getTargetAddress(); - } - break; - case QUEUED_RESPONSE: - QueuedResponse qr = (QueuedResponse)message; - - if( mLastResponseEventID == null || !qr.getTargetAddress() - .contentEquals( mLastResponseEventID ) ) - { - event = new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .details( "QUEUED REASON: " + qr.getReason().name() + - " REQUESTED: " + qr.getServiceType().name() ) - .from( qr.getSourceAddress() ) - .to( qr.getTargetAddress() ) - .build(); - - mLastResponseEventID = qr.getTargetAddress(); - } - break; - case STATUS_UPDATE: - StatusUpdate su = (StatusUpdate)message; - - if( mLastResponseEventID == null || !su.getTargetAddress() - .contentEquals( mLastResponseEventID ) ) - { - event = new P25CallEvent.Builder( CallEventType.RESPONSE ) - .aliasList( mAliasList ) - .details( "STATUS USER: " + su.getUserStatus() + - " UNIT: " + su.getUnitStatus() ) - .from( su.getSourceAddress() ) - .to( su.getTargetAddress() ) - .build(); - - mLastResponseEventID = su.getTargetAddress(); - } - - break; - case UNIT_REGISTRATION_RESPONSE: - UnitRegistrationResponse urr = (UnitRegistrationResponse)message; - - if( urr.getResponse() == Response.ACCEPT ) - { - mRegistrations.put( urr.getSourceAddress(), - System.currentTimeMillis() ); - } - - if( mLastRegistrationEventID == null || - !mLastRegistrationEventID.contentEquals( urr.getSourceAddress() ) ) - { - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .details( "REGISTRATION:" + urr.getResponse().name() + - " SYSTEM: " + urr.getSystemID() + - " SOURCE ID: " + urr.getSourceID() ) - .from( urr.getSourceAddress() ) - .build() ); - - mLastRegistrationEventID = urr.getSourceAddress(); - } - break; - case UNIT_DEREGISTRATION_ACKNOWLEDGE: - UnitDeregistrationAcknowledge udr = - (UnitDeregistrationAcknowledge)message; - - if( mLastRegistrationEventID == null || - !mLastRegistrationEventID.contentEquals( udr.getSourceID() ) ) - { - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.DEREGISTER ) - .aliasList( mAliasList ) - .from( udr.getSourceID() ) - .build() ); - - List keysToRemove = new ArrayList(); - - /* Remove this radio from the registrations set */ - for( String key: mRegistrations.keySet() ) - { - if( key.startsWith( udr.getSourceID() ) ) - { - keysToRemove.add( key ); - } - } - - for( String key: keysToRemove ) - { - mRegistrations.remove( key ); - } - - mLastRegistrationEventID = udr.getSourceID(); - } - break; - default: - break; - } - - if( event != null ) - { - mCallEventModel.add( event ); - } - } - - private void processTSBKRFSSStatus( RFSSStatusBroadcast message ) - { - updateNAC( message.getNAC() ); - - updateSystem( message.getSystemID() ); - - updateSite( message.getRFSubsystemID() + "-" + message.getSiteID() ); - } - - /** - * Process a call event message - */ - private void processTSBKCall( TSBKMessage message ) - { - switch( message.getOpcode() ) - { - case CALL_ALERT: - CallAlert ca = (CallAlert)message; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .from( ca.getSourceID() ) - .to( ca.getTargetAddress() ) - .build() ); - break; - case GROUP_DATA_CHANNEL_ANNOUNCEMENT: - GroupDataChannelAnnouncement gdca = (GroupDataChannelAnnouncement)message; - - if( isActiveCall( gdca.getChannel1(), gdca.getGroupAddress1() ) ) - { - updateCall( State.DATA, - gdca.getChannel1(), - gdca.getGroupAddress2(), - gdca.getGroupAddress1() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( gdca.getChannel1() ) - .details( ( gdca.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gdca.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gdca.getDownlinkFrequency1() ) - .to( gdca.getGroupAddress1() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - - if( gdca.hasChannelNumber2() ) - { - if( isActiveCall( gdca.getChannel2(), gdca.getGroupAddress2() ) ) - { - updateCall( State.DATA, - gdca.getChannel2(), - null, - gdca.getGroupAddress2() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( gdca.getChannel2() ) - .details( ( gdca.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gdca.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gdca.getDownlinkFrequency2() ) - .to( gdca.getGroupAddress2() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - } - break; - case GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: - GroupDataChannelAnnouncementExplicit gdcae = - (GroupDataChannelAnnouncementExplicit)message; - - if( isActiveCall( gdcae.getTransmitChannel(), gdcae.getGroupAddress() ) ) - { - updateCall( State.DATA, - gdcae.getTransmitChannel(), - null, - gdcae.getGroupAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( gdcae.getTransmitChannel() ) - .details( ( gdcae.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gdcae.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gdcae.getDownlinkFrequency() ) - .to( gdcae.getGroupAddress() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - break; - case GROUP_DATA_CHANNEL_GRANT: - GroupDataChannelGrant gdcg = (GroupDataChannelGrant)message; - - if( isActiveCall( gdcg.getChannel(), gdcg.getGroupAddress() ) ) - { - updateCall( State.DATA, - gdcg.getChannel(), null, - gdcg.getGroupAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( gdcg.getChannel() ) - .details( ( gdcg.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gdcg.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gdcg.getDownlinkFrequency() ) - .from( gdcg.getSourceAddress() ) - .to( gdcg.getGroupAddress() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - break; - case GROUP_VOICE_CHANNEL_GRANT: - GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)message; - - if( isActiveCall( gvcg.getChannel(), gvcg.getGroupAddress() ) ) - { - updateCall( State.CALL, - gvcg.getChannel(), - gvcg.getSourceAddress(), - gvcg.getGroupAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcg.getChannel() ) - .details( ( gvcg.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcg.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcg.getDownlinkFrequency() ) - .from( gvcg.getSourceAddress() ) - .to( gvcg.getGroupAddress() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - break; - case GROUP_VOICE_CHANNEL_GRANT_UPDATE: - GroupVoiceChannelGrantUpdate gvcgu = - (GroupVoiceChannelGrantUpdate)message; - - if( isActiveCall( gvcgu.getChannel1(), gvcgu.getGroupAddress1() ) ) - { - updateCall( State.CALL, - gvcgu.getChannel1(), - gvcgu.getGroupAddress2(), - gvcgu.getGroupAddress1() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcgu.getChannel1() ) - .details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcgu.getDownlinkFrequency1() ) - .to( gvcgu.getGroupAddress1() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - - if( gvcgu.hasChannelNumber2() ) - { - if( isActiveCall( gvcgu.getChannel2(), gvcgu.getGroupAddress2() ) ) - { - updateCall( State.CALL, - gvcgu.getChannel2(), null, - gvcgu.getGroupAddress2() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcgu.getChannel2() ) - .details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcgu.getDownlinkFrequency2() ) - .to( gvcgu.getGroupAddress2() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - } - break; - case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: - GroupVoiceChannelGrantUpdateExplicit gvcgue = - (GroupVoiceChannelGrantUpdateExplicit)message; - - mCallEventModel.add( - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( gvcgue.getTransmitChannelIdentifier() + "-" + - gvcgue.getTransmitChannelNumber() ) - .details( ( gvcgue.isEncrypted() ? "ENCRYPTED" : "" ) + - ( gvcgue.isEmergency() ? " EMERGENCY" : "") ) - .frequency( gvcgue.getDownlinkFrequency() ) - .to( gvcgue.getGroupAddress() ) - .build() ); - break; - case INDIVIDUAL_DATA_CHANNEL_GRANT: - IndividualDataChannelGrant idcg = (IndividualDataChannelGrant)message; - - if( isActiveCall( idcg.getChannel(), idcg.getTargetAddress() ) ) - { - updateCall( State.DATA, - idcg.getChannel(), - idcg.getSourceAddress(), - idcg.getTargetAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( idcg.getChannel() ) - .details( ( idcg.isEncrypted() ? "ENCRYPTED" : "" ) + - ( idcg.isEmergency() ? " EMERGENCY" : "") ) - .frequency( idcg.getDownlinkFrequency() ) - .from( idcg.getSourceAddress() ) - .to( idcg.getTargetAddress() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - break; - case SNDCP_DATA_CHANNEL_GRANT: - SNDCPDataChannelGrant sdcg = (SNDCPDataChannelGrant)message; - - if( isActiveCall( sdcg.getTransmitChannel(), sdcg.getTargetAddress() ) ) - { - updateCall( State.DATA, - sdcg.getTransmitChannel(), - null, - sdcg.getTargetAddress() ); - } - else - { - P25CallEvent event = - new P25CallEvent.Builder( CallEventType.DATA_CALL ) - .aliasList( mAliasList ) - .channel( sdcg.getTransmitChannel() ) - .details( "SNDCP DATA NSAPI:" + sdcg.getNSAPI() ) - .frequency( sdcg.getDownlinkFrequency() ) - .to( sdcg.getTargetAddress() ) - .build(); - - mCallEventModel.add( event ); - - addCall( event ); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: - TelephoneInterconnectVoiceChannelGrant tivcg = - (TelephoneInterconnectVoiceChannelGrant)message; - - if( isActiveCall( tivcg.getChannel(), tivcg.getAddress() )) - { - updateCall( State.CALL, - tivcg.getChannel(), - tivcg.getSourceAddress(), - tivcg.getAddress() ); - } - else - { - P25CallEvent tivcgEvent = - new P25CallEvent.Builder( CallEventType.TELEPHONE_CALL ) - .aliasList( mAliasList ) - .channel( tivcg.getChannel() ) - .details( ( tivcg.isEncrypted() ? "ENCRYPTED" : "" ) + - ( tivcg.isEmergency() ? " EMERGENCY" : "") + - " CALL TIMER:" + tivcg.getCallTimer() ) - .frequency( tivcg.getDownlinkFrequency() ) - .from( tivcg.getAddress() ) - .build(); - - mCallEventModel.add( tivcgEvent ); - - addCall( tivcgEvent ); - } - break; - case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: - TelephoneInterconnectVoiceChannelGrantUpdate tivcgu = - (TelephoneInterconnectVoiceChannelGrantUpdate)message; - - if( isActiveCall( tivcgu.getChannel(), tivcgu.getAddress() ) ) - { - updateCall( State.CALL, - tivcgu.getChannel(), - tivcgu.getSourceAddress(), - tivcgu.getAddress() ); - } - else - { - P25CallEvent tivcguEvent = - new P25CallEvent.Builder( CallEventType.TELEPHONE_CALL ) - .aliasList( mAliasList ) - .channel( tivcgu.getChannelIdentifier() + "-" + - tivcgu.getChannelNumber() ) - .details( ( tivcgu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( tivcgu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( tivcgu.getDownlinkFrequency() ) - .from( tivcgu.getAddress() ) - .build(); - - mCallEventModel.add( tivcguEvent ); - - addCall( tivcguEvent ); - } - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: - UnitToUnitVoiceChannelGrant uuvcg = - (UnitToUnitVoiceChannelGrant)message; - - if( isActiveCall( uuvcg.getChannel(), uuvcg.getTargetAddress() ) ) - { - updateCall( State.CALL, - uuvcg.getChannel(), - uuvcg.getSourceAddress(), - uuvcg.getTargetAddress() ); - } - else - { - P25CallEvent uuvcgEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( uuvcg.getChannelIdentifier() + "-" + - uuvcg.getChannelNumber() ) - .details( ( uuvcg.isEncrypted() ? "ENCRYPTED" : "" ) + - ( uuvcg.isEmergency() ? " EMERGENCY" : "") ) - .frequency( uuvcg.getDownlinkFrequency() ) - .from( uuvcg.getSourceAddress() ) - .to( uuvcg.getTargetAddress() ) - .build(); - - mCallEventModel.add( uuvcgEvent ); - - addCall( uuvcgEvent ); - } - - break; - case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: - UnitToUnitVoiceChannelGrantUpdate uuvcgu = - (UnitToUnitVoiceChannelGrantUpdate)message; - - if( isActiveCall( uuvcgu.getChannel(), uuvcgu.getTargetAddress() ) ) - { - updateCall( State.CALL, - uuvcgu.getChannel(), - uuvcgu.getSourceAddress(), - uuvcgu.getTargetAddress() ); - } - else - { - P25CallEvent uuvcguEvent = - new P25CallEvent.Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( uuvcgu.getChannelIdentifier() + "-" + - uuvcgu.getChannelNumber() ) - .details( ( uuvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + - ( uuvcgu.isEmergency() ? " EMERGENCY" : "") ) - .frequency( uuvcgu.getDownlinkFrequency() ) - .from( uuvcgu.getSourceAddress() ) - .to( uuvcgu.getTargetAddress() ) - .build(); - - mCallEventModel.add( uuvcguEvent ); - - addCall( uuvcguEvent ); - } - - break; - default: - break; - } - } - - /** - * Indicates if either the current call, or any current call detects contain - * the talkgroup id. For the current call, only the TO talkgroup is tested - * - * @param channel - * @param talkgroup - * @return - */ - private boolean isActiveCall( String channel, String talkgroup ) - { - if( channel != null ) - { - if( mCurrentChannel.contentEquals( channel ) ) - { - return mCurrentActiveCall != null && - ( talkgroup == null || - mCurrentActiveCall.getCallEvent().getToID().contentEquals( talkgroup ) ); - } - else - { - ActiveCall call = mCallDetects.get( channel ); - - if( call != null ) - { - return talkgroup != null && call.getTalkgroups().contains( talkgroup ); - } - } - } - - return false; - } - - /** - * Starts a new call event. For P25 Phase 1, only 1 call event should ever - * have a call event type of CALL. All other detected call events on other - * channels should use call event type CALL DETECT. - */ - private void addCall( P25CallEvent event ) - { - mCallEventModel.add( event ); - - String channel = event.getChannel(); - - ActiveCall call = new ActiveCall( getProcessingChain() - .getResourceManager().getThreadPoolManager(), event ); - - if( channel.contentEquals( mCurrentChannel ) ) - { - if( mCurrentActiveCall != null ) - { - mCurrentActiveCall.end(); - } - - mCurrentActiveCall = call; - - switch( event.getCallEventType() ) - { - case CALL: - setState( State.CALL ); - updateFrom( event.getFromID() ); - updateTo( event.getToID() ); - break; - case DATA_CALL: - setState( State.DATA ); - updateFrom( event.getFromID() ); - updateTo( event.getToID() ); - break; - case CALL_DETECT: - /* don't update call state */ - default: - break; - } - } - else - { - /* End the current channel call event, if there is one */ - if( mCallDetects.containsKey( channel ) ) - { - mCallDetects.get( channel ).end(); - } - - mCallDetects.put( channel, call ); - } - - } - - /** - * Ends all calls in the mActiveCalls queue - */ - private void endAllCalls() - { - if( mCurrentActiveCall != null ) - { - mCurrentActiveCall.end(); - } - - for( ActiveCall call: mCallDetects.values() ) - { - call.end(); - } - } - - /** - * Updates state, channel, from and to for a currently active call. Use the - * isActiveCall() method to verify that a call is currently active, otherwise - * this invocation is ignored - */ - private void updateCall( State state, - String channel, - String from, - String to ) - { - if( mCurrentChannel.contentEquals( channel ) ) - { - if( mCurrentActiveCall != null ) - { - mCurrentActiveCall.update(); - - P25CallEvent event = mCurrentActiveCall.getCallEvent(); - - switch( event.getCallEventType() ) - { - case CALL: - setState( State.CALL ); - - /* Broacast call state update */ - updateFrom( from ); - updateTo( to ); - - if( from != null && - !from.contentEquals( "0000" ) && - !from.contentEquals( "000000" ) && - ( !event.hasFromID() || !event.getFromID().contentEquals( from ) ) ) - { - event.setFromID( from ); - } - break; - case DATA_CALL: - setState( State.DATA ); - - updateFrom( from ); - updateTo( to ); - - if( from != null && - !from.contentEquals( "0000" ) && - !from.contentEquals( "000000" ) && - ( !event.hasFromID() || !event.getFromID().contentEquals( from ) ) ) - { - event.setFromID( from ); - } - break; - case CALL_DETECT: - /* don't update call state */ - default: - break; - } - } - else - { - mLog.debug( "Request to update non-existent current call with " - + "state:" + state + " channel:" + channel + " from:" + - from + " to:" + to ); - } - } - else - { - ActiveCall call = mCallDetects.get( channel ); - - if( call != null ) - { - /* Update the timestamp to now */ - call.update(); - } - else - { - mLog.debug( "Request to update non-existent call detect with " - + "state:" + state + " channel:" + channel + " from:" + - from + " to:" + to ); - } - } - } - - /** - * Broadcasts an update for the TO group or unit address - */ - private void updateTo( String to ) - { - if( to != null && - !to.contentEquals( "0000" ) && - !to.contentEquals( "000000" ) && - ( mToTalkgroup == null || !mToTalkgroup.contentEquals( to ) ) ) - { - mToTalkgroup = to; - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - - if( mAliasList != null ) - { - mToAlias = mAliasList.getTalkgroupAlias( to ); - } - else - { - mToAlias = null; - } - - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - } - - /** - * Broadcasts an update for the FROM group or unit address - */ - private void updateFrom( String from ) - { - if( from != null && - !from.contentEquals( "0000" ) && - !from.contentEquals( "000000" ) && - ( mFromTalkgroup == null || !mFromTalkgroup.contentEquals( from ) ) ) - { - mFromTalkgroup = from; - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - - if( mAliasList != null ) - { - mFromAlias = mAliasList.getTalkgroupAlias( from ); - } - else - { - mFromAlias = null; - } - - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - } - } - - /** - * Broadcasts an update to the NAC - */ - private void updateNAC( String nac ) - { - if( mNAC == null && nac != null ) - { - mNAC = nac; - - broadcastChange( ChangedAttribute.NAC ); - } - } - - /** - * Updates the site information - */ - private void updateSite( String site ) - { - if( mSite == null || !mSite.contentEquals( site ) ) - { - mSite = site; - - broadcastChange( ChangedAttribute.SITE ); - - if( mAliasList != null ) - { - Alias alias = mAliasList.getSiteID( mSite ); - - if( alias != null ) - { - mSiteAlias = alias.getName(); - } - else - { - mSiteAlias = null; - } - } - - broadcastChange( ChangedAttribute.SITE_ALIAS ); - } - } - - private void updateSystem( String system ) - { - if( mSystem == null || !mSystem.contentEquals( system ) ) - { - mSystem = system; - - broadcastChange( ChangedAttribute.SYSTEM ); - } - } - - - /** - * Process a unit paging event message - */ - private void processTSBKPage( TSBKMessage message ) - { - P25CallEvent event = null; - - switch( message.getOpcode() ) - { - case UNIT_TO_UNIT_ANSWER_REQUEST: - UnitToUnitAnswerRequest utuar = (UnitToUnitAnswerRequest)message; - - if( mLastPageEventID == null || !mLastPageEventID - .contentEquals( utuar.getTargetAddress() ) ) - { - event = new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .details( ( utuar.isEmergency() ? "EMERGENCY" : "" ) ) - .from( utuar.getSourceAddress() ) - .to( utuar.getTargetAddress() ) - .build(); - - mLastPageEventID = utuar.getTargetAddress(); - } - break; - case SNDCP_DATA_PAGE_REQUEST: - SNDCPDataPageRequest sdpr = (SNDCPDataPageRequest)message; - - if( mLastPageEventID == null || !mLastPageEventID - .contentEquals( sdpr.getTargetAddress() ) ) - { - event = new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .details( "SNDCP DATA DAC: " + - sdpr.getDataAccessControl() + - " NSAPI:" + sdpr.getNSAPI() ) - .to( sdpr.getTargetAddress() ) - .build(); - - mLastPageEventID = sdpr.getTargetAddress(); - } - break; - case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: - TelephoneInterconnectAnswerRequest tiar = - (TelephoneInterconnectAnswerRequest)message; - - if( mLastPageEventID == null || !mLastPageEventID - .contentEquals( tiar.getTargetAddress() ) ) - { - event = new P25CallEvent.Builder( CallEventType.PAGE ) - .aliasList( mAliasList ) - .details( ( "TELEPHONE INTERCONNECT" ) ) - .from( tiar.getTelephoneNumber() ) - .to( tiar.getTargetAddress() ) - .build(); - - mLastPageEventID = tiar.getTargetAddress(); - } - break; - default: - break; - } - - if( event != null ) - { - mCallEventModel.add( event ); - } - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mActivitySummary.getSummary() ); - - for( AuxChannelState state: mAuxChannelStates ) - { - sb.append( state.getActivitySummary() ); - sb.append( "\n\n" ); - } - - return sb.toString(); - } - - public String getNAC() - { - return mNAC; - } - - public String getSystem() - { - return mSystem; - } - - public String getSiteAlias() - { - return mSiteAlias; - } - - public String getSite() - { - return mSite; - } - - public String getFromTalkgroup() - { - return mFromTalkgroup; - } - - public Alias getFromAlias() - { - return mFromAlias; - } - - public String getToTalkgroup() - { - return mToTalkgroup; - } - - public Alias getToAlias() - { - return mToAlias; - } - - public class ActiveCall - { - public static final long CALL_TIMEOUT = 2000; //milliseconds - public static final long CALL_DETECT_TIMEOUT = 5000; //milliseconds - - private P25CallEvent mEvent; - private long mLastUpdate; - private ThreadPoolManager mThreadPoolManager; - private AtomicBoolean mEnded = new AtomicBoolean( false ); - private long mTimeout; - private List mTalkgroups = new ArrayList(); - - public ActiveCall( ThreadPoolManager threadManager, P25CallEvent event ) - { - mThreadPoolManager = threadManager; - mEvent = event; - - if( event.getFromID() != null ) - { - mTalkgroups.add( event.getFromID() ); - } - - if( event.getToID() != null ) - { - mTalkgroups.add( event.getToID() ); - } - - mLastUpdate = System.currentTimeMillis(); - - mTimeout = event.getCallEventType() == - CallEventType.CALL_DETECT ? CALL_DETECT_TIMEOUT : CALL_TIMEOUT; - - mThreadPoolManager.scheduleOnce( new CallExpirationCheck( this ), - mTimeout, TimeUnit.MILLISECONDS ); - } - - public P25CallEvent getCallEvent() - { - return mEvent; - } - - public List getTalkgroups() - { - return mTalkgroups; - } - - public boolean matches( String channel, String id ) - { - return mEvent.getChannel() != null && - channel != null && - mEvent.getChannel().contentEquals( channel ) && - id != null && - mEvent.getToID().contentEquals( id ); - } - - /** - * Sets the last update timestamp to now - */ - public void update() - { - mLastUpdate = System.currentTimeMillis(); - } - - public void checkExpiration() - { - long now = System.currentTimeMillis(); - - if( !mEnded.get() ) - { - if( now > mLastUpdate + mTimeout ) - { - cleanup(); - } - else - { - mThreadPoolManager.scheduleOnce( new CallExpirationCheck( this ), - mTimeout, TimeUnit.MILLISECONDS ); - } - } - } - - public void end() - { - /* The expire check timer could fire while we're doing this, so we - * need to safely switch the flag and then run cleanup */ - if( mEnded.compareAndSet( false, true ) ) - { - cleanup(); - } - } - - private void cleanup() - { - setState( State.FADE ); - mCallEventModel.setEnd( mEvent ); - - if( mEvent.getChannel().contentEquals( mCurrentChannel ) ) - { - if( mCurrentActiveCall == this ) - { - mCurrentActiveCall = null; - } - } - else - { - mCallDetects.remove( this ); - } - } - - public long getExpiration() - { - return mLastUpdate + 2000; //2 seconds - } - } - - /** - * Timer to check active calls for call end - */ - public class CallExpirationCheck implements Runnable - { - private ActiveCall mActiveCall; - - public CallExpirationCheck( ActiveCall activeCall ) - { - mActiveCall = activeCall; - } - @Override - public void run() - { - mActiveCall.checkExpiration(); - } - } -} diff --git a/src/decode/p25/P25Decoder.java b/src/decode/p25/P25Decoder.java deleted file mode 100644 index cfd96a66..00000000 --- a/src/decode/p25/P25Decoder.java +++ /dev/null @@ -1,406 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.p25; - -import instrument.Instrumentable; -import instrument.tap.Tap; -import instrument.tap.stream.ComplexTap; -import instrument.tap.stream.DibitTap; -import instrument.tap.stream.FloatTap; -import instrument.tap.stream.QPSKTap; - -import java.util.ArrayList; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.real.RealSampleListener; -import source.Source.SampleType; -import source.tuner.frequency.FrequencyCorrectionControl; -import alias.AliasList; -import controller.ResourceManager; -import decode.Decoder; -import decode.DecoderType; -import decode.p25.audio.P25AudioOutput; -import dsp.filter.ComplexFIRFilter; -import dsp.filter.FilterFactory; -import dsp.filter.FloatFIRFilter; -import dsp.filter.Window.WindowType; -import dsp.filter.equalizer.CMAEqualizer; -import dsp.gain.ComplexFeedForwardGainControl; -import dsp.gain.DirectGainControl; -import dsp.nbfm.FMDiscriminator; -import dsp.psk.CQPSKDemodulator; -import dsp.psk.QPSKPolarSlicer; - -public class P25Decoder extends Decoder implements Instrumentable -{ - private final static Logger mLog = LoggerFactory.getLogger( P25Decoder.class ); - - private final static int MAXIMUM_FREQUENCY_CORRECTION = 3000; //Hertz, +/- - - /* Instrumentation Taps */ - private static final String INSTRUMENT_COMPLEX_INPUT = "Tap Point: Complex Input"; - private static final String INSTRUMENT_BASEBAND_FILTER_OUTPUT = "Tap Point: Baseband Filter Output"; - - private static final String INSTRUMENT_AGC_OUTPUT = "Tap Point: AGC Output"; - private static final String INSTRUMENT_QPSK_DEMODULATOR_OUTPUT = "Tap Point: CQPSK Demodulator Output"; - private static final String INSTRUMENT_CQPSK_SLICER_OUTPUT = "Tap Point: CQPSK Slicer Output"; - - private static final String INSTRUMENT_REAL_INPUT = "Tap Point: Real Input"; - private static final String INSTRUMENT_DGC_OUTPUT = "Tap Point: DGC Output"; - private static final String INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT = "Tap Point: Symbol Filter Output"; - private static final String INSTRUMENT_C4FM_SLICER_OUTPUT = "Tap Point: C4FM Slicer Output"; - - private List mAvailableTaps; - - /* Demods */ - private FMDiscriminator mFMDemodulator; - private CQPSKDemodulator mCQPSKDemodulator; - - /* Gain */ - private ComplexFeedForwardGainControl mAGC; - private DirectGainControl mDGC; - private CMAEqualizer mEqualizer; - - /* Filters */ - private ComplexFIRFilter mBasebandFilter; - private FloatFIRFilter mAudioFilter; - private C4FMSymbolFilter mSymbolFilter; - - /* Slicers */ - private C4FMSlicer mC4FMSlicer; - private QPSKPolarSlicer mCQPSKSlicer; - - /* Message framers and processors */ - private P25MessageFramer mMessageFramer; - private P25MessageProcessor mMessageProcessor; - - /* Audio */ - private P25AudioOutput mAudioOutput; - - private AliasList mAliasList; - private Modulation mModulation; - - public P25Decoder( ResourceManager resourceManager, - SampleType sampleType, - Modulation modulation, - AliasList aliasList ) - { - super( sampleType ); - mModulation = modulation; - - mAliasList = aliasList; - - mMessageProcessor = new P25MessageProcessor( mAliasList ); - mMessageProcessor.addMessageListener( this ); - - if( modulation == Modulation.CQPSK ) - { - /* Provide message framer with reference to the demod so that it - * can detect and issue corrections for costas phase lock errors */ - mCQPSKDemodulator = new CQPSKDemodulator(); - mMessageFramer = new P25MessageFramer( mAliasList, mCQPSKDemodulator ); - } - else - { - mMessageFramer = new P25MessageFramer( mAliasList ); - } - - mMessageFramer.setListener( mMessageProcessor ); - - /* Setup demodulation chains based on sample type (real or complex) and - * modulation (C4FM or CQPSK) */ - if( mSourceSampleType == SampleType.COMPLEX ) - { - if( modulation == Modulation.CQPSK ) - { - mBasebandFilter = new ComplexFIRFilter( FilterFactory.getLowPass( - 48000, 7250, 8000, 60, WindowType.HANNING, true ), 1.0 ); - - this.addComplexListener( mBasebandFilter ); - -// mEqualizer = new CMAEqualizer( 1.0f, 0.005f ); -// mBasebandFilter.setListener( mEqualizer ); - - mAGC = new ComplexFeedForwardGainControl( 32 ); -// mEqualizer.setListener( mAGC ); - mBasebandFilter.setListener( mAGC ); - - mAGC.setListener( mCQPSKDemodulator ); - - mCQPSKSlicer = new QPSKPolarSlicer(); - mCQPSKDemodulator.setListener( mCQPSKSlicer ); - - mCQPSKSlicer.addListener( mMessageFramer ); - } - else /* C4FM */ - { - mBasebandFilter = new ComplexFIRFilter( FilterFactory.getLowPass( - 48000, 6750, 7500, 60, WindowType.HANNING, true ), 1.0 ); - - this.addComplexListener( mBasebandFilter ); - - mFMDemodulator = new FMDiscriminator( 1.0f ); - mBasebandFilter.setListener( mFMDemodulator ); - - /* Route output of the FM demod back to this channel so that we - * can process the output as if it were coming from any other - * real sample source */ - mFMDemodulator.setListener( getRealReceiver() ); - } - } - - /* Processing chain for real samples & demodulated complex samples */ - if( modulation == Modulation.C4FM ) - { - mAudioFilter = new FloatFIRFilter( FilterFactory.getLowPass( 48000, - 3000, 4000, 48, WindowType.HAMMING, true ), 1.0 ); - addRealSampleListener( mAudioFilter ); - - mDGC = new DirectGainControl( 15.0f, 0.1f, 35.0f, 0.3f ); - mAudioFilter.setListener( mDGC ); - - mFrequencyCorrection = new FrequencyCorrectionControl( - MAXIMUM_FREQUENCY_CORRECTION ); - - mSymbolFilter = new C4FMSymbolFilter( mDGC, mFrequencyCorrection ); - mDGC.setListener( mSymbolFilter ); - - mC4FMSlicer = new C4FMSlicer(); - mSymbolFilter.setListener( mC4FMSlicer ); - - mC4FMSlicer.addListener( mMessageFramer ); - } - - mAudioOutput = new P25AudioOutput( resourceManager ); - mMessageProcessor.addMessageListener( mAudioOutput ); - } - - public P25AudioOutput getAudioOutput() - { - return mAudioOutput; - } - - @Override - public DecoderType getType() - { - return DecoderType.P25_PHASE1; - } - - @Override - public List getTaps() - { - if( mAvailableTaps == null ) - { - mAvailableTaps = new ArrayList(); - - if( mSourceSampleType == SampleType.COMPLEX ) - { - if( mCQPSKDemodulator != null ) - { - mAvailableTaps.addAll( mCQPSKDemodulator.getTaps() ); - } -// mAvailableTaps.add( new ComplexTap( INSTRUMENT_COMPLEX_INPUT, 0, 1.0f ) ); -// mAvailableTaps.add( new ComplexTap( INSTRUMENT_BASEBAND_FILTER_OUTPUT, 0, 1.0f ) ); - } - else - { - mAvailableTaps.add( new FloatTap( INSTRUMENT_REAL_INPUT, 0, 1.0f ) ); - } - - if( mModulation == Modulation.C4FM ) - { - mAvailableTaps.add( new FloatTap( INSTRUMENT_DGC_OUTPUT, 0, 1.0f ) ); - mAvailableTaps.add( new FloatTap( INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT, 0, 0.1f ) ); - mAvailableTaps.add( new DibitTap( INSTRUMENT_C4FM_SLICER_OUTPUT, 0, 0.1f ) ); - - if( mSymbolFilter != null ) - { - mAvailableTaps.addAll( mSymbolFilter.getTaps() ); - } - } - else - { - mAvailableTaps.add( new ComplexTap( INSTRUMENT_AGC_OUTPUT, 0, 1.0f ) ); - mAvailableTaps.add( new QPSKTap( INSTRUMENT_QPSK_DEMODULATOR_OUTPUT, 0, 1.0f ) ); - mAvailableTaps.add( new DibitTap( INSTRUMENT_CQPSK_SLICER_OUTPUT, 0, 0.1f ) ); - } - } - - return mAvailableTaps; - } - - @Override - public void addTap( Tap tap ) - { - if( mSymbolFilter != null ) - { - mSymbolFilter.addTap( tap ); - } - - if( mCQPSKDemodulator != null ) - { - mCQPSKDemodulator.addTap( tap ); - } - - switch( tap.getName() ) - { - case INSTRUMENT_COMPLEX_INPUT: - addComplexListener( (ComplexTap)tap ); - break; - case INSTRUMENT_BASEBAND_FILTER_OUTPUT: - ComplexTap baseband = (ComplexTap)tap; - mBasebandFilter.setListener( baseband ); - baseband.setListener( mAGC ); - break; - case INSTRUMENT_AGC_OUTPUT: - ComplexTap agcSymbol = (ComplexTap)tap; - mAGC.setListener( agcSymbol ); - agcSymbol.setListener( mCQPSKDemodulator ); - break; - case INSTRUMENT_QPSK_DEMODULATOR_OUTPUT: - QPSKTap qpsk = (QPSKTap)tap; - mCQPSKDemodulator.setListener( qpsk ); - qpsk.setListener( mCQPSKSlicer ); - break; - case INSTRUMENT_CQPSK_SLICER_OUTPUT: - mCQPSKSlicer.addListener( (DibitTap)tap ); - break; - case INSTRUMENT_REAL_INPUT: - FloatTap inputAGC = (FloatTap)tap; - removeRealListener( mDGC ); - addRealSampleListener( inputAGC ); - inputAGC.setListener( mDGC ); - break; - case INSTRUMENT_DGC_OUTPUT: - FloatTap dgcSymbol = (FloatTap)tap; - mDGC.setListener( dgcSymbol ); - dgcSymbol.setListener( mSymbolFilter ); - break; - case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT: - FloatTap symbolSlicer = (FloatTap)tap; - mSymbolFilter.setListener( symbolSlicer ); - symbolSlicer.setListener( mC4FMSlicer ); - break; - case INSTRUMENT_C4FM_SLICER_OUTPUT: - DibitTap slicer = (DibitTap)tap; - - if( mC4FMSlicer != null ) - { - mC4FMSlicer.addListener( slicer ); - } - - if( mCQPSKSlicer != null ) - { - mCQPSKSlicer.addListener( slicer ); - } - break; - } - } - - @Override - public void removeTap( Tap tap ) - { - if( mSymbolFilter != null ) - { - mSymbolFilter.removeTap( tap ); - } - - if( mCQPSKDemodulator != null ) - { - mCQPSKDemodulator.removeTap( tap ); - } - - switch( tap.getName() ) - { - case INSTRUMENT_COMPLEX_INPUT: - removeComplexListener( (ComplexTap)tap ); - break; - case INSTRUMENT_BASEBAND_FILTER_OUTPUT: - mBasebandFilter.setListener( mAGC ); - break; - case INSTRUMENT_AGC_OUTPUT: - mAGC.setListener( mCQPSKDemodulator ); - break; - case INSTRUMENT_QPSK_DEMODULATOR_OUTPUT: - mCQPSKDemodulator.setListener( mCQPSKSlicer ); - break; - case INSTRUMENT_CQPSK_SLICER_OUTPUT: - mCQPSKSlicer.removeListener( (DibitTap)tap ); - break; - case INSTRUMENT_REAL_INPUT: - FloatTap inputAGC = (FloatTap)tap; - removeRealListener( inputAGC ); - addRealSampleListener( mDGC ); - break; - case INSTRUMENT_DGC_OUTPUT: - mDGC.setListener( mSymbolFilter ); - break; - case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT: - mSymbolFilter.setListener( mC4FMSlicer ); - break; - case INSTRUMENT_C4FM_SLICER_OUTPUT: - DibitTap slicerTap = (DibitTap)tap; - - if( mC4FMSlicer != null ) - { - mC4FMSlicer.removeListener( slicerTap ); - } - break; - } - } - - @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - throw new IllegalArgumentException( "unfiltered real sample provider " - + "not implemented in P25 Decoder" ); - } - - public enum Modulation - { - CQPSK( "LSM SIMULCAST", "LSM" ), - C4FM( "C4FM", "C4FM" ); - - private String mLabel; - private String mShortLabel; - - private Modulation( String label, String shortLabel ) - { - mLabel = label; - mShortLabel = shortLabel; - } - - public String getLabel() - { - return mLabel; - } - - public String getShortLabel() - { - return mShortLabel; - } - - public String toString() - { - return getLabel(); - } - }; -} diff --git a/src/decode/p25/audio/IMBETargetDataLine.java b/src/decode/p25/audio/IMBETargetDataLine.java deleted file mode 100644 index 041e233f..00000000 --- a/src/decode/p25/audio/IMBETargetDataLine.java +++ /dev/null @@ -1,273 +0,0 @@ -package decode.p25.audio; - -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.Control; -import javax.sound.sampled.Control.Type; -import javax.sound.sampled.DataLine; -import javax.sound.sampled.LineListener; -import javax.sound.sampled.LineUnavailableException; -import javax.sound.sampled.TargetDataLine; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import audio.AudioFormats; - -public class IMBETargetDataLine implements TargetDataLine -{ - private final static Logger mLog = - LoggerFactory.getLogger( IMBETargetDataLine.class ); - - public static final int BUFFER_SIZE = 18; - - public byte[] mFrame; - - /** - * IMBE target dataline provides a java audio system compatible interface to - * ingest and buffer raw IMBE audio frames and expose a standard audio - * system interface for follow-on audio processors to consume the output audio - * stream provided by this interface. - */ - public IMBETargetDataLine() - { - } - - @Override - public void open( AudioFormat format, int bufferSize ) - throws LineUnavailableException - { - if( format != AudioFormats.IMBE_AUDIO_FORMAT ) - { - throw new LineUnavailableException( "Unsupported format" ); - } - } - - @Override - public void open() throws LineUnavailableException - { - open( AudioFormats.IMBE_AUDIO_FORMAT, BUFFER_SIZE ); - } - - @Override - public void close() - { - } - - @Override - public boolean isOpen() - { - return true; - } - - @Override - public void open( AudioFormat format ) throws LineUnavailableException - { - open( format, BUFFER_SIZE ); - } - - @Override - public int read( byte[] buffer, int offset, int length ) - { - if( mFrame != null ) - { - System.arraycopy( mFrame, 0, buffer, 0, mFrame.length ); - - return mFrame.length; - } - - return 0; - } - -// private int getBytesAvailable() -// { -// return 1920; -// return mFrame.length; -// } - - /** - * Primary inject point for submitting IMBE frame data into the audio - * system via this data line - */ - public void receive( byte[] data ) - { - mFrame = data; - } - - /** - * Not implemented - */ - @Override - public void drain() - { - } - - /** - * Flushes (deletes) any buffered audio frames. If a frame is currently - * being processed it will continue and a stop event will be broadcast - * after the current frame has been dispatched. - */ - @Override - public void flush() - { - mFrame = null; - } - - /** - * If the line is open, sets the running state to true and attempts to - * retrieve a frame from the buffer. If successful, a line start event is - * broadcast. If no frames are currently buffered, then a line start event - * will be broadcast once the first frame is received. - * - * If the line is closed, or if the running state is already set to true, - * then the start() is ignored. - */ - @Override - public void start() - { - } - - /** - * Stops this data line, sets running and active states to false and purges - * any currently buffered data frames. Note: the line will remain open - * until close() is invoked and incoming frame data will continue to buffer. - */ - @Override - public void stop() - { - } - - /** - * Indicates if this line us currently opened, but does not reflect if the - * line is currently streaming data. Use the isActive() method to determine - * streaming status. - */ - @Override - public boolean isRunning() - { - return true; - } - - /** - * Indicates if this line is currently streaming data - */ - @Override - public boolean isActive() - { - return true; - } - - /** - * IMBE AudioFormat produced by this target data line. - */ - @Override - public AudioFormat getFormat() - { - return AudioFormats.IMBE_AUDIO_FORMAT; - } - - /** - * Size of the internal buffer in bytes. This method will not return the - * true size of the buffer if this data line was constructed with a buffer - * size other than the default (250 frames) buffer size. - */ - @Override - public int getBufferSize() - { - return 18; - } - - /** - * Number of bytes of imbe frame data available. Value will be the number - * of buffered frames times 18 bytes. - */ - @Override - public int available() - { - return 18; - } - - /** - * Current number of frames provided by this data line since it was opened. - * Counter rolls over once it exceeds the max integer value. - */ - @Override - public int getFramePosition() - { - return 0; - } - - /** - * Current number of frames provided by this data line since it was opened - */ - @Override - public long getLongFramePosition() - { - return 0; - } - - /** - * Number of microseconds worth of data that has been sourced by this - * target data line. Returns the frame counter times 20,000 microseconds. - */ - @Override - public long getMicrosecondPosition() - { - return 0; - } - - /** - * Not implemented - */ - @Override - public float getLevel() - { - return AudioSystem.NOT_SPECIFIED; - } - - @Override - public javax.sound.sampled.Line.Info getLineInfo() - { - return new DataLine.Info( IMBETargetDataLine.class, - AudioFormats.IMBE_AUDIO_FORMAT ); - } - - /** - * No controls are implemented for this data line - */ - @Override - public Control[] getControls() - { - return null; - } - - /** - * No controls are implemented for this data line - */ - @Override - public boolean isControlSupported( Type control ) - { - return false; - } - - /** - * No controls are implemented for this data line - */ - @Override - public Control getControl( Type control ) - { - return null; - } - - @Override - public void addLineListener( LineListener listener ) - { - // TODO Auto-generated method stub - } - - @Override - public void removeLineListener( LineListener listener ) - { - // TODO Auto-generated method stub - } -} diff --git a/src/decode/p25/audio/P25AudioCodecTest.java b/src/decode/p25/audio/P25AudioCodecTest.java deleted file mode 100644 index c35f330d..00000000 --- a/src/decode/p25/audio/P25AudioCodecTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package decode.p25.audio; - -import java.util.Iterator; -import java.util.ServiceLoader; - -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.TargetDataLine; -import javax.sound.sampled.spi.FormatConversionProvider; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import audio.AudioFormats; - -public class P25AudioCodecTest -{ - private final static Logger mLog = LoggerFactory.getLogger( P25AudioCodecTest.class ); - - public static void main( String[] args ) - { - mLog.debug( "Starting ..." ); - AudioFormat from = AudioFormats.IMBE_AUDIO_FORMAT; - AudioFormat to = AudioFormats.PCM_SIGNED_8KHZ_16BITS; - - ServiceLoader loader = ServiceLoader.load( FormatConversionProvider.class ); - - Iterator it = loader.iterator(); - - while( it.hasNext() ) - { - mLog.debug( "Provider:" + it.next().getClass() ); - } - - - -// try -// { -// TargetDataLine tdl = AudioSystem.getTargetDataLine( from ); -// -// mLog.debug( "We got a tdl ..." ); -// } -// catch ( Exception e ) -// { -// e.printStackTrace(); -// } - - mLog.debug( "Finished" ); - } -} diff --git a/src/decode/p25/audio/P25AudioOutput.java b/src/decode/p25/audio/P25AudioOutput.java deleted file mode 100644 index da4bf084..00000000 --- a/src/decode/p25/audio/P25AudioOutput.java +++ /dev/null @@ -1,154 +0,0 @@ -package decode.p25.audio; - -import jmbe.iface.AudioConversionLibrary; -import jmbe.iface.AudioConverter; -import message.Message; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.Listener; -import audio.AudioFormats; -import audio.AudioPacket; -import audio.IAudioOutput; -import audio.MonoAudioOutput; -import audio.SquelchListener; -import audio.inverted.AudioType; -import audio.inverted.IAudioTypeListener; -import controller.ResourceManager; -import decode.p25.message.ldu.LDUMessage; -import decode.p25.message.tdu.TDUMessage; -import decode.p25.message.tdu.lc.TDULinkControlMessage; - -/** - * P25 Phase 1 IMBE Audio Output converter. Processes 18-byte IMBE audio frames - * and produces PCM 48k MONO 16-bit sample output. - * - * Note: this class depends on the external JMBE library. - */ -public class P25AudioOutput implements IAudioOutput, Listener, - SquelchListener, IAudioTypeListener -{ - private final static Logger mLog = LoggerFactory.getLogger( P25AudioOutput.class ); - - private static final String IMBE_CODEC = "IMBE"; - - private boolean mCanConvertAudio = false; - private boolean mEncryptedAudio = false; - - private AudioConverter mAudioConverter; - - private MonoAudioOutput mAudioOutput = new MonoAudioOutput(); - - public P25AudioOutput( ResourceManager resourceManager ) - { - loadConverter(); - } - - /** - * Primary inject point for p25 imbe audio frame messages. Each LDU audio - * message contains 9 imbe voice frames. If the audio is not encrypted, - * we insert each audio frame asynchronously into the audio converter. - */ - public void receive( Message message ) - { - if( mCanConvertAudio ) - { - if( message instanceof LDUMessage ) - { - LDUMessage ldu = (LDUMessage)message; - - /* If we detect a valid LDU message with the encryption flag - * set to true, we toggle the encrypted audio state until we - * receive the first TDU or TDULC message, then reset it. */ - if( ldu.isValid() && ldu.isEncrypted() ) - { - mEncryptedAudio = true; - } - - if( !mEncryptedAudio && mAudioConverter != null ) - { - for( byte[] frame: ((LDUMessage)message).getIMBEFrames() ) - { - byte[] audio = mAudioConverter.convert( frame ); - - mAudioOutput.receive( new AudioPacket( "P25 Audio", audio, 1 ) ); - } - } - } - else if( message instanceof TDUMessage || message instanceof TDULinkControlMessage ) - { - mEncryptedAudio = false; - } - } - } - - /** - * Loads audio frame processing chain. Constructs an imbe targetdataline - * to receive the raw imbe frames. Adds an IMBE to 8k PCM format conversion - * stream wrapper. Finally, adds an upsampling (8k to 48k) stream wrapper. - */ - private void loadConverter() - { - AudioConversionLibrary library = null; - - try - { - @SuppressWarnings( "rawtypes" ) - Class temp = Class.forName( "jmbe.JMBEAudioLibrary" ); - - library = (AudioConversionLibrary)temp.newInstance(); - - mAudioConverter = library.getAudioConverter( IMBE_CODEC, - AudioFormats.PCM_SIGNED_48KHZ_16BITS ); - - if( mAudioConverter != null ) - { - mCanConvertAudio = true; - mLog.info( "JMBE audio conversion library successfully loaded" - + " - P25 audio will be available" ); - } - else - { - mLog.info( "JMBE audio conversion library NOT FOUND" ); - } - } - catch ( ClassNotFoundException e1 ) - { - mLog.error( "Couldn't find/load JMBE audio conversion library" ); - } - catch ( InstantiationException e1 ) - { - mLog.error( "Couldn't instantiate JMBE audio conversion library class" ); - } - catch ( IllegalAccessException e1 ) - { - mLog.error( "Couldn't load JMBE audio conversion library due to " - + "security restrictions" ); - } - } - - @Override - public void setAudioPlaybackEnabled( boolean enabled ) - { - mAudioOutput.setMuted( !enabled ); - } - - @Override - public void dispose() - { - mAudioOutput.dispose(); - } - - @Override - public void setSquelch( SquelchState state ) - { - /* not implemented */ - } - - @Override - public void setAudioType( AudioType type ) - { - //Not implemented - } -} \ No newline at end of file diff --git a/src/decode/passport/PassportActivitySummary.java b/src/decode/passport/PassportActivitySummary.java deleted file mode 100644 index 055ca424..00000000 --- a/src/decode/passport/PassportActivitySummary.java +++ /dev/null @@ -1,233 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.passport; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.TreeSet; - -import message.Message; -import sample.Listener; -import alias.Alias; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; - -public class PassportActivitySummary implements ActivitySummaryProvider, - Listener -{ - private static final int sINT_NULL_VALUE = -1; - private DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000"); - - private AliasList mAliasList; - private HashSet mTalkgroupsFirstHeard = new HashSet(); - private TreeSet mTalkgroups = new TreeSet(); - private TreeSet mMobileIDs = new TreeSet(); - private HashMap mSiteLCNs = new HashMap(); - private HashMap mNeighborLCNs = new HashMap(); - - /** - * Compiles a summary of active talkgroups, unique radio ids encountered - * in the decoded messages. Talkgroups have to be heard a minimum of twice - * to be considered valid ... in order to weed out the invalid ones. - * - * Returns a textual summary of the activity - */ - public PassportActivitySummary( AliasList list ) - { - mAliasList = list; - } - - @Override - public void receive( Message message ) - { - if( message instanceof PassportMessage ) - { - PassportMessage passport = ((PassportMessage)message); - - if( passport.isValid() ) - { - switch( passport.getMessageType() ) - { - case CA_STRT: - mSiteLCNs.put( passport.getLCN(), - passport.getLCNFrequencyFormatted() ); - case CA_ENDD: - String talkgroup = - String.valueOf( passport.getTalkgroupID() ); - - if( mTalkgroupsFirstHeard.contains( talkgroup ) ) - { - mTalkgroups.add( talkgroup ); - } - else - { - mTalkgroupsFirstHeard.add( talkgroup ); - } - break; - case ID_RDIO: - String min = passport.getMobileID(); - - if( min != null ) - { - mMobileIDs.add( min ); - } - break; - case SY_IDLE: - if( passport.getFree() != 0 ) - { - mNeighborLCNs.put( passport.getFree(), - passport.getFreeFrequencyFormatted() ); - } - break; - default: - break; - } - } - } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "Activity Summary\n" ); - sb.append( "Decoder:\tPassport\n\n" ); - sb.append( "Site Channels\n" ); - - if( mSiteLCNs.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - ArrayList channels = new ArrayList( mSiteLCNs.keySet() ); - Collections.sort( channels ); - - for( Integer channel: channels ) - { - sb.append( " " + channel ); - sb.append( "\t" + mSiteLCNs.get( channel ) ); - sb.append( "\n" ); - } - } - - sb.append( "\nNeighbor Channels\n" ); - - if( mNeighborLCNs.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - ArrayList channels = new ArrayList( mNeighborLCNs.keySet() ); - Collections.sort( channels ); - - for( Integer channel: channels ) - { - sb.append( " " + channel ); - sb.append( "\t" + mNeighborLCNs.get( channel ) ); - sb.append( "\n" ); - } - } - - sb.append( "\nTalkgroups\n" ); - - if( mTalkgroups.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mTalkgroups.iterator(); - - while( it.hasNext() ) - { - String tgid = it.next(); - - sb.append( " " ); - sb.append( tgid ); - sb.append( " " ); - - Alias alias = mAliasList.getTalkgroupAlias( tgid ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - sb.append( "\nMobile ID Numbers\n" ); - - if( mMobileIDs.isEmpty() ) - { - sb.append( " None\n" ); - } - else - { - Iterator it = mMobileIDs.iterator(); - - while( it.hasNext() ) - { - String min = it.next(); - - sb.append( " " ); - sb.append( min ); - sb.append( " " ); - - Alias alias = mAliasList.getMobileIDNumberAlias( min ); - - if( alias != null ) - { - sb.append( alias.getName() ); - } - - sb.append( "\n" ); - } - } - - return sb.toString(); - } - - public static String formatTalkgroup( String talkgroup ) - { - StringBuilder sb = new StringBuilder(); - - if( talkgroup.length() == 6 ) - { - sb.append( talkgroup.substring( 0, 1 ) ); - sb.append( "-" ); - sb.append( talkgroup.substring( 1, 3 ) ); - sb.append( "-" ); - sb.append( talkgroup.substring( 3, 6 ) ); - - return sb.toString(); - } - else - { - return talkgroup; - } - } -} diff --git a/src/decode/passport/PassportChannelState.java b/src/decode/passport/PassportChannelState.java deleted file mode 100644 index f64d34ea..00000000 --- a/src/decode/passport/PassportChannelState.java +++ /dev/null @@ -1,369 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.passport; - -import gui.SDRTrunk; - -import java.util.HashMap; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import message.Message; -import alias.Alias; -import alias.AliasList; -import audio.SquelchListener; -import audio.inverted.AudioType; -import controller.activity.CallEvent; -import controller.activity.CallEvent.CallEventType; -import controller.channel.ProcessingChain; -import controller.state.AuxChannelState; -import controller.state.ChannelState; - -public class PassportChannelState extends ChannelState -{ - private final static Logger mLog = - LoggerFactory.getLogger( PassportChannelState.class ); - - private String mTalkgroup; - private Alias mTalkgroupAlias; - private String mMobileID; - private Alias mMobileIDAlias; - private int mChannelNumber; - private int mSiteNumber; - private PassportBand mSiteBand; - private PassportActivitySummary mActivitySummary; - private HashMap mActiveCalls = new HashMap(); - - public PassportChannelState( ProcessingChain channel, AliasList aliasList ) - { - super( channel, aliasList ); - - mActivitySummary = new PassportActivitySummary( aliasList ); - } - - @Override - public String getActivitySummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( mActivitySummary.getSummary() ); - - for( AuxChannelState state: mAuxChannelStates ) - { - sb.append( state.getActivitySummary() ); - sb.append( "\n\n" ); - } - - return sb.toString(); - } - - @Override - @SuppressWarnings( "incomplete-switch" ) - public void receive( Message message ) - { - super.receive( message ); - - if( message instanceof PassportMessage ) - { - mActivitySummary.receive( message ); - - PassportMessage passport = (PassportMessage)message; - - if( passport.isValid() ) - { - switch( passport.getMessageType() ) - { - case CA_STRT: - if( mChannelNumber == 0 ) - { - mChannelNumber = passport.getLCN(); - } - - if( passport.getLCN() == mChannelNumber ) - { - setTalkgroup( String.valueOf( passport.getTalkgroupID() ) ); - setTalkgroupAlias( passport.getTalkgroupIDAlias() ); - - if( mTalkgroupAlias != null ) - { - try - { - if( mTalkgroupAlias.hasAudioType() ) - { - mAudioTypeListener.setAudioType( - mTalkgroupAlias.getAudioType() ); - } - } - catch( Exception e ) - { - mLog.error( "Error setting audio for " - + "passport call", e ); - } - } - - PassportCallEvent current = - getCurrentPassportCallEvent(); - - /* If we're already in a call event, add the message - * to the current call event ... if false, then we - * have a different call ... cleanup the old one. */ - if( current != null && !current.addMessage( passport ) ) - { - if( current.isValid() ) - { - final PassportCallEvent endEvent = - getCurrentPassportCallEvent(); - - mCallEventModel.setEnd( endEvent ); - } - else - { - mCallEventModel.remove( current ); - } - - current = null; - setCurrentCallEvent( null ); - } - - if( current == null ) - { - mCurrentCallEvent = new PassportCallEvent - .Builder( CallEventType.CALL ) - .aliasList( mAliasList ) - .channel( String.valueOf( mChannelNumber ) ) - .frequency( passport.getLCNFrequency() ) - .to( String.valueOf( passport.getTalkgroupID() ) ) - .build(); - - mCallEventModel.add( mCurrentCallEvent ); - } - - State state = getState(); - - if( state == State.IDLE || state == State.CALL ) - { - setState( State.CALL ); - } - else if( state == State.DATA ) - { - setState( State.DATA ); - } - } - else - { - //Call Detection - int lcn = passport.getLCN(); - String tg = String.valueOf( passport.getTalkgroupID() ); - - if( !mActiveCalls.containsKey( lcn ) || - !mActiveCalls.get( lcn ).contentEquals( tg ) ) - { - mActiveCalls.put( lcn, tg ); - - PassportCallEvent event = new PassportCallEvent - .Builder( CallEventType.CALL_DETECT ) - .aliasList( mAliasList ) - .channel( String.valueOf( lcn ) ) - .details( "Site: " + passport.getSite() ) - .frequency( passport.getLCNFrequency() ) - .to( tg ) - .build(); - - mCallEventModel.add( event ); - } - } - break; - case DA_STRT: -//TODO: add channel number checking and log a call event if on another site - setTalkgroup( String.valueOf( passport.getTalkgroupID() ) ); - setTalkgroupAlias( passport.getTalkgroupIDAlias() ); - setState( State.DATA ); - break; - case CA_ENDD: - setTalkgroup( String.valueOf( passport.getTalkgroupID() ) ); - setTalkgroupAlias( passport.getTalkgroupIDAlias() ); - fade( CallEventType.CALL_END ); - break; - case ID_RDIO: - setMobileID( passport.getMobileID() ); - setMobileIDAlias( passport.getMobileIDAlias() ); - - final String mid = passport.getMobileID(); - - final PassportCallEvent event = - getCurrentPassportCallEvent(); - - if( event != null && mid != null ) - { - mCallEventModel.setFromID( event, mid ); - } - - setState( State.CALL ); - break; - case RA_REGI: - mCallEventModel.add( new PassportCallEvent - .Builder( CallEventType.REGISTER ) - .aliasList( mAliasList ) - .channel( String.valueOf( passport.getLCN() ) ) - .frequency( passport.getLCNFrequency() ) - .to( passport.getToID() ) - .build() ); -//TODO: add logic to recognize the register event and suppress the subsequent -//call messages, but capture the group assignments. - setState( State.DATA ); - break; - case SY_IDLE: - PassportBand band = passport.getSiteBand(); - - if( mSiteBand == null || mSiteBand != band ) - { - mSiteBand = band; - - mSiteNumber = passport.getSite(); - - long frequency = mProcessingChain.getChannel() - .getTunerChannel().getFrequency(); - - mChannelNumber = mSiteBand.getChannel( frequency ); - - broadcastChange( ChangedAttribute.CHANNEL_NUMBER ); - } - default: - break; - } - } - } - } - - /** - * Intercept the fade event so that we can generate a call end event - */ - @Override - public void fade( final CallEventType type ) - { - /* - * We can receive multiple call tear-down messages -- only post a call - * end event for the one that can change the state to fade - */ - if( getState().canChangeTo( State.FADE ) ) - { - final PassportCallEvent current = getCurrentPassportCallEvent(); - - if( current != null ) - { - mCallEventModel.setEnd( current ); - } - } - - setCurrentCallEvent( null ); - - mActiveCalls.clear(); - - super.fade( type ); - } - - public PassportCallEvent getCurrentPassportCallEvent() - { - CallEvent current = getCurrentCallEvent(); - - if( current != null ) - { - return (PassportCallEvent)current; - } - - return null; - } - - /** - * Make the ConventionalChannelState always unsquelched - */ - public void addListener( SquelchListener listener ) - { - super.addListener( listener ); - } - - public void reset() - { - mAudioTypeListener.setAudioType( AudioType.NORMAL ); - - mCurrentCallEvent = null; - - setTalkgroup( null ); - setTalkgroupAlias( null ); - - setMobileID( null ); - setMobileIDAlias( null ); - - super.reset(); - } - - public int getChannelNumber() - { - return mChannelNumber; - } - - public int getSiteNumber() - { - return mSiteNumber; - } - - public String getTalkgroup() - { - return mTalkgroup; - } - - public void setTalkgroup( String talkgroup ) - { - mTalkgroup = talkgroup; - broadcastChange( ChangedAttribute.TO_TALKGROUP ); - } - - public Alias getTalkgroupAlias() - { - return mTalkgroupAlias; - } - - public void setTalkgroupAlias( Alias alias ) - { - mTalkgroupAlias = alias; - broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS ); - } - - public String getMobileID() - { - return mMobileID; - } - - public void setMobileID( String id ) - { - mMobileID = id; - broadcastChange( ChangedAttribute.FROM_TALKGROUP ); - } - - public Alias getMobileIDAlias() - { - return mMobileIDAlias; - } - - public void setMobileIDAlias( Alias alias ) - { - mMobileIDAlias = alias; - broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - } -} diff --git a/src/decode/passport/PassportDecoder.java b/src/decode/passport/PassportDecoder.java deleted file mode 100644 index 8293123e..00000000 --- a/src/decode/passport/PassportDecoder.java +++ /dev/null @@ -1,165 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see - ******************************************************************************/ -package decode.passport; - -import instrument.Instrumentable; -import instrument.tap.Tap; - -import java.util.ArrayList; -import java.util.List; - -import sample.real.RealSampleListener; -import source.Source.SampleType; -import alias.AliasList; -import audio.AudioOutputImpl; -import audio.IAudioOutput; -import bits.MessageFramer; -import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; -import decode.config.DecodeConfiguration; -import dsp.filter.DCRemovalFilter2; -import dsp.fsk.LTRFSKDecoder; -import dsp.nbfm.FilteringNBFMDemodulator; - -public class PassportDecoder extends Decoder implements Instrumentable -{ - /** - * This value determines how quickly the DC remove filter responds to - * changes in frequency. - */ - private static final double sDC_REMOVAL_RATIO = 0.000003; - - public static final int sPASSPORT_MESSAGE_LENGTH = 68; - public static final int sPASSPORT_SYNC_LENGTH = 9; - private FilteringNBFMDemodulator mNBFMDemodulator; - private DCRemovalFilter2 mDCRemovalFilter; - private LTRFSKDecoder mPassportFSKDecoder; - private MessageFramer mPassportMessageFramer; - private PassportMessageProcessor mPassportMessageProcessor; - private AudioOutputImpl mAudioOutput = new AudioOutputImpl( "Passport Decoder Audio Output" ); - - private List mAvailableTaps; - - public PassportDecoder( DecodeConfiguration config, - SampleType sampleType, - AliasList aliasList ) - { - super( config, 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 Short - * 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 - */ - mNBFMDemodulator = new FilteringNBFMDemodulator(); - this.addComplexListener( mNBFMDemodulator ); - - /** - * Remove the DC component that is present when we're mistuned - */ - mDCRemovalFilter = new DCRemovalFilter2( sDC_REMOVAL_RATIO ); - mNBFMDemodulator.addListener( mDCRemovalFilter ); - mDCRemovalFilter.setListener( this.getRealReceiver() ); - } - - mPassportFSKDecoder = - new LTRFSKDecoder(); - - /** - * The DC removal filter will impact performance of the LTRFSKDemod, - * so we get the samples straight from the NBFM demod - */ - if( mSourceSampleType == SampleType.COMPLEX ) - { - mNBFMDemodulator.addListener( mPassportFSKDecoder ); - } - else - { - addRealSampleListener( mPassportFSKDecoder ); - } - - - mPassportMessageFramer = - new MessageFramer( SyncPattern.PASSPORT.getPattern(), - sPASSPORT_MESSAGE_LENGTH ); - - mPassportFSKDecoder.addListener( mPassportMessageFramer ); - - mPassportMessageProcessor = new PassportMessageProcessor( aliasList ); - mPassportMessageFramer.addMessageListener( mPassportMessageProcessor ); - mPassportMessageProcessor.addMessageListener( this ); - - addRealSampleListener( mAudioOutput ); - } - - @Override - public DecoderType getType() - { - return DecoderType.PASSPORT; - } - - @Override - public List getTaps() - { - if( mAvailableTaps == null ) - { - mAvailableTaps = new ArrayList(); - - mAvailableTaps.addAll( mPassportFSKDecoder.getTaps() ); - } - - return mAvailableTaps; - } - - @Override - public void addTap( Tap tap ) - { - mPassportFSKDecoder.addTap( tap ); - } - - @Override - public void removeTap( Tap tap ) - { - mPassportFSKDecoder.removeTap( tap ); - } - - @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - if( mNBFMDemodulator != null ) - { - mNBFMDemodulator.addListener( listener ); - } - } - - @Override - public IAudioOutput getAudioOutput() - { - return mAudioOutput; - } -} diff --git a/src/decode/tait/Tait1200MessageFilter.java b/src/decode/tait/Tait1200MessageFilter.java deleted file mode 100644 index 291a0e4c..00000000 --- a/src/decode/tait/Tait1200MessageFilter.java +++ /dev/null @@ -1,35 +0,0 @@ -package decode.tait; - -import java.util.Collections; -import java.util.List; - -import message.Message; -import filter.Filter; -import filter.FilterElement; - -public class Tait1200MessageFilter extends Filter -{ - - public Tait1200MessageFilter() - { - super( "Tait-1200 Message Filter" ); - } - - @Override - public boolean passes( Message message ) - { - return mEnabled && canProcess( message ); - } - - @Override - public boolean canProcess( Message message ) - { - return message instanceof Tait1200GPSMessage; - } - - @Override - public List> getFilterElements() - { - return Collections.EMPTY_LIST; - } -} diff --git a/src/dsp/am/AMDemodulator.java b/src/dsp/am/AMDemodulator.java index 64cfc62b..261adc80 100644 --- a/src/dsp/am/AMDemodulator.java +++ b/src/dsp/am/AMDemodulator.java @@ -1,45 +1,22 @@ package dsp.am; -import sample.Listener; -import sample.complex.ComplexSample; -import sample.real.RealSampleListener; -import sample.real.RealSampleProvider; - -public class AMDemodulator implements Listener, RealSampleProvider +public abstract class AMDemodulator { - private RealSampleListener mListener; - - public AMDemodulator() + private float mGain; + + public AMDemodulator( float gain ) + { + mGain = gain; + } + + public float demodulate( float inphase, float quadrature ) { + return (float)Math.sqrt( ( inphase * inphase ) + + ( quadrature * quadrature ) ) * mGain; } - @Override - public void receive( ComplexSample sample ) - { - float demodulated = (float)Math.sqrt( ( sample.real() * - sample.real() ) + - ( sample.imaginary() * - sample.imaginary() ) ); - - if( mListener != null ) - { - mListener.receive( demodulated * 500.0f ); - } - } - - /** - * Adds a listener to receive demodulated samples - */ - public void setListener( RealSampleListener listener ) - { - mListener = listener; - } - - /** - * Removes a listener from receiving demodulated samples - */ - public void removeListener( RealSampleListener listener ) - { - mListener = null; - } + public void setGain( float gain ) + { + mGain = gain; + } } diff --git a/src/decode/tait/Tait1200ActivitySummary.java b/src/dsp/am/AMDemodulator_CB.java similarity index 52% rename from src/decode/tait/Tait1200ActivitySummary.java rename to src/dsp/am/AMDemodulator_CB.java index 5402b9b8..543dff37 100644 --- a/src/decode/tait/Tait1200ActivitySummary.java +++ b/src/dsp/am/AMDemodulator_CB.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,46 +15,55 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package dsp.am; -import message.Message; import sample.Listener; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; +import sample.complex.ComplexBuffer; +import sample.real.RealBuffer; -public class Tait1200ActivitySummary implements ActivitySummaryProvider, - Listener +public class AMDemodulator_CB extends AMDemodulator implements Listener { - private AliasList mAliasList; - - public Tait1200ActivitySummary( AliasList list ) + private Listener mListener; + + public AMDemodulator_CB( float gain ) { - mAliasList = list; + super( gain ); } + @Override + public void receive( ComplexBuffer buffer ) + { + if( mListener != null ) + { + float[] samples = buffer.getSamples(); + + int half = samples.length / 2; + + float[] demodulated = new float[ half ]; + + for( int x = 0; x < half; x++ ) + { + int index = x * 2; + + demodulated[ x ] = demodulate( samples[ index ], samples[ index + 1 ] ); + } + + mListener.receive( new RealBuffer( demodulated ) ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } + public void dispose() { + mListener = null; } - - @Override - public void receive( Message message ) - { - if( message instanceof Tait1200GPSMessage ) - { - Tait1200GPSMessage tait = ((Tait1200GPSMessage)message); - - //Do something here in the future - } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "=============================\n" ); - sb.append( "Decoder:\tTait-1200I\n\n" ); - - return sb.toString(); - } } diff --git a/src/dsp/filter/ComplexCICDecimate.java b/src/dsp/filter/ComplexCICDecimate.java index ae9f2c41..f82fde74 100644 --- a/src/dsp/filter/ComplexCICDecimate.java +++ b/src/dsp/filter/ComplexCICDecimate.java @@ -19,7 +19,7 @@ package dsp.filter; import sample.Listener; import sample.Provider; -import sample.complex.ComplexSample; +import sample.complex.Complex; import dsp.filter.Window.WindowType; /** @@ -30,7 +30,7 @@ import dsp.filter.Window.WindowType; * Note: comb delay is fixed at 1 for this filter. */ public class ComplexCICDecimate extends ComplexFilter - implements Provider + implements Provider { private Integrator[] mIntegrator; private Comb[] mComb; @@ -99,7 +99,7 @@ public class ComplexCICDecimate extends ComplexFilter * Input method for this filter to receive complex samples. */ @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mIntegrator[ 0 ].receive( (double)sample.left(), (double)sample.right() ); } @@ -108,7 +108,7 @@ public class ComplexCICDecimate extends ComplexFilter * Sets the listener to receive the output of this CIC filter */ @Override - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mOutput.setListener( listener ); } @@ -117,13 +117,13 @@ public class ComplexCICDecimate extends ComplexFilter * Removes the listener from receiving the output of this CIC filter */ @Override - public void removeListener( Listener listener ) + public void removeListener( Listener listener ) { mOutput.removeListener( listener ); } @Override - public Listener getListener() + public Listener getListener() { return mOutput.getListener(); } @@ -260,11 +260,11 @@ public class ComplexCICDecimate extends ComplexFilter { mStageCount = stageCount; mDecimation = decimation; - mCleanupFilter = new ComplexFIRFilter( FilterFactory + mCleanupFilter = new ComplexFIRFilterOld( FilterFactory .getCICCleanupFilter( outputSampleRate, 12000, 60, - WindowType.BLACKMAN ), 1.0d ); + WindowType.BLACKMAN ), 1.0f ); setGain(); } @@ -278,7 +278,7 @@ public class ComplexCICDecimate extends ComplexFilter public void receive( double left, double right ) { mCleanupFilter.receive( - new ComplexSample( (float)( left * mGain ), + new Complex( (float)( left * mGain ), (float)( right * mGain ) ) ); } @@ -294,7 +294,7 @@ public class ComplexCICDecimate extends ComplexFilter /** * Adds a listener to receive output samples */ - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mCleanupFilter.setListener( listener ); } @@ -302,12 +302,12 @@ public class ComplexCICDecimate extends ComplexFilter /** * Removes the listener from receiving output samples */ - public void removeListener( Listener listener ) + public void removeListener( Listener listener ) { mCleanupFilter.setListener( null ); } - public Listener getListener() + public Listener getListener() { return mCleanupFilter.getListener(); } diff --git a/src/dsp/filter/ComplexFIRFilter.java b/src/dsp/filter/ComplexFIRFilterOld.java similarity index 90% rename from src/dsp/filter/ComplexFIRFilter.java rename to src/dsp/filter/ComplexFIRFilterOld.java index 22f13482..84e9fc02 100644 --- a/src/dsp/filter/ComplexFIRFilter.java +++ b/src/dsp/filter/ComplexFIRFilterOld.java @@ -19,30 +19,30 @@ package dsp.filter; import java.util.ArrayList; -import sample.complex.ComplexSample; +import sample.complex.Complex; -public class ComplexFIRFilter extends ComplexFilter +public class ComplexFIRFilterOld extends ComplexFilter { - private ArrayList mBuffer; + private ArrayList mBuffer; private int mBufferSize = 1; //Temporary initial value private int mBufferPointer = 0; - private double[] mCoefficients; + private float[] mCoefficients; private int[][] mIndexMap; private int mCenterCoefficient; private int mCenterCoefficientMapIndex; - private double mGain; + private float mGain; - public ComplexFIRFilter( double[] coefficients, double gain ) + public ComplexFIRFilterOld( float[] coefficients, float gain ) { mCoefficients = coefficients; - mBuffer = new ArrayList(); + mBuffer = new ArrayList(); mBufferSize = mCoefficients.length; mGain = gain; //Fill the buffer with zero valued samples, so we don't have to check for null for( int x = 0; x < mCoefficients.length; x++ ) { - mBuffer.add( new ComplexSample( (float)0.0, (float)0.0 ) ); + mBuffer.add( new Complex( (float)0.0, (float)0.0 ) ); } generateIndexMap( mCoefficients.length ); @@ -57,7 +57,7 @@ public class ComplexFIRFilter extends ComplexFilter mIndexMap = null; } - public void receive( ComplexSample newSample ) + public void receive( Complex newSample ) { //Add the new sample to the buffer mBuffer.set( mBufferPointer, newSample ); @@ -96,7 +96,7 @@ public class ComplexFIRFilter extends ComplexFilter //We're almost finished ... apply gain, cast the doubles to floats and //send it on it's merry way - send( new ComplexSample( (float)( leftAccumulator * mGain ), + send( new Complex( (float)( leftAccumulator * mGain ), (float)( rightAccumulator * mGain ) ) ); } diff --git a/src/dsp/filter/ComplexFilter.java b/src/dsp/filter/ComplexFilter.java index d1b4f266..b5e08191 100644 --- a/src/dsp/filter/ComplexFilter.java +++ b/src/dsp/filter/ComplexFilter.java @@ -18,13 +18,14 @@ package dsp.filter; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; -public abstract class ComplexFilter implements Listener +@Deprecated +public abstract class ComplexFilter implements Listener { - private Listener mListener; + private Listener mListener; - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mListener = listener; } @@ -34,7 +35,7 @@ public abstract class ComplexFilter implements Listener mListener = null; } - public Listener getListener() + public Listener getListener() { return mListener; } @@ -44,7 +45,7 @@ public abstract class ComplexFilter implements Listener return mListener != null; } - protected void send( ComplexSample sample ) + protected void send( Complex sample ) { if( mListener != null ) { diff --git a/src/dsp/filter/ComplexHalfBandFilter.java b/src/dsp/filter/ComplexHalfBandFilter.java index 49589286..a8dc266d 100644 --- a/src/dsp/filter/ComplexHalfBandFilter.java +++ b/src/dsp/filter/ComplexHalfBandFilter.java @@ -19,15 +19,15 @@ package dsp.filter; import java.util.ArrayList; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class ComplexHalfBandFilter extends ComplexFilter { - private ArrayList mBuffer; + private ArrayList mBuffer; private int mBufferSize = 1; //Temporary initial value private int mBufferPointer = 0; - private double mGain; - private double[] mCoefficients; + private float mGain; + private float[] mCoefficients; private int[][] mIndexMap; private int mCenterCoefficient; private int mCenterCoefficientMapIndex; @@ -49,16 +49,16 @@ public class ComplexHalfBandFilter extends ComplexFilter * @param filter - filter coefficients * @param gain - gain multiplier. Use 1.0 for unity/no gain */ - public ComplexHalfBandFilter( Filters filter, double gain ) + public ComplexHalfBandFilter( Filters filter, float gain ) { mCoefficients = filter.getCoefficients(); - mBuffer = new ArrayList(); + mBuffer = new ArrayList(); mBufferSize = mCoefficients.length; //Fill the buffer with zero valued samples for( int x = 0; x < mCoefficients.length; x++ ) { - mBuffer.add( new ComplexSample( (float)0.0, (float)0.0 ) ); + mBuffer.add( new Complex( (float)0.0, (float)0.0 ) ); } generateIndexMap( mCoefficients.length ); @@ -69,7 +69,7 @@ public class ComplexHalfBandFilter extends ComplexFilter * Calculate the filtered value by applying the coefficients against * the complex samples in mBuffer */ - public void receive( ComplexSample newSample ) + public void receive( Complex newSample ) { //Add the new sample to the buffer mBuffer.set( mBufferPointer, newSample ); @@ -115,7 +115,7 @@ public class ComplexHalfBandFilter extends ComplexFilter //We're almost finished ... apply gain, cast the doubles to floats and //send it on it's merry way - send( new ComplexSample( (float)( leftAccumulator * mGain ), + send( new Complex( (float)( leftAccumulator * mGain ), (float)( rightAccumulator * mGain ) ) ); } } diff --git a/src/dsp/filter/ComplexHalfBandNoDecimateFilter.java b/src/dsp/filter/ComplexHalfBandNoDecimateFilter.java index aa459ae6..72e40f06 100644 --- a/src/dsp/filter/ComplexHalfBandNoDecimateFilter.java +++ b/src/dsp/filter/ComplexHalfBandNoDecimateFilter.java @@ -20,16 +20,16 @@ package dsp.filter; import java.util.ArrayList; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class ComplexHalfBandNoDecimateFilter extends ComplexFilter { - private Listener mListener; - private ArrayList mBuffer; + private Listener mListener; + private ArrayList mBuffer; private int mBufferSize = 1; //Temporary initial value private int mBufferPointer = 0; - private double mGain; - private double[] mCoefficients; + private float mGain; + private float[] mCoefficients; private int[][] mIndexMap; private int mCenterCoefficient; private int mCenterCoefficientMapIndex; @@ -51,16 +51,16 @@ public class ComplexHalfBandNoDecimateFilter extends ComplexFilter * @param filter - filter coefficients * @param gain - gain multiplier. Use 1.0 for unity/no gain */ - public ComplexHalfBandNoDecimateFilter( Filters filter, double gain ) + public ComplexHalfBandNoDecimateFilter( Filters filter, float gain ) { mCoefficients = filter.getCoefficients(); - mBuffer = new ArrayList(); + mBuffer = new ArrayList(); mBufferSize = mCoefficients.length; //Fill the buffer with zero valued samples for( int x = 0; x < mCoefficients.length; x++ ) { - mBuffer.add( new ComplexSample( (float)0.0, (float)0.0 ) ); + mBuffer.add( new Complex( (float)0.0, (float)0.0 ) ); } generateIndexMap( mCoefficients.length ); @@ -71,7 +71,7 @@ public class ComplexHalfBandNoDecimateFilter extends ComplexFilter * Calculate the filtered value by applying the coefficients against * the complex samples in mBuffer */ - public void receive( ComplexSample newSample ) + public void receive( Complex newSample ) { //Add the new sample to the buffer mBuffer.set( mBufferPointer, newSample ); @@ -86,8 +86,8 @@ public class ComplexHalfBandNoDecimateFilter extends ComplexFilter //Convolution - multiply filter coefficients by the circular buffer //samples to calculate a new filtered value - double leftAccumulator = 0; - double rightAccumulator = 0; + float leftAccumulator = 0; + float rightAccumulator = 0; //Start with the center tap value leftAccumulator += mCoefficients[ mCenterCoefficient ] * @@ -108,10 +108,10 @@ public class ComplexHalfBandNoDecimateFilter extends ComplexFilter mBuffer.get( mIndexMap[ mBufferPointer ][ x + 1 ] ).right() ); } - //We're almost finished ... apply gain, cast the doubles to floats and + //We're almost finished ... apply gain, cast the floats to floats and //send it on it's merry way - send( new ComplexSample( (float)( leftAccumulator * mGain ), - (float)( rightAccumulator * mGain ) ) ); + send( new Complex( ( leftAccumulator * mGain ), + ( rightAccumulator * mGain ) ) ); } /** @@ -180,9 +180,9 @@ public class ComplexHalfBandNoDecimateFilter extends ComplexFilter public static void main( String[] args ) { - double[] c = Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.mCoefficients; + float[] c = Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.mCoefficients; - double sum = 0; + float sum = 0; for( int x = 0; x < c.length; x++ ) { diff --git a/src/dsp/filter/ComplexPolyphaseDecimationFilter.java b/src/dsp/filter/ComplexPolyphaseDecimationFilter.java index e3e71e05..c2383050 100644 --- a/src/dsp/filter/ComplexPolyphaseDecimationFilter.java +++ b/src/dsp/filter/ComplexPolyphaseDecimationFilter.java @@ -18,7 +18,7 @@ package dsp.filter; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import buffer.ComplexTappedCircularBuffer; public class ComplexPolyphaseDecimationFilter extends ComplexFilter @@ -68,7 +68,7 @@ public class ComplexPolyphaseDecimationFilter extends ComplexFilter * Inputs a new sample into the filter at the non-decimated rate */ @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mFilterPhases[ mPhasePointer++ ].receive( sample ); @@ -97,13 +97,13 @@ public class ComplexPolyphaseDecimationFilter extends ComplexFilter for( int x = 0; x < mPhaseCount; x++ ) { - ComplexSample phaseSample = mFilterPhases[ x ].calculate(); + Complex phaseSample = mFilterPhases[ x ].calculate(); left += phaseSample.left(); right += phaseSample.right(); } - send( new ComplexSample( left, right ) ); + send( new Complex( left, right ) ); } } @@ -130,7 +130,7 @@ public class ComplexPolyphaseDecimationFilter extends ComplexFilter /** * Implements a single phase of a poly-phase filter bank */ - public class FilterPhase implements Listener + public class FilterPhase implements Listener { private int mLength; private double[] mCoefficients; @@ -150,7 +150,7 @@ public class ComplexPolyphaseDecimationFilter extends ComplexFilter } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mBuffer.add( sample ); } @@ -159,7 +159,7 @@ public class ComplexPolyphaseDecimationFilter extends ComplexFilter * Performs convolution by multiplying the buffer samples by their * corresponding filter coefficients and returns the sum */ - public ComplexSample calculate() + public Complex calculate() { /* Accumulators */ double left = 0.0d; @@ -171,7 +171,7 @@ public class ComplexPolyphaseDecimationFilter extends ComplexFilter right += ( mBuffer.get( x ).right() * mCoefficients[ x ] ); } - return new ComplexSample( (float)left, (float)right ); + return new Complex( (float)left, (float)right ); } } } diff --git a/src/dsp/filter/FilterFactory.java b/src/dsp/filter/FilterFactory.java index 0b65b419..5f35da33 100644 --- a/src/dsp/filter/FilterFactory.java +++ b/src/dsp/filter/FilterFactory.java @@ -27,10 +27,12 @@ import java.util.Set; import java.util.TreeSet; import org.jtransforms.fft.DoubleFFT_1D; +import org.jtransforms.fft.FloatFFT_1D; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import dsp.filter.Window.WindowType; +import dsp.filter.cic.ComplexPrimeCICDecimate; public class FilterFactory { @@ -45,24 +47,24 @@ public class FilterFactory * @param window - to apply against the coefficients * @return */ - public static double[] getSinc( int sampleRate, - long frequency, - int length, - WindowType window ) + public static float[] getSinc( int sampleRate, + long frequency, + int length, + WindowType window ) { //Ensure we have an odd length assert( length % 2 == 0 ); //Get unity response array (one element longer to align with IDFT size) - double[] frequencyResponse = getUnityResponseArray( sampleRate, frequency, length + 1 ); + float[] frequencyResponse = getUnityResponseArray( sampleRate, frequency, length + 1 ); //Apply Inverse DFT against frequency response unity values, leaving the //IDFT bin results in the frequency response array - DoubleFFT_1D idft = new DoubleFFT_1D( length + 1 ); + FloatFFT_1D idft = new FloatFFT_1D( length + 1 ); idft.realInverseFull( frequencyResponse, false ); //Transfer the IDFT results to the return array - double[] coefficients = new double[ length ]; + float[] coefficients = new float[ length ]; int middleCoefficient = (int)( length / 2 ); //Bin 0 of the idft is our center coefficient @@ -91,9 +93,9 @@ public class FilterFactory * @param coefficients * @return */ - private static double[] normalize( double[] coefficients ) + private static float[] normalize( float[] coefficients ) { - double accumulator = 0; + float accumulator = 0; for( int x = 0; x < coefficients.length; x++ ) { @@ -124,51 +126,48 @@ public class FilterFactory * @param length * @return */ - public static double[] getUnityResponseArray( int sampleRate, - long frequency, - int length ) + public static float[] getUnityResponseArray( int sampleRate, + long frequency, + int length ) { - double[] unityArray = new double[ length * 2 ]; + float[] unityArray = new float[ length * 2 ]; int binCount = (int)( ( Math.round( - (double)frequency / (double)sampleRate * (double)length ) ) ); + (float)frequency / (float)sampleRate * (float)length ) ) ); -// if( length % 2 == 0 ) //even length -// { -// for( int x = 0; x < binCount; x++ ) -// { -// unityArray[ x ] = 1.0d; -// unityArray[ length - 1 - x ] = 1.0d; -// } -// } -// else //odd length -// { - unityArray[ 0 ] = 1.0d; + if( length % 2 == 0 ) //even length + { + for( int x = 0; x < binCount; x++ ) + { + unityArray[ x ] = 1.0f; + unityArray[ length - 1 - x ] = 1.0f; + } + } + else //odd length + { + unityArray[ 0 ] = 1.0f; for( int x = 1; x <= binCount; x++ ) { - unityArray[ x ] = 1.0d; - unityArray[ length - x ] = 1.0d; + unityArray[ x ] = 1.0f; + unityArray[ length - x ] = 1.0f; } -// } + } return unityArray; } - public static double[] getSine( double sampleRate, double frequency, int length ) + public static float[] getSine( float sampleRate, float frequency, int length ) { - //Ensure we have an odd length - assert( length % 2 == 0 ); + float[] retVal = new float[ length ]; - double[] retVal = new double[ length ]; - - double radianFrequency = 2.0D * Math.PI * ( frequency / sampleRate ); + float radianFrequency = (float)( 2.0d * Math.PI * ( frequency / sampleRate ) ); int middle = (int)( length / 2 ); for( int x = 0; x < middle; x++ ) { - double val = Math.sin( radianFrequency * x ); + float val = (float)Math.sin( radianFrequency * x ); retVal[ middle + x ] = val; retVal[ middle - x ] = -val; @@ -190,7 +189,7 @@ public class FilterFactory * ... * Index length - 1: same */ - public static double[] invert( double[] coefficients ) + public static float[] invert( float[] coefficients ) { for( int x = 1; x < coefficients.length; x += 2 ) { @@ -210,21 +209,21 @@ public class FilterFactory * @param windowType - window to apply against the generated coefficients * @return */ - public static double[] getLowPass( int sampleRate, - long cutoff, - int filterLength, - WindowType windowType ) + public static float[] getLowPass( int sampleRate, + long cutoff, + int filterLength, + WindowType windowType ) { if( filterLength % 2 == 0 ) //even length { - double[] values = getSinc( sampleRate, cutoff, filterLength + 2, windowType ); + float[] values = getSinc( sampleRate, cutoff, filterLength + 2, windowType ); //throw away the 0 index and the last index return Arrays.copyOfRange( values, 1, values.length - 2 ); } else { - double[] values = getSinc( sampleRate, cutoff, filterLength + 1, windowType ); + float[] values = getSinc( sampleRate, cutoff, filterLength + 1, windowType ); //throw away the 0 index return Arrays.copyOfRange( values, 1, values.length ); @@ -245,12 +244,12 @@ public class FilterFactory * - passFrequency < stopFrequency * - stopFrequency <= sampleRate/2 */ - public static double[] getLowPass( int sampleRate, - int passFrequency, - int stopFrequency, - int attenuation, - WindowType windowType, - boolean forceOddLength ) + public static float[] getLowPass( int sampleRate, + int passFrequency, + int stopFrequency, + int attenuation, + WindowType windowType, + boolean forceOddLength ) { if( stopFrequency < passFrequency || stopFrequency > ( sampleRate / 2 ) ) { @@ -288,10 +287,10 @@ public class FilterFactory * @param windowType - window to apply against the generated coefficients * @return */ - public static double[] getHighPass( int sampleRate, - long cutoff, - int filterLength, - WindowType windowType ) + public static float[] getHighPass( int sampleRate, + long cutoff, + int filterLength, + WindowType windowType ) { //Convert the high frequency cutoff to its low frequency cutoff //equivalent, so that when we generate the low pass filter, prior to @@ -304,12 +303,12 @@ public class FilterFactory windowType ) ); } - public static double[] getHighPass( int sampleRate, - long stopFrequency, - long passFrequency, - int attenuation, - WindowType windowType, - boolean forceOddLength ) + public static float[] getHighPass( int sampleRate, + long stopFrequency, + long passFrequency, + int attenuation, + WindowType windowType, + boolean forceOddLength ) { /* reverse the stop and pass frequency to get the low pass variant */ int tapCount = getTapCount( sampleRate, @@ -331,7 +330,7 @@ public class FilterFactory /** * Utility to log the arrays of doubles with line breaks */ - public static String arrayToString( double[] array, boolean breaks ) + public static String arrayToString( float[] array, boolean breaks ) { StringBuilder sb = new StringBuilder(); for( int x = 0; x < array.length; x++ ) @@ -574,10 +573,10 @@ public class FilterFactory * @param stageCount * @return */ - public static double[] getCICCleanupFilter( int outputSampleRate, - int passFrequency, - int attenuation, - WindowType window ) + public static float[] getCICCleanupFilter( int outputSampleRate, + int passFrequency, + int attenuation, + WindowType window ) { int taps = getTapCount( outputSampleRate, passFrequency, passFrequency + 1500, attenuation ); @@ -588,16 +587,16 @@ public class FilterFactory taps ++; } - double[] frequencyResponse = + float[] frequencyResponse = getCICResponseArray( outputSampleRate, passFrequency, taps ); //Apply Inverse DFT against frequency response unity values, leaving the //IDFT bin results in the frequency response array - DoubleFFT_1D idft = new DoubleFFT_1D( taps ); + FloatFFT_1D idft = new FloatFFT_1D( taps ); idft.realInverseFull( frequencyResponse, false ); //Transfer the IDFT results to the odd length return array - double[] coefficients = new double[ taps ]; + float[] coefficients = new float[ taps ]; int middleCoefficient = (int)( taps / 2 ); //Bin 0 of the idft is our center coefficient @@ -612,32 +611,32 @@ public class FilterFactory } //Apply the window against the coefficients - coefficients = Window.apply( window, coefficients ); +// coefficients = Window.apply( window, coefficients ); normalize( coefficients ); return coefficients; } - public static double[] getCICResponseArray( int sampleRate, + public static float[] getCICResponseArray( int sampleRate, int frequency, int length ) { - double[] cicArray = new double[ length * 2 ]; + float[] cicArray = new float[ length * 2 ]; int binCount = (int)( ( Math.round( - (double)frequency / (double)sampleRate * (double)length ) ) ); + (double)frequency / (double)sampleRate * 2.0d * (double)length ) ) ); - cicArray[ 0 ] = 1.0d; + cicArray[ 0 ] = 1.0f; - double unityResponse = Math.sin( 1.0d / (double)length ) / - ( 1.0d / (double)length ); + float unityResponse = (float)( Math.sin( 1.0d / (double)length ) / + ( 1.0d / (double)length ) ); for( int x = 1; x <= binCount; x++ ) { /* Calculate unity response plus amplification for droop */ - double compensated = 1.0d + ( unityResponse - - ( Math.sin( (double)x / (double)length ) / + float compensated = 1.0f + ( unityResponse - + (float)( Math.sin( (double)x / (double)length ) / ( (double)x / (double)length ) ) ); cicArray[ x ] = compensated; @@ -666,64 +665,64 @@ public class FilterFactory * * @return - filter coefficients */ - public static double[] getRootRaisedCosine( int samplesPerSymbol, - int symbols, - double alpha ) + public static float[] getRootRaisedCosine( int samplesPerSymbol, + int symbols, + float alpha ) { int taps = samplesPerSymbol * symbols + 1; - double scale = 0; + float scale = 0; - double[] coefficients = new double[ taps ]; + float[] coefficients = new float[ taps ]; for( int x = 0; x < taps; x++ ) { - double index = x - ( taps / 2 ); + float index = (float)x - ( (float)taps / 2.0f ); - double x1 = Math.PI * index / (double)samplesPerSymbol; - double x2 = 4.0 * alpha * index / (double)samplesPerSymbol; - double x3 = x2 * x2 - 1; + float x1 = (float)Math.PI * index / (float)samplesPerSymbol; + float x2 = 4.0f * alpha * index / (float)samplesPerSymbol; + float x3 = x2 * x2 - 1.0f; - double numerator, denominator; + float numerator, denominator; if( Math.abs( x3 ) >= 0.000001 ) { if( x != taps / 2 ) { - numerator = Math.cos( ( 1.0 + alpha ) * x1 ) + - Math.sin( ( 1.0 - alpha ) * x1 ) / - ( 4.0 * alpha * index / (double)samplesPerSymbol ); + numerator = (float)Math.cos( ( 1.0 + alpha ) * x1 ) + + (float)Math.sin( ( 1.0f - alpha ) * x1 ) / + ( 4.0f * alpha * index / (float)samplesPerSymbol ); } else { - numerator = Math.cos( ( 1.0 + alpha ) * x1 ) + - ( 1.0 - alpha ) * Math.PI / ( 4.0 * alpha ); + numerator = (float)Math.cos( ( 1.0f + alpha ) * x1 ) + + ( 1.0f - alpha ) * (float)Math.PI / ( 4.0f * alpha ); } - denominator = x3 * Math.PI; + denominator = x3 * (float)Math.PI; } else { - if( alpha == 1.0 ) + if( alpha == 1.0f ) { - coefficients[ x ] = -1.0; + coefficients[ x ] = -1.0f; continue; } - x3 = ( 1.0 - alpha ) * x1; - x2 = ( 1.0 + alpha ) * x1; + x3 = ( 1.0f - alpha ) * x1; + x2 = ( 1.0f + alpha ) * x1; - numerator = ( Math.sin( x2 ) * ( 1.0 + alpha ) * Math.PI - + numerator = (float)( Math.sin( x2 ) * ( 1.0f + alpha ) * Math.PI - Math.cos( x3 ) * ( ( 1.0 - alpha ) * Math.PI * (double)samplesPerSymbol ) / ( 4 * alpha * index ) + Math.sin( x3 ) * (double)samplesPerSymbol * (double)samplesPerSymbol / ( 4.0 * alpha * index * index ) ); - denominator = -32.0 * Math.PI * alpha * alpha * index / - (double)samplesPerSymbol; + denominator = (float)( -32.0 * Math.PI * alpha * alpha * index / + (double)samplesPerSymbol ); } - coefficients[ x ] = 4.0 * alpha * numerator / denominator; + coefficients[ x ] = 4.0f * alpha * numerator / denominator; scale += coefficients[ x ]; } @@ -738,13 +737,4 @@ public class FilterFactory return coefficients; } - - public static void main( String[] args ) - { - double[] c = FilterFactory.getRootRaisedCosine( 10, 4, 0.2 ); - - mLog.debug( "C:" + Arrays.toString( c ) ); - } - - } diff --git a/src/dsp/filter/Filters.java b/src/dsp/filter/Filters.java index ab881c89..0381e5ea 100644 --- a/src/dsp/filter/Filters.java +++ b/src/dsp/filter/Filters.java @@ -4,189 +4,662 @@ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or + * the Free Software Foundationf, either version 3 of the Licensef, or * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, + * This program is distributed in the hope that it will be usefulf, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see + * along with this program. If notf, see ******************************************************************************/ package dsp.filter; public enum Filters { - FIRHP_LTR_DCBLOCK_750FS_50FC( new double[] + /** + * Designed with t-filter: + * + * 48000 Fsf, 243 taps + * 0 - 200 Hz: gain=0f, -60 db attenuation + * 500 - 3500 Hz: gain=1f, 5 db ripple + * 3800 - 24000 Hz: gain=0f, -60 db attenuation + */ + FIR_BANDPASS_AUDIO_48KHZ( new float[] { - 0.0679294122464217, - -0.07324095797199973, - -0.03838823381974882, - -0.013430897335300878, - 0.006449207638138327, - 0.022750490924149575, - 0.034175688012529584, - 0.037600969376502945, - 0.030002787357015913, - 0.009961076769098255, - -0.02214359076495753, - -0.062375656373163425, - -0.10487871348895818, - -0.14241968373731975, - -0.16818470634383087, - 0.8226518956829582, - -0.16818470634383087, - -0.14241968373731975, - -0.10487871348895818, - -0.062375656373163425, - -0.02214359076495753, - 0.009961076769098255, - 0.030002787357015913, - 0.037600969376502945, - 0.034175688012529584, - 0.022750490924149575, - 0.006449207638138327, - -0.013430897335300878, - -0.03838823381974882, - -0.07324095797199973, - 0.0679294122464217 + 0.0011306590506720503f, + 0.0017695582496011488f, + 0.0029288606290379564f, + 0.004343691928519147f, + 0.005911947383484083f, + 0.007475952925152455f, + 0.008834973531280245f, + 0.009767639642827303f, + 0.010062783359359985f, + 0.009554323352836855f, + 0.008154040477083785f, + 0.005876856248820146f, + 0.002853293692694345f, + -0.0006754620931569513f, + -0.004380476393750235f, + -0.007883982012534343f, + -0.010810030687940836f, + -0.01283857015700313f, + -0.013754750291231674f, + -0.013485225355167158f, + -0.012113350628392553f, + -0.009870878494518202f, + -0.007105858136673649f, + -0.00423044627271302f, + -0.0016581171943216698f, + 0.00026139558898877413f, + 0.0012960093376189904f, + 0.001367153398534282f, + 0.0005589404072224782f, + -0.0008998262476139348f, + -0.002678680933513012f, + -0.004405628120562205f, + -0.005733894494038466f, + -0.006403098522792899f, + -0.0062840458452382f, + -0.005398740409728341f, + -0.003912212267810966f, + -0.0020979501642753354f, + -0.0002835213596901672f, + 0.0012129422773069776f, + 0.002143441512259471f, + 0.0023762621098547794f, + 0.0019187818063353935f, + 0.000912904484557303f, + -0.0003959227311914178f, + -0.0017094242365984f, + -0.0027368218536981716f, + -0.0032529110615098964f, + -0.003141865590241452f, + -0.0024186267939756956f, + -0.0012232782401918676f, + 0.00020964495297755235f, + 0.0016007876062091302f, + 0.0026840078948525774f, + 0.003261066093523398f, + 0.0032416075292524913f, + 0.0026604426307957627f, + 0.0016685677475029945f, + 0.0005002180750227251f, + -0.0005771658431263752f, + -0.001319472490248709f, + -0.0015570243575186143f, + -0.001229709577267077f, + -0.00039860814610671267f, + 0.000768061742498229f, + 0.0020316064815484648f, + 0.0031342758646814173f, + 0.00385441982862473f, + 0.004053691192616693f, + 0.003706243244244987f, + 0.0029036666957800066f, + 0.0018348347602710623f, + 0.0007447703354245662f, + -0.00011835819081745851f, + -0.000556901991979898f, + -0.0004664033507391891f, + 0.00014190043902796031f, + 0.001143626440427841f, + 0.0023282929683617563f, + 0.0034451626872284316f, + 0.004257806518147909f, + 0.004595703675845672f, + 0.00439150448243702f, + 0.0036959141111179176f, + 0.0026669294496819663f, + 0.00153556329292172f, + 0.0005555624481582027f, + -0.000052181722788229754f, + -0.00014822483506696354f, + 0.0002944283222452884f, + 0.0011838275788186557f, + 0.0023285887949390117f, + 0.003479479143540588f, + 0.004383876725492502f, + 0.004841511744105689f, + 0.004748927884234896f, + 0.0041230326070274305f, + 0.003098290748206268f, + 0.0018980051891599843f, + 0.0007858270723442799f, + 0.000007808226096047636f, + -0.00026221415675647226f, + 0.000038194038926661374f, + 0.0008450198034699045f, + 0.001980683488085903f, + 0.0031920946811098026f, + 0.004205885473834353f, + 0.004788786880449399f, + 0.004799721085068433f, + 0.004222198149342628f, + 0.0031694947167750084f, + 0.0018613072553364282f, + 0.0005764055253590191f, + -0.00040837636254631156f, + -0.0008797864800588528f, + -0.0007362748788250025f, + -0.000012335427115590454f, + 0.001127487196311246f, + 0.0024230493118988245f, + 0.0035750267398429276f, + 0.004310745684369589f, + 0.004445118905106224f, + 0.003923338081120285f, + 0.0028351651513023415f, + 0.0013974479410391886f, + -0.00009204763249292721f, + -0.0013196387156487223f, + -0.0020251151156756176f, + -0.0020605167563326203f, + -0.001424951849329983f, + -0.00026755861117644484f, + 0.0011426434337099476f, + 0.00247453898902279f, + 0.0034075979415458474f, + 0.0037034647048603147f, + 0.003260419292510863f, + 0.002138889696298502f, + 0.0005507292550901969f, + -0.0011837011788563476f, + -0.002705803369109791f, + -0.0036965273158039683f, + -0.003950008183602666f, + -0.0034202362142058866f, + -0.002235167107431455f, + -0.0006724174233870484f, + 0.0008995473194704798f, + 0.0021008342984556796f, + 0.0026253485580778205f, + 0.002310873178862991f, + 0.0011772529466448514f, + -0.0005730908358460114f, + -0.002594671172069867f, + -0.0044734850087020245f, + -0.005818409323341712f, + -0.006347432664306618f, + -0.005952913139489849f, + -0.004732154819606038f, + -0.0029702110984690744f, + -0.0010791651288411521f, + 0.00048707995367597095f, + 0.0013335442378021411f, + 0.0012177475494058294f, + 0.00010122252104995588f, + -0.0018287043085791768f, + -0.004200030123435349f, + -0.006529365603823069f, + -0.00832894491185446f, + -0.009214117900210558f, + -0.008992727392755416f, + -0.007713940553513187f, + -0.005665102752109283f, + -0.0033154814998344715f, + -0.001217542957348092f, + 0.00011370751541866088f, + 0.00031672583066655f, + -0.0007305199337543714f, + -0.002874449900177386f, + -0.005710232857479199f, + -0.008662772551611736f, + -0.01110852438766387f, + -0.01251284869187753f, + -0.012552561484583724f, + -0.011196629466395516f, + -0.008725049876131727f, + -0.005679263313240144f, + -0.0027527053850089833f, + -0.0006423534393274008f, + 0.00010613666306157791f, + -0.0007726767835356292f, + -0.0031920086018739423f, + -0.006715187307484388f, + -0.010635986592925665f, + -0.014122526644297223f, + -0.016396230121947006f, + -0.016907881072255753f, + -0.015472266669959437f, + -0.01232951904769663f, + -0.008115919582767236f, + -0.0037455665411447506f, + -0.00022384102223090357f, + 0.0015702170495901357f, + 0.0010871601110870473f, + -0.0017492942655052172f, + -0.006493113668388964f, + -0.012237738308695591f, + -0.017782884779579476f, + -0.021877188332801504f, + -0.02348982727475883f, + -0.022055126496704366f, + -0.017635878927117044f, + -0.010965170848193264f, + -0.0033482781673564166f, + 0.003565343786600591f, + 0.008103372983477226f, + 0.008926637620116478f, + 0.005350637848922081f, + -0.002434639433805644f, + -0.013297914897997802f, + -0.025277752058596798f, + -0.03586329253291671f, + -0.042403748965243175f, + -0.04257882777767779f, + -0.034847147989454305f, + -0.018789707031704826f, + 0.004719508132752991f, + 0.033554756785942955f, + 0.06459420543249415f, + 0.09415420005424562f, + 0.11853369816477846f, + 0.13457931451963423f, + 0.14017594101985517f, + 0.13457931451963423f, + 0.11853369816477848f, + 0.09415420005424562f, + 0.06459420543249415f, + 0.033554756785942955f, + 0.004719508132752991f, + -0.018789707031704822f, + -0.034847147989454305f, + -0.04257882777767779f, + -0.042403748965243175f, + -0.03586329253291671f, + -0.025277752058596798f, + -0.01329791489799781f, + -0.002434639433805644f, + 0.005350637848922081f, + 0.008926637620116478f, + 0.008103372983477226f, + 0.003565343786600591f, + -0.0033482781673564166f, + -0.01096517084819326f, + -0.017635878927117044f, + -0.022055126496704366f, + -0.02348982727475883f, + -0.021877188332801504f, + -0.017782884779579476f, + -0.012237738308695591f, + -0.006493113668388964f, + -0.0017492942655052172f, + 0.0010871601110870439f, + 0.0015702170495901396f, + -0.00022384102223090309f, + -0.0037455665411447506f, + -0.00811591958276724f, + -0.01232951904769663f, + -0.015472266669959437f, + -0.016907881072255753f, + -0.016396230121947006f, + -0.014122526644297223f, + -0.010635986592925665f, + -0.006715187307484388f, + -0.0031920086018739444f, + -0.0007726767835356292f, + 0.0001061366630615779f, + -0.0006423534393274008f, + -0.0027527053850089833f, + -0.005679263313240145f, + -0.00872504987613173f, + -0.011196629466395517f, + -0.012552561484583724f, + -0.01251284869187753f, + -0.011108524387663866f, + -0.008662772551611736f, + -0.005710232857479197f, + -0.002874449900177386f, + -0.0007305199337543743f, + 0.00031672583066655f, + 0.0001137075154186604f, + -0.001217542957348092f, + -0.0033154814998344715f, + -0.005665102752109283f, + -0.007713940553513191f, + -0.008992727392755416f, + -0.009214117900210558f, + -0.008328944911854458f, + -0.006529365603823069f, + -0.004200030123435349f, + -0.0018287043085791778f, + 0.00010122252104995396f, + 0.0012177475494058294f, + 0.0013335442378021411f, + 0.00048707995367597095f, + -0.0010791651288411521f, + -0.002970211098469072f, + -0.004732154819606038f, + -0.005952913139489849f, + -0.0063474326643066185f, + -0.005818409323341712f, + -0.004473485008702026f, + -0.002594671172069867f, + -0.0005730908358460094f, + 0.0011772529466448514f, + 0.002310873178862991f, + 0.002625348558077821f, + 0.0021008342984556796f, + 0.0008995473194704788f, + -0.0006724174233870484f, + -0.002235167107431454f, + -0.0034202362142058866f, + -0.003950008183602666f, + -0.0036965273158039683f, + -0.002705803369109791f, + -0.0011837011788563506f, + 0.0005507292550901969f, + 0.002138889696298502f, + 0.003260419292510862f, + 0.0037034647048603147f, + 0.0034075979415458474f, + 0.00247453898902279f, + 0.0011426434337099476f, + -0.00026755861117644543f, + -0.0014249518493299838f, + -0.0020605167563326203f, + -0.0020251151156756176f, + -0.001319638715648723f, + -0.00009204763249292625f, + 0.0013974479410391886f, + 0.0028351651513023415f, + 0.003923338081120284f, + 0.004445118905106224f, + 0.00431074568436959f, + 0.0035750267398429276f, + 0.0024230493118988228f, + 0.001127487196311246f, + -0.000012335427115590454f, + -0.0007362748788250025f, + -0.0008797864800588528f, + -0.0004083763625463096f, + 0.000576405525359021f, + 0.0018613072553364282f, + 0.0031694947167750084f, + 0.0042221981493426285f, + 0.004799721085068431f, + 0.004788786880449399f, + 0.004205885473834353f, + 0.0031920946811098017f, + 0.001980683488085902f, + 0.0008450198034699026f, + 0.00003819403892666041f, + -0.0002622141567564703f, + 0.000007808226096045708f, + 0.0007858270723442809f, + 0.001898005189159986f, + 0.003098290748206268f, + 0.004123032607027432f, + 0.004748927884234897f, + 0.004841511744105689f, + 0.004383876725492502f, + 0.003479479143540588f, + 0.0023285887949390117f, + 0.0011838275788186557f, + 0.00029442832224528743f, + -0.00014822483506696354f, + -0.000052181722788230235f, + 0.0005555624481582017f, + 0.0015355632929217181f, + 0.002666929449681966f, + 0.0036959141111179176f, + 0.004391504482437019f, + 0.004595703675845674f, + 0.004257806518147909f, + 0.0034451626872284316f, + 0.002328292968361755f, + 0.0011436264404278407f, + 0.0001419004390279603f, + -0.00046640335073919057f, + -0.000556901991979898f, + -0.00011835819081745802f, + 0.0007447703354245662f, + 0.0018348347602710623f, + 0.0029036666957800066f, + 0.0037062432442449863f, + 0.0040536911926166925f, + 0.003854419828624731f, + 0.0031342758646814165f, + 0.0020316064815484656f, + 0.00076806174249823f, + -0.00039860814610671267f, + -0.001229709577267077f, + -0.0015570243575186143f, + -0.001319472490248709f, + -0.0005771658431263752f, + 0.000500218075022725f, + 0.001668567747502994f, + 0.002660442630795762f, + 0.0032416075292524913f, + 0.0032610660935233986f, + 0.002684007894852579f, + 0.0016007876062091293f, + 0.00020964495297755235f, + -0.0012232782401918676f, + -0.002418626793975698f, + -0.003141865590241452f, + -0.003252911061509897f, + -0.002736821853698173f, + -0.0017094242365984003f, + -0.0003959227311914187f, + 0.0009129044845573019f, + 0.0019187818063353941f, + 0.0023762621098547807f, + 0.00214344151225947f, + 0.0012129422773069776f, + -0.0002835213596901672f, + -0.0020979501642753354f, + -0.003912212267810966f, + -0.005398740409728341f, + -0.0062840458452382f, + -0.006403098522792899f, + -0.005733894494038467f, + -0.004405628120562203f, + -0.002678680933513013f, + -0.0008998262476139358f, + 0.0005589404072224788f, + 0.001367153398534282f, + 0.0012960093376189895f, + 0.0002613955889887717f, + -0.0016581171943216698f, + -0.00423044627271302f, + -0.007105858136673649f, + -0.009870878494518204f, + -0.012113350628392553f, + -0.013485225355167158f, + -0.01375475029123167f, + -0.01283857015700313f, + -0.010810030687940836f, + -0.007883982012534343f, + -0.004380476393750235f, + -0.0006754620931569513f, + 0.0028532936926943457f, + 0.005876856248820146f, + 0.008154040477083787f, + 0.009554323352836855f, + 0.010062783359359985f, + 0.009767639642827303f, + 0.008834973531280245f, + 0.007475952925152455f, + 0.005911947383484083f, + 0.004343691928519149f, + 0.0029288606290379547f, + 0.00176955824960115f, + 0.0011306590506720503f + } ), + + FIRHP_LTR_DCBLOCK_750FS_50FC( new float[] + { + 0.0679294122464217f, + -0.07324095797199973f, + -0.03838823381974882f, + -0.013430897335300878f, + 0.006449207638138327f, + 0.022750490924149575f, + 0.034175688012529584f, + 0.037600969376502945f, + 0.030002787357015913f, + 0.009961076769098255f, + -0.02214359076495753f, + -0.062375656373163425f, + -0.10487871348895818f, + -0.14241968373731975f, + -0.16818470634383087f, + 0.8226518956829582f, + -0.16818470634383087f, + -0.14241968373731975f, + -0.10487871348895818f, + -0.062375656373163425f, + -0.02214359076495753f, + 0.009961076769098255f, + 0.030002787357015913f, + 0.037600969376502945f, + 0.034175688012529584f, + 0.022750490924149575f, + 0.006449207638138327f, + -0.013430897335300878f, + -0.03838823381974882f, + -0.07324095797199973f, + 0.0679294122464217f }), - FIRLP_LTR_1500FS( new double[] + FIRLP_LTR_1500FS( new float[] { - -0.008272676875607624, - -0.0024939020178146617, - 0.010376092557732189, - 0.011962525412519345, - -0.003977368587769054, - -0.013289773851060555, - -0.0029497606301098858, - 0.003941811083306652, - -0.003154954069081114, - 0.005485070321679545, - 0.03171909301543344, - 0.015387284536986508, - -0.055467824110903244, - -0.07246950346172203, - 0.03466378905394898, - 0.1322297692443333, - 0.04683098737206109, - -0.13469992806577527, - -0.14450919452286415, - 0.0578348928408703, - 0.18926196545202326, - 0.0578348928408703, - -0.14450919452286415, - -0.13469992806577527, - 0.04683098737206109, - 0.1322297692443333, - 0.03466378905394898, - -0.07246950346172203, - -0.055467824110903244, - 0.015387284536986508, - 0.03171909301543344, - 0.005485070321679545, - -0.003154954069081114, - 0.003941811083306652, - -0.0029497606301098858, - -0.013289773851060555, - -0.003977368587769054, - 0.011962525412519345, - 0.010376092557732189, - -0.0024939020178146617, - -0.008272676875607624 + -0.008272676875607624f, + -0.0024939020178146617f, + 0.010376092557732189f, + 0.011962525412519345f, + -0.003977368587769054f, + -0.013289773851060555f, + -0.0029497606301098858f, + 0.003941811083306652f, + -0.003154954069081114f, + 0.005485070321679545f, + 0.03171909301543344f, + 0.015387284536986508f, + -0.055467824110903244f, + -0.07246950346172203f, + 0.03466378905394898f, + 0.1322297692443333f, + 0.04683098737206109f, + -0.13469992806577527f, + -0.14450919452286415f, + 0.0578348928408703f, + 0.18926196545202326f, + 0.0578348928408703f, + -0.14450919452286415f, + -0.13469992806577527f, + 0.04683098737206109f, + 0.1322297692443333f, + 0.03466378905394898f, + -0.07246950346172203f, + -0.055467824110903244f, + 0.015387284536986508f, + 0.03171909301543344f, + 0.005485070321679545f, + -0.003154954069081114f, + 0.003941811083306652f, + -0.0029497606301098858f, + -0.013289773851060555f, + -0.003977368587769054f, + 0.011962525412519345f, + 0.010376092557732189f, + -0.0024939020178146617f, + -0.008272676875607624f }), - FIRBP_1200FSK_24000FS( new double[] + FIRBP_1200FSK_24000FS( new float[] { - 0.004792788663425844, - 0.004034377807422182, - 0.004699808237099943, - 0.00443210843891432, - 0.0028638644082473, - -0.00015292185838515035, - -0.004394278846594682, - -0.009212560275995686, - -0.013596659741332163, - -0.016383584186535002, - -0.016573550574018152, - -0.013657566836436204, - -0.007851047182221552, - -0.00014425394402752814, - 0.007885453072038891, - 0.014465082677564993, - 0.01811703090261803, - 0.01810997536219774, - 0.014727244087571567, - 0.009244939917342813, - 0.0036035605020527947, - -0.00014781830127321208, - -0.0004913597198315713, - 0.0030136991548897693, - 0.009453462203915387, - 0.01666055505335309, - 0.02170180561798826, - 0.021656701887523472, - 0.014498683513932197, - -0.0001565221289617459, - -0.02065343799220559, - -0.04339467795574746, - -0.06347784859804807, - -0.07576298936541592, - -0.07612281593093975, - -0.06256712023008996, - -0.03596397841747506, - -0.00012170810308311971, - 0.03885147059625151, - 0.07377979316631962, - 0.09792980519752477, - 0.10655596332800969, - 0.09792980519752477, - 0.07377979316631962, - 0.03885147059625151, - -0.00012170810308311971, - -0.03596397841747506, - -0.06256712023008996, - -0.07612281593093975, - -0.07576298936541592, - -0.06347784859804807, - -0.04339467795574746, - -0.02065343799220559, - -0.0001565221289617459, - 0.014498683513932197, - 0.021656701887523472, - 0.02170180561798826, - 0.01666055505335309, - 0.009453462203915387, - 0.0030136991548897693, - -0.0004913597198315713, - -0.00014781830127321208, - 0.0036035605020527947, - 0.009244939917342813, - 0.014727244087571567, - 0.01810997536219774, - 0.01811703090261803, - 0.014465082677564993, - 0.007885453072038891, - -0.00014425394402752814, - -0.007851047182221552, - -0.013657566836436204, - -0.016573550574018152, - -0.016383584186535002, - -0.013596659741332163, - -0.009212560275995686, - -0.004394278846594682, - -0.00015292185838515035, - 0.0028638644082473, - 0.00443210843891432, - 0.004699808237099943, - 0.004034377807422182, - 0.004792788663425844 + 0.004792788663425844f, + 0.004034377807422182f, + 0.004699808237099943f, + 0.00443210843891432f, + 0.0028638644082473f, + -0.00015292185838515035f, + -0.004394278846594682f, + -0.009212560275995686f, + -0.013596659741332163f, + -0.016383584186535002f, + -0.016573550574018152f, + -0.013657566836436204f, + -0.007851047182221552f, + -0.00014425394402752814f, + 0.007885453072038891f, + 0.014465082677564993f, + 0.01811703090261803f, + 0.01810997536219774f, + 0.014727244087571567f, + 0.009244939917342813f, + 0.0036035605020527947f, + -0.00014781830127321208f, + -0.0004913597198315713f, + 0.0030136991548897693f, + 0.009453462203915387f, + 0.01666055505335309f, + 0.02170180561798826f, + 0.021656701887523472f, + 0.014498683513932197f, + -0.0001565221289617459f, + -0.02065343799220559f, + -0.04339467795574746f, + -0.06347784859804807f, + -0.07576298936541592f, + -0.07612281593093975f, + -0.06256712023008996f, + -0.03596397841747506f, + -0.00012170810308311971f, + 0.03885147059625151f, + 0.07377979316631962f, + 0.09792980519752477f, + 0.10655596332800969f, + 0.09792980519752477f, + 0.07377979316631962f, + 0.03885147059625151f, + -0.00012170810308311971f, + -0.03596397841747506f, + -0.06256712023008996f, + -0.07612281593093975f, + -0.07576298936541592f, + -0.06347784859804807f, + -0.04339467795574746f, + -0.02065343799220559f, + -0.0001565221289617459f, + 0.014498683513932197f, + 0.021656701887523472f, + 0.02170180561798826f, + 0.01666055505335309f, + 0.009453462203915387f, + 0.0030136991548897693f, + -0.0004913597198315713f, + -0.00014781830127321208f, + 0.0036035605020527947f, + 0.009244939917342813f, + 0.014727244087571567f, + 0.01810997536219774f, + 0.01811703090261803f, + 0.014465082677564993f, + 0.007885453072038891f, + -0.00014425394402752814f, + -0.007851047182221552f, + -0.013657566836436204f, + -0.016573550574018152f, + -0.016383584186535002f, + -0.013596659741332163f, + -0.009212560275995686f, + -0.004394278846594682f, + -0.00015292185838515035f, + 0.0028638644082473f, + 0.00443210843891432f, + 0.004699808237099943f, + 0.004034377807422182f, + 0.004792788663425844f }), - FIRLP_3K( new double[] + FIRLP_3K( new float[] { /* @@ -207,112 +680,112 @@ public enum Filters */ - -0.00011443124614437417, - -0.00017293152659867192, - -0.0002537395679787903, - -0.0002960101735353313, - -0.00023955416024142116, - -0.000005428557963321982, - 0.0004964100334261843, - 0.0013538842757409063, - 0.002634918910611058, - 0.00436693575408337, - 0.0065175161697321555, - 0.00897952475800971, - 0.011566379161822652, - 0.014018876408451886, - 0.016028053953955006, - 0.017270887068042688, - 0.01745751423179922, - 0.0163833187291431, - 0.013977100662194252, - 0.010337169842377791, - 0.005746282323474722, - 0.0006589019184637925, - -0.004341600521758248, - -0.008613131831428159, - -0.011545791615276019, - -0.012660543617859996, - -0.011700549440186059, - -0.008698639621450092, - -0.004006684845818583, - 0.00172317360274727, - 0.007608807120062409, - 0.012654244324839246, - 0.015898391911284164, - 0.016575938784747404, - 0.014265883702010186, - 0.009002241493722784, - 0.0013255862247370318, - -0.007739007276591945, - -0.0167810834076902, - -0.024172675639705345, - -0.02828537119270343, - -0.027727506101182933, - -0.02156733481426441, - -0.009507700677949931, - 0.008016566866990643, - 0.02983764491633055, - 0.05415546347475946, - 0.0787307236559999, - 0.10114644750135711, - 0.11910360136213424, - 0.13071022495045617, - 0.13472344727490876, - 0.13071022495045617, - 0.11910360136213424, - 0.10114644750135711, - 0.0787307236559999, - 0.05415546347475946, - 0.02983764491633055, - 0.008016566866990643, - -0.009507700677949931, - -0.02156733481426441, - -0.027727506101182933, - -0.02828537119270343, - -0.024172675639705345, - -0.0167810834076902, - -0.007739007276591945, - 0.0013255862247370318, - 0.009002241493722784, - 0.014265883702010186, - 0.016575938784747404, - 0.015898391911284164, - 0.012654244324839246, - 0.007608807120062409, - 0.00172317360274727, - -0.004006684845818583, - -0.008698639621450092, - -0.011700549440186059, - -0.012660543617859996, - -0.011545791615276019, - -0.008613131831428159, - -0.004341600521758248, - 0.0006589019184637925, - 0.005746282323474722, - 0.010337169842377791, - 0.013977100662194252, - 0.0163833187291431, - 0.01745751423179922, - 0.017270887068042688, - 0.016028053953955006, - 0.014018876408451886, - 0.011566379161822652, - 0.00897952475800971, - 0.0065175161697321555, - 0.00436693575408337, - 0.002634918910611058, - 0.0013538842757409063, - 0.0004964100334261843, - -0.000005428557963321982, - -0.00023955416024142116, - -0.0002960101735353313, - -0.0002537395679787903, - -0.00017293152659867192, - -0.00011443124614437417 + -0.00011443124614437417f, + -0.00017293152659867192f, + -0.0002537395679787903f, + -0.0002960101735353313f, + -0.00023955416024142116f, + -0.000005428557963321982f, + 0.0004964100334261843f, + 0.0013538842757409063f, + 0.002634918910611058f, + 0.00436693575408337f, + 0.0065175161697321555f, + 0.00897952475800971f, + 0.011566379161822652f, + 0.014018876408451886f, + 0.016028053953955006f, + 0.017270887068042688f, + 0.01745751423179922f, + 0.0163833187291431f, + 0.013977100662194252f, + 0.010337169842377791f, + 0.005746282323474722f, + 0.0006589019184637925f, + -0.004341600521758248f, + -0.008613131831428159f, + -0.011545791615276019f, + -0.012660543617859996f, + -0.011700549440186059f, + -0.008698639621450092f, + -0.004006684845818583f, + 0.00172317360274727f, + 0.007608807120062409f, + 0.012654244324839246f, + 0.015898391911284164f, + 0.016575938784747404f, + 0.014265883702010186f, + 0.009002241493722784f, + 0.0013255862247370318f, + -0.007739007276591945f, + -0.0167810834076902f, + -0.024172675639705345f, + -0.02828537119270343f, + -0.027727506101182933f, + -0.02156733481426441f, + -0.009507700677949931f, + 0.008016566866990643f, + 0.02983764491633055f, + 0.05415546347475946f, + 0.0787307236559999f, + 0.10114644750135711f, + 0.11910360136213424f, + 0.13071022495045617f, + 0.13472344727490876f, + 0.13071022495045617f, + 0.11910360136213424f, + 0.10114644750135711f, + 0.0787307236559999f, + 0.05415546347475946f, + 0.02983764491633055f, + 0.008016566866990643f, + -0.009507700677949931f, + -0.02156733481426441f, + -0.027727506101182933f, + -0.02828537119270343f, + -0.024172675639705345f, + -0.0167810834076902f, + -0.007739007276591945f, + 0.0013255862247370318f, + 0.009002241493722784f, + 0.014265883702010186f, + 0.016575938784747404f, + 0.015898391911284164f, + 0.012654244324839246f, + 0.007608807120062409f, + 0.00172317360274727f, + -0.004006684845818583f, + -0.008698639621450092f, + -0.011700549440186059f, + -0.012660543617859996f, + -0.011545791615276019f, + -0.008613131831428159f, + -0.004341600521758248f, + 0.0006589019184637925f, + 0.005746282323474722f, + 0.010337169842377791f, + 0.013977100662194252f, + 0.0163833187291431f, + 0.01745751423179922f, + 0.017270887068042688f, + 0.016028053953955006f, + 0.014018876408451886f, + 0.011566379161822652f, + 0.00897952475800971f, + 0.0065175161697321555f, + 0.00436693575408337f, + 0.002634918910611058f, + 0.0013538842757409063f, + 0.0004964100334261843f, + -0.000005428557963321982f, + -0.00023955416024142116f, + -0.0002960101735353313f, + -0.0002537395679787903f, + -0.00017293152659867192f, + -0.00011443124614437417f } ), - FIRLP_8K( new double[] { + FIRLP_8K( new float[] { /* @@ -333,132 +806,132 @@ public enum Filters */ - -0.0005876043141107231, - 0.0000395798795893033, - 0.0007936108691418746, - 0.002271220583758663, - 0.0045690708238380244, - 0.007632833240537176, - 0.011210445195711924, - 0.014848077882921171, - 0.01794155760934873, - 0.019840991376714804, - 0.01999155424676768, - 0.018081523929298803, - 0.01415831726867514, - 0.008676730505642374, - 0.0024562153938831365, - -0.0034571527303437705, - -0.008001879410852286, - -0.010340843984508226, - -0.010072395969237698, - -0.007354895192546896, - -0.0029039203557233197, - 0.0021492484218134564, - 0.006514352308475102, - 0.009056570176096615, - 0.009090754245710363, - 0.006576743943510026, - 0.002156484338606342, - -0.0029881335341335434, - -0.007431040256860951, - -0.009881347307264159, - -0.009547761214420764, - -0.006387716720906473, - -0.00116384277476187, - 0.004722257199180802, - 0.0095865822102477, - 0.011924484237795421, - 0.010856600363787754, - 0.006429626923814576, - -0.00032219256325637487, - -0.007591488679896054, - -0.01325346967699737, - -0.015451428687349951, - -0.013159262174429455, - -0.006567549275313882, - 0.0028476895325932673, - 0.012598008810195174, - 0.019781804328972176, - 0.02186025227953089, - 0.017422058628364847, - 0.006738234527549823, - -0.008066547704296321, - -0.023306710233840622, - -0.0344567035523893, - -0.037158269971142174, - -0.028289474602001076, - -0.006838858354697991, - 0.02563946671507246, - 0.06512400470969, - 0.10578445793560229, - 0.141076536562424, - 0.16504202130157974, - 0.17352664849913732, - 0.16504202130157974, - 0.141076536562424, - 0.10578445793560229, - 0.06512400470969, - 0.02563946671507246, - -0.006838858354697991, - -0.028289474602001076, - -0.037158269971142174, - -0.0344567035523893, - -0.023306710233840622, - -0.008066547704296321, - 0.006738234527549823, - 0.017422058628364847, - 0.02186025227953089, - 0.019781804328972176, - 0.012598008810195174, - 0.0028476895325932673, - -0.006567549275313882, - -0.013159262174429455, - -0.015451428687349951, - -0.01325346967699737, - -0.007591488679896054, - -0.00032219256325637487, - 0.006429626923814576, - 0.010856600363787754, - 0.011924484237795421, - 0.0095865822102477, - 0.004722257199180802, - -0.00116384277476187, - -0.006387716720906473, - -0.009547761214420764, - -0.009881347307264159, - -0.007431040256860951, - -0.0029881335341335434, - 0.002156484338606342, - 0.006576743943510026, - 0.009090754245710363, - 0.009056570176096615, - 0.006514352308475102, - 0.0021492484218134564, - -0.0029039203557233197, - -0.007354895192546896, - -0.010072395969237698, - -0.010340843984508226, - -0.008001879410852286, - -0.0034571527303437705, - 0.0024562153938831365, - 0.008676730505642374, - 0.01415831726867514, - 0.018081523929298803, - 0.01999155424676768, - 0.019840991376714804, - 0.01794155760934873, - 0.014848077882921171, - 0.011210445195711924, - 0.007632833240537176, - 0.0045690708238380244, - 0.002271220583758663, - 0.0007936108691418746, - 0.0000395798795893033, - -0.0005876043141107231 + -0.0005876043141107231f, + 0.0000395798795893033f, + 0.0007936108691418746f, + 0.002271220583758663f, + 0.0045690708238380244f, + 0.007632833240537176f, + 0.011210445195711924f, + 0.014848077882921171f, + 0.01794155760934873f, + 0.019840991376714804f, + 0.01999155424676768f, + 0.018081523929298803f, + 0.01415831726867514f, + 0.008676730505642374f, + 0.0024562153938831365f, + -0.0034571527303437705f, + -0.008001879410852286f, + -0.010340843984508226f, + -0.010072395969237698f, + -0.007354895192546896f, + -0.0029039203557233197f, + 0.0021492484218134564f, + 0.006514352308475102f, + 0.009056570176096615f, + 0.009090754245710363f, + 0.006576743943510026f, + 0.002156484338606342f, + -0.0029881335341335434f, + -0.007431040256860951f, + -0.009881347307264159f, + -0.009547761214420764f, + -0.006387716720906473f, + -0.00116384277476187f, + 0.004722257199180802f, + 0.0095865822102477f, + 0.011924484237795421f, + 0.010856600363787754f, + 0.006429626923814576f, + -0.00032219256325637487f, + -0.007591488679896054f, + -0.01325346967699737f, + -0.015451428687349951f, + -0.013159262174429455f, + -0.006567549275313882f, + 0.0028476895325932673f, + 0.012598008810195174f, + 0.019781804328972176f, + 0.02186025227953089f, + 0.017422058628364847f, + 0.006738234527549823f, + -0.008066547704296321f, + -0.023306710233840622f, + -0.0344567035523893f, + -0.037158269971142174f, + -0.028289474602001076f, + -0.006838858354697991f, + 0.02563946671507246f, + 0.06512400470969f, + 0.10578445793560229f, + 0.141076536562424f, + 0.16504202130157974f, + 0.17352664849913732f, + 0.16504202130157974f, + 0.141076536562424f, + 0.10578445793560229f, + 0.06512400470969f, + 0.02563946671507246f, + -0.006838858354697991f, + -0.028289474602001076f, + -0.037158269971142174f, + -0.0344567035523893f, + -0.023306710233840622f, + -0.008066547704296321f, + 0.006738234527549823f, + 0.017422058628364847f, + 0.02186025227953089f, + 0.019781804328972176f, + 0.012598008810195174f, + 0.0028476895325932673f, + -0.006567549275313882f, + -0.013159262174429455f, + -0.015451428687349951f, + -0.01325346967699737f, + -0.007591488679896054f, + -0.00032219256325637487f, + 0.006429626923814576f, + 0.010856600363787754f, + 0.011924484237795421f, + 0.0095865822102477f, + 0.004722257199180802f, + -0.00116384277476187f, + -0.006387716720906473f, + -0.009547761214420764f, + -0.009881347307264159f, + -0.007431040256860951f, + -0.0029881335341335434f, + 0.002156484338606342f, + 0.006576743943510026f, + 0.009090754245710363f, + 0.009056570176096615f, + 0.006514352308475102f, + 0.0021492484218134564f, + -0.0029039203557233197f, + -0.007354895192546896f, + -0.010072395969237698f, + -0.010340843984508226f, + -0.008001879410852286f, + -0.0034571527303437705f, + 0.0024562153938831365f, + 0.008676730505642374f, + 0.01415831726867514f, + 0.018081523929298803f, + 0.01999155424676768f, + 0.019840991376714804f, + 0.01794155760934873f, + 0.014848077882921171f, + 0.011210445195711924f, + 0.007632833240537176f, + 0.0045690708238380244f, + 0.002271220583758663f, + 0.0007936108691418746f, + 0.0000395798795893033f, + -0.0005876043141107231f } ), - FIRLP_15K( new double[] { + FIRLP_15K( new float[] { /* FIR filter designed with http://t-filter.appspot.com sampling frequency: 48000 Hz @@ -471,77 +944,77 @@ public enum Filters desired attenuation = -40 dB actual attenuation = -41.03342332843521 dB */ - -0.014082901225560598, - -0.02333969799770533, - -0.022033345340200808, - -0.0004492742003176776, - 0.034217272170844526, - 0.05847499164066433, - 0.04845184454729058, - 0.0029885635174003813, - -0.045972124010673326, - -0.04957432533335844, - 0.02225593631403953, - 0.15189293741405197, - 0.27718300156351594, - 0.32893125787628796, - 0.27718300156351594, - 0.15189293741405197, - 0.02225593631403953, - -0.04957432533335844, - -0.045972124010673326, - 0.0029885635174003813, - 0.04845184454729058, - 0.05847499164066433, - 0.034217272170844526, - -0.0004492742003176776, - -0.022033345340200808, - -0.02333969799770533, - -0.014082901225560598 + -0.014082901225560598f, + -0.02333969799770533f, + -0.022033345340200808f, + -0.0004492742003176776f, + 0.034217272170844526f, + 0.05847499164066433f, + 0.04845184454729058f, + 0.0029885635174003813f, + -0.045972124010673326f, + -0.04957432533335844f, + 0.02225593631403953f, + 0.15189293741405197f, + 0.27718300156351594f, + 0.32893125787628796f, + 0.27718300156351594f, + 0.15189293741405197f, + 0.02225593631403953f, + -0.04957432533335844f, + -0.045972124010673326f, + 0.0029885635174003813f, + 0.04845184454729058f, + 0.05847499164066433f, + 0.034217272170844526f, + -0.0004492742003176776f, + -0.022033345340200808f, + -0.02333969799770533f, + -0.014082901225560598f } ), //FS=192000 FC=40000 WIN: Kaiser-Bessel - FIR_HALF_BAND_31T_ONE_EIGHTH_FCO( new double[] { - -0.000045886616046836, 0, - 0.000591369980811269, 0, - -0.002865894676690263, 0, - 0.008258847333251895, 0, - -0.019555838964709603, 0, - 0.041885910942306370, 0, - -0.091494773870957058, 0, - 0.313146427083389300, 0.5, //.499981677577289670, - 0.313146427083389300, 0, - -0.091494773870957058, 0, - 0.041885910942306370, 0, - -0.019555838964709603, 0, - 0.008258847333251895, 0, - -0.002865894676690263, 0, - 0.000591369980811269, 0, - -0.000045886616046836 + FIR_HALF_BAND_31T_ONE_EIGHTH_FCO( new float[] { + -0.000045886616046836f, 0f, + 0.000591369980811269f, 0f, + -0.002865894676690263f, 0f, + 0.008258847333251895f, 0f, + -0.019555838964709603f, 0f, + 0.041885910942306370f, 0f, + -0.091494773870957058f, 0f, + 0.313146427083389300f, 0.5f, //.499981677577289670f, + 0.313146427083389300f, 0f, + -0.091494773870957058f, 0f, + 0.041885910942306370f, 0f, + -0.019555838964709603f, 0f, + 0.008258847333251895f, 0f, + -0.002865894676690263f, 0f, + 0.000591369980811269f, 0f, + -0.000045886616046836f } ), - FIRHP_31T_48KFS_300FCO( new double[] { 0.079454311268354116, - -0.431252134555296540, -0.080866558157993096, -0.018201087229858796, - -0.006959451230405745, -0.005082182047181806, -0.004827982359353442, - -0.004678622485731351, -0.004518783884113521, -0.004590986714193629, - -0.004788367734908081, -0.004864687584309190, -0.004847881778381928, - -0.004819115432733451, -0.004899065399109821, 0.995074461482826860, - -0.004899065399109821, -0.004819115432733451, -0.004847881778381928, - -0.004864687584309190, -0.004788367734908081, -0.004590986714193629, - -0.004518783884113521, -0.004678622485731351, -0.004827982359353442, - -0.005082182047181806, -0.006959451230405745, -0.018201087229858796, - -0.080866558157993096, -0.431252134555296540, 0.079454311268354116 } ), + FIRHP_31T_48KFS_300FCO( new float[] { 0.079454311268354116f, + -0.431252134555296540f, -0.080866558157993096f, -0.018201087229858796f, + -0.006959451230405745f, -0.005082182047181806f, -0.004827982359353442f, + -0.004678622485731351f, -0.004518783884113521f, -0.004590986714193629f, + -0.004788367734908081f, -0.004864687584309190f, -0.004847881778381928f, + -0.004819115432733451f, -0.004899065399109821f, 0.995074461482826860f, + -0.004899065399109821f, -0.004819115432733451f, -0.004847881778381928f, + -0.004864687584309190f, -0.004788367734908081f, -0.004590986714193629f, + -0.004518783884113521f, -0.004678622485731351f, -0.004827982359353442f, + -0.005082182047181806f, -0.006959451230405745f, -0.018201087229858796f, + -0.080866558157993096f, -0.431252134555296540f, 0.079454311268354116f } ), - FIRHP_31T_12KFS_400FCO( new double[] { 0.053389156955298771, - 0.182002884434506210, -0.086127791245869528, -0.016516534724731153, - -0.036968390772931715, -0.035475484228869217, -0.040734954194563675, - -0.044789081155425826, -0.049513180269900807, -0.054086500917566893, - -0.058563271660686450, -0.062449865988963169, -0.065441928590436960, - -0.068170371167388846, -0.069540820637351994, 0.930065488549365990, - -0.069540820637351994, -0.068170371167388846, -0.065441928590436960, - -0.062449865988963169, -0.058563271660686450, -0.054086500917566893, - -0.049513180269900807, -0.044789081155425826, -0.040734954194563675, - -0.035475484228869217, -0.036968390772931715, -0.016516534724731153, - -0.086127791245869528, 0.182002884434506210, 0.053389156955298771 } ), + FIRHP_31T_12KFS_400FCO( new float[] { 0.053389156955298771f, + 0.182002884434506210f, -0.086127791245869528f, -0.016516534724731153f, + -0.036968390772931715f, -0.035475484228869217f, -0.040734954194563675f, + -0.044789081155425826f, -0.049513180269900807f, -0.054086500917566893f, + -0.058563271660686450f, -0.062449865988963169f, -0.065441928590436960f, + -0.068170371167388846f, -0.069540820637351994f, 0.930065488549365990f, + -0.069540820637351994f, -0.068170371167388846f, -0.065441928590436960f, + -0.062449865988963169f, -0.058563271660686450f, -0.054086500917566893f, + -0.049513180269900807f, -0.044789081155425826f, -0.040734954194563675f, + -0.035475484228869217f, -0.036968390772931715f, -0.016516534724731153f, + -0.086127791245869528f, 0.182002884434506210f, 0.053389156955298771f } ), /** * FIR Low Pass Filter @@ -551,25 +1024,25 @@ public enum Filters * Designer: http://t-filter.appspot.com/fir/index.html */ - FIRLP_55TAP_48000FS_3000FC( new double[] { -0.0052419787903715655, - -0.0003789909443307852, 0.0012970137854134386, 0.004210460544067679, - 0.008221266221665309, 0.012951576579356689, 0.017794312853913318, - 0.021959871931790928, 0.024583800118549094, 0.024878144443784132, - 0.022296319647647538, 0.016685495012933472, 0.008390268267799136, - -0.001718264469387386, -0.012288389660616603, -0.02161633130497537, - -0.027882128567957774, -0.02943377233328899, -0.025006516193566884, - -0.014040074880106284, 0.0032217508351258103, 0.025660031586092195, - 0.05136009984280478, 0.0778336106888181, 0.10230926486919244, - 0.12209961841343948, 0.13496813439824185, 0.13943117253534673, - 0.13496813439824185, 0.12209961841343948, 0.10230926486919244, - 0.0778336106888181, 0.05136009984280478, 0.025660031586092195, - 0.0032217508351258103, -0.014040074880106284, -0.025006516193566884, - -0.02943377233328899, -0.027882128567957774, -0.02161633130497537, - -0.012288389660616603, -0.001718264469387386, 0.008390268267799136, - 0.016685495012933472, 0.022296319647647538, 0.024878144443784132, - 0.024583800118549094, 0.021959871931790928, 0.017794312853913318, - 0.012951576579356689, 0.008221266221665309, 0.004210460544067679, - 0.0012970137854134386, -0.0003789909443307852, -0.0052419787903715655 } ), + FIRLP_55TAP_48000FS_3000FC( new float[] { -0.0052419787903715655f, + -0.0003789909443307852f, 0.0012970137854134386f, 0.004210460544067679f, + 0.008221266221665309f, 0.012951576579356689f, 0.017794312853913318f, + 0.021959871931790928f, 0.024583800118549094f, 0.024878144443784132f, + 0.022296319647647538f, 0.016685495012933472f, 0.008390268267799136f, + -0.001718264469387386f, -0.012288389660616603f, -0.02161633130497537f, + -0.027882128567957774f, -0.02943377233328899f, -0.025006516193566884f, + -0.014040074880106284f, 0.0032217508351258103f, 0.025660031586092195f, + 0.05136009984280478f, 0.0778336106888181f, 0.10230926486919244f, + 0.12209961841343948f, 0.13496813439824185f, 0.13943117253534673f, + 0.13496813439824185f, 0.12209961841343948f, 0.10230926486919244f, + 0.0778336106888181f, 0.05136009984280478f, 0.025660031586092195f, + 0.0032217508351258103f, -0.014040074880106284f, -0.025006516193566884f, + -0.02943377233328899f, -0.027882128567957774f, -0.02161633130497537f, + -0.012288389660616603f, -0.001718264469387386f, 0.008390268267799136f, + 0.016685495012933472f, 0.022296319647647538f, 0.024878144443784132f, + 0.024583800118549094f, 0.021959871931790928f, 0.017794312853913318f, + 0.012951576579356689f, 0.008221266221665309f, 0.004210460544067679f, + 0.0012970137854134386f, -0.0003789909443307852f, -0.0052419787903715655f } ), /* @@ -589,7 +1062,7 @@ public enum Filters actual attenuation = -40.00189415137373 dB */ - FIRLP_24000FS_TO_3000FS( new double[] { + FIRLP_24000FS_TO_3000FS( new float[] { /* @@ -610,10 +1083,10 @@ public enum Filters */ - .2,.2,.2,.2,.2,.2,.2 + .2f,.2f,.2f,.2f,.2f,.2f,.2f }), - FIRLP_3000FS_TO_150FC( new double[] { + FIRLP_3000FS_TO_150FC( new float[] { /* FIR filter designed with @@ -633,605 +1106,605 @@ public enum Filters */ - -0.006122920352830965, - -0.0023884346335371165, - -0.002599310268302821, - -0.002619226598223891, - -0.0024058915455907196, - -0.0019288301578398123, - -0.0011732914244696805, - -0.00014348647168516363, - 0.0011360279713313209, - 0.002620018618160862, - 0.004244279668461443, - 0.005929041211240287, - 0.007583700132903604, - 0.009112816853143859, - 0.010422968037142797, - 0.011429759533610568, - 0.012064461696610605, - 0.01227985703081096, - 0.012054670302231996, - 0.011395973228693276, - 0.010339492321850014, - 0.008947826461137941, - 0.007306289027941143, - 0.005516680433668245, - 0.003689888284206567, - 0.0019376716631701368, - 0.00036406537300421297, - -0.0009423273945619157, - -0.0019147615431778597, - -0.0025137861299055595, - -0.00272885000805846, - -0.002579780077995427, - -0.00211370881796881, - -0.001401086778987431, - -0.0005294931107453994, - 0.00040506185948651675, - 0.0013056441129537509, - 0.0020838354221374305, - 0.0026667562654551246, - 0.003003052919640715, - 0.003066845756573675, - 0.002858826770125135, - 0.002405247908616018, - 0.0017550758096436146, - 0.0009750187684900944, - 0.0001428944628485573, - -0.0006600057708364918, - -0.0013572018806166464, - -0.0018849613428995964, - -0.0021983440926621103, - -0.002274533536918245, - -0.0021136819231921474, - -0.0017380160407736585, - -0.0011899674040168643, - -0.0005293364966829129, - 0.00017203581592664565, - 0.0008397052861045773, - 0.0014076463254305174, - 0.0018238726340835366, - 0.0020485384828633777, - 0.002054368304440125, - 0.00184518937028339, - 0.001463759939291864, - 0.0009254779772329933, - 0.00030908834306052194, - -0.00033072041687547837, - -0.0009242244673079876, - -0.001411151838185113, - -0.0017423327816480623, - -0.001885184212057672, - -0.001827180245955061, - -0.0015767572410517868, - -0.0011623018660948076, - -0.0006290757127071664, - -0.000034277486241399186, - 0.0005590116846630576, - 0.0010883547994854647, - 0.0014984460140958136, - 0.0017469004431845741, - 0.0018085733127357138, - 0.00167799070404902, - 0.0013699023886079994, - 0.0009178041117923993, - 0.00037029761935479554, - -0.00021414834948363428, - -0.0007733385753572961, - -0.0012479991812573157, - -0.0015880719715790936, - -0.0017577706864892022, - -0.0017393843977854672, - -0.0015352614436320124, - -0.0011668823340746201, - -0.0006738084260047715, - -0.00010821707107113083, - 0.00046927876230741587, - 0.0009969768021073388, - 0.001418713685742809, - 0.001689241246422699, - 0.0017799814611489096, - 0.0016808579160732414, - 0.001401938565514821, - 0.0009725070918705811, - 0.00043789676780500817, - -0.00014493157532811753, - -0.0007134285789517371, - -0.0012066408059786802, - -0.0015718235937901522, - -0.0017696752225057275, - -0.0017782857455891608, - -0.0015955792180418218, - -0.001239958862296361, - -0.00074891505028209, - -0.0001750373426426684, - 0.0004203972508663324, - 0.0009743368473658559, - 0.001427583798294352, - 0.0017300695180374488, - 0.0018474313131084748, - 0.0017665803736364497, - 0.0014959523321337515, - 0.0010620969968500548, - 0.0005087929550228872, - -0.00010287888971484906, - -0.0007071978327354896, - -0.0012439856702169039, - -0.0016485023632017621, - -0.0018816779265696613, - -0.0019137822019732152, - -0.0017410363220249359, - -0.0013797190792108685, - -0.0008669516848493666, - -0.0002564820252824784, - 0.00038702570052167175, - 0.0009946245737452108, - 0.0015005576753828536, - 0.0018494543772917837, - 0.002002271293795752, - 0.0019405399480025168, - 0.001668583653638679, - 0.0012133648938024923, - 0.0006218226272857692, - -0.00004396586555373278, - -0.0007131888239163173, - -0.0013137753201852866, - -0.0017802301804471922, - -0.002060772342951284, - -0.0021230164816546412, - -0.0019576726289922586, - -0.001579698202436061, - -0.0010269473735399154, - -0.0003566571442405154, - 0.00036046532149158213, - 0.001047910209033917, - 0.0016308959748307101, - 0.0020457879850275806, - 0.0022452826272775055, - 0.0022053677261764473, - 0.0019268362424314605, - 0.001436104198970914, - 0.000783301048300022, - 0.000036170113001030666, - -0.0007258491765731604, - -0.0014208542690678963, - -0.001973351045206107, - -0.0023218012876373207, - -0.002425430974535461, - -0.0022692307984843695, - -0.0018659822196923194, - -0.0012553733554405261, - -0.0005001275988053344, - 0.0003207533398910953, - 0.0011202470135660719, - 0.0018119245687932325, - 0.0023191095738080646, - 0.0025840361579153536, - 0.0025746366972740037, - 0.0022874947808971076, - 0.0017482590981993708, - 0.001010469529718797, - 0.00015080284754578053, - -0.0007401321025391216, - -0.0015677281601321103, - -0.002241555313336647, - -0.002685049904781321, - -0.002847415468166842, - -0.002706332216634437, - -0.0022693572465456347, - -0.0015807551810501244, - -0.0007073681818317356, - 0.00025858155245919815, - 0.0012156374711696657, - 0.0020601250277244316, - 0.002698864465909288, - 0.003058968365749582, - 0.003095930462187589, - 0.002799180323785052, - 0.0021938662312239255, - 0.0013387749941891575, - 0.00032073673872905727, - -0.0007540149497279023, - -0.0017707823148707424, - -0.0026186149816637165, - -0.0032023981947833136, - -0.0034533803843191375, - -0.0033370666249195324, - -0.002857634299302074, - -0.0020583398437455315, - -0.0010178803764328268, - 0.00015714303240366978, - 0.00134312158248896, - 0.0024123674642028033, - 0.0032466971416879246, - 0.003750429521389635, - 0.0038610298842211605, - 0.0035563826341158213, - 0.0028590450497876694, - 0.0018334905123878731, - 0.0005825119501486656, - -0.0007653431253644171, - -0.002066921044075508, - -0.00318050748566926, - -0.003981099599522876, - -0.004372950526822947, - -0.004302245531766623, - -0.0037634623427922525, - -0.0028010281264617036, - -0.0015063684237781073, - -0.000009312999857163947, - 0.0015348340498211675, - 0.002960705851903475, - 0.00411025264482529, - 0.004850497409323919, - 0.00508852374186094, - 0.004782051229146126, - 0.003945677268175109, - 0.002651861057270526, - 0.001024987337059874, - -0.000770898857558077, - -0.0025480549808695363, - -0.0041134371938038455, - -0.005289473467472686, - -0.0059345294154884536, - -0.005958790652492603, - -0.0053348632172493865, - -0.004104133685973202, - -0.0023757889373754375, - -0.0003154863263286177, - 0.0018679968592255536, - 0.003942664171895544, - 0.005680643711722206, - 0.006877057686831076, - 0.007378348952494577, - 0.007096715159487296, - 0.0060250187066667035, - 0.004239992353086172, - 0.0018984740602875209, - -0.0007749973681354589, - -0.0035085807133030038, - -0.0060094188052075385, - -0.007993440148818328, - -0.009215708970605762, - -0.00949819102645701, - -0.008752165260963618, - -0.006992771039067642, - -0.004343750499151247, - -0.0010313778997952953, - 0.002632475569255834, - 0.006276994042887084, - 0.009506525921431109, - 0.011939275012512792, - 0.013247115357734634, - 0.013192709905840089, - 0.011660378948982, - 0.008677354388180201, - 0.0044226259943339384, - -0.0007776763994276021, - -0.006468598011493521, - -0.01209866234178173, - -0.01705984630504337, - -0.020740073978089375, - -0.022572656974485978, - -0.022091348052909576, - -0.018974044064189968, - -0.01307870343426492, - -0.004467461602914141, - 0.006588374127832776, - 0.019617756508396658, - 0.03397630130130575, - 0.04888871785298119, - 0.06350208150118342, - 0.07694602080488765, - 0.08839667839951919, - 0.09713857345310689, - 0.10261800054825827, - 0.10448449122868045, - 0.10261800054825827, - 0.09713857345310689, - 0.08839667839951919, - 0.07694602080488765, - 0.06350208150118342, - 0.04888871785298119, - 0.03397630130130575, - 0.019617756508396658, - 0.006588374127832776, - -0.004467461602914141, - -0.01307870343426492, - -0.018974044064189968, - -0.022091348052909576, - -0.022572656974485978, - -0.020740073978089375, - -0.01705984630504337, - -0.01209866234178173, - -0.006468598011493521, - -0.0007776763994276021, - 0.0044226259943339384, - 0.008677354388180201, - 0.011660378948982, - 0.013192709905840089, - 0.013247115357734634, - 0.011939275012512792, - 0.009506525921431109, - 0.006276994042887084, - 0.002632475569255834, - -0.0010313778997952953, - -0.004343750499151247, - -0.006992771039067642, - -0.008752165260963618, - -0.00949819102645701, - -0.009215708970605762, - -0.007993440148818328, - -0.0060094188052075385, - -0.0035085807133030038, - -0.0007749973681354589, - 0.0018984740602875209, - 0.004239992353086172, - 0.0060250187066667035, - 0.007096715159487296, - 0.007378348952494577, - 0.006877057686831076, - 0.005680643711722206, - 0.003942664171895544, - 0.0018679968592255536, - -0.0003154863263286177, - -0.0023757889373754375, - -0.004104133685973202, - -0.0053348632172493865, - -0.005958790652492603, - -0.0059345294154884536, - -0.005289473467472686, - -0.0041134371938038455, - -0.0025480549808695363, - -0.000770898857558077, - 0.001024987337059874, - 0.002651861057270526, - 0.003945677268175109, - 0.004782051229146126, - 0.00508852374186094, - 0.004850497409323919, - 0.00411025264482529, - 0.002960705851903475, - 0.0015348340498211675, - -0.000009312999857163947, - -0.0015063684237781073, - -0.0028010281264617036, - -0.0037634623427922525, - -0.004302245531766623, - -0.004372950526822947, - -0.003981099599522876, - -0.00318050748566926, - -0.002066921044075508, - -0.0007653431253644171, - 0.0005825119501486656, - 0.0018334905123878731, - 0.0028590450497876694, - 0.0035563826341158213, - 0.0038610298842211605, - 0.003750429521389635, - 0.0032466971416879246, - 0.0024123674642028033, - 0.00134312158248896, - 0.00015714303240366978, - -0.0010178803764328268, - -0.0020583398437455315, - -0.002857634299302074, - -0.0033370666249195324, - -0.0034533803843191375, - -0.0032023981947833136, - -0.0026186149816637165, - -0.0017707823148707424, - -0.0007540149497279023, - 0.00032073673872905727, - 0.0013387749941891575, - 0.0021938662312239255, - 0.002799180323785052, - 0.003095930462187589, - 0.003058968365749582, - 0.002698864465909288, - 0.0020601250277244316, - 0.0012156374711696657, - 0.00025858155245919815, - -0.0007073681818317356, - -0.0015807551810501244, - -0.0022693572465456347, - -0.002706332216634437, - -0.002847415468166842, - -0.002685049904781321, - -0.002241555313336647, - -0.0015677281601321103, - -0.0007401321025391216, - 0.00015080284754578053, - 0.001010469529718797, - 0.0017482590981993708, - 0.0022874947808971076, - 0.0025746366972740037, - 0.0025840361579153536, - 0.0023191095738080646, - 0.0018119245687932325, - 0.0011202470135660719, - 0.0003207533398910953, - -0.0005001275988053344, - -0.0012553733554405261, - -0.0018659822196923194, - -0.0022692307984843695, - -0.002425430974535461, - -0.0023218012876373207, - -0.001973351045206107, - -0.0014208542690678963, - -0.0007258491765731604, - 0.000036170113001030666, - 0.000783301048300022, - 0.001436104198970914, - 0.0019268362424314605, - 0.0022053677261764473, - 0.0022452826272775055, - 0.0020457879850275806, - 0.0016308959748307101, - 0.001047910209033917, - 0.00036046532149158213, - -0.0003566571442405154, - -0.0010269473735399154, - -0.001579698202436061, - -0.0019576726289922586, - -0.0021230164816546412, - -0.002060772342951284, - -0.0017802301804471922, - -0.0013137753201852866, - -0.0007131888239163173, - -0.00004396586555373278, - 0.0006218226272857692, - 0.0012133648938024923, - 0.001668583653638679, - 0.0019405399480025168, - 0.002002271293795752, - 0.0018494543772917837, - 0.0015005576753828536, - 0.0009946245737452108, - 0.00038702570052167175, - -0.0002564820252824784, - -0.0008669516848493666, - -0.0013797190792108685, - -0.0017410363220249359, - -0.0019137822019732152, - -0.0018816779265696613, - -0.0016485023632017621, - -0.0012439856702169039, - -0.0007071978327354896, - -0.00010287888971484906, - 0.0005087929550228872, - 0.0010620969968500548, - 0.0014959523321337515, - 0.0017665803736364497, - 0.0018474313131084748, - 0.0017300695180374488, - 0.001427583798294352, - 0.0009743368473658559, - 0.0004203972508663324, - -0.0001750373426426684, - -0.00074891505028209, - -0.001239958862296361, - -0.0015955792180418218, - -0.0017782857455891608, - -0.0017696752225057275, - -0.0015718235937901522, - -0.0012066408059786802, - -0.0007134285789517371, - -0.00014493157532811753, - 0.00043789676780500817, - 0.0009725070918705811, - 0.001401938565514821, - 0.0016808579160732414, - 0.0017799814611489096, - 0.001689241246422699, - 0.001418713685742809, - 0.0009969768021073388, - 0.00046927876230741587, - -0.00010821707107113083, - -0.0006738084260047715, - -0.0011668823340746201, - -0.0015352614436320124, - -0.0017393843977854672, - -0.0017577706864892022, - -0.0015880719715790936, - -0.0012479991812573157, - -0.0007733385753572961, - -0.00021414834948363428, - 0.00037029761935479554, - 0.0009178041117923993, - 0.0013699023886079994, - 0.00167799070404902, - 0.0018085733127357138, - 0.0017469004431845741, - 0.0014984460140958136, - 0.0010883547994854647, - 0.0005590116846630576, - -0.000034277486241399186, - -0.0006290757127071664, - -0.0011623018660948076, - -0.0015767572410517868, - -0.001827180245955061, - -0.001885184212057672, - -0.0017423327816480623, - -0.001411151838185113, - -0.0009242244673079876, - -0.00033072041687547837, - 0.00030908834306052194, - 0.0009254779772329933, - 0.001463759939291864, - 0.00184518937028339, - 0.002054368304440125, - 0.0020485384828633777, - 0.0018238726340835366, - 0.0014076463254305174, - 0.0008397052861045773, - 0.00017203581592664565, - -0.0005293364966829129, - -0.0011899674040168643, - -0.0017380160407736585, - -0.0021136819231921474, - -0.002274533536918245, - -0.0021983440926621103, - -0.0018849613428995964, - -0.0013572018806166464, - -0.0006600057708364918, - 0.0001428944628485573, - 0.0009750187684900944, - 0.0017550758096436146, - 0.002405247908616018, - 0.002858826770125135, - 0.003066845756573675, - 0.003003052919640715, - 0.0026667562654551246, - 0.0020838354221374305, - 0.0013056441129537509, - 0.00040506185948651675, - -0.0005294931107453994, - -0.001401086778987431, - -0.00211370881796881, - -0.002579780077995427, - -0.00272885000805846, - -0.0025137861299055595, - -0.0019147615431778597, - -0.0009423273945619157, - 0.00036406537300421297, - 0.0019376716631701368, - 0.003689888284206567, - 0.005516680433668245, - 0.007306289027941143, - 0.008947826461137941, - 0.010339492321850014, - 0.011395973228693276, - 0.012054670302231996, - 0.01227985703081096, - 0.012064461696610605, - 0.011429759533610568, - 0.010422968037142797, - 0.009112816853143859, - 0.007583700132903604, - 0.005929041211240287, - 0.004244279668461443, - 0.002620018618160862, - 0.0011360279713313209, - -0.00014348647168516363, - -0.0011732914244696805, - -0.0019288301578398123, - -0.0024058915455907196, - -0.002619226598223891, - -0.002599310268302821, - -0.0023884346335371165, - -0.006122920352830965 + -0.006122920352830965f, + -0.0023884346335371165f, + -0.002599310268302821f, + -0.002619226598223891f, + -0.0024058915455907196f, + -0.0019288301578398123f, + -0.0011732914244696805f, + -0.00014348647168516363f, + 0.0011360279713313209f, + 0.002620018618160862f, + 0.004244279668461443f, + 0.005929041211240287f, + 0.007583700132903604f, + 0.009112816853143859f, + 0.010422968037142797f, + 0.011429759533610568f, + 0.012064461696610605f, + 0.01227985703081096f, + 0.012054670302231996f, + 0.011395973228693276f, + 0.010339492321850014f, + 0.008947826461137941f, + 0.007306289027941143f, + 0.005516680433668245f, + 0.003689888284206567f, + 0.0019376716631701368f, + 0.00036406537300421297f, + -0.0009423273945619157f, + -0.0019147615431778597f, + -0.0025137861299055595f, + -0.00272885000805846f, + -0.002579780077995427f, + -0.00211370881796881f, + -0.001401086778987431f, + -0.0005294931107453994f, + 0.00040506185948651675f, + 0.0013056441129537509f, + 0.0020838354221374305f, + 0.0026667562654551246f, + 0.003003052919640715f, + 0.003066845756573675f, + 0.002858826770125135f, + 0.002405247908616018f, + 0.0017550758096436146f, + 0.0009750187684900944f, + 0.0001428944628485573f, + -0.0006600057708364918f, + -0.0013572018806166464f, + -0.0018849613428995964f, + -0.0021983440926621103f, + -0.002274533536918245f, + -0.0021136819231921474f, + -0.0017380160407736585f, + -0.0011899674040168643f, + -0.0005293364966829129f, + 0.00017203581592664565f, + 0.0008397052861045773f, + 0.0014076463254305174f, + 0.0018238726340835366f, + 0.0020485384828633777f, + 0.002054368304440125f, + 0.00184518937028339f, + 0.001463759939291864f, + 0.0009254779772329933f, + 0.00030908834306052194f, + -0.00033072041687547837f, + -0.0009242244673079876f, + -0.001411151838185113f, + -0.0017423327816480623f, + -0.001885184212057672f, + -0.001827180245955061f, + -0.0015767572410517868f, + -0.0011623018660948076f, + -0.0006290757127071664f, + -0.000034277486241399186f, + 0.0005590116846630576f, + 0.0010883547994854647f, + 0.0014984460140958136f, + 0.0017469004431845741f, + 0.0018085733127357138f, + 0.00167799070404902f, + 0.0013699023886079994f, + 0.0009178041117923993f, + 0.00037029761935479554f, + -0.00021414834948363428f, + -0.0007733385753572961f, + -0.0012479991812573157f, + -0.0015880719715790936f, + -0.0017577706864892022f, + -0.0017393843977854672f, + -0.0015352614436320124f, + -0.0011668823340746201f, + -0.0006738084260047715f, + -0.00010821707107113083f, + 0.00046927876230741587f, + 0.0009969768021073388f, + 0.001418713685742809f, + 0.001689241246422699f, + 0.0017799814611489096f, + 0.0016808579160732414f, + 0.001401938565514821f, + 0.0009725070918705811f, + 0.00043789676780500817f, + -0.00014493157532811753f, + -0.0007134285789517371f, + -0.0012066408059786802f, + -0.0015718235937901522f, + -0.0017696752225057275f, + -0.0017782857455891608f, + -0.0015955792180418218f, + -0.001239958862296361f, + -0.00074891505028209f, + -0.0001750373426426684f, + 0.0004203972508663324f, + 0.0009743368473658559f, + 0.001427583798294352f, + 0.0017300695180374488f, + 0.0018474313131084748f, + 0.0017665803736364497f, + 0.0014959523321337515f, + 0.0010620969968500548f, + 0.0005087929550228872f, + -0.00010287888971484906f, + -0.0007071978327354896f, + -0.0012439856702169039f, + -0.0016485023632017621f, + -0.0018816779265696613f, + -0.0019137822019732152f, + -0.0017410363220249359f, + -0.0013797190792108685f, + -0.0008669516848493666f, + -0.0002564820252824784f, + 0.00038702570052167175f, + 0.0009946245737452108f, + 0.0015005576753828536f, + 0.0018494543772917837f, + 0.002002271293795752f, + 0.0019405399480025168f, + 0.001668583653638679f, + 0.0012133648938024923f, + 0.0006218226272857692f, + -0.00004396586555373278f, + -0.0007131888239163173f, + -0.0013137753201852866f, + -0.0017802301804471922f, + -0.002060772342951284f, + -0.0021230164816546412f, + -0.0019576726289922586f, + -0.001579698202436061f, + -0.0010269473735399154f, + -0.0003566571442405154f, + 0.00036046532149158213f, + 0.001047910209033917f, + 0.0016308959748307101f, + 0.0020457879850275806f, + 0.0022452826272775055f, + 0.0022053677261764473f, + 0.0019268362424314605f, + 0.001436104198970914f, + 0.000783301048300022f, + 0.000036170113001030666f, + -0.0007258491765731604f, + -0.0014208542690678963f, + -0.001973351045206107f, + -0.0023218012876373207f, + -0.002425430974535461f, + -0.0022692307984843695f, + -0.0018659822196923194f, + -0.0012553733554405261f, + -0.0005001275988053344f, + 0.0003207533398910953f, + 0.0011202470135660719f, + 0.0018119245687932325f, + 0.0023191095738080646f, + 0.0025840361579153536f, + 0.0025746366972740037f, + 0.0022874947808971076f, + 0.0017482590981993708f, + 0.001010469529718797f, + 0.00015080284754578053f, + -0.0007401321025391216f, + -0.0015677281601321103f, + -0.002241555313336647f, + -0.002685049904781321f, + -0.002847415468166842f, + -0.002706332216634437f, + -0.0022693572465456347f, + -0.0015807551810501244f, + -0.0007073681818317356f, + 0.00025858155245919815f, + 0.0012156374711696657f, + 0.0020601250277244316f, + 0.002698864465909288f, + 0.003058968365749582f, + 0.003095930462187589f, + 0.002799180323785052f, + 0.0021938662312239255f, + 0.0013387749941891575f, + 0.00032073673872905727f, + -0.0007540149497279023f, + -0.0017707823148707424f, + -0.0026186149816637165f, + -0.0032023981947833136f, + -0.0034533803843191375f, + -0.0033370666249195324f, + -0.002857634299302074f, + -0.0020583398437455315f, + -0.0010178803764328268f, + 0.00015714303240366978f, + 0.00134312158248896f, + 0.0024123674642028033f, + 0.0032466971416879246f, + 0.003750429521389635f, + 0.0038610298842211605f, + 0.0035563826341158213f, + 0.0028590450497876694f, + 0.0018334905123878731f, + 0.0005825119501486656f, + -0.0007653431253644171f, + -0.002066921044075508f, + -0.00318050748566926f, + -0.003981099599522876f, + -0.004372950526822947f, + -0.004302245531766623f, + -0.0037634623427922525f, + -0.0028010281264617036f, + -0.0015063684237781073f, + -0.000009312999857163947f, + 0.0015348340498211675f, + 0.002960705851903475f, + 0.00411025264482529f, + 0.004850497409323919f, + 0.00508852374186094f, + 0.004782051229146126f, + 0.003945677268175109f, + 0.002651861057270526f, + 0.001024987337059874f, + -0.000770898857558077f, + -0.0025480549808695363f, + -0.0041134371938038455f, + -0.005289473467472686f, + -0.0059345294154884536f, + -0.005958790652492603f, + -0.0053348632172493865f, + -0.004104133685973202f, + -0.0023757889373754375f, + -0.0003154863263286177f, + 0.0018679968592255536f, + 0.003942664171895544f, + 0.005680643711722206f, + 0.006877057686831076f, + 0.007378348952494577f, + 0.007096715159487296f, + 0.0060250187066667035f, + 0.004239992353086172f, + 0.0018984740602875209f, + -0.0007749973681354589f, + -0.0035085807133030038f, + -0.0060094188052075385f, + -0.007993440148818328f, + -0.009215708970605762f, + -0.00949819102645701f, + -0.008752165260963618f, + -0.006992771039067642f, + -0.004343750499151247f, + -0.0010313778997952953f, + 0.002632475569255834f, + 0.006276994042887084f, + 0.009506525921431109f, + 0.011939275012512792f, + 0.013247115357734634f, + 0.013192709905840089f, + 0.011660378948982f, + 0.008677354388180201f, + 0.0044226259943339384f, + -0.0007776763994276021f, + -0.006468598011493521f, + -0.01209866234178173f, + -0.01705984630504337f, + -0.020740073978089375f, + -0.022572656974485978f, + -0.022091348052909576f, + -0.018974044064189968f, + -0.01307870343426492f, + -0.004467461602914141f, + 0.006588374127832776f, + 0.019617756508396658f, + 0.03397630130130575f, + 0.04888871785298119f, + 0.06350208150118342f, + 0.07694602080488765f, + 0.08839667839951919f, + 0.09713857345310689f, + 0.10261800054825827f, + 0.10448449122868045f, + 0.10261800054825827f, + 0.09713857345310689f, + 0.08839667839951919f, + 0.07694602080488765f, + 0.06350208150118342f, + 0.04888871785298119f, + 0.03397630130130575f, + 0.019617756508396658f, + 0.006588374127832776f, + -0.004467461602914141f, + -0.01307870343426492f, + -0.018974044064189968f, + -0.022091348052909576f, + -0.022572656974485978f, + -0.020740073978089375f, + -0.01705984630504337f, + -0.01209866234178173f, + -0.006468598011493521f, + -0.0007776763994276021f, + 0.0044226259943339384f, + 0.008677354388180201f, + 0.011660378948982f, + 0.013192709905840089f, + 0.013247115357734634f, + 0.011939275012512792f, + 0.009506525921431109f, + 0.006276994042887084f, + 0.002632475569255834f, + -0.0010313778997952953f, + -0.004343750499151247f, + -0.006992771039067642f, + -0.008752165260963618f, + -0.00949819102645701f, + -0.009215708970605762f, + -0.007993440148818328f, + -0.0060094188052075385f, + -0.0035085807133030038f, + -0.0007749973681354589f, + 0.0018984740602875209f, + 0.004239992353086172f, + 0.0060250187066667035f, + 0.007096715159487296f, + 0.007378348952494577f, + 0.006877057686831076f, + 0.005680643711722206f, + 0.003942664171895544f, + 0.0018679968592255536f, + -0.0003154863263286177f, + -0.0023757889373754375f, + -0.004104133685973202f, + -0.0053348632172493865f, + -0.005958790652492603f, + -0.0059345294154884536f, + -0.005289473467472686f, + -0.0041134371938038455f, + -0.0025480549808695363f, + -0.000770898857558077f, + 0.001024987337059874f, + 0.002651861057270526f, + 0.003945677268175109f, + 0.004782051229146126f, + 0.00508852374186094f, + 0.004850497409323919f, + 0.00411025264482529f, + 0.002960705851903475f, + 0.0015348340498211675f, + -0.000009312999857163947f, + -0.0015063684237781073f, + -0.0028010281264617036f, + -0.0037634623427922525f, + -0.004302245531766623f, + -0.004372950526822947f, + -0.003981099599522876f, + -0.00318050748566926f, + -0.002066921044075508f, + -0.0007653431253644171f, + 0.0005825119501486656f, + 0.0018334905123878731f, + 0.0028590450497876694f, + 0.0035563826341158213f, + 0.0038610298842211605f, + 0.003750429521389635f, + 0.0032466971416879246f, + 0.0024123674642028033f, + 0.00134312158248896f, + 0.00015714303240366978f, + -0.0010178803764328268f, + -0.0020583398437455315f, + -0.002857634299302074f, + -0.0033370666249195324f, + -0.0034533803843191375f, + -0.0032023981947833136f, + -0.0026186149816637165f, + -0.0017707823148707424f, + -0.0007540149497279023f, + 0.00032073673872905727f, + 0.0013387749941891575f, + 0.0021938662312239255f, + 0.002799180323785052f, + 0.003095930462187589f, + 0.003058968365749582f, + 0.002698864465909288f, + 0.0020601250277244316f, + 0.0012156374711696657f, + 0.00025858155245919815f, + -0.0007073681818317356f, + -0.0015807551810501244f, + -0.0022693572465456347f, + -0.002706332216634437f, + -0.002847415468166842f, + -0.002685049904781321f, + -0.002241555313336647f, + -0.0015677281601321103f, + -0.0007401321025391216f, + 0.00015080284754578053f, + 0.001010469529718797f, + 0.0017482590981993708f, + 0.0022874947808971076f, + 0.0025746366972740037f, + 0.0025840361579153536f, + 0.0023191095738080646f, + 0.0018119245687932325f, + 0.0011202470135660719f, + 0.0003207533398910953f, + -0.0005001275988053344f, + -0.0012553733554405261f, + -0.0018659822196923194f, + -0.0022692307984843695f, + -0.002425430974535461f, + -0.0023218012876373207f, + -0.001973351045206107f, + -0.0014208542690678963f, + -0.0007258491765731604f, + 0.000036170113001030666f, + 0.000783301048300022f, + 0.001436104198970914f, + 0.0019268362424314605f, + 0.0022053677261764473f, + 0.0022452826272775055f, + 0.0020457879850275806f, + 0.0016308959748307101f, + 0.001047910209033917f, + 0.00036046532149158213f, + -0.0003566571442405154f, + -0.0010269473735399154f, + -0.001579698202436061f, + -0.0019576726289922586f, + -0.0021230164816546412f, + -0.002060772342951284f, + -0.0017802301804471922f, + -0.0013137753201852866f, + -0.0007131888239163173f, + -0.00004396586555373278f, + 0.0006218226272857692f, + 0.0012133648938024923f, + 0.001668583653638679f, + 0.0019405399480025168f, + 0.002002271293795752f, + 0.0018494543772917837f, + 0.0015005576753828536f, + 0.0009946245737452108f, + 0.00038702570052167175f, + -0.0002564820252824784f, + -0.0008669516848493666f, + -0.0013797190792108685f, + -0.0017410363220249359f, + -0.0019137822019732152f, + -0.0018816779265696613f, + -0.0016485023632017621f, + -0.0012439856702169039f, + -0.0007071978327354896f, + -0.00010287888971484906f, + 0.0005087929550228872f, + 0.0010620969968500548f, + 0.0014959523321337515f, + 0.0017665803736364497f, + 0.0018474313131084748f, + 0.0017300695180374488f, + 0.001427583798294352f, + 0.0009743368473658559f, + 0.0004203972508663324f, + -0.0001750373426426684f, + -0.00074891505028209f, + -0.001239958862296361f, + -0.0015955792180418218f, + -0.0017782857455891608f, + -0.0017696752225057275f, + -0.0015718235937901522f, + -0.0012066408059786802f, + -0.0007134285789517371f, + -0.00014493157532811753f, + 0.00043789676780500817f, + 0.0009725070918705811f, + 0.001401938565514821f, + 0.0016808579160732414f, + 0.0017799814611489096f, + 0.001689241246422699f, + 0.001418713685742809f, + 0.0009969768021073388f, + 0.00046927876230741587f, + -0.00010821707107113083f, + -0.0006738084260047715f, + -0.0011668823340746201f, + -0.0015352614436320124f, + -0.0017393843977854672f, + -0.0017577706864892022f, + -0.0015880719715790936f, + -0.0012479991812573157f, + -0.0007733385753572961f, + -0.00021414834948363428f, + 0.00037029761935479554f, + 0.0009178041117923993f, + 0.0013699023886079994f, + 0.00167799070404902f, + 0.0018085733127357138f, + 0.0017469004431845741f, + 0.0014984460140958136f, + 0.0010883547994854647f, + 0.0005590116846630576f, + -0.000034277486241399186f, + -0.0006290757127071664f, + -0.0011623018660948076f, + -0.0015767572410517868f, + -0.001827180245955061f, + -0.001885184212057672f, + -0.0017423327816480623f, + -0.001411151838185113f, + -0.0009242244673079876f, + -0.00033072041687547837f, + 0.00030908834306052194f, + 0.0009254779772329933f, + 0.001463759939291864f, + 0.00184518937028339f, + 0.002054368304440125f, + 0.0020485384828633777f, + 0.0018238726340835366f, + 0.0014076463254305174f, + 0.0008397052861045773f, + 0.00017203581592664565f, + -0.0005293364966829129f, + -0.0011899674040168643f, + -0.0017380160407736585f, + -0.0021136819231921474f, + -0.002274533536918245f, + -0.0021983440926621103f, + -0.0018849613428995964f, + -0.0013572018806166464f, + -0.0006600057708364918f, + 0.0001428944628485573f, + 0.0009750187684900944f, + 0.0017550758096436146f, + 0.002405247908616018f, + 0.002858826770125135f, + 0.003066845756573675f, + 0.003003052919640715f, + 0.0026667562654551246f, + 0.0020838354221374305f, + 0.0013056441129537509f, + 0.00040506185948651675f, + -0.0005294931107453994f, + -0.001401086778987431f, + -0.00211370881796881f, + -0.002579780077995427f, + -0.00272885000805846f, + -0.0025137861299055595f, + -0.0019147615431778597f, + -0.0009423273945619157f, + 0.00036406537300421297f, + 0.0019376716631701368f, + 0.003689888284206567f, + 0.005516680433668245f, + 0.007306289027941143f, + 0.008947826461137941f, + 0.010339492321850014f, + 0.011395973228693276f, + 0.012054670302231996f, + 0.01227985703081096f, + 0.012064461696610605f, + 0.011429759533610568f, + 0.010422968037142797f, + 0.009112816853143859f, + 0.007583700132903604f, + 0.005929041211240287f, + 0.004244279668461443f, + 0.002620018618160862f, + 0.0011360279713313209f, + -0.00014348647168516363f, + -0.0011732914244696805f, + -0.0019288301578398123f, + -0.0024058915455907196f, + -0.002619226598223891f, + -0.002599310268302821f, + -0.0023884346335371165f, + -0.006122920352830965f } ); - double[] mCoefficients; + float[] mCoefficients; - private Filters( double[] coefficients ) + private Filters( float[] coefficients ) { mCoefficients = coefficients; } - public double[] getCoefficients() + public float[] getCoefficients() { return mCoefficients; } diff --git a/src/dsp/filter/FloatFIRFilter.java b/src/dsp/filter/FloatFIRFilter.java index c2c8d69e..8355fbf0 100644 --- a/src/dsp/filter/FloatFIRFilter.java +++ b/src/dsp/filter/FloatFIRFilter.java @@ -27,13 +27,13 @@ public class FloatFIRFilter implements RealSampleListener private ArrayList mBuffer; private int mBufferSize = 1; //Temporary initial value private int mBufferPointer = 0; - private double[] mCoefficients; + private float[] mCoefficients; private int[][] mIndexMap; private int mCenterCoefficient; private int mCenterCoefficientMapIndex; - private double mGain; + private float mGain; - public FloatFIRFilter( double[] coefficients, double gain ) + public FloatFIRFilter( float[] coefficients, float gain ) { mCoefficients = coefficients; mBuffer = new ArrayList(); @@ -86,7 +86,7 @@ public class FloatFIRFilter implements RealSampleListener //Convolution - multiply filter coefficients by the circular buffer //samples to calculate a new filtered value - double accumulator = 0.0d; + float accumulator = 0.0f; //Start with the center tap value accumulator += mCoefficients[ mCenterCoefficient ] * @@ -103,7 +103,7 @@ public class FloatFIRFilter implements RealSampleListener //We're almost finished ... apply gain, cast the doubles to shorts and //return the value - return (float)( accumulator * mGain ); + return accumulator * mGain; } /** diff --git a/src/dsp/filter/FloatHalfBandFilter.java b/src/dsp/filter/FloatHalfBandFilter.java index e1a47392..56fae571 100644 --- a/src/dsp/filter/FloatHalfBandFilter.java +++ b/src/dsp/filter/FloatHalfBandFilter.java @@ -27,8 +27,8 @@ public class FloatHalfBandFilter implements RealSampleListener private ArrayList mBuffer; private int mBufferSize = 1; //Temporary initial value private int mBufferPointer = 0; - private double mGain; - private double[] mCoefficients; + private float mGain; + private float[] mCoefficients; private int[][] mIndexMap; private int mCenterCoefficient; private int mCenterCoefficientMapIndex; @@ -50,7 +50,7 @@ public class FloatHalfBandFilter implements RealSampleListener * @param filter - filter coefficients * @param gain - gain multiplier. Use 1.0 for unity/no gain */ - public FloatHalfBandFilter( Filters filter, double gain ) + public FloatHalfBandFilter( Filters filter, float gain ) { mCoefficients = filter.getCoefficients(); mBuffer = new ArrayList(); @@ -97,7 +97,7 @@ public class FloatHalfBandFilter implements RealSampleListener { //Convolution - multiply filter coefficients by the circular buffer //samples to calculate a new filtered value - double accumulator = 0; + float accumulator = 0; //Start with the center tap value accumulator += mCoefficients[ mCenterCoefficient ] * @@ -112,7 +112,7 @@ public class FloatHalfBandFilter implements RealSampleListener mBuffer.get( mIndexMap[ mBufferPointer ][ x + 1 ] ) ); } - //We're almost finished ... apply gain, cast the doubles to shorts and + //We're almost finished ... apply gain, cast the floats to shorts and //send it on it's merry way if( mListener != null ) { diff --git a/src/dsp/filter/FloatHalfBandNoDecimateFilter.java b/src/dsp/filter/FloatHalfBandNoDecimateFilter.java index 57937df1..6f38db1f 100644 --- a/src/dsp/filter/FloatHalfBandNoDecimateFilter.java +++ b/src/dsp/filter/FloatHalfBandNoDecimateFilter.java @@ -27,8 +27,8 @@ public class FloatHalfBandNoDecimateFilter implements RealSampleListener private ArrayList mBuffer; private int mBufferSize = 1; //Temporary initial value private int mBufferPointer = 0; - private double mGain; - private double[] mCoefficients; + private float mGain; + private float[] mCoefficients; private int[][] mIndexMap; private int mCenterCoefficient; private int mCenterCoefficientMapIndex; @@ -48,7 +48,7 @@ public class FloatHalfBandNoDecimateFilter implements RealSampleListener * @param filter - filter coefficients * @param gain - gain multiplier. Use 1.0 for unity/no gain */ - public FloatHalfBandNoDecimateFilter( Filters filter, double gain ) + public FloatHalfBandNoDecimateFilter( Filters filter, float gain ) { mCoefficients = filter.getCoefficients(); mBuffer = new ArrayList(); @@ -88,7 +88,7 @@ public class FloatHalfBandNoDecimateFilter implements RealSampleListener //Convolution - multiply filter coefficients by the circular buffer //samples to calculate a new filtered value - double accumulator = 0; + float accumulator = 0; //Start with the center tap value accumulator += mCoefficients[ mCenterCoefficient ] * @@ -103,7 +103,7 @@ public class FloatHalfBandNoDecimateFilter implements RealSampleListener mBuffer.get( mIndexMap[ mBufferPointer ][ x + 1 ] ) ); } - //We're almost finished ... apply gain, cast the doubles to floats and + //We're almost finished ... apply gain, cast the floats to floats and //send it on it's merry way if( mListener != null ) { diff --git a/src/dsp/filter/ComplexPrimeCICDecimate.java b/src/dsp/filter/cic/ComplexPrimeCICDecimate.java similarity index 75% rename from src/dsp/filter/ComplexPrimeCICDecimate.java rename to src/dsp/filter/cic/ComplexPrimeCICDecimate.java index 8bdd6bd5..33ee6149 100644 --- a/src/dsp/filter/ComplexPrimeCICDecimate.java +++ b/src/dsp/filter/cic/ComplexPrimeCICDecimate.java @@ -1,22 +1,38 @@ -package dsp.filter; +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package dsp.filter.cic; import java.util.ArrayList; import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.ComplexBuffer; import sample.complex.ComplexSampleListener; +import sample.complex.ComplexToComplexBufferAssembler; import sample.decimator.ComplexDecimator; +import dsp.filter.FilterFactory; +import dsp.filter.Filters; import dsp.filter.Window.WindowType; +import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; +import dsp.filter.halfband.complex.HalfBandFilter_CB_CB; public class ComplexPrimeCICDecimate { - private final static Logger mLog = - LoggerFactory.getLogger( ComplexPrimeCICDecimate.class ); - public static int[] PRIMES = { 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53, 59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151, 157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251, @@ -31,9 +47,7 @@ public class ComplexPrimeCICDecimate private DecimatingStage mFirstDecimatingStage; private Output mOutput; - - private Listener mListener; - + /** * Non-Recursive Prime-Factor CIC Filter with float sample array inputs and * decimated, single paired i/q float sample output. @@ -55,6 +69,7 @@ public class ComplexPrimeCICDecimate { assert( decimation <= 700 ); + List stageSizes = getPrimeFactors( decimation ); for( int x = 0; x < stageSizes.size(); x++ ) @@ -89,12 +104,17 @@ public class ComplexPrimeCICDecimate } mDecimatingStages.clear(); + mDecimatingStages = null; + mFirstDecimatingStage = null; + + mOutput.dispose(); + mOutput = null; } /** * Adds a listener to receive the output of this CIC decimation filter */ - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mOutput.setListener( listener ); } @@ -102,9 +122,9 @@ public class ComplexPrimeCICDecimate /** * Removes listener from output of this CIC decimation filter */ - public void removeListener( Listener listener ) + public void removeListener() { - mOutput.removeListener( listener ); + mOutput.removeListener(); } /** @@ -143,14 +163,17 @@ public class ComplexPrimeCICDecimate /** * Primary input method for receiving sample arrays composed as I,Q,I,Q, etc. */ - public void receive( float[] samples ) + public void receive( ComplexBuffer buffer ) { - for( int x = 0; x < samples.length; x += 2 ) - { - mFirstDecimatingStage.receive( samples[ x ], samples[ x + 1 ] ); - } - - samples = null; + if( mFirstDecimatingStage != null ) + { + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x += 2 ) + { + mFirstDecimatingStage.receive( samples[ x ], samples[ x + 1 ] ); + } + } } /** @@ -326,22 +349,33 @@ public class ComplexPrimeCICDecimate */ public class Output implements ComplexSampleListener { - private ComplexFilter mCleanupFilter; - private ComplexHalfBandNoDecimateFilter mHalfBandFilter = - new ComplexHalfBandNoDecimateFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 0.4 ); + /* Decimated output buffers will contain 1024 samples */ + private ComplexToComplexBufferAssembler mAssembler = + new ComplexToComplexBufferAssembler( 2048 ); + + private ComplexFIRFilter_CB_CB mCleanupFilter; + private HalfBandFilter_CB_CB mHalfBandFilter = new HalfBandFilter_CB_CB( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 0.4f, false ); public Output( int outputSampleRate, int passFrequency, int attenuation, WindowType windowType ) { - mCleanupFilter = new ComplexFIRFilter( FilterFactory + mCleanupFilter = new ComplexFIRFilter_CB_CB( FilterFactory .getCICCleanupFilter( outputSampleRate, passFrequency, attenuation, - windowType ), 0.4d ); + windowType ), 0.4f ); + mAssembler.setListener( mCleanupFilter ); mCleanupFilter.setListener( mHalfBandFilter ); } + + public void dispose() + { + mAssembler.dispose(); + mCleanupFilter.dispose(); + mHalfBandFilter.dispose(); + } /** * Receiver method for the output adapter to receive a filtered, @@ -349,15 +383,15 @@ public class ComplexPrimeCICDecimate * and output the sample values as a complex sample. */ @Override - public void receive( float left, float right ) + public void receive( float inphase, float quadrature ) { - mCleanupFilter.receive( new ComplexSample( left, right ) ); + mAssembler.receive( inphase, quadrature ); } /** * Adds a listener to receive output samples */ - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mHalfBandFilter.setListener( listener ); } @@ -365,14 +399,9 @@ public class ComplexPrimeCICDecimate /** * Removes the listener from receiving output samples */ - public void removeListener( Listener listener ) + public void removeListener() { - mHalfBandFilter.setListener( null ); + mHalfBandFilter.removeListener(); } - - public Listener getListener() - { - return mHalfBandFilter.getListener(); - } } } diff --git a/src/decode/lj1200/LJ1200ActivitySummary.java b/src/dsp/filter/dc/AveragingDCRemovalFilter_RB.java similarity index 58% rename from src/decode/lj1200/LJ1200ActivitySummary.java rename to src/dsp/filter/dc/AveragingDCRemovalFilter_RB.java index 2171d2c8..a2fc95f4 100644 --- a/src/decode/lj1200/LJ1200ActivitySummary.java +++ b/src/dsp/filter/dc/AveragingDCRemovalFilter_RB.java @@ -15,46 +15,52 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.lj1200; +package dsp.filter.dc; -import message.Message; import sample.Listener; -import alias.AliasList; -import controller.activity.ActivitySummaryProvider; +import sample.real.RealBuffer; -public class LJ1200ActivitySummary implements ActivitySummaryProvider, - Listener +public class AveragingDCRemovalFilter_RB extends DCRemovalFilter_RB + implements Listener { - private AliasList mAliasList; + private float mAverage; + private float mRatio; - public LJ1200ActivitySummary( AliasList list ) + public AveragingDCRemovalFilter_RB( float ratio ) { - mAliasList = list; + mRatio = ratio; } public void dispose() { + mListener = null; } + public void reset() + { + mAverage = 0.0f; + } + + public float filter( float sample ) + { + mAverage += mRatio * ( sample - mAverage ); + + return sample - mAverage; + } + @Override - public void receive( Message message ) - { - if( message instanceof LJ1200Message ) + public void receive( RealBuffer buffer ) + { + if( mListener != null ) { - LJ1200Message lj = ((LJ1200Message)message); + float[] samples = buffer.getSamples(); - //Do something here in the future + for( int x = 0; x < samples.length; x++ ) + { + samples[ x ] = filter( samples[ x ] ); + } + + mListener.receive( buffer ); } - } - - @Override - public String getSummary() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "=============================\n" ); - sb.append( "Decoder:\tLJ-1200I\n\n" ); - - return sb.toString(); - } + } } diff --git a/src/audio/SquelchListener.java b/src/dsp/filter/dc/DCRemovalFilter.java similarity index 68% rename from src/audio/SquelchListener.java rename to src/dsp/filter/dc/DCRemovalFilter.java index db55d6ab..ef2fadf2 100644 --- a/src/audio/SquelchListener.java +++ b/src/dsp/filter/dc/DCRemovalFilter.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,17 +15,29 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package audio; +package dsp.filter.dc; -public interface SquelchListener +public abstract class DCRemovalFilter { - /** - * Squelch Control: - */ - public void setSquelch( SquelchState state ); - - public enum SquelchState + private float mAverage; + private float mRatio; + + public DCRemovalFilter( float ratio ) { - UNSQUELCH, SQUELCH; + mRatio = ratio; } -} + + public abstract void dispose(); + + public void reset() + { + mAverage = 0.0f; + } + + public float filter( float sample ) + { + mAverage += mRatio * ( sample - mAverage ); + + return sample - mAverage; + } +} \ No newline at end of file diff --git a/src/dsp/filter/DCRemovalFilter.java b/src/dsp/filter/dc/DCRemovalFilterLyons.java similarity index 87% rename from src/dsp/filter/DCRemovalFilter.java rename to src/dsp/filter/dc/DCRemovalFilterLyons.java index 4806ccaa..d24aa5a9 100644 --- a/src/dsp/filter/DCRemovalFilter.java +++ b/src/dsp/filter/dc/DCRemovalFilterLyons.java @@ -15,22 +15,21 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package dsp.filter; +package dsp.filter.dc; -import sample.Listener; import sample.real.RealSampleListener; /** * Implements the real-time general DC removal algorithm described on page * 762, figure 13-62d, in Understanding Digital Signal Processing 3e, Lyons. */ -public class DCRemovalFilter implements RealSampleListener +public class DCRemovalFilterLyons implements RealSampleListener { - private double mAlpha; + private float mAlpha; private float mPrevious; private RealSampleListener mListener; - public DCRemovalFilter( double alpha ) + public DCRemovalFilterLyons( float alpha ) { mAlpha = alpha; } @@ -43,7 +42,7 @@ public class DCRemovalFilter implements RealSampleListener @Override public void receive( float sample ) { - send( (float)( sample + ( mPrevious * mAlpha ) - mPrevious ) ); + send( sample + ( mPrevious * mAlpha ) - mPrevious ); mPrevious = sample; } diff --git a/src/dsp/filter/dc/DCRemovalFilter_RB.java b/src/dsp/filter/dc/DCRemovalFilter_RB.java new file mode 100644 index 00000000..3b7edd66 --- /dev/null +++ b/src/dsp/filter/dc/DCRemovalFilter_RB.java @@ -0,0 +1,31 @@ +package dsp.filter.dc; + +import sample.Listener; +import sample.real.RealBuffer; + +public abstract class DCRemovalFilter_RB implements Listener +{ + protected Listener mListener; + + public DCRemovalFilter_RB() + { + + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } + + public void dispose() + { + mListener = null; + } + + public abstract void reset(); +} diff --git a/src/dsp/filter/DCRemovalFilter2.java b/src/dsp/filter/dc/DCRemovalFilter_R_R.java similarity index 70% rename from src/dsp/filter/DCRemovalFilter2.java rename to src/dsp/filter/dc/DCRemovalFilter_R_R.java index 36827935..de4fd627 100644 --- a/src/dsp/filter/DCRemovalFilter2.java +++ b/src/dsp/filter/dc/DCRemovalFilter_R_R.java @@ -15,43 +15,41 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package dsp.filter; +package dsp.filter.dc; -import sample.Listener; import sample.real.RealSampleListener; -public class DCRemovalFilter2 implements RealSampleListener +public class DCRemovalFilter_R_R extends DCRemovalFilter implements RealSampleListener { - private double mAverage; - private double mRatio; - private RealSampleListener mListener; + RealSampleListener mListener; - public DCRemovalFilter2( double ratio ) + public DCRemovalFilter_R_R( float ratio ) { - mRatio = ratio; + super( ratio ); + } + + @Override + public void dispose() + { + mListener = null; } @Override public void receive( float sample ) { - mAverage += mRatio * ( (double)sample - mAverage ); - - send( (float)( sample - mAverage ) ); - } - - /** - * Sends the filtered sample to all registered listeners - */ - private void send( float sample ) - { if( mListener != null ) { - mListener.receive( sample ); + mListener.receive( filter( sample ) ); } - } + } public void setListener( RealSampleListener listener ) { mListener = listener; } + + public void removeListener() + { + mListener = null; + } } diff --git a/src/dsp/filter/DCRemovalFilter3.java b/src/dsp/filter/dc/IIRSinglePoleDCRemovalFilter.java similarity index 86% rename from src/dsp/filter/DCRemovalFilter3.java rename to src/dsp/filter/dc/IIRSinglePoleDCRemovalFilter.java index 43d331c6..8edcffc2 100644 --- a/src/dsp/filter/DCRemovalFilter3.java +++ b/src/dsp/filter/dc/IIRSinglePoleDCRemovalFilter.java @@ -1,8 +1,8 @@ -package dsp.filter; +package dsp.filter.dc; import sample.real.RealSampleListener; -public class DCRemovalFilter3 implements RealSampleListener +public class IIRSinglePoleDCRemovalFilter implements RealSampleListener { private float mAlpha; private float mPreviousInput = 0.0f; @@ -19,7 +19,7 @@ public class DCRemovalFilter3 implements RealSampleListener * @param alpha 0.0 - 1.0 float - the closer alpha is to unity, the closer * the cutoff frequency is to DC. */ - public DCRemovalFilter3( float alpha ) + public IIRSinglePoleDCRemovalFilter( float alpha ) { mAlpha = alpha; } diff --git a/src/dsp/filter/dc/IIRSinglePoleDCRemovalFilter_RB.java b/src/dsp/filter/dc/IIRSinglePoleDCRemovalFilter_RB.java new file mode 100644 index 00000000..c4f4a371 --- /dev/null +++ b/src/dsp/filter/dc/IIRSinglePoleDCRemovalFilter_RB.java @@ -0,0 +1,58 @@ +package dsp.filter.dc; + +import sample.real.RealBuffer; + +public class IIRSinglePoleDCRemovalFilter_RB extends DCRemovalFilter_RB +{ + private float mAlpha; + private float mPreviousInput = 0.0f; + private float mPreviousOutput = 0.0f; + + /** + * IIR single-pole DC removal filter, as described by J M de Freitas in + * 29Jan2007 paper at: + * + * http://www.mathworks.com/matlabcentral/fileexchange/downloads/72134/download + * + * @param alpha 0.0 - 1.0 float - the closer alpha is to unity, the closer + * the cutoff frequency is to DC. + */ + public IIRSinglePoleDCRemovalFilter_RB( float alpha ) + { + mAlpha = alpha; + } + + @Override + public void receive( RealBuffer buffer ) + { + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x++ ) + { + samples[ x ] = filter( samples[ x ] ); + } + + if( mListener != null ) + { + mListener.receive( buffer ); + } + } + + public float filter( float sample ) + { + float currentOutput = ( sample - mPreviousInput ) + + ( mAlpha * mPreviousOutput ); + + mPreviousInput = sample; + mPreviousOutput = currentOutput; + + return currentOutput; + } + + @Override + public void reset() + { + mPreviousInput = 0.0f; + mPreviousOutput = 0.0f; + } +} diff --git a/src/dsp/filter/equalizer/CMAEqualizer.java b/src/dsp/filter/equalizer/CMAEqualizer.java index 882367f6..bbf79332 100644 --- a/src/dsp/filter/equalizer/CMAEqualizer.java +++ b/src/dsp/filter/equalizer/CMAEqualizer.java @@ -4,9 +4,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; -public class CMAEqualizer implements Listener +public class CMAEqualizer implements Listener { private final static Logger mLog = LoggerFactory.getLogger( CMAEqualizer.class ); @@ -14,14 +14,14 @@ public class CMAEqualizer implements Listener private float mModulus; private float mMu; - private ComplexSample mError = new ComplexSample( 0.0f, 0.0f ); + private Complex mError = new Complex( 0.0f, 0.0f ); - private ComplexSample[] mTaps = new ComplexSample[ TAP_COUNT ]; - private ComplexSample[] mBuffer = new ComplexSample[ TAP_COUNT ]; + private Complex[] mTaps = new Complex[ TAP_COUNT ]; + private Complex[] mBuffer = new Complex[ TAP_COUNT ]; private int mBufferPointer = 0; - private Listener mListener; + private Listener mListener; public CMAEqualizer( float modulus, float mu ) { @@ -31,21 +31,21 @@ public class CMAEqualizer implements Listener /* Set first tap to 1,0 and all other taps to 0,0 as a starting point. * This means that the output is solely determined by the last sample * entering the filter, initially. */ - mTaps[ 0 ] = new ComplexSample( 1.0f, 0.0f ); + mTaps[ 0 ] = new Complex( 1.0f, 0.0f ); for( int x = 1; x < TAP_COUNT; x++ ) { - mTaps[ x ] = new ComplexSample( 0.0f, 0.0f ); - mBuffer[ x ] = new ComplexSample( 0.0f, 0.0f ); + mTaps[ x ] = new Complex( 0.0f, 0.0f ); + mBuffer[ x ] = new Complex( 0.0f, 0.0f ); } } - private void updateTaps( ComplexSample sample ) + private void updateTaps( Complex sample ) { /* Calculate the error. The ideal sample magnitude is unity (1.0). * When the sample is not at unity, the error vector points in the * opposite direction with a magnitude equal to 1 - sample magnitude.*/ - mError = ComplexSample.multiply( sample, sample.norm() - mModulus ); + mError = Complex.multiply( sample, sample.norm() - mModulus ); /* Ensure we don't exceed unity */ mError.clip( 1.0f ); @@ -54,14 +54,14 @@ public class CMAEqualizer implements Listener { // tap -= mu * conj(sample) * error - mTaps[ x ] = ComplexSample.subtract( mTaps[ x ], - ComplexSample.multiply( ComplexSample.multiply( + mTaps[ x ] = Complex.subtract( mTaps[ x ], + Complex.multiply( Complex.multiply( mBuffer[ x ].conjugate(), mError ), mMu ) ); } } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mBuffer[ mBufferPointer ] = sample; @@ -69,7 +69,7 @@ public class CMAEqualizer implements Listener mBufferPointer = mBufferPointer % TAP_COUNT; - ComplexSample equalized = filter(); + Complex equalized = filter(); updateTaps( equalized ); @@ -81,13 +81,13 @@ public class CMAEqualizer implements Listener mLog.debug( "Tap 0:" + mTaps[ 0 ].toString() + " error:" + mError.toString() ); } - private ComplexSample filter() + private Complex filter() { - ComplexSample sum = new ComplexSample( 0.0f, 0.0f ); + Complex sum = new Complex( 0.0f, 0.0f ); for( int x = 0; x < TAP_COUNT; x++ ) { - sum = ComplexSample.add( sum, ComplexSample.multiply( + sum = Complex.add( sum, Complex.multiply( mBuffer[ ( x + mBufferPointer ) % TAP_COUNT ], mTaps[ TAP_COUNT - x - 1 ] ) ); } @@ -95,7 +95,7 @@ public class CMAEqualizer implements Listener return sum; } - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mListener = listener; } diff --git a/src/dsp/filter/fir/FIRFilter.java b/src/dsp/filter/fir/FIRFilter.java new file mode 100644 index 00000000..f0f19d06 --- /dev/null +++ b/src/dsp/filter/fir/FIRFilter.java @@ -0,0 +1,6 @@ +package dsp.filter.fir; + +public abstract class FIRFilter +{ + public abstract void dispose(); +} diff --git a/src/dsp/filter/fir/complex/ComplexFIRFilter.java b/src/dsp/filter/fir/complex/ComplexFIRFilter.java new file mode 100644 index 00000000..744c27b9 --- /dev/null +++ b/src/dsp/filter/fir/complex/ComplexFIRFilter.java @@ -0,0 +1,54 @@ +package dsp.filter.fir.complex; + +import sample.complex.Complex; +import dsp.filter.fir.FIRFilter; +import dsp.filter.fir.real.RealFIRFilter; + +public class ComplexFIRFilter extends FIRFilter +{ + private RealFIRFilter mIFilter; + private RealFIRFilter mQFilter; + + /** + * Complex FIR Filter for processing complex sample pairs. Wraps two real + * FIR filters for processing each of the inphase and quadrature samples. + * + * @param coefficients - filter taps + * @param gain - gain to apply to filtered outputs - use 1.0f for no gain + */ + public ComplexFIRFilter( float[] coefficients, float gain ) + { + mIFilter = new RealFIRFilter( coefficients, gain ); + mQFilter = new RealFIRFilter( coefficients, gain ); + } + + public float[] getCoefficients() + { + return mIFilter.getCoefficients(); + } + + public float filterInphase( float sample ) + { + return mIFilter.filter( sample ); + } + + public float filterQuadrature( float sample ) + { + return mQFilter.filter( sample ); + } + + public Complex filter( Complex sample ) + { + float i = filterInphase( sample.inphase() ); + float q = filterQuadrature( sample.quadrature() ); + + return new Complex( i, q ); + } + + @Override + public void dispose() + { + mIFilter.dispose(); + mQFilter.dispose(); + } +} diff --git a/src/dsp/filter/fir/complex/ComplexFIRFilter_CB_CB.java b/src/dsp/filter/fir/complex/ComplexFIRFilter_CB_CB.java new file mode 100644 index 00000000..259d631a --- /dev/null +++ b/src/dsp/filter/fir/complex/ComplexFIRFilter_CB_CB.java @@ -0,0 +1,54 @@ +package dsp.filter.fir.complex; + +import sample.Listener; +import sample.complex.ComplexBuffer; + +public class ComplexFIRFilter_CB_CB extends ComplexFIRFilter + implements Listener +{ + private Listener mListener; + + /** + * Complex FIR Filter for processing complex buffers. Wraps two real FIR + * filters for processing each of the inphase and quadrature samples. + * + * @param coefficients - odd length symmetric filter taps + * @param gain - gain value to apply to each of the filtered samples + */ + public ComplexFIRFilter_CB_CB( float[] coefficients, float gain ) + { + super( coefficients, gain ); + } + + public void dispose() + { + mListener = null; + } + + @Override + public void receive( ComplexBuffer buffer ) + { + if( mListener != null ) + { + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x += 2 ) + { + samples[ x ] = filterInphase( samples[ x ] ); + samples[ x + 1 ] = filterQuadrature( samples[ x + 1 ] ); + } + + mListener.receive( buffer ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } +} diff --git a/src/dsp/filter/fir/real/RealFIRFilter.java b/src/dsp/filter/fir/real/RealFIRFilter.java new file mode 100644 index 00000000..fa3e28e7 --- /dev/null +++ b/src/dsp/filter/fir/real/RealFIRFilter.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package dsp.filter.fir.real; + +import dsp.filter.fir.FIRFilter; + +public class RealFIRFilter extends FIRFilter +{ + private float[] mBuffer; + private int mBufferSize = 1; + private int mBufferPointer = 0; + private int[][] mIndexMap; + + private float[] mCoefficients; + private float mGain; + + /** + * Float sample FIR filter base class. + * + * @param coefficients - filter coefficients + * @param gain value to apply to the filtered output - use 1.0f for no gain + */ + public RealFIRFilter( float[] coefficients, float gain ) + { + mCoefficients = coefficients; + mGain = gain; + + mBufferSize = mCoefficients.length; + + mBuffer = new float[ mBufferSize ]; + mBufferPointer = mBufferSize - 1; + + generateIndexMap( mBufferSize ); + } + + public float[] getCoefficients() + { + return mCoefficients; + } + + @Override + public void dispose() + { + mCoefficients = null; + mIndexMap = null; + mBuffer = null; + } + + public float filter( float sample ) + { + mBuffer[ mBufferPointer ] = sample; + + float accumulator = 0.0f; + + for( int x = 0; x < mBufferSize; x++ ) + { + accumulator += mCoefficients[ x ] * + mBuffer[ mIndexMap[ mBufferPointer][ x ] ]; + } + + mBufferPointer--; + + if( mBufferPointer < 0 ) + { + mBufferPointer += mBufferSize; + } + + /* Apply gain and return the filtered value */ + return accumulator * mGain; + } + + /** + * Generates a circular buffer index map to support lookup of the translated + * index based on the current buffer pointer and the desired sample index. + */ + private void generateIndexMap( int size ) + { + mIndexMap = new int[ size ][ size ]; + + for( int x = 0; x < size; x++ ) + { + for( int y = 0; y < size; y++ ) + { + int z = x + y; + + mIndexMap[ x ][ y ] = z < size ? z : z - size; + } + } + } +} \ No newline at end of file diff --git a/src/dsp/filter/fir/real/RealFIRFilter_RB_RB.java b/src/dsp/filter/fir/real/RealFIRFilter_RB_RB.java new file mode 100644 index 00000000..7f9fc957 --- /dev/null +++ b/src/dsp/filter/fir/real/RealFIRFilter_RB_RB.java @@ -0,0 +1,67 @@ +package dsp.filter.fir.real; + +import sample.Listener; +import sample.real.RealBuffer; + +public class RealFIRFilter_RB_RB extends RealFIRFilter + implements Listener +{ + private Listener mListener; + + /** + * Real Buffer Float FIR filter with streaming float buffer provider and + * listener interfaces. + * + * @param coefficients + * @param gain + */ + public RealFIRFilter_RB_RB( float[] coefficients, float gain ) + { + super( coefficients, gain ); + } + + @Override + public void dispose() + { + super.dispose(); + + mListener = null; + } + + @Override + public void receive( RealBuffer buffer ) + { + if( mListener != null ) + { + mListener.receive( filter( buffer ) ); + } + } + + /** + * Filters the buffer samples + * + * @param buffer + * @return + */ + public RealBuffer filter( RealBuffer buffer ) + { + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x++ ) + { + samples[ x ] = filter( samples[ x ] ); + } + + return buffer; + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } +} diff --git a/src/dsp/filter/fir/real/RealFIRFilter_R_R.java b/src/dsp/filter/fir/real/RealFIRFilter_R_R.java new file mode 100644 index 00000000..b00bf317 --- /dev/null +++ b/src/dsp/filter/fir/real/RealFIRFilter_R_R.java @@ -0,0 +1,56 @@ +package dsp.filter.fir.real; + +import sample.real.RealSampleListener; + +public class RealFIRFilter_R_R extends RealFIRFilter implements RealSampleListener +{ + private RealSampleListener mListener; + + /** + * Float FIR filter with streaming float provider and listener interfaces. + * + * @param coefficients + * @param gain + */ + public RealFIRFilter_R_R( float[] coefficients, float gain ) + { + super( coefficients, gain ); + } + + @Override + public void dispose() + { + super.dispose(); + + mListener = null; + } + + @Override + public void receive( float sample ) + { + if( mListener != null ) + { + mListener.receive( filter( sample ) ); + } + } + + public float[] filter( float[] samples ) + { + for( int x = 0; x < samples.length; x++ ) + { + samples[ x ] = filter( samples[ x ] ); + } + + return samples; + } + + public void setListener( RealSampleListener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } +} diff --git a/src/dsp/filter/halfband/complex/HalfBandFilter_CB_CB.java b/src/dsp/filter/halfband/complex/HalfBandFilter_CB_CB.java new file mode 100644 index 00000000..a2707208 --- /dev/null +++ b/src/dsp/filter/halfband/complex/HalfBandFilter_CB_CB.java @@ -0,0 +1,117 @@ +package dsp.filter.halfband.complex; + +import sample.Listener; +import sample.complex.ComplexBuffer; +import dsp.filter.halfband.real.HalfBandFilter; + +public class HalfBandFilter_CB_CB implements Listener +{ + private HalfBandFilter mIFilter; + private HalfBandFilter mQFilter; + + private boolean mDecimate; + private boolean mDecimateFlag; + + private Listener mListener; + + /** + * + * @param coefficients + * @param gain + * @param decimate + */ + public HalfBandFilter_CB_CB( float[] coefficients, float gain, boolean decimate ) + { + mIFilter = new HalfBandFilter( coefficients, gain ); + mQFilter = new HalfBandFilter( coefficients, gain ); + + mDecimate = decimate; + } + + public void dispose() + { + mIFilter.dispose(); + mQFilter.dispose(); + + mListener = null; + } + + @Override + public void receive( ComplexBuffer buffer ) + { + if( mListener != null ) + { + if( mDecimate ) + { + float[] samples = buffer.getSamples(); + + int half = samples.length / 2; + + float[] decimated; + + if( half % 2 == 0 || mDecimateFlag ) + { + decimated = new float[ half ]; + } + else + { + /* If inbound buffer size is odd-length, then we have to + * adjust when the first operation is non-decimation, since + * that will produce an outbound buffer 1 sample larger */ + decimated = new float[ half + 1 ]; + } + + int decimatedPointer = 0; + + for( int x = 0; x < samples.length; x += 2 ) + { + /* Insert the sample but don't filter */ + if( mDecimateFlag ) + { + mIFilter.insert( samples[ x ] ); + mQFilter.insert( samples[ x + 1 ] ); + } + else + { + decimated[ decimatedPointer++ ] = mIFilter.filter( samples[ x ] ); + decimated[ decimatedPointer++ ] = mQFilter.filter( samples[ x + 1 ] ); + } + + /* Toggle the decimation flag for every sample */ + mDecimateFlag = !mDecimateFlag; + } + + if( mListener != null ) + { + mListener.receive( new ComplexBuffer( decimated ) ); + } + } + else + { + /* Non Decimate - filter each of the values and return the buffer */ + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x += 2 ) + { + samples[ x ] = mIFilter.filter( samples[ x ] ); + samples[ x + 1 ] = mQFilter.filter( samples[ x + 1 ] ); + } + + if( mListener != null ) + { + mListener.receive( buffer ); + } + } + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } +} diff --git a/src/dsp/filter/halfband/real/HalfBandFilter.java b/src/dsp/filter/halfband/real/HalfBandFilter.java new file mode 100644 index 00000000..7b84616f --- /dev/null +++ b/src/dsp/filter/halfband/real/HalfBandFilter.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package dsp.filter.halfband.real; + +public class HalfBandFilter +{ + private float[] mBuffer; + private int mBufferSize = 1; //Temporary initial value + private int mBufferPointer = 0; + + private float mGain; + private float[] mCoefficients; + private int[][] mIndexMap; + + private int mCenterCoefficient; + private int mCenterCoefficientMapIndex; + + /** + * Half-Band filter for processing real float samples. + * + * Takes advantage of the 0-valued FIR half-band coefficents inherent + * in the half-band filter and does not calculate those coefficients. + * + * This reduces the workload to (tap-size - 1) / 4 + 1 calculations per sample. + * + * @param filter - filter coefficients + * @param gain - gain multiplier. Use 1.0 for unity/no gain + */ + public HalfBandFilter( float[] coefficients, float gain ) + { + mCoefficients = coefficients; + mGain = gain; + + mBufferSize = mCoefficients.length; + mBuffer = new float[ mBufferSize ]; + + generateIndexMap( mCoefficients.length ); + } + + public void dispose() + { + } + + /** + * Inserts the sample into the buffer without calculating a filtered value + */ + public void insert( float sample ) + { + mBuffer[ mBufferPointer ] = sample; + + mBufferPointer++; + + mBufferPointer = mBufferPointer % mBufferSize; + } + + /** + * Calculate the filtered value by applying the coefficients against + * the complex samples in mBuffer + */ + public float filter( float sample ) + { + insert( sample ); + + float accumulator = 0; + + //Start with the center tap value + accumulator += mCoefficients[ mCenterCoefficient ] * + mBuffer[ mIndexMap[ mBufferPointer ][ mCenterCoefficientMapIndex ] ]; + + //For the remaining coefficients, add the symmetric samples, oldest and newest + //first, then multiply by the single coefficient + for( int x = 0; x < mCenterCoefficientMapIndex; x += 2 ) + { + accumulator += mCoefficients[ x ] * + ( mBuffer[ mIndexMap[ mBufferPointer ][ x ] ] + + mBuffer[ mIndexMap[ mBufferPointer ][ x + 1 ] ] ); + } + + return accumulator * mGain; + } + + /** + * Creates an n X (n + 1 / 2) index map enabling quick access to the + * circular buffer samples. + * + * As the buffer shifts right with each subsequent sample, we have to move + * the index pointers with it, for efficient access of the samples. + * + * The first array index value in the index map corresponds to the current + * buffer pointer location. + * + * The second array index value points to the samples that should be + * multiplied by the coefficients as follows: + * + * 0 = center tap sample, to be multiplied by center coefficient + * + * 0 = sample( 1 ) + * 1 = sample( size - 1 ) + * + * Indexes 0 and 1 will be multiplied by coefficient( 0 ). + * + * Subsequent indexes 3, 4, etc, point to the oldest and newest samples that + * correspond to the matching ( 3 ) coefficient index. + * + * @param odd-sized number of filter taps (ie coefficients) and buffer + */ + private void generateIndexMap( int size ) + { + //Ensure we have an odd size + assert( size % 2 == 1 ); + + int mapWidth = ( ( size + 1 ) / 2 ) + 1; + + //Set center tap index for coefficients array + mCenterCoefficient = ( size - 1 ) / 2; + mCenterCoefficientMapIndex = mCenterCoefficient + 1; + + mIndexMap = new int[ size ][ mapWidth ]; + + //Setup the first row, buffer pointer index 0, as a starting point + for( int x = 0; x < mapWidth - 2; x += 2 ) + { + mIndexMap[ 0 ][ x ] = x; + mIndexMap[ 0 ][ x + 1 ] = size - 1 - x; + } + + //Place center tap index in last element + mIndexMap[ 0 ][ mCenterCoefficientMapIndex ] = mCenterCoefficient; + + //For each subsequent row, increment the previous row's value by 1, + //subtracting size as needed, to keep the values between 0 and size - 1 + for( int x = 1; x < size; x++ ) + { + for( int y = 0; y < mapWidth; y++ ) + { + mIndexMap[ x ][ y ] = mIndexMap[ x - 1 ][ y ] + 1; + + if( mIndexMap[ x ][ y ] >= size ) + { + mIndexMap[ x ][ y ] -= size; + } + } + } + } +} diff --git a/src/dsp/filter/halfband/real/HalfBandFilter_RB_RB.java b/src/dsp/filter/halfband/real/HalfBandFilter_RB_RB.java new file mode 100644 index 00000000..90414814 --- /dev/null +++ b/src/dsp/filter/halfband/real/HalfBandFilter_RB_RB.java @@ -0,0 +1,101 @@ +package dsp.filter.halfband.real; + +import sample.Listener; +import sample.real.RealBuffer; + +public class HalfBandFilter_RB_RB extends HalfBandFilter implements Listener +{ + private boolean mDecimate; + private boolean mDecimateFlag; + private Listener mListener; + + /** + * + * @param coefficients + * @param gain + * @param decimate + */ + public HalfBandFilter_RB_RB( float[] coefficients, float gain, boolean decimate ) + { + super( coefficients, gain ); + + mDecimate = decimate; + } + + public void dispose() + { + super.dispose(); + + mListener = null; + } + + @Override + public void receive( RealBuffer buffer ) + { + if( mListener != null ) + { + if( mDecimate ) + { + float[] samples = buffer.getSamples(); + + int half = samples.length / 2; + + float[] decimated; + + if( half % 2 == 0 || mDecimateFlag ) + { + decimated = new float[ half ]; + } + else + { + /* If inbound buffer size is odd-length, then we have to + * adjust when the first operation is non-decimation, since + * that will produce an outbound buffer 1 sample larger */ + decimated = new float[ half + 1 ]; + } + + int decimatedPointer = 0; + + for( float sample: samples ) + { + /* Insert the sample but don't filter */ + if( mDecimateFlag ) + { + insert( sample ); + } + else + { + decimated[ decimatedPointer++ ] = filter( sample ); + } + + /* Toggle the decimation flag for every sample */ + mDecimateFlag = !mDecimateFlag; + } + + mListener.receive( new RealBuffer( decimated ) ); + } + else + { + /* Non Decimate - filter each of the values and return the buffer */ + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x++ ) + { + samples[ x ] = filter( samples[ x ] ); + } + + mListener.receive( buffer ); + } + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } +} diff --git a/src/dsp/filter/interpolator/QPSKInterpolator.java b/src/dsp/filter/interpolator/QPSKInterpolator.java deleted file mode 100644 index 6c096158..00000000 --- a/src/dsp/filter/interpolator/QPSKInterpolator.java +++ /dev/null @@ -1,61 +0,0 @@ -package dsp.filter.interpolator; - -import java.util.Arrays; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.complex.ComplexSample; - -public class QPSKInterpolator extends Interpolator -{ - private final static Logger mLog = LoggerFactory.getLogger( QPSKInterpolator.class ); - - private float mGain; - - public QPSKInterpolator( float gain ) - { - mGain = gain; - } - /** - * Calculates an interpolated value from the 8 samples beginning at the - * offset index. Uses the mu value to determine which of 128 filter kernels - * to use in order to closely approximate the ideal frequency response for - * the value of mu, ranging from 0.0 to 1.0, indicating the interpolated - * position in the sample set ranging from offset to offset + 7. - * - * @param samples - sample array of length at least offset + 7 - * - * @param mu - interpolated sample position between 0 and 1.0 - * - * @return - interpolated sample value - */ - public ComplexSample filter( ComplexSample[] samples, int offset, float mu ) - { - /* Ensure we have enough samples in the array */ - assert( samples.length >= offset + 7 ); - - /* Identify the filter bank that corresponds to mu */ - int index = (int)( NSTEPS * mu ); - - float realAccumulator = ( TAPS[ index ][ 7 ] * samples[ offset ].real() ); - realAccumulator += ( TAPS[ index ][ 6 ] * samples[ offset + 1 ].real() ); - realAccumulator += ( TAPS[ index ][ 5 ] * samples[ offset + 2 ].real() ); - realAccumulator += ( TAPS[ index ][ 4 ] * samples[ offset + 3 ].real() ); - realAccumulator += ( TAPS[ index ][ 3 ] * samples[ offset + 4 ].real() ); - realAccumulator += ( TAPS[ index ][ 2 ] * samples[ offset + 5 ].real() ); - realAccumulator += ( TAPS[ index ][ 1 ] * samples[ offset + 6 ].real() ); - realAccumulator += ( TAPS[ index ][ 0 ] * samples[ offset + 7 ].real() ); - - float imaginaryAccumulator = ( TAPS[ index ][ 7 ] * samples[ offset ].imaginary() ); - imaginaryAccumulator += ( TAPS[ index ][ 6 ] * samples[ offset + 1 ].imaginary() ); - imaginaryAccumulator += ( TAPS[ index ][ 5 ] * samples[ offset + 2 ].imaginary() ); - imaginaryAccumulator += ( TAPS[ index ][ 4 ] * samples[ offset + 3 ].imaginary() ); - imaginaryAccumulator += ( TAPS[ index ][ 3 ] * samples[ offset + 4 ].imaginary() ); - imaginaryAccumulator += ( TAPS[ index ][ 2 ] * samples[ offset + 5 ].imaginary() ); - imaginaryAccumulator += ( TAPS[ index ][ 1 ] * samples[ offset + 6 ].imaginary() ); - imaginaryAccumulator += ( TAPS[ index ][ 0 ] * samples[ offset + 7 ].imaginary() ); - - return new ComplexSample( realAccumulator * mGain, imaginaryAccumulator * mGain ); - } -} diff --git a/src/dsp/filter/interpolator/RealInterpolator.java b/src/dsp/filter/interpolator/RealInterpolator.java new file mode 100644 index 00000000..f840a3ed --- /dev/null +++ b/src/dsp/filter/interpolator/RealInterpolator.java @@ -0,0 +1,58 @@ +package dsp.filter.interpolator; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.complex.Complex; + +public class RealInterpolator extends Interpolator +{ + private final static Logger mLog = LoggerFactory.getLogger( RealInterpolator.class ); + + private float mGain; + + public RealInterpolator( float gain ) + { + mGain = gain; + } + /** + * Calculates an interpolated value from the 8 samples beginning at the + * offset index. Uses the mu value to determine which of 128 filter kernels + * to use in order to closely approximate the ideal frequency response for + * the value of mu, ranging from 0.0 to 1.0, indicating the interpolated + * position in the sample set ranging from offset to offset + 7. + * + * @param samples - sample array of length at least offset + 7 + * + * @param mu - interpolated sample position between 0 and 1.0 + * + * @return - interpolated sample value + */ + public float filter( float[] samples, int offset, float mu ) + { + /* Ensure we have enough samples in the array */ + assert( samples.length >= offset + 7 ); + + /* Identify the filter bank that corresponds to mu */ + int index = (int)( NSTEPS * mu ); + + float accumulator = ( TAPS[ index ][ 7 ] * samples[ offset ] ); + accumulator += ( TAPS[ index ][ 6 ] * samples[ offset + 1 ] ); + accumulator += ( TAPS[ index ][ 5 ] * samples[ offset + 2 ] ); + accumulator += ( TAPS[ index ][ 4 ] * samples[ offset + 3 ] ); + accumulator += ( TAPS[ index ][ 3 ] * samples[ offset + 4 ] ); + accumulator += ( TAPS[ index ][ 2 ] * samples[ offset + 5 ] ); + accumulator += ( TAPS[ index ][ 1 ] * samples[ offset + 6 ] ); + accumulator += ( TAPS[ index ][ 0 ] * samples[ offset + 7 ] ); + + return accumulator * mGain; + } + + public Complex filter( float[] iSamples, float[] qSamples, int offset, float mu ) + { + float i = filter( iSamples, offset, mu ); + float q = filter( qSamples, offset, mu ); + + return new Complex( i, q ); + } +} diff --git a/src/dsp/fm/FMDemodulator.java b/src/dsp/fm/FMDemodulator.java new file mode 100644 index 00000000..238915bb --- /dev/null +++ b/src/dsp/fm/FMDemodulator.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package dsp.fm; + +public abstract class FMDemodulator +{ + private float mPreviousI = 0.0f; + private float mPreviousQ = 0.0f; + protected float mGain; + + public FMDemodulator( float gain ) + { + mGain = gain; + } + + public float demodulate( float currentI, float currentQ ) + { + /** + * Multiply the current sample against the complex conjugate of the + * previous sample to derive the phase delta between the two samples + * + * Negating the previous sample quadrature produces the conjugate + */ + double inphase = ( currentI * mPreviousI ) - ( currentQ * -mPreviousQ ); + double quadrature = ( currentQ * mPreviousI ) + ( currentI * -mPreviousQ ); + + double angle = 0.0f; + + //Check for divide by zero + if( inphase != 0 ) + { + /** + * Use the arc-tangent of quadrature divided by inphase to + * get the phase angle (+/-) which was directly manipulated by the + * original message waveform during the modulation. This value now + * serves as the instantaneous amplitude of the demodulated signal + */ + double denominator = 1.0d / inphase; + angle = Math.atan( (double)quadrature * denominator ); + } + + /** + * Store the current sample to use during the next iteration + */ + mPreviousI = currentI; + mPreviousQ = currentQ; + + return (float)( angle * mGain ); + } + + public abstract void dispose(); + + public void reset() + { + mPreviousI = 0.0f; + mPreviousQ = 0.0f; + } + + public void setGain( float gain ) + { + mGain = gain; + } +} \ No newline at end of file diff --git a/src/dsp/fm/FMDemodulator_CB.java b/src/dsp/fm/FMDemodulator_CB.java new file mode 100644 index 00000000..30ae35b0 --- /dev/null +++ b/src/dsp/fm/FMDemodulator_CB.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package dsp.fm; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import record.wave.ComplexWaveRecorder; +import sample.Listener; +import sample.complex.ComplexBuffer; +import sample.real.RateCounter_RB; +import sample.real.RealBuffer; + +public class FMDemodulator_CB extends FMDemodulator implements Listener +{ + private final static Logger mLog = + LoggerFactory.getLogger( FMDemodulator_CB.class ); + + private Listener mListener; + + private RateCounter_RB mRateCounter = new RateCounter_RB( "FM DEMOD OUTPUT" ); + + public FMDemodulator_CB( float gain ) + { + super( gain ); + } + + @Override + public void receive( ComplexBuffer buffer ) + { + if( mListener != null ) + { + float[] samples = buffer.getSamples(); + + int half = samples.length / 2; + + float[] demodulated = new float[ half ]; + + for( int x = 0; x < half; x++ ) + { + int index = x * 2; + + demodulated[ x ] = demodulate( samples[ index ], + samples[ index + 1 ] ); + } + + mListener.receive( new RealBuffer( demodulated ) ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } + + @Override + public void dispose() + { + mListener = null; + } +} diff --git a/src/dsp/nbfm/FMDiscriminator.java b/src/dsp/fm/FMDiscriminator.java similarity index 90% rename from src/dsp/nbfm/FMDiscriminator.java rename to src/dsp/fm/FMDiscriminator.java index 2fc6d8ce..3e32d2d2 100644 --- a/src/dsp/nbfm/FMDiscriminator.java +++ b/src/dsp/fm/FMDiscriminator.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,16 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package dsp.nbfm; +package dsp.fm; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import sample.real.RealSampleListener; -public class FMDiscriminator implements Listener +@Deprecated +public class FMDiscriminator implements Listener { private RealSampleListener mListener; - private ComplexSample mPreviousSample = new ComplexSample( 0.0f, 0.0f ); + private Complex mPreviousSample = new Complex( 0.0f, 0.0f ); private double mGain; /** @@ -51,7 +52,7 @@ public class FMDiscriminator implements Listener } @Override - public void receive( ComplexSample currentSample ) + public void receive( Complex currentSample ) { /** * Multiply the current sample against the complex conjugate of the diff --git a/src/dsp/nbfm/FilteringNBFMDemodulator.java b/src/dsp/fm/FilteringNBFMDemodulator.java similarity index 87% rename from src/dsp/nbfm/FilteringNBFMDemodulator.java rename to src/dsp/fm/FilteringNBFMDemodulator.java index 5644c1be..391d4938 100644 --- a/src/dsp/nbfm/FilteringNBFMDemodulator.java +++ b/src/dsp/fm/FilteringNBFMDemodulator.java @@ -15,34 +15,35 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package dsp.nbfm; +package dsp.fm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import sample.real.RealSampleBroadcaster; import sample.real.RealSampleListener; -import dsp.filter.ComplexFIRFilter; +import dsp.filter.ComplexFIRFilterOld; import dsp.filter.FilterFactory; import dsp.filter.FloatFIRFilter; import dsp.filter.Window.WindowType; -public class FilteringNBFMDemodulator implements Listener +@Deprecated +public class FilteringNBFMDemodulator implements Listener { private final static Logger mLog = LoggerFactory.getLogger( FilteringNBFMDemodulator.class ); private RealSampleBroadcaster mBroadcaster = new RealSampleBroadcaster(); - private ComplexFIRFilter mIQFilter; + private ComplexFIRFilterOld mIQFilter; private FloatFIRFilter mAudioFilter; private FMDiscriminator mDiscriminator; - public FilteringNBFMDemodulator( double[] iqFilter, double iqGain, - double[] audioFilter, double audioGain ) + public FilteringNBFMDemodulator( float[] iqFilter, float iqGain, + float[] audioFilter, float audioGain ) { - mIQFilter = new ComplexFIRFilter( iqFilter, iqGain ); + mIQFilter = new ComplexFIRFilterOld( iqFilter, iqGain ); mAudioFilter = new FloatFIRFilter( audioFilter, audioGain ); @@ -62,7 +63,7 @@ public class FilteringNBFMDemodulator implements Listener public FilteringNBFMDemodulator() { this( FilterFactory.getLowPass( 48000, 5000, 7000, 48, WindowType.HAMMING, true ), - 1.0002, + 1.0002f, FilterFactory.getLowPass( 48000, 3000, 6000, 48, WindowType.HAMMING, true ), 2 ); } @@ -72,7 +73,7 @@ public class FilteringNBFMDemodulator implements Listener * processed */ @Override - public void receive( ComplexSample quadratureSample ) + public void receive( Complex quadratureSample ) { mIQFilter.receive( quadratureSample ); } diff --git a/src/dsp/nbfm/NBFMDemodulator.java b/src/dsp/fm/NBFMDemodulator.java similarity index 85% rename from src/dsp/nbfm/NBFMDemodulator.java rename to src/dsp/fm/NBFMDemodulator.java index 94e78f20..ef0e33e9 100644 --- a/src/dsp/nbfm/NBFMDemodulator.java +++ b/src/dsp/fm/NBFMDemodulator.java @@ -15,20 +15,21 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package dsp.nbfm; +package dsp.fm; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import sample.real.RealSampleBroadcaster; import sample.real.RealSampleListener; -import dsp.filter.ComplexFIRFilter; +import dsp.filter.ComplexFIRFilterOld; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; -public class NBFMDemodulator implements Listener +@Deprecated +public class NBFMDemodulator implements Listener { private RealSampleBroadcaster mBroadcaster = new RealSampleBroadcaster(); - private ComplexFIRFilter mIQFilter; + private ComplexFIRFilterOld mIQFilter; private FMDiscriminator mDiscriminator; /** @@ -38,12 +39,12 @@ public class NBFMDemodulator implements Listener public NBFMDemodulator() { this( FilterFactory.getLowPass( 48000, 6250, 73, WindowType.HAMMING ), - 1.0002, true ); + 1.0002f, true ); } - public NBFMDemodulator( double[] iqFilter, double iqGain, boolean afc ) + public NBFMDemodulator( float[] iqFilter, float iqGain, boolean afc ) { - mIQFilter = new ComplexFIRFilter( iqFilter, iqGain ); + mIQFilter = new ComplexFIRFilterOld( iqFilter, iqGain ); mDiscriminator = new FMDiscriminator( 1 ); @@ -66,7 +67,7 @@ public class NBFMDemodulator implements Listener * processed */ @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mIQFilter.receive( sample ); } diff --git a/src/dsp/fsk/FSK2Decoder.java b/src/dsp/fsk/FSK2Decoder.java index 99a5e392..13646d06 100644 --- a/src/dsp/fsk/FSK2Decoder.java +++ b/src/dsp/fsk/FSK2Decoder.java @@ -26,7 +26,7 @@ import java.util.BitSet; import java.util.List; import sample.Listener; -import sample.real.RealSampleListener; +import sample.real.RealBuffer; import buffer.BooleanAveragingBuffer; import dsp.symbol.SymbolEvent; import dsp.symbol.SymbolEvent.Shift; @@ -51,7 +51,7 @@ import dsp.symbol.SymbolEvent.Shift; * Implements instrumentable interface, so that slice events can be received * externally to analyze decoder performance. */ -public class FSK2Decoder implements Instrumentable, RealSampleListener +public class FSK2Decoder implements Instrumentable, Listener { public enum Output{ NORMAL, INVERTED }; @@ -95,23 +95,26 @@ public class FSK2Decoder implements Instrumentable, RealSampleListener * Primary sample input */ @Override - public void receive( float floatSample ) + public void receive( RealBuffer buffer ) { - /* Square the sample. Greater than zero is a 1 (true) and less than - * zero is a 0 (false) */ - boolean bitSample = ( floatSample >= 0.0f ); + for( float sample: buffer.getSamples() ) + { + /* Square the sample. Greater than zero is a 1 (true) and less than + * zero is a 0 (false) */ + boolean bitSample = ( sample >= 0.0f ); - /* Feed the delay buffer and fetch the one-baud delayed sample */ - boolean delayedBitSample = mDelayBuffer.get( bitSample ); + /* Feed the delay buffer and fetch the one-baud delayed sample */ + boolean delayedBitSample = mDelayBuffer.get( bitSample ); - /* Correlation: xor current bit with delayed bit */ - boolean softBit = bitSample ^ delayedBitSample; + /* Correlation: xor current bit with delayed bit */ + boolean softBit = bitSample ^ delayedBitSample; - /* Low pass filter to smooth the correlated values */ - boolean filteredSoftBit = mLowPassFilter.getAverage( softBit ); + /* Low pass filter to smooth the correlated values */ + boolean filteredSoftBit = mLowPassFilter.getAverage( softBit ); - /* Send the filtered correlated bit to the slicer */ - mSlicer.receive( filteredSoftBit ); + /* Send the filtered correlated bit to the slicer */ + mSlicer.receive( filteredSoftBit ); + } } /** diff --git a/src/dsp/fsk/LTRFSKDecoder.java b/src/dsp/fsk/LTRFSKDecoder.java index 9a81180c..089be152 100644 --- a/src/dsp/fsk/LTRFSKDecoder.java +++ b/src/dsp/fsk/LTRFSKDecoder.java @@ -27,17 +27,17 @@ import java.util.ArrayList; import java.util.List; import sample.Listener; -import sample.real.RealSampleListener; -import dsp.filter.DCRemovalFilter3; +import sample.real.RealBuffer; import dsp.filter.Filters; import dsp.filter.FloatHalfBandFilter; import dsp.filter.FloatHalfBandNoDecimateFilter; import dsp.filter.LTRPulseShapingFilter; import dsp.filter.SquaringFilter; +import dsp.filter.dc.IIRSinglePoleDCRemovalFilter; import dsp.symbol.Slicer; import dsp.symbol.Slicer.Output; -public class LTRFSKDecoder implements RealSampleListener, Instrumentable +public class LTRFSKDecoder implements Listener, Instrumentable { private FloatHalfBandFilter mHBFilter1; private FloatHalfBandFilter mHBFilter2; @@ -45,7 +45,7 @@ public class LTRFSKDecoder implements RealSampleListener, Instrumentable private FloatHalfBandFilter mHBFilter4; private FloatHalfBandFilter mHBFilter5; private FloatHalfBandNoDecimateFilter mHBFilter6; - private DCRemovalFilter3 mDCFilter; + private IIRSinglePoleDCRemovalFilter mDCFilter; private SquaringFilter mSquaringFilter; private LTRPulseShapingFilter mPulseShaper; private Slicer mSlicer; @@ -69,29 +69,29 @@ public class LTRFSKDecoder implements RealSampleListener, Instrumentable public LTRFSKDecoder() { mHBFilter1 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f ); mHBFilter2 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f ); mHBFilter1.setListener( mHBFilter2 ); mHBFilter3 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f ); mHBFilter2.setListener( mHBFilter3 ); mHBFilter4 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f ); mHBFilter3.setListener( mHBFilter4 ); mHBFilter5 = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f ); mHBFilter4.setListener( mHBFilter5 ); mHBFilter6 = new FloatHalfBandNoDecimateFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002 ); + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.1002f ); mHBFilter5.setListener( mHBFilter6 ); - mDCFilter = new DCRemovalFilter3( 0.9946f ); + mDCFilter = new IIRSinglePoleDCRemovalFilter( 0.9946f ); mHBFilter6.setListener( mDCFilter ); mSquaringFilter = new SquaringFilter(); @@ -105,9 +105,12 @@ public class LTRFSKDecoder implements RealSampleListener, Instrumentable } @Override - public void receive( float sample ) + public void receive( RealBuffer buffer ) { - mHBFilter1.receive( sample ); + for( float sample: buffer.getSamples() ) + { + mHBFilter1.receive( sample ); + } } public void addListener( Listener listener ) diff --git a/src/dsp/gain/RealAutomaticGainControl.java b/src/dsp/gain/AutomaticGainControl.java similarity index 88% rename from src/dsp/gain/RealAutomaticGainControl.java rename to src/dsp/gain/AutomaticGainControl.java index 53e98d90..f7afe8da 100644 --- a/src/dsp/gain/RealAutomaticGainControl.java +++ b/src/dsp/gain/AutomaticGainControl.java @@ -2,9 +2,6 @@ package dsp.gain; import java.util.concurrent.atomic.AtomicBoolean; -import sample.real.RealBuffer; -import sample.real.RealSampleListener; -import sample.real.RealSampleProvider; import buffer.DoubleCircularBuffer; import buffer.FloatCircularBuffer; @@ -61,8 +58,7 @@ import buffer.FloatCircularBuffer; * those of the authors and should not be interpreted as representing * official policies, either expressed or implied, of Moe Wheatley. ******************************************************************************/ -public class RealAutomaticGainControl implements RealSampleListener, - RealSampleProvider +public class AutomaticGainControl { private static final double SAMPLE_RATE = 48000; @@ -139,38 +135,10 @@ public class RealAutomaticGainControl implements RealSampleListener, private DoubleCircularBuffer mMagnitudeBuffer = new DoubleCircularBuffer( (int)( SAMPLE_RATE * WINDOW_TIME_CONSTANT ) ); - private RealSampleListener mListener; - - public RealAutomaticGainControl() + public AutomaticGainControl() { } - @Override - public void receive( float currentSample ) - { - float processed = process( currentSample ); - - if( mListener != null ) - { - mListener.receive( processed ); - } - } - - /** - * Applies AGC to a buffer of real (float) samples - */ - public RealBuffer process( RealBuffer buffer ) - { - float[] samples = buffer.getSamples(); - - for( int x = 0; x < samples.length; x++ ) - { - samples[ x ] = process( samples[ x ] ); - } - - return new RealBuffer( samples ); - } - public float process( float currentSample ) { float delayedSample = mDelayBuffer.get( currentSample ); @@ -233,9 +201,7 @@ public class RealAutomaticGainControl implements RealSampleListener, } - delayedSample = (float)( delayedSample * gain ); - - return delayedSample; + return (float)( delayedSample * gain ); } /** @@ -254,16 +220,4 @@ public class RealAutomaticGainControl implements RealSampleListener, { return mAGCEnabled.get(); } - - @Override - public void setListener( RealSampleListener listener ) - { - mListener = listener; - } - - @Override - public void removeListener( RealSampleListener listener ) - { - mListener = null; - } } diff --git a/src/dsp/gain/AutomaticGainControl_RB.java b/src/dsp/gain/AutomaticGainControl_RB.java new file mode 100644 index 00000000..e96c600a --- /dev/null +++ b/src/dsp/gain/AutomaticGainControl_RB.java @@ -0,0 +1,53 @@ +package dsp.gain; + +import sample.Listener; +import sample.real.RealBuffer; + +public class AutomaticGainControl_RB extends AutomaticGainControl + implements Listener +{ + private Listener mListener; + + public AutomaticGainControl_RB() + { + } + + public void dispose() + { + mListener = null; + } + + /** + * Applies AGC to a buffer of real (float) samples + */ + public RealBuffer process( RealBuffer buffer ) + { + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x++ ) + { + samples[ x ] = process( samples[ x ] ); + } + + return buffer; + } + + @Override + public void receive( RealBuffer buffer ) + { + if( mListener != null ) + { + mListener.receive( process( buffer ) ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } +} diff --git a/src/dsp/gain/ComplexAutomaticGainControl.java b/src/dsp/gain/ComplexAutomaticGainControl.java index 9fef2ace..4ecbb2ad 100644 --- a/src/dsp/gain/ComplexAutomaticGainControl.java +++ b/src/dsp/gain/ComplexAutomaticGainControl.java @@ -4,7 +4,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import sample.Listener; import sample.Provider; -import sample.complex.ComplexSample; +import sample.complex.Complex; import buffer.ComplexCircularBuffer; import buffer.DoubleCircularBuffer; @@ -61,8 +61,8 @@ import buffer.DoubleCircularBuffer; * those of the authors and should not be interpreted as representing * official policies, either expressed or implied, of Moe Wheatley. ******************************************************************************/ -public class ComplexAutomaticGainControl implements Listener, - Provider +public class ComplexAutomaticGainControl implements Listener, + Provider { private static final double SAMPLE_RATE = 48000; @@ -138,16 +138,16 @@ public class ComplexAutomaticGainControl implements Listener, private DoubleCircularBuffer mMagnitudeBuffer = new DoubleCircularBuffer( (int)( SAMPLE_RATE * WINDOW_TIME_CONSTANT ) ); - private Listener mListener; + private Listener mListener; public ComplexAutomaticGainControl() { } @Override - public void receive( ComplexSample currentSample ) + public void receive( Complex currentSample ) { - ComplexSample delayedSample = mDelayBuffer.get( currentSample ); + Complex delayedSample = mDelayBuffer.get( currentSample ); double gain = MANUAL_AGC_GAIN; @@ -235,13 +235,13 @@ public class ComplexAutomaticGainControl implements Listener, } @Override - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mListener = listener; } @Override - public void removeListener( Listener listener ) + public void removeListener( Listener listener ) { mListener = null; } diff --git a/src/dsp/gain/ComplexFeedForwardGainControl.java b/src/dsp/gain/ComplexFeedForwardGainControl.java index 674b038e..89d3f057 100644 --- a/src/dsp/gain/ComplexFeedForwardGainControl.java +++ b/src/dsp/gain/ComplexFeedForwardGainControl.java @@ -4,8 +4,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import sample.Provider; -import sample.complex.ComplexSample; +import sample.complex.Complex; +import sample.complex.ComplexSampleListener; import buffer.FloatCircularBuffer; /******************************************************************************* @@ -31,8 +31,7 @@ import buffer.FloatCircularBuffer; * feedforward_agc_cc_impl.cc * ******************************************************************************/ -public class ComplexFeedForwardGainControl - implements Listener, Provider +public class ComplexFeedForwardGainControl implements ComplexSampleListener { private final static Logger mLog = LoggerFactory.getLogger( ComplexFeedForwardGainControl.class ); @@ -40,7 +39,7 @@ public class ComplexFeedForwardGainControl public static final float OBJECTIVE_ENVELOPE = 1.0f; public static final float MINIMUM_ENVELOPE = 0.0001f; - private Listener mListener; + private ComplexSampleListener mListener; private FloatCircularBuffer mEnvelopeHistory; @@ -65,9 +64,9 @@ public class ComplexFeedForwardGainControl } @Override - public void receive( ComplexSample sample ) + public void receive( float inphase, float quadrature ) { - float envelope = sample.envelope(); + float envelope = Complex.envelope( inphase, quadrature ); if( envelope > mMaxEnvelope ) { @@ -99,9 +98,7 @@ public class ComplexFeedForwardGainControl /* Apply current gain value to the sample and send to the listener */ if( mListener != null ) { - sample.multiply( mGain ); - - mListener.receive( sample ); + mListener.receive( inphase *= mGain, quadrature *= mGain ); } } @@ -110,15 +107,8 @@ public class ComplexFeedForwardGainControl mGain = OBJECTIVE_ENVELOPE / mMaxEnvelope; } - @Override - public void setListener( Listener listener ) + public void setListener( ComplexSampleListener listener ) { mListener = listener; } - - @Override - public void removeListener( Listener listener ) - { - mListener = null; - } } diff --git a/src/dsp/gain/ComplexGain.java b/src/dsp/gain/ComplexGain.java index a8ea1b4e..e86e0048 100644 --- a/src/dsp/gain/ComplexGain.java +++ b/src/dsp/gain/ComplexGain.java @@ -1,12 +1,12 @@ package dsp.gain; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; -public class ComplexGain implements Listener +public class ComplexGain implements Listener { private float mGain; - private Listener mListener; + private Listener mListener; public ComplexGain( float gain ) { @@ -14,7 +14,7 @@ public class ComplexGain implements Listener } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { if( mListener != null ) { @@ -24,7 +24,7 @@ public class ComplexGain implements Listener } } - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mListener = listener; } diff --git a/src/dsp/psk/CQPSKDemodulator.java b/src/dsp/psk/CQPSKDemodulator.java index 94aa97de..c968c685 100644 --- a/src/dsp/psk/CQPSKDemodulator.java +++ b/src/dsp/psk/CQPSKDemodulator.java @@ -35,7 +35,6 @@ import instrument.tap.Tap; import instrument.tap.stream.EyeDiagramData; import instrument.tap.stream.EyeDiagramDataTap; -import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -44,24 +43,22 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import sample.Provider; -import sample.complex.ComplexSample; +import sample.complex.Complex; +import sample.complex.ComplexSampleListener; import source.tuner.frequency.FrequencyChangeListener; import buffer.FloatAveragingBuffer; -import dsp.filter.interpolator.QPSKInterpolator; +import dsp.filter.interpolator.RealInterpolator; import dsp.symbol.Dibit; /** - * Implements a CQPSK demodulator using a Gardner Detector to determine optimal - * sampling timing and a Costas Loop as a phase locked loop synchronized with - * the incoming signal carrier frequency. + * Implements a LSM (CQPSK) demodulator using a Gardner Detector to determine + * optimal sampling timing and a Costas Loop as a phase locked loop synchronized + * with the incoming signal carrier frequency. * * Sample Rate: 48000 * Symbol Rate: 4800 */ -public class CQPSKDemodulator implements Instrumentable, - Listener, - Provider +public class CQPSKDemodulator implements Instrumentable, ComplexSampleListener { private final static Logger mLog = LoggerFactory.getLogger( CQPSKDemodulator.class ); @@ -70,12 +67,10 @@ public class CQPSKDemodulator implements Instrumentable, public static final float THETA = (float)( Math.PI / 4.0d ); /* 45 degree point */ - public static final ComplexSample POINT_45_DEGREES = - new ComplexSample( (float)Math.sin( THETA ), (float)Math.cos( THETA ) ); + public static final Complex POINT_45_DEGREES = + new Complex( (float)Math.sin( THETA ), (float)Math.cos( THETA ) ); - private DecimalFormat mDecimalFormat = new DecimalFormat( "0.000000" ); - - private Listener mListener; + private Listener mSymbolListener; private GardnerSymbolTiming mGardnerDetector = new GardnerSymbolTiming(); @@ -89,10 +84,20 @@ public class CQPSKDemodulator implements Instrumentable, { } - @Override - public void receive( ComplexSample sample ) + public void setSymbolListener( Listener listener ) { - mGardnerDetector.receive( sample ); + mSymbolListener = listener; + } + + public void removeSymbolListener() + { + mSymbolListener = null; + } + + @Override + public void receive( float inphase, float quadrature ) + { + mGardnerDetector.receive( inphase, quadrature ); } /** @@ -211,14 +216,15 @@ public class CQPSKDemodulator implements Instrumentable, /** * Gardner symbol timing detector */ - public class GardnerSymbolTiming implements Listener + public class GardnerSymbolTiming implements ComplexSampleListener { public static final int HALF_SAMPLES_PER_SYMBOL = 5; public static final int SAMPLES_PER_SYMBOL = 10; public static final int TWICE_SAMPLES_PER_SYMBOL = 20; - - private ComplexSample[] mDelayLine = - new ComplexSample[ 2 * TWICE_SAMPLES_PER_SYMBOL ]; + + private float[] mDelayLineInphase = new float[ 2 * TWICE_SAMPLES_PER_SYMBOL ]; + private float[] mDelayLineQuadrature = new float[ 2 * TWICE_SAMPLES_PER_SYMBOL ]; + private int mDelayLinePointer = 0; /* Sampling point */ @@ -231,25 +237,21 @@ public class CQPSKDemodulator implements Instrumentable, private float mOmegaRel = 0.005f; private float mOmegaMid = 10.0f; - private QPSKInterpolator mInterpolator = new QPSKInterpolator( 1.0f ); + private RealInterpolator mInterpolator = new RealInterpolator( 1.0f ); + + private Complex mPreviousSample; + private Complex mPreviousMiddleSample; + private Complex mPreviousSymbol; - private ComplexSample mPreviousSample = new ComplexSample( 0.0f, 0.0f ); - private ComplexSample mPreviousMiddleSample = new ComplexSample( 0.0f, 0.0f ); - private ComplexSample mPreviousSymbol = new ComplexSample( 0.0f, 0.0f ); - /** * Provides symbol sampling timing control */ public GardnerSymbolTiming() { - for( int x = 0; x < ( 2 * TWICE_SAMPLES_PER_SYMBOL ); x++ ) - { - mDelayLine[ x ] = new ComplexSample( 0.0f, 0.0f ); - } } @Override - public void receive( ComplexSample sample ) + public void receive( float inphase, float quadrature ) { /* Count down samples per symbol until we calculate the symbol */ mMu--; @@ -258,14 +260,20 @@ public class CQPSKDemodulator implements Instrumentable, /* Mix incoming sample with costas loop to remove any rotation * that is present from a mis-tuned carrier frequency */ - ComplexSample derotatedSample = ComplexSample - .multiply( mCostasLoop.getCurrentVector(), sample ); - + + Complex costas = mCostasLoop.getCurrentVector(); + + float derotatedInphase = Complex.multiplyInphase( inphase, quadrature, costas.inphase(), costas.quadrature() ); + float derotatedQuadrature = Complex.multiplyQuadrature( inphase, quadrature, costas.inphase(), costas.quadrature() ); + /* Fill up the delay line to use with the interpolator */ - mDelayLine[ mDelayLinePointer ] = derotatedSample; - mDelayLine[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedSample; + mDelayLineInphase[ mDelayLinePointer ] = derotatedInphase; + mDelayLineInphase[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedInphase; - /* Imcrement pointer and keep pointer in bounds */ + mDelayLineQuadrature[ mDelayLinePointer ] = derotatedQuadrature; + mDelayLineQuadrature[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedQuadrature; + + /* Increment pointer and keep pointer in bounds */ mDelayLinePointer = ( mDelayLinePointer + 1 ) % TWICE_SAMPLES_PER_SYMBOL; /* Calculate the symbol once we've stored enough samples */ @@ -282,19 +290,24 @@ public class CQPSKDemodulator implements Instrumentable, } /* Calculate interpolated middle sample and current sample */ - ComplexSample middleSample = mInterpolator - .filter( mDelayLine, mDelayLinePointer, mMu ); - - ComplexSample currentSample = mInterpolator - .filter( mDelayLine, mDelayLinePointer + half_sps, half_mu ); - - /* Multiply current and previous samples to get symbols to use - * for gardner error feedback */ - ComplexSample middleSymbol = ComplexSample.multiply( - middleSample, mPreviousMiddleSample.conjugate() ); + float middleSampleInphase = mInterpolator.filter( + mDelayLineInphase, mDelayLinePointer, mMu ); + float middleSampleQuadrature = mInterpolator.filter( + mDelayLineQuadrature, mDelayLinePointer, mMu ); - ComplexSample currentSymbol = ComplexSample.multiply( - currentSample, mPreviousSample.conjugate() ); + int index = mDelayLinePointer + half_sps; + + float currentSampleInphase = mInterpolator.filter( + mDelayLineInphase, index, half_mu ); + float currentSampleQuadrature = mInterpolator.filter( + mDelayLineQuadrature, index, half_mu ); + + /* Multiply current sample and conjugate (negated quadrature) of + * previous sample to get symbols to use for gardner error feedback */ + Complex middleSymbol = Complex.multiply( middleSampleInphase, + middleSampleQuadrature, mPreviousMiddleSample.conjugate() ); + Complex currentSymbol = Complex.multiply( currentSampleInphase, + currentSampleQuadrature, mPreviousSample.conjugate() ); /* Set gain to unity */ middleSymbol.normalize(); @@ -312,8 +325,10 @@ public class CQPSKDemodulator implements Instrumentable, if( mEyeDiagramDataTap != null ) { mEyeDiagramDataTap.receive( - new EyeDiagramData( Arrays.copyOfRange( mDelayLine, - 0, 20 ), mMu, (float)half_sps + half_mu, gardnerError ) ); + new EyeDiagramData( + Arrays.copyOfRange( mDelayLineInphase, 0, 20 ), + Arrays.copyOfRange( mDelayLineQuadrature, 0, 20 ), + mMu, (float)half_sps + half_mu, gardnerError ) ); } /* mOmega is samples per symbol and is constrained to floating @@ -325,8 +340,8 @@ public class CQPSKDemodulator implements Instrumentable, mMu += mOmega + ( mGainMu * gardnerError ); /* Store current samples/symbols to use for the next period */ - mPreviousSample = currentSample; - mPreviousMiddleSample = middleSample; + mPreviousSample.setValues( currentSampleInphase, currentSampleQuadrature ); + mPreviousMiddleSample.setValues( middleSampleInphase, middleSampleQuadrature ); mPreviousSymbol = currentSymbol; /* Update costas loop using phase error present in current @@ -337,9 +352,9 @@ public class CQPSKDemodulator implements Instrumentable, // ComplexSample.multiply( currentSymbol, POINT_45_DEGREES ) ); /* Dispatch the differentiated symbol to the registered listener */ - if( mListener != null ) + if( mSymbolListener != null ) { - mListener.receive( currentSymbol ); + mSymbolListener.receive( currentSymbol ); } } } @@ -356,7 +371,7 @@ public class CQPSKDemodulator implements Instrumentable, * gnuradio/mpsk_receiver_cc using initialization values from KA1RBI's * OP25/cqpsk.py */ - public class CostasLoop implements Listener + public class CostasLoop implements Listener { public static final double TWO_PI = 2.0 * Math.PI; @@ -442,19 +457,19 @@ public class CQPSKDemodulator implements Instrumentable, /** * Current vector of the loop rotated by THETA degrees. */ - public ComplexSample getCurrentVector() + public Complex getCurrentVector() { // return ComplexSample.fromAngle( mLoopPhase + THETA ); - return ComplexSample.fromAngle( mLoopPhase ); + return Complex.fromAngle( mLoopPhase ); } @Override - public void receive( ComplexSample sample ) + public void receive( Complex complex ) { /* Calculate phase error */ - float phaseError = getPhaseError( sample ); + float phaseError = getPhaseError( complex ); - Dibit d = QPSKPolarSlicer.decide( sample ); + Dibit d = QPSKPolarSlicer.decide( complex ); // mLog.debug( "Dibit: " + d.name() + " Error: " + phaseError ); /* Adjust for phase error */ @@ -503,30 +518,30 @@ public class CQPSKDemodulator implements Instrumentable, * with the carrier frequency. Provides error feedback to adjust * mixer frequency. */ - public float getPhaseError( ComplexSample sample ) + public float getPhaseError( Complex complex ) { float phase_error = 0; - if( Math.abs( sample.real() ) > Math.abs( sample.imaginary() ) ) + if( Math.abs( complex.inphase() ) > Math.abs( complex.quadrature() ) ) { - if( sample.real() > 0 ) + if( complex.inphase() > 0 ) { - phase_error = -sample.imaginary(); + phase_error = -complex.quadrature(); } else { - phase_error = sample.imaginary(); + phase_error = complex.quadrature(); } } else { - if( sample.imaginary() > 0 ) + if( complex.quadrature() > 0 ) { - phase_error = sample.real(); + phase_error = complex.inphase(); } else { - phase_error = -sample.real(); + phase_error = -complex.inphase(); } } @@ -534,18 +549,6 @@ public class CQPSKDemodulator implements Instrumentable, } } - @Override - public void setListener( Listener listener ) - { - mListener = listener; - } - - @Override - public void removeListener( Listener listener ) - { - mListener = null; - } - @Override public List getTaps() { diff --git a/src/dsp/psk/CQPSKDemodulator2.java b/src/dsp/psk/CQPSKDemodulator2.java index 1b0fe271..def7d62c 100644 --- a/src/dsp/psk/CQPSKDemodulator2.java +++ b/src/dsp/psk/CQPSKDemodulator2.java @@ -45,10 +45,11 @@ import org.slf4j.LoggerFactory; import sample.Listener; import sample.Provider; -import sample.complex.ComplexSample; +import sample.complex.Complex; +import sample.complex.ComplexSampleListener; import source.tuner.frequency.FrequencyChangeListener; import buffer.FloatAveragingBuffer; -import dsp.filter.interpolator.QPSKInterpolator; +import dsp.filter.interpolator.RealInterpolator; import dsp.symbol.Dibit; /** @@ -59,9 +60,8 @@ import dsp.symbol.Dibit; * Sample Rate: 48000 * Symbol Rate: 4800 */ -public class CQPSKDemodulator2 implements Instrumentable, - Listener, - Provider +public class CQPSKDemodulator2 implements Instrumentable, ComplexSampleListener, + Provider { private final static Logger mLog = LoggerFactory.getLogger( CQPSKDemodulator2.class ); @@ -70,12 +70,12 @@ public class CQPSKDemodulator2 implements Instrumentable, public static final float THETA = (float)( Math.PI / 4.0d ); /* 45 degree point */ - public static final ComplexSample POINT_45_DEGREES = - new ComplexSample( (float)Math.sin( THETA ), (float)Math.cos( THETA ) ); + public static final Complex POINT_45_DEGREES = + new Complex( (float)Math.sin( THETA ), (float)Math.cos( THETA ) ); private DecimalFormat mDecimalFormat = new DecimalFormat( "0.000000" ); - private Listener mListener; + private Listener mListener; private EarlyLateSymbolTiming mGardnerDetector = new EarlyLateSymbolTiming(); @@ -90,9 +90,9 @@ public class CQPSKDemodulator2 implements Instrumentable, } @Override - public void receive( ComplexSample sample ) + public void receive( float inphase, float quadrature ) { - mGardnerDetector.receive( sample ); + mGardnerDetector.receive( inphase, quadrature ); } /** @@ -218,13 +218,14 @@ public class CQPSKDemodulator2 implements Instrumentable, * attempting to find the largest magnitude value between two interpolated * sample points during each symbol period. */ - public class EarlyLateSymbolTiming implements Listener + public class EarlyLateSymbolTiming implements ComplexSampleListener { public static final int SAMPLES_PER_SYMBOL = 10; public static final int TWICE_SAMPLES_PER_SYMBOL = 20; - private ComplexSample[] mDelayLine = - new ComplexSample[ 2 * TWICE_SAMPLES_PER_SYMBOL ]; + private float[] mDelayLineInphase = new float[ 2 * TWICE_SAMPLES_PER_SYMBOL ]; + private float[] mDelayLineQuadrature = new float[ 2 * TWICE_SAMPLES_PER_SYMBOL ]; + private int mDelayLinePointer = 0; /* Sampling point */ @@ -237,23 +238,19 @@ public class CQPSKDemodulator2 implements Instrumentable, private float mOmegaRel = 0.005f; private float mOmegaMid = 10.0f; - private QPSKInterpolator mInterpolator = new QPSKInterpolator( 1.0f ); + private RealInterpolator mInterpolator = new RealInterpolator( 1.0f ); - private ComplexSample mPreviousSample = new ComplexSample( 0.0f, 0.0f ); + private Complex mPreviousSample = new Complex( 0.0f, 0.0f ); /** * Provides symbol sampling timing control */ public EarlyLateSymbolTiming() { - for( int x = 0; x < ( 2 * TWICE_SAMPLES_PER_SYMBOL ); x++ ) - { - mDelayLine[ x ] = new ComplexSample( 0.0f, 0.0f ); - } } @Override - public void receive( ComplexSample sample ) + public void receive( float inphase, float quadrature ) { /* Count down samples per symbol until we calculate the symbol */ mMu--; @@ -262,29 +259,31 @@ public class CQPSKDemodulator2 implements Instrumentable, /* Mix incoming sample with costas loop to remove any rotation * that is present from a mis-tuned carrier frequency */ - ComplexSample derotatedSample = ComplexSample - .multiply( mCostasLoop.getCurrentVector(), sample ); + Complex derotatedSample = Complex.multiply( + mCostasLoop.getCurrentVector(), inphase, quadrature ); /* Fill up the delay line to use with the interpolator */ - mDelayLine[ mDelayLinePointer ] = derotatedSample; - mDelayLine[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedSample; - + mDelayLineInphase[ mDelayLinePointer ] = derotatedSample.inphase(); + mDelayLineInphase[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedSample.inphase(); + mDelayLineQuadrature[ mDelayLinePointer ] = derotatedSample.quadrature(); + mDelayLineQuadrature[ mDelayLinePointer + TWICE_SAMPLES_PER_SYMBOL ] = derotatedSample.quadrature(); + /* Imcrement pointer and keep pointer in bounds */ mDelayLinePointer = ( mDelayLinePointer + 1 ) % TWICE_SAMPLES_PER_SYMBOL; /* Calculate the symbol once we've stored enough samples */ if( mMu <= 1.0f ) { - ComplexSample earlySample = mInterpolator - .filter( mDelayLine, mDelayLinePointer, mMu ); + Complex earlySample = mInterpolator.filter( mDelayLineInphase, + mDelayLineQuadrature, mDelayLinePointer, mMu ); - ComplexSample middleSample = mInterpolator - .filter( mDelayLine, mDelayLinePointer + 1, mMu ); + Complex middleSample = mInterpolator.filter( mDelayLineInphase, + mDelayLineQuadrature, mDelayLinePointer + 1, mMu ); - ComplexSample lateSample = mInterpolator - .filter( mDelayLine, mDelayLinePointer + 2, mMu ); + Complex lateSample = mInterpolator.filter( mDelayLineInphase, + mDelayLineQuadrature, mDelayLinePointer + 2, mMu ); - ComplexSample currentSymbol = ComplexSample.multiply( + Complex currentSymbol = Complex.multiply( middleSample, mPreviousSample.conjugate() ); /* Set symbol gain to unity */ @@ -295,10 +294,12 @@ public class CQPSKDemodulator2 implements Instrumentable, if( mEyeDiagramDataTap != null ) { - mEyeDiagramDataTap.receive( - new EyeDiagramData( Arrays.copyOfRange( mDelayLine, 0, 20 ), - (float)mDelayLinePointer + 4.0f + mMu, - (float)mDelayLinePointer + 4.0f + mMu + 0.25f, earlyLateError ) ); + mEyeDiagramDataTap.receive( new EyeDiagramData( + Arrays.copyOfRange( mDelayLineInphase, 0, 20 ), + Arrays.copyOfRange( mDelayLineQuadrature, 0, 20 ), + (float)mDelayLinePointer + 4.0f + mMu, + (float)mDelayLinePointer + 4.0f + mMu + 0.25f, + earlyLateError ) ); } /* mOmega is samples per symbol and is constrained to floating @@ -341,7 +342,7 @@ public class CQPSKDemodulator2 implements Instrumentable, * gnuradio/mpsk_receiver_cc using initialization values from KA1RBI's * OP25/cqpsk.py */ - public class CostasLoop implements Listener + public class CostasLoop implements Listener { public static final double TWO_PI = 2.0 * Math.PI; @@ -427,14 +428,14 @@ public class CQPSKDemodulator2 implements Instrumentable, /** * Current vector of the loop rotated by THETA degrees. */ - public ComplexSample getCurrentVector() + public Complex getCurrentVector() { // return ComplexSample.fromAngle( mLoopPhase + THETA ); - return ComplexSample.fromAngle( mLoopPhase ); + return Complex.fromAngle( mLoopPhase ); } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { /* Calculate phase error */ float phaseError = getPhaseError( sample ); @@ -488,7 +489,7 @@ public class CQPSKDemodulator2 implements Instrumentable, * with the carrier frequency. Provides error feedback to adjust * mixer frequency. */ - public float getPhaseError( ComplexSample sample ) + public float getPhaseError( Complex sample ) { float phase_error = 0; @@ -520,13 +521,13 @@ public class CQPSKDemodulator2 implements Instrumentable, } @Override - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mListener = listener; } @Override - public void removeListener( Listener listener ) + public void removeListener( Listener listener ) { mListener = null; } diff --git a/src/dsp/psk/QPSKPolarSlicer.java b/src/dsp/psk/QPSKPolarSlicer.java index 73537a30..db6469e6 100644 --- a/src/dsp/psk/QPSKPolarSlicer.java +++ b/src/dsp/psk/QPSKPolarSlicer.java @@ -2,10 +2,27 @@ package dsp.psk; import sample.Broadcaster; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import dsp.symbol.Dibit; -public class QPSKPolarSlicer implements Listener +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +public class QPSKPolarSlicer implements Listener { private Broadcaster mBroadcaster = new Broadcaster(); @@ -15,7 +32,7 @@ public class QPSKPolarSlicer implements Listener * * \ 00 / * \ / - * 11 \/ 10 + * 01 \/ 10 * /\ * / \ * / 11 \ @@ -41,16 +58,16 @@ public class QPSKPolarSlicer implements Listener } @Override - public void receive( ComplexSample sample ) + public void receive( Complex complex ) { - mBroadcaster.broadcast( decide( sample ) ); + mBroadcaster.broadcast( decide( complex ) ); } - public static Dibit decide( ComplexSample sample ) + public static Dibit decide( Complex complex ) { - if( sample.inPhaseAbsolute() > sample.quadratureAbsolute() ) + if( Math.abs( complex.inphase() ) > Math.abs( complex.quadrature() ) ) { - if( sample.inphase() > 0 ) + if( complex.inphase() > 0 ) { return Dibit.D10_MINUS_1; } @@ -61,7 +78,7 @@ public class QPSKPolarSlicer implements Listener } else { - if( sample.quadrature() > 0 ) + if( complex.quadrature() > 0 ) { return Dibit.D00_PLUS_1; } diff --git a/src/dsp/psk/QPSKStarSlicer.java b/src/dsp/psk/QPSKStarSlicer.java index 44b80fcf..7f16f38d 100644 --- a/src/dsp/psk/QPSKStarSlicer.java +++ b/src/dsp/psk/QPSKStarSlicer.java @@ -2,10 +2,10 @@ package dsp.psk; import sample.Broadcaster; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import dsp.symbol.Dibit; -public class QPSKStarSlicer implements Listener +public class QPSKStarSlicer implements Listener { private Broadcaster mBroadcaster = new Broadcaster(); @@ -38,12 +38,12 @@ public class QPSKStarSlicer implements Listener } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mBroadcaster.broadcast( decide( sample ) ); } - public static Dibit decide( ComplexSample sample ) + public static Dibit decide( Complex sample ) { if( sample.inphase() > 0 ) { diff --git a/src/eventlog/CallEventLogger.java b/src/eventlog/CallEventLogger.java index a2195090..71363dbe 100644 --- a/src/eventlog/CallEventLogger.java +++ b/src/eventlog/CallEventLogger.java @@ -19,8 +19,8 @@ package eventlog; import java.nio.file.Path; +import module.decode.event.CallEvent; import sample.Listener; -import controller.activity.CallEvent; public class CallEventLogger extends EventLogger implements Listener { diff --git a/src/eventlog/EventLogManager.java b/src/eventlog/EventLogManager.java index 2db0b41b..d67607f0 100644 --- a/src/eventlog/EventLogManager.java +++ b/src/eventlog/EventLogManager.java @@ -19,8 +19,8 @@ package eventlog; import java.nio.file.Path; +import module.ProcessingChainOld; import properties.SystemProperties; -import controller.channel.ProcessingChain; import eventlog.MessageEventLogger.Type; public class EventLogManager @@ -33,7 +33,7 @@ public class EventLogManager .getApplicationFolder( "event_logs" ); } - public EventLogger getLogger( ProcessingChain chain, EventLogType eventLogType ) + public EventLogger getLogger( ProcessingChainOld chain, EventLogType eventLogType ) { StringBuilder sb = new StringBuilder(); diff --git a/src/filter/FilterSet.java b/src/filter/FilterSet.java index eba21694..10823aea 100644 --- a/src/filter/FilterSet.java +++ b/src/filter/FilterSet.java @@ -14,6 +14,11 @@ public class FilterSet implements IFilter mName = name; } + public FilterSet() + { + mName = "Message Filter"; + } + public String getName() { return mName; diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index 57aacf78..d4104990 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -73,9 +73,6 @@ public class SDRTrunk public SDRTrunk() { -// LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory(); -// StatusPrinter.print( lc ); - mLog.info( "" ); mLog.info( "" ); mLog.info( "*******************************************************************" ); diff --git a/src/gui/control/ConstellationViewer.java b/src/gui/control/ConstellationViewer.java index 3aa317a7..124b7d47 100644 --- a/src/gui/control/ConstellationViewer.java +++ b/src/gui/control/ConstellationViewer.java @@ -20,11 +20,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import buffer.CircularBuffer; public class ConstellationViewer extends JPanel - implements Listener + implements Listener { private static final long serialVersionUID = 1L; @@ -36,9 +36,9 @@ public class ConstellationViewer extends JPanel private float mSamplesPerSymbol; private float mCounter = 0; private float mOffset = 0; - private CircularBuffer mBuffer = - new CircularBuffer( 5000 ); - private ComplexSample mPrevious = new ComplexSample( 1, 1 ); + private CircularBuffer mBuffer = + new CircularBuffer( 5000 ); + private Complex mPrevious = new Complex( 1, 1 ); public ConstellationViewer( int sampleRate, int symbolRate ) { @@ -76,11 +76,11 @@ public class ConstellationViewer extends JPanel } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mBuffer.receive( sample ); - ComplexSample angle = ComplexSample.multiply( sample, mPrevious.conjugate() ); + Complex angle = Complex.multiply( sample, mPrevious.conjugate() ); repaint(); } @@ -109,7 +109,7 @@ public class ConstellationViewer extends JPanel graphics.setColor( Color.BLUE ); - List samples = mBuffer.getElements(); + List samples = mBuffer.getElements(); double centerX = (double)getHeight() / 2.0d; double centerY = (double)getWidth() / 2.0d; @@ -118,7 +118,7 @@ public class ConstellationViewer extends JPanel mCounter = 0; - for( ComplexSample sample: samples ) + for( Complex sample: samples ) { if( mCounter > ( mOffset + mSamplesPerSymbol ) ) { diff --git a/src/instrument/gui/AudioSourceFrame.java b/src/instrument/gui/AudioSourceFrame.java index ff2c0e74..109b64f1 100644 --- a/src/instrument/gui/AudioSourceFrame.java +++ b/src/instrument/gui/AudioSourceFrame.java @@ -34,7 +34,13 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; -import message.MessageDirection; +import module.decode.Decoder; +import module.decode.DecoderType; +import module.decode.fleetsync2.Fleetsync2Decoder; +import module.decode.lj1200.LJ1200Decoder; +import module.decode.mdc1200.MDCDecoder; +import module.decode.p25.P25Decoder.Modulation; +import module.decode.tait.Tait1200Decoder; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; @@ -43,21 +49,6 @@ import org.slf4j.LoggerFactory; import source.wave.WaveSource; import source.wave.WaveSource.PositionListener; import controller.ResourceManager; -import decode.Decoder; -import decode.DecoderType; -import decode.config.DecodeConfigLTRNet; -import decode.config.DecodeConfigMPT1327; -import decode.config.DecodeConfigPassport; -import decode.fleetsync2.Fleetsync2Decoder; -import decode.lj1200.LJ1200Decoder; -import decode.ltrnet.LTRNetDecoder; -import decode.mdc1200.MDCDecoder; -import decode.mpt1327.MPT1327Decoder; -import decode.mpt1327.MPT1327Decoder.Sync; -import decode.p25.P25Decoder; -import decode.p25.P25Decoder.Modulation; -import decode.passport.PassportDecoder; -import decode.tait.Tait1200Decoder; public class AudioSourceFrame extends JInternalFrame implements PositionListener { @@ -188,25 +179,25 @@ public class AudioSourceFrame extends JInternalFrame implements PositionListener decoder = new LJ1200Decoder( null ); break; case LTR_NET: - decoder = new LTRNetDecoder( new DecodeConfigLTRNet(), - mSource.getSampleType(), null, MessageDirection.OSW ); +// decoder = new LTRNetDecoder( new DecodeConfigLTRNet(), +// mSource.getSampleType(), null, MessageDirection.OSW ); break; case MDC1200: decoder = new MDCDecoder( null ); break; case MPT1327: - decoder = new MPT1327Decoder( new DecodeConfigMPT1327(), - mSource.getSampleType(), null, Sync.NORMAL ); +// decoder = new MPT1327Decoder( new DecodeConfigMPT1327(), +// mSource.getSampleType(), null, Sync.NORMAL ); break; case PASSPORT: - decoder = new PassportDecoder( new DecodeConfigPassport(), - mSource.getSampleType(), null ); +// decoder = new PassportDecoder( new DecodeConfigPassport(), +// mSource.getSampleType(), null ); break; case P25_PHASE1: ResourceManager rm = new ResourceManager(); - decoder = new P25Decoder( rm, mSource.getSampleType(), - (Modulation)mComboModulations.getSelectedItem(), null ); +// decoder = new P25Decoder( rm, mSource.getSampleType(), +// (Modulation)mComboModulations.getSelectedItem(), null ); break; case TAIT_1200: decoder = new Tait1200Decoder( null ); diff --git a/src/instrument/gui/ConstellationPanel.java b/src/instrument/gui/ConstellationPanel.java index ed1069b5..615bda8a 100644 --- a/src/instrument/gui/ConstellationPanel.java +++ b/src/instrument/gui/ConstellationPanel.java @@ -29,11 +29,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import source.wave.WaveSource.PositionListener; public class ConstellationPanel extends JInternalFrame - implements PositionListener, Listener + implements PositionListener, Listener { private static final long serialVersionUID = 1L; private final static Logger mLog = @@ -69,7 +69,7 @@ public class ConstellationPanel extends JInternalFrame } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mConstellation.receive( sample ); } diff --git a/src/instrument/gui/DecoderViewFrame.java b/src/instrument/gui/DecoderViewFrame.java index 5836f0bf..494652f6 100644 --- a/src/instrument/gui/DecoderViewFrame.java +++ b/src/instrument/gui/DecoderViewFrame.java @@ -22,6 +22,8 @@ import instrument.tap.Tap; import instrument.tap.TapViewPanel; import instrument.tap.stream.BinaryTap; import instrument.tap.stream.BinaryTapViewPanel; +import instrument.tap.stream.ComplexSampleTap; +import instrument.tap.stream.ComplexSampleTapViewPanel; import instrument.tap.stream.ComplexTap; import instrument.tap.stream.ComplexTapViewPanel; import instrument.tap.stream.DibitTap; @@ -51,18 +53,18 @@ import javax.swing.JPopupMenu; import javax.swing.JSeparator; import message.Message; +import module.decode.Decoder; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import source.Source.SampleType; +import sample.SampleType; import source.wave.ComplexWaveSource; import source.wave.FloatWaveSource; import source.wave.WaveSource; import source.wave.WaveSource.PositionListener; -import decode.Decoder; public class DecoderViewFrame extends JInternalFrame implements PositionListener, Listener @@ -85,12 +87,12 @@ public class DecoderViewFrame extends JInternalFrame if( source.getSampleType() == SampleType.COMPLEX ) { ComplexWaveSource cws = (ComplexWaveSource)mWaveSource; - cws.addListener( mDecoder.getComplexReceiver() ); +// cws.addListener( mDecoder.getComplexReceiver() ); } else if( source.getSampleType() == SampleType.REAL ) { FloatWaveSource fws = (FloatWaveSource)mWaveSource; - fws.setListener( mDecoder.getRealReceiver() ); +// fws.setListener( mDecoder.getRealReceiver() ); } mDecoder.addMessageListener( this ); @@ -102,7 +104,7 @@ public class DecoderViewFrame extends JInternalFrame { setLayout( new MigLayout( "insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]" ) ); - setTitle( "Decoder [" + mDecoder.getType().getDisplayString() + "]" ); + setTitle( "Decoder [" + mDecoder.getDecoderType().getDisplayString() + "]" ); setPreferredSize( new Dimension( 450, 250 ) ); setSize( 450, 250 ); @@ -177,6 +179,9 @@ public class DecoderViewFrame extends JInternalFrame case STREAM_COMPLEX: panel = new ComplexTapViewPanel( (ComplexTap)tap ); break; + case STREAM_COMPLEX_SAMPLE: + panel = new ComplexSampleTapViewPanel( (ComplexSampleTap)tap ); + break; case STREAM_DIBIT: panel = new DibitTapViewPanel( (DibitTap)tap ); break; diff --git a/src/instrument/tap/TapType.java b/src/instrument/tap/TapType.java index dbb3f55d..0c3dd212 100644 --- a/src/instrument/tap/TapType.java +++ b/src/instrument/tap/TapType.java @@ -22,6 +22,7 @@ public enum TapType EVENT_SYNC_DETECT, STREAM_BINARY, STREAM_COMPLEX, + STREAM_COMPLEX_SAMPLE, STREAM_DIBIT, STREAM_EYE_DIAGRAM, STREAM_FLOAT, diff --git a/src/instrument/tap/stream/ComplexSampleTap.java b/src/instrument/tap/stream/ComplexSampleTap.java new file mode 100644 index 00000000..9b7ee035 --- /dev/null +++ b/src/instrument/tap/stream/ComplexSampleTap.java @@ -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 + ******************************************************************************/ +package instrument.tap.stream; + +import instrument.tap.TapListener; +import instrument.tap.TapType; +import sample.Listener; +import sample.complex.Complex; +import sample.complex.ComplexSampleListener; + +public class ComplexSampleTap extends StreamTap implements ComplexSampleListener +{ + private ComplexSampleListener mListener; + + public ComplexSampleTap( String name, + int delay, + float sampleRateRatio ) + { + super( TapType.STREAM_COMPLEX_SAMPLE, name, delay, sampleRateRatio ); + } + + public ComplexSampleTap( TapType tapType, String name, int delay, float sampleRateRatio ) + { + super( tapType, name, delay, sampleRateRatio ); + } + + @Override + public void receive( float inphase, float quadrature ) + { + if( mListener != null ) + { + mListener.receive( inphase, quadrature ); + } + + for( TapListener listener: mListeners ) + { + listener.receive( new Complex( inphase, quadrature ) ); + } + } + + public void setListener( ComplexSampleListener listener ) + { + mListener = listener; + } + + public void removeListener( ComplexSampleListener listener ) + { + mListener = null; + } +} diff --git a/src/instrument/tap/stream/ComplexSampleTapViewPanel.java b/src/instrument/tap/stream/ComplexSampleTapViewPanel.java new file mode 100644 index 00000000..8c5713af --- /dev/null +++ b/src/instrument/tap/stream/ComplexSampleTapViewPanel.java @@ -0,0 +1,155 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package instrument.tap.stream; + +import gui.SDRTrunk; +import instrument.gui.SampleModel; +import instrument.tap.Tap; +import instrument.tap.TapViewPanel; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.geom.Path2D; +import java.awt.geom.Point2D; +import java.util.List; +import java.util.Observable; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.complex.Complex; + +public class ComplexSampleTapViewPanel extends TapViewPanel +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class ); + + private Tap mTap; + private List mSamples; + private int mSampleCount; + + public ComplexSampleTapViewPanel( ComplexSampleTap tap ) + { + super( new SampleModel(), tap.getName() ); + + setSize( new Dimension( 400, 400 ) ); + + mTap = tap; + mTap.addListener( getModel() ); + + mSampleCount = (int)( 2000 * tap.getSampleRateRatio() ); + + getModel().setSampleCount( mSampleCount ); + getModel().setDelay( tap.getDelay() ); + + mLog.info( "Complex Tap Panel [" + tap.getName() + "] count: " + + getModel().getSampleCount() + " delay:" + getModel().getDelay() ); + } + + @Override + public void update( Observable arg0, Object arg1 ) + { + mSamples = (List)getModel().getSamples(); + repaint(); + } + + public void paintComponent( Graphics g ) + { + super.paintComponent( g ); + + Graphics2D g2 = (Graphics2D)g; + + int height = getHeight(); + int width = getWidth(); + + g2.setColor( Color.DARK_GRAY ); + + float middle = (float)height / 4.0f; + + g2.drawLine( 0, (int)middle, width, (int)middle ); + g2.drawLine( 0, (int)( 3 * middle ), width, (int)( 3 * middle ) ); + + if( mSamples != null && + mSamples.size() > 0 ) + { + g2.setColor( getForeground() ); + + g2.drawString( mLabel, 5 , 15 ); + + Path2D.Float polylineReal = + new Path2D.Float( Path2D.Float.WIND_EVEN_ODD, mSamples.size() ); + Path2D.Float polylineImag = + new Path2D.Float( Path2D.Float.WIND_EVEN_ODD, mSamples.size() ); + + /* Move to the first point */ + Point2D.Float firstReal = resolve( 0, mSamples.get( 0 ), Type.REAL ); + polylineReal.moveTo( firstReal.x, firstReal.y ); + + Point2D.Float firstImag = resolve( 0, mSamples.get( 0 ), Type.IMAGINERY ); + polylineImag.moveTo( firstImag.x, firstImag.y ); + + for( int x = 1; x < mSamples.size(); x++ ) + { + Point2D.Float pointReal = resolve( x, mSamples.get( x ), Type.REAL ); + polylineReal.lineTo( pointReal.x, pointReal.y ); + + Point2D.Float pointImag = resolve( x, mSamples.get( x ), Type.IMAGINERY ); + polylineImag.lineTo( pointImag.x, pointImag.y ); + +// mLog.debug( "Imag y:" + pointImag.getY() + " height:" + getHeight() ); + } + + g2.draw( polylineReal ); + g2.draw( polylineImag ); + } + + g2.dispose(); + } + + + private Point2D.Float resolve( int index, Complex sample, Type type ) + { + int middle = (int)( getHeight() / 4 ); + + float y = middle - ( middle * mVerticalZoom * ( type == Type.REAL ? + sample.real() : sample.imaginary()) ); + + if( type == Type.IMAGINERY ) + { + y += ( middle * 2 ); + } + + /* Clip y to zero as necessary */ + y = ( y < 0 ) ? 0 : y; + + /* Clip y to max height as necessary */ + y = ( y > getHeight() ) ? getHeight() : y; + + float indexWidth = (float)getWidth() / (float)mSampleCount; + + float x = (float)index * indexWidth; + + Point2D.Float point = new Point2D.Float( x, y ); + + return new Point2D.Float( x , y ); + } + + public enum Type{ REAL,IMAGINERY }; +} diff --git a/src/instrument/tap/stream/ComplexTap.java b/src/instrument/tap/stream/ComplexTap.java index 43b81144..2d6cf9d2 100644 --- a/src/instrument/tap/stream/ComplexTap.java +++ b/src/instrument/tap/stream/ComplexTap.java @@ -20,16 +20,15 @@ package instrument.tap.stream; import instrument.tap.TapListener; import instrument.tap.TapType; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; -public class ComplexTap extends StreamTap - implements Listener +public class ComplexTap extends StreamTap implements Listener { - private Listener mListener; + private Listener mListener; public ComplexTap( String name, - int delay, - float sampleRateRatio ) + int delay, + float sampleRateRatio ) { super( TapType.STREAM_COMPLEX, name, delay, sampleRateRatio ); } @@ -40,25 +39,25 @@ public class ComplexTap extends StreamTap } @Override - public void receive( ComplexSample t ) + public void receive( Complex sample ) { if( mListener != null ) { - mListener.receive( t ); + mListener.receive( sample ); } for( TapListener listener: mListeners ) { - listener.receive( t ); + listener.receive( sample ); } } - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mListener = listener; } - public void removeListener( Listener listener ) + public void removeListener( Listener listener ) { mListener = null; } diff --git a/src/instrument/tap/stream/ComplexTapViewPanel.java b/src/instrument/tap/stream/ComplexTapViewPanel.java index 5bc9dda8..a23b997c 100644 --- a/src/instrument/tap/stream/ComplexTapViewPanel.java +++ b/src/instrument/tap/stream/ComplexTapViewPanel.java @@ -34,7 +34,7 @@ import java.util.Observable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class ComplexTapViewPanel extends TapViewPanel { @@ -42,7 +42,7 @@ public class ComplexTapViewPanel extends TapViewPanel private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class ); private Tap mTap; - private List mSamples; + private List mSamples; private int mSampleCount; public ComplexTapViewPanel( ComplexTap tap ) @@ -66,7 +66,7 @@ public class ComplexTapViewPanel extends TapViewPanel @Override public void update( Observable arg0, Object arg1 ) { - mSamples = (List)getModel().getSamples(); + mSamples = (List)getModel().getSamples(); repaint(); } @@ -124,7 +124,7 @@ public class ComplexTapViewPanel extends TapViewPanel } - private Point2D.Float resolve( int index, ComplexSample sample, Type type ) + private Point2D.Float resolve( int index, Complex sample, Type type ) { int middle = (int)( getHeight() / 4 ); diff --git a/src/instrument/tap/stream/EyeDiagramData.java b/src/instrument/tap/stream/EyeDiagramData.java index 143269c3..8cd2828b 100644 --- a/src/instrument/tap/stream/EyeDiagramData.java +++ b/src/instrument/tap/stream/EyeDiagramData.java @@ -1,26 +1,32 @@ package instrument.tap.stream; -import sample.complex.ComplexSample; public class EyeDiagramData { - private ComplexSample[] mSamples; + private float[] mSamplesInphase; + private float[] mSamplesQuadrature; private float mLeftPoint; private float mRightPoint; private float mError; - public EyeDiagramData( ComplexSample[] samples, float leftPoint, - float rightPoint, float error ) + public EyeDiagramData( float[] samplesInphase, float[] samplesQuadrature, + float leftPoint, float rightPoint, float error ) { - mSamples = samples; + mSamplesInphase = samplesInphase; + mSamplesQuadrature = samplesQuadrature; mLeftPoint = leftPoint; mRightPoint = rightPoint; mError = error; } - public ComplexSample[] getSamples() + public float[] getInphaseSamples() { - return mSamples; + return mSamplesInphase; + } + + public float[] getQuadratureSamples() + { + return mSamplesQuadrature; } public float getLeftPoint() diff --git a/src/instrument/tap/stream/EyeDiagramDataTap.java b/src/instrument/tap/stream/EyeDiagramDataTap.java index 5358806a..4f446746 100644 --- a/src/instrument/tap/stream/EyeDiagramDataTap.java +++ b/src/instrument/tap/stream/EyeDiagramDataTap.java @@ -17,10 +17,11 @@ ******************************************************************************/ package instrument.tap.stream; +import module.decode.p25.P25_LSMDecoder; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.P25Decoder; import instrument.tap.TapListener; import instrument.tap.TapType; import sample.Listener; diff --git a/src/instrument/tap/stream/EyeDiagramDataTapPanel.java b/src/instrument/tap/stream/EyeDiagramDataTapPanel.java index 9edbc6fd..b4cbd84d 100644 --- a/src/instrument/tap/stream/EyeDiagramDataTapPanel.java +++ b/src/instrument/tap/stream/EyeDiagramDataTapPanel.java @@ -32,7 +32,7 @@ import java.util.Observable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class EyeDiagramDataTapPanel extends TapViewPanel { @@ -94,10 +94,11 @@ public class EyeDiagramDataTapPanel extends TapViewPanel EyeDiagramData data = mSamples.get( z ); - ComplexSample[] samples = data.getSamples(); + float[] inphaseSamples = data.getInphaseSamples(); + float[] quadratureSamples = data.getQuadratureSamples(); /* Draw the sample set containing two symbol periods of data */ - if( samples.length >= 1 ) + if( inphaseSamples.length >= 1 ) { Path2D.Float iLine = new Path2D.Float( GeneralPath.WIND_EVEN_ODD, 20 ); Path2D.Float qLine = new Path2D.Float( GeneralPath.WIND_EVEN_ODD, 20 ); @@ -105,17 +106,17 @@ public class EyeDiagramDataTapPanel extends TapViewPanel float xAxis = 0; - iLine.moveTo( xAxis, getSampleHeight( halfHeight, samples[ 0 ].inphase() ) ); - qLine.moveTo( xAxis + third, getSampleHeight( halfHeight, samples[ 0 ].quadrature() ) ); - mLine.moveTo( xAxis + ( two_thirds ), getMagnitudeHeight( samples[ 0 ].magnitude() ) ); + iLine.moveTo( xAxis, getSampleHeight( halfHeight, inphaseSamples[ 0 ] ) ); + qLine.moveTo( xAxis + third, getSampleHeight( halfHeight, quadratureSamples[ 0 ] ) ); + mLine.moveTo( xAxis + ( two_thirds ), getMagnitudeHeight( Complex.magnitude( inphaseSamples[ 0 ], quadratureSamples[ 0 ] ) ) ); - for( int x = 1; x < samples.length; x++ ) + for( int x = 1; x < inphaseSamples.length; x++ ) { xAxis += xAxisIncrement; - iLine.lineTo( xAxis, getSampleHeight( halfHeight, samples[ x ].inphase() ) ); - qLine.lineTo( xAxis + third, getSampleHeight( halfHeight, samples[ x ].quadrature() ) ); - mLine.lineTo( xAxis + two_thirds, getMagnitudeHeight( samples[ x ].magnitude() ) ); + iLine.lineTo( xAxis, getSampleHeight( halfHeight, inphaseSamples[ x ] ) ); + qLine.lineTo( xAxis + third, getSampleHeight( halfHeight, quadratureSamples[ x ] ) ); + mLine.lineTo( xAxis + two_thirds, getMagnitudeHeight( Complex.magnitude( inphaseSamples[ x ], quadratureSamples[ x ] ) ) ); } g2.draw( iLine ); diff --git a/src/instrument/tap/stream/FloatBufferTap.java b/src/instrument/tap/stream/FloatBufferTap.java new file mode 100644 index 00000000..55bbfd93 --- /dev/null +++ b/src/instrument/tap/stream/FloatBufferTap.java @@ -0,0 +1,38 @@ +package instrument.tap.stream; + +import sample.Listener; +import sample.real.RealBuffer; + +public class FloatBufferTap extends FloatTap implements Listener +{ + private Listener mListener; + + public FloatBufferTap( String name, int delay, float sampleRateRatio ) + { + super( name, delay, sampleRateRatio ); + } + + @Override + public void receive( RealBuffer buffer ) + { + if( mListener != null ) + { + mListener.receive( buffer ); + } + + for( float sample: buffer.getSamples() ) + { + receive( sample ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener( Listener listener ) + { + mListener = null; + } +} diff --git a/src/instrument/tap/stream/QPSKTapPanel.java b/src/instrument/tap/stream/QPSKTapPanel.java index 46d24fd1..f92773f1 100644 --- a/src/instrument/tap/stream/QPSKTapPanel.java +++ b/src/instrument/tap/stream/QPSKTapPanel.java @@ -32,7 +32,7 @@ import java.util.Observable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class QPSKTapPanel extends TapViewPanel { @@ -40,7 +40,7 @@ public class QPSKTapPanel extends TapViewPanel private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class ); private Tap mTap; - private List mSamples; + private List mSamples; private int mSampleCount; public QPSKTapPanel( ComplexTap tap ) @@ -62,7 +62,7 @@ public class QPSKTapPanel extends TapViewPanel @Override public void update( Observable arg0, Object arg1 ) { - mSamples = (List)getModel().getSamples(); + mSamples = (List)getModel().getSamples(); repaint(); } @@ -107,7 +107,7 @@ public class QPSKTapPanel extends TapViewPanel int threshold = (int)( mSamples.size() * 0.9f ); - for( ComplexSample sample: mSamples ) + for( Complex sample: mSamples ) { /* Set color to foreground for newest 10% of constellations */ if( counter == threshold ) diff --git a/src/map/MapService.java b/src/map/MapService.java index 5757e331..0b6c9f87 100644 --- a/src/map/MapService.java +++ b/src/map/MapService.java @@ -29,6 +29,7 @@ import message.Message; import sample.Listener; import settings.SettingsManager; import alias.Alias; +import controller.NamingThreadFactory; import controller.ResourceManager; public class MapService implements Listener @@ -39,7 +40,8 @@ public class MapService implements Listener private static final Color sDEFAULT_COLOR = Color.BLACK; private final ScheduledExecutorService scheduler = - Executors.newScheduledThreadPool(1); + Executors.newScheduledThreadPool(1, + new NamingThreadFactory( "map tile fetcher" ) ); private ArrayList mListeners = new ArrayList(); diff --git a/src/message/IMessageListener.java b/src/message/IMessageListener.java new file mode 100644 index 00000000..9c176e52 --- /dev/null +++ b/src/message/IMessageListener.java @@ -0,0 +1,8 @@ +package message; + +import sample.Listener; + +public interface IMessageListener +{ + public Listener getMessageListener(); +} diff --git a/src/message/IMessageProvider.java b/src/message/IMessageProvider.java new file mode 100644 index 00000000..4618020a --- /dev/null +++ b/src/message/IMessageProvider.java @@ -0,0 +1,9 @@ +package message; + +import sample.Listener; + +public interface IMessageProvider +{ + public void addMessageListener( Listener listener ); + public void removeMessageListener( Listener listener ); +} diff --git a/src/message/Message.java b/src/message/Message.java index 0defd5ec..ac1ab775 100644 --- a/src/message/Message.java +++ b/src/message/Message.java @@ -23,8 +23,8 @@ import java.util.Date; import java.util.List; import map.Plottable; +import module.decode.event.MessageDetailsProvider; import alias.Alias; -import controller.activity.MessageDetailsProvider; public abstract class Message implements MessageDetailsProvider { diff --git a/src/message/MessageDirection.java b/src/message/MessageDirection.java index 37210c26..87d8e7d2 100644 --- a/src/message/MessageDirection.java +++ b/src/message/MessageDirection.java @@ -19,6 +19,18 @@ package message; public enum MessageDirection { - ISW, //Inbound Status Word - OSW; //Outbound Status Word + ISW( "Mobile (ISW)" ), //Inbound Status Word + OSW( "Tower (OSW)" ); //Outbound Status Word + + private String mLabel; + + private MessageDirection( String label ) + { + mLabel = label; + } + + public String toString() + { + return mLabel; + } } diff --git a/src/message/MessageFilterFactory.java b/src/message/MessageFilterFactory.java deleted file mode 100644 index 36f8c63a..00000000 --- a/src/message/MessageFilterFactory.java +++ /dev/null @@ -1,111 +0,0 @@ -package message; - -import java.util.ArrayList; -import java.util.List; - -import controller.channel.Channel; -import decode.DecoderType; -import decode.config.AuxDecodeConfiguration; -import decode.fleetsync2.FleetsyncMessageFilter; -import decode.lj1200.LJ1200MessageFilter; -import decode.ltrnet.LTRNetMessageFilter; -import decode.ltrstandard.LTRStandardMessageFilter; -import decode.mdc1200.MDCMessageFilter; -import decode.mpt1327.MPT1327MessageFilter; -import decode.p25.message.filter.P25MessageFilterSet; -import decode.passport.PassportMessageFilter; -import filter.AllPassFilter; -import filter.FilterSet; -import filter.IFilter; - -/** - * Constructs message filters for each of the decoder types - */ -public class MessageFilterFactory -{ - /** - * Assembles a filter set containing filters for the primary channel - * decoder and each of the auxiliary decoders - */ - public static FilterSet getMessageFilter( Channel channel ) - { - FilterSet filterSet = new FilterSet( - "Message Filter: " + channel.getChannelDisplayName() ); - - filterSet.addFilters( getMessageFilters( - channel.getDecodeConfiguration().getDecoderType() ) ); - - AuxDecodeConfiguration auxConfig = channel.getAuxDecodeConfiguration(); - - for( DecoderType decoderType: auxConfig.getAuxDecoders() ) - { - filterSet.addFilters( getMessageFilters( decoderType ) ); - } - - /* If we don't have any filters, add an ALL-PASS filter */ - if( filterSet.getFilters().isEmpty() ) - { - filterSet.addFilter( new AllPassFilter() ); - } - - return filterSet; - } - - /** - * Returns a set of IMessageFilter objects (FilterSets or Filters) that - * can process all of the messages produced by the specified decoder type. - */ - public static List> getMessageFilters( DecoderType decoder ) - { - ArrayList> filters = new ArrayList>(); - - switch( decoder ) - { - case ACARS: - break; - case AIS: - break; - case AM: - break; - case APRS: - break; - case DMR: - break; - case FLEETSYNC2: - filters.add( new FleetsyncMessageFilter() ); - break; - case LJ_1200: - filters.add( new LJ1200MessageFilter() ); - break; - case LTR_NET: - filters.add( new LTRNetMessageFilter() ); - break; - case LTR_STANDARD: - filters.add( new LTRStandardMessageFilter() ); - break; - case MDC1200: - filters.add( new MDCMessageFilter() ); - break; - case MOTOROLA_TYPE_2: - break; - case MPT1327: - filters.add( new MPT1327MessageFilter() ); - break; - case NBFM: - break; - case NXDN: - break; - case P25_PHASE1: - case P25_PHASE2: - filters.add( new P25MessageFilterSet() ); - break; - case PASSPORT: - filters.add( new PassportMessageFilter() ); - break; - default: - break; - } - - return filters; - } -} diff --git a/src/module/Module.java b/src/module/Module.java new file mode 100644 index 00000000..27f89420 --- /dev/null +++ b/src/module/Module.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ + +package module; + +public abstract class Module +{ + /** + * Defines the basic component level class for all processing, demodulation + * and decoding components that can operate within a processing chain. + * + * Modules can optionally implement any of the following interfaces: + * + * IAudioPacketProvider produces audio packets + * ICallEventProvider call events + * IChannelEventProvider channel events (reset, selection, start, etc) + * IComplexBufferListener consumes complex samples and normally + * produces demodulated real sample buffers + * IFrequencyCorrectionController provides tuned frequency error corrections + * IMessageProvider produces messages + * IRealBufferListener consumes demodulated real sample buffers + * IRealBufferListener produces demodulated real sample buffers + * ISquelchStateListener consumes squelch states + * ISquelchStateProvider provides squelch states + */ + public Module() + { + } + + /** + * Dispose of all resources and listeners and prepare for garbage collection + */ + public abstract void dispose(); + + /** + * Initializes or reset all internal states to default - prepare to process + * again. This method is usually invoked between stopping a module and + * starting it again processing a newly tuned channel, like a traffic + * channel. + */ + public abstract void init(); +} diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java new file mode 100644 index 00000000..c11e85d0 --- /dev/null +++ b/src/module/ProcessingChain.java @@ -0,0 +1,661 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.LinkedTransferQueue; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import message.IMessageProvider; +import message.Message; +import module.decode.Decoder; +import module.decode.event.CallEvent; +import module.decode.event.ICallEventProvider; +import module.decode.state.DecoderStateEvent; +import module.decode.state.DecoderStateEvent.Event; +import module.decode.state.IDecoderStateEventListener; +import module.decode.state.IDecoderStateEventProvider; +import module.decode.state.State; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Broadcaster; +import sample.Listener; +import sample.complex.ComplexBuffer; +import sample.complex.IComplexBufferListener; +import sample.real.IRealBufferListener; +import sample.real.IRealBufferProvider; +import sample.real.RealBuffer; +import source.ComplexSource; +import source.RealSource; +import source.Source; +import source.SourceException; +import source.tuner.TunerChannelSource; +import source.tuner.frequency.FrequencyCorrectionControl; +import source.tuner.frequency.IFrequencyCorrectionController; +import alias.Metadata; +import audio.AudioPacket; +import audio.IAudioPacketProvider; +import audio.metadata.IMetadataListener; +import audio.metadata.IMetadataProvider; +import audio.squelch.ISquelchStateListener; +import audio.squelch.ISquelchStateProvider; +import audio.squelch.SquelchState; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; +import controller.channel.ChannelEvent; +import controller.channel.IChannelEventListener; +import controller.channel.IChannelEventProvider; + +/** + * Processing chain provides a framework for connecting a complex or real sample + * source to a set of one primary decoder and zero or more auxiliary decoders. + * All decoded messages and call events produced by the decoders and the decoder + * call states are aggregated by the various broadcasters. You can register + * listeners to receive aggregated messages, call events, and audio packets. + * + * Normal setup sequence: + * + * 1) Add one or more modules + * 2) Register listeners to receive messages, call events, audio, etc. + * 3) Add a valid source + * 4) Invoke the start() method to start processing. + * 5) Invoke the stop() method to stop processing. + * + * Optional: if you want to reuse the processing chain with a new sample source, + * invoke the following method sequence: stop(), reset(), setSource(), start() + */ +public class ProcessingChain implements IChannelEventListener +{ + private final static Logger mLog = + LoggerFactory.getLogger( ProcessingChain.class ); + + private BufferReceiver mComplexReceiver = new BufferReceiver<>( 5 ); + private BufferReceiver mRealReceiver = new BufferReceiver<>( 5 ); + + private Broadcaster mAudioPacketBroadcaster = new Broadcaster<>(); + private Broadcaster mMetadataBroadcaster = new Broadcaster<>(); + private Broadcaster mCallEventBroadcaster = new Broadcaster<>(); + private Broadcaster mChannelEventBroadcaster = new Broadcaster<>(); + private Broadcaster mComplexBufferBroadcaster = new Broadcaster<>(); + private Broadcaster mDecoderStateEventBroadcaster = new Broadcaster<>(); + private Broadcaster mMessageBroadcaster = new Broadcaster<>(); + private Broadcaster mRealBufferBroadcaster = new Broadcaster<>(); + private Broadcaster mSquelchStateBroadcaster = new Broadcaster<>(); + + private ThreadPoolManager mThreadPoolManager; + private ScheduledFuture mBufferProcessingTask; + private AtomicBoolean mRunning = new AtomicBoolean(); + + private Source mSource; + private List mModules = new ArrayList<>(); + private IFrequencyCorrectionController mFrequencyCorrectionController; + + public ProcessingChain() + { + this( new ThreadPoolManager() ); + } + + public ProcessingChain( ThreadPoolManager threadManager ) + { + mThreadPoolManager = threadManager; + } + + public void dispose() + { + stop(); + + for( Module module: mModules ) + { + module.dispose(); + } + + mModules.clear(); + + mComplexReceiver.dispose(); + mComplexReceiver = null; + + mRealReceiver.dispose(); + mRealReceiver = null; + + mThreadPoolManager = null; + mBufferProcessingTask = null; + mFrequencyCorrectionController = null; + + mAudioPacketBroadcaster.dispose(); + mCallEventBroadcaster.dispose(); + mChannelEventBroadcaster.dispose(); + mComplexBufferBroadcaster.dispose(); + mMessageBroadcaster.dispose(); + mRealBufferBroadcaster.dispose(); + mSquelchStateBroadcaster.dispose(); + } + + /** + * Indicates if this processing chain is currently receiving samples from + * a source and sending those samples to the decoders. + */ + public boolean processing() + { + return mRunning.get(); + } + + /** + * Indicates if this chain currently has a valid sample source. + */ + public boolean hasSource() + { + return mSource != null; + } + + /** + * Indicates if one of the modules provides frequency error control. + */ + public boolean hasFrequencyCorrectionControl() + { + return mFrequencyCorrectionController != null && + mFrequencyCorrectionController.hasFrequencyCorrectionControl(); + } + + /** + * Frequency correction controller. Can be null. + */ + public FrequencyCorrectionControl getFrequencyCorrectionControl() + { + if( mFrequencyCorrectionController != null ) + { + return mFrequencyCorrectionController.getFrequencyCorrectionControl(); + } + + return null; + } + + /** + * Applies a sample source to this processing chain. Processing won't + * start until the start() method is invoked. + * + * @param source - real or complex sample source + * + * @throws IllegalStateException if the processing chain is currently + * processing with another source. Invoke stop() before applying a new + * source. + */ + public void setSource( Source source ) throws IllegalStateException + { + if( processing() ) + { + throw new IllegalStateException( "Processing chain is currently " + + "processing. Invoke stop() on the processing chain before " + + "applying a new sample source" ); + } + + mSource = source; + + /* Establish two-way communication between the tuner channel source and + * the decoder for fine tuning frequency error correction feedback from + * the decoder. The tuner channel source will also notify the frequency + * controller during frequency changes to allow the controller to reset + * frequency error tracking */ + if( mSource != null && + mSource instanceof TunerChannelSource && + hasFrequencyCorrectionControl() ) + { + TunerChannelSource channel = (TunerChannelSource)mSource; + + FrequencyCorrectionControl control = getFrequencyCorrectionControl(); + + channel.setFrequencyChangeListener( control ); + control.setFrequencyChangeListener( channel ); + } + } + + /** + * List of current modules for this processing chain + */ + public List getModules() + { + return mModules; + } + + /** + * List of decoders for this processing chain + */ + public List getDecoders() + { + List decoders = new ArrayList<>(); + + for( Module module: mModules ) + { + if( module instanceof Decoder ) + { + decoders.add( (Decoder)module ); + } + } + + return decoders; + } + + /** + * Adds a module to the processing chain. Each module is tested for the + * interfaces that it supports and is registered or receives a listener + * to consume or produce the supported interface data type. All elements + * that are produced by any component are automatically routed to all other + * components that support the corresponding listener interface. + * + * At least one module should consume complex samples and either produce + * decoded messages and/or audio, or produce decoded real sample buffers + * for all other modules to consume. + * + * @param module - processing module, demodulator or decoder + */ + public void addModule( Module module ) + { + mModules.add( module ); + + /* Listeners */ + if( module instanceof IMetadataListener ) + { + mMetadataBroadcaster.addListener( + ((IMetadataListener)module).getMetadataListener() ); + } + + if( module instanceof IChannelEventListener ) + { + mChannelEventBroadcaster.addListener( + ((IChannelEventListener)module).getChannelEventListener() ); + } + + if( module instanceof IComplexBufferListener ) + { + mComplexBufferBroadcaster.addListener( + ((IComplexBufferListener)module).getComplexBufferListener() ); + } + + if( module instanceof IDecoderStateEventListener ) + { + mDecoderStateEventBroadcaster.addListener( + ((IDecoderStateEventListener)module).getDecoderStateListener() ); + } + + if( module instanceof IRealBufferListener ) + { + mRealBufferBroadcaster.addListener( + ((IRealBufferListener)module).getRealBufferListener() ); + } + + if( module instanceof ISquelchStateListener ) + { + mSquelchStateBroadcaster.addListener( + ((ISquelchStateListener)module).getSquelchStateListener() ); + } + + /* Providers */ + if( module instanceof IMetadataProvider ) + { + ((IMetadataProvider)module).setMetadataListener( + mMetadataBroadcaster ); + } + + if( module instanceof IAudioPacketProvider ) + { + ((IAudioPacketProvider)module).setAudioPacketListener( + mAudioPacketBroadcaster ); + } + + if( module instanceof ICallEventProvider ) + { + ((ICallEventProvider)module).setCallEventListener( mCallEventBroadcaster ); + } + + if( module instanceof IChannelEventProvider ) + { + ((IChannelEventProvider)module).setChannelEventListener( + mChannelEventBroadcaster ); + } + + if( module instanceof IDecoderStateEventProvider ) + { + ((IDecoderStateEventProvider)module).setDecoderStateListener( + mDecoderStateEventBroadcaster ); + } + + if( module instanceof IFrequencyCorrectionController ) + { + mFrequencyCorrectionController = (IFrequencyCorrectionController)module; + } + + if( module instanceof IMessageProvider ) + { + ((IMessageProvider)module).addMessageListener( mMessageBroadcaster ); + } + + if( module instanceof IRealBufferProvider ) + { + ((IRealBufferProvider)module).setRealBufferListener( mRealBufferBroadcaster ); + } + + if( module instanceof ISquelchStateProvider ) + { + ((ISquelchStateProvider)module).setSquelchStateListener( + mSquelchStateBroadcaster ); + } + } + + /** + * Adds the list of modules to this processing chain + */ + public void addModules( List modules ) + { + for( Module module: modules ) + { + addModule( module ); + } + } + + + /** + * Starts processing if the chain has a valid source. Invocations on an + * already started chain have no effect. + */ + public void start() + { + if( mRunning.compareAndSet( false, true ) ) + { + if( mSource != null ) + { + /* Initialize each of the modules */ + for( Module module: mModules ) + { + module.init(); + } + + /* Register with the source to receive sample data. Setup a + * timer task to process the buffer queues 50 times a second + * (every 20 ms) */ + mBufferProcessingTask = mThreadPoolManager.scheduleFixedRate( + ThreadType.DECODER, new BufferProcessor(), 20, TimeUnit.MILLISECONDS ); + + switch( mSource.getSampleType() ) + { + case COMPLEX: + ((ComplexSource)mSource).setListener( mComplexBufferBroadcaster ); + break; + case REAL: + ((RealSource)mSource).setListener( mRealBufferBroadcaster ); + break; + default: + throw new IllegalArgumentException( "Unrecognized source " + + "sample type - cannot start processing chain" ); + } + + /* If this is a tuner source, broadcast the frequency to all + * of the decoder state's */ + if( mSource instanceof TunerChannelSource ) + { + try + { + long frequency = ((TunerChannelSource)mSource).getFrequency(); + + mDecoderStateEventBroadcaster.broadcast( + new DecoderStateEvent( this, Event.SOURCE_FREQUENCY, + State.IDLE, frequency ) ); + } + catch( SourceException e ) + { + mLog.error( "Error getting frequency from tuner channel source", e ); + } + } + } + } + } + + /** + * Stops processing if the chain is currently processing. Invocations on + * an already stopped chain have no effect. + */ + public void stop() + { + if( mRunning.compareAndSet( true, false ) ) + { + if( mSource != null ) + { + switch( mSource.getSampleType() ) + { + case COMPLEX: + ((ComplexSource)mSource).removeListener( mComplexBufferBroadcaster ); + break; + case REAL: + ((RealSource)mSource).removeListener( mRealBufferBroadcaster ); + break; + default: + throw new IllegalArgumentException( "Unrecognized source " + + "sample type - cannot start processing chain" ); + } + + /* Release the source */ + mSource.dispose(); + mSource = null; + } + + if( mBufferProcessingTask != null ) + { + mThreadPoolManager.cancel( mBufferProcessingTask ); + mBufferProcessingTask = null; + } + } + } + + /** + * Adds the listener to receive audio packets from all modules. + */ + public void addAudioPacketListener( Listener listener ) + { + mAudioPacketBroadcaster.addListener( listener ); + } + + public void removeAudioPacketListener( Listener listener ) + { + mAudioPacketBroadcaster.removeListener( listener ); + } + + /** + * Adds the listener to receive call events from all modules. + */ + public void addCallEventListener( Listener listener ) + { + mCallEventBroadcaster.addListener( listener ); + } + + public void removeCallEventListener( Listener listener ) + { + mCallEventBroadcaster.removeListener( listener ); + } + + /** + * Adds the listener to receive call events from all modules. + */ + public void addChannelEventListener( Listener listener ) + { + mChannelEventBroadcaster.addListener( listener ); + } + + public void removeChannelEventListener( Listener listener ) + { + mChannelEventBroadcaster.removeListener( listener ); + } + + /** + * Adds the listener to receive decoder state events from decoder modules + */ + public void addDecoderStateEventListener( Listener listener ) + { + mDecoderStateEventBroadcaster.addListener( listener ); + } + + public void removeDecoderStateEventListener( Listener listener) + { + mDecoderStateEventBroadcaster.removeListener( listener ); + } + + public Listener getDecoderStateEventListener() + { + return mDecoderStateEventBroadcaster; + } + + /** + * Adds the listener to receive decoded messages from all decoders. + */ + public void addMessageListener( Listener listener ) + { + mMessageBroadcaster.addListener( listener ); + } + + /** + * Adds the list of listeners to receive decoded messages from all decoders. + */ + public void addMessageListeners( List> listeners ) + { + for( Listener listener: listeners ) + { + mMessageBroadcaster.addListener( listener ); + } + } + + public void removeMessageListener( Listener listener ) + { + mMessageBroadcaster.removeListener( listener ); + } + + /** + * Adds the listener to receive call events from all modules. + */ + public void addSquelchStateListener( Listener listener ) + { + mSquelchStateBroadcaster.addListener( listener ); + } + + public void removeSquelchStateListener( Listener listener ) + { + mSquelchStateBroadcaster.removeListener( listener ); + } + + public void addRealBufferListener( Listener listener ) + { + mRealBufferBroadcaster.addListener( listener ); + } + + public void removeRealBufferListener( Listener listener ) + { + mRealBufferBroadcaster.removeListener( listener ); + } + + + /** + * Processes complex and real sample buffers and broadcasts buffers to any + * registered listeners. + */ + private class BufferProcessor implements Runnable + { + private AtomicBoolean mProcessing = new AtomicBoolean(); + + @Override + public void run() + { + /* Allow only one instance of this processor to run to ensure + * correct serial processing of incoming buffers. Overlapping + * invocations of this processor by the timer service will end + * immediately without doing anything for cases where a single + * iteration runs longer than the scheduled processing interval */ + if( mProcessing.compareAndSet( false, true ) ) + { + try + { + for( ComplexBuffer buffer: mComplexReceiver.get() ) + { + mComplexBufferBroadcaster.broadcast( buffer ); + } + } + catch( Exception e ) + { + mLog.error( "Error processing complex sample buffers", e ); + } + + try + { + for( RealBuffer buffer: mRealReceiver.get() ) + { + mRealBufferBroadcaster.broadcast( buffer ); + } + } + catch( Exception e ) + { + mLog.error( "Error processing real sample buffers", e ); + } + + mProcessing.set( false ); + } + } + } + + /** + * Buffer receiver - non-blocking receiver to allow calling thread to return + * without blocking on any internal processing. This ensures that we don't + * impair any receiver sample processing. + */ + private class BufferReceiver implements Listener + { + private LinkedTransferQueue mQueue = new LinkedTransferQueue<>(); + private int mMaxFetchSize; + + public BufferReceiver( int maxFetchSize ) + { + mMaxFetchSize = maxFetchSize; + } + + @Override + public void receive( T t ) + { + mQueue.add( t ); + } + + public List get() + { + List buffers = new ArrayList<>(); + + mQueue.drainTo( buffers, mMaxFetchSize ); + + return buffers; + } + + public void dispose() + { + mQueue.clear(); + mQueue = null; + } + } + + @Override + public Listener getChannelEventListener() + { + return mChannelEventBroadcaster; + } +} diff --git a/src/controller/channel/ProcessingChain.java b/src/module/ProcessingChainOld.java similarity index 76% rename from src/controller/channel/ProcessingChain.java rename to src/module/ProcessingChainOld.java index 38a54674..2d97275a 100644 --- a/src/controller/channel/ProcessingChain.java +++ b/src/module/ProcessingChainOld.java @@ -15,9 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel; - -import gui.SDRTrunk; +package module; import java.io.IOException; import java.util.ArrayList; @@ -29,6 +27,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import message.Message; +import module.decode.Decoder; +import module.decode.state.ChannelState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,30 +38,21 @@ import record.wave.ComplexWaveRecorder; import record.wave.FloatWaveRecorder; import sample.Broadcaster; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import sample.real.RealBuffer; import sample.real.RealSampleListener; import source.ComplexSource; import source.RealSource; import source.Source; -import source.SourceException; import source.config.SourceConfigMixer; import source.tuner.Tuner; import source.tuner.TunerChannelSource; -import source.tuner.frequency.AutomaticFrequencyControl; -import source.tuner.frequency.FrequencyCorrectionControl; +import source.tuner.frequency.AutomaticFrequencyControl_R; import alias.AliasList; -import audio.IAudioOutput; -import audio.SquelchListener; -import audio.inverted.IAudioTypeListener; import controller.ResourceManager; import controller.ThreadPoolManager.ThreadType; -import controller.activity.CallEvent; +import controller.channel.Channel; import controller.channel.Channel.ChannelType; -import controller.state.ChannelState; -import controller.state.ChannelState.State; -import decode.Decoder; -import decode.DecoderFactory; import eventlog.EventLogType; import eventlog.EventLogger; @@ -69,9 +60,9 @@ import eventlog.EventLogger; * A channel has all of the pieces needed to wire together a source, decoder, * event logger and recorder and be started and stopped. */ -public class ProcessingChain implements Listener +public class ProcessingChainOld implements Listener { - private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class ); + private final static Logger mLog = LoggerFactory.getLogger( ProcessingChainOld.class ); protected AtomicBoolean mRunning = new AtomicBoolean(); protected Channel mChannel; @@ -79,15 +70,15 @@ public class ProcessingChain implements Listener protected Decoder mDecoder; protected List mRecorders = new ArrayList(); protected ArrayList mEventLoggers = new ArrayList(); - protected AutomaticFrequencyControl mAFC; + protected AutomaticFrequencyControl_R mAFC; protected ChannelState mChannelState; protected ResourceManager mResourceManager; - private ScheduledFuture mProcessorTask; + private ScheduledFuture mSampleProcessorTask; private AliasList mAliasList; private ComplexSampleReceiver mComplexReceiver = new ComplexSampleReceiver(); - private LinkedTransferQueue> mComplexQueue = - new LinkedTransferQueue>(); + private LinkedTransferQueue> mComplexQueue = + new LinkedTransferQueue>(); private RealSampleReceiver mRealReceiver = new RealSampleReceiver(); private LinkedTransferQueue mRealQueue = @@ -95,7 +86,7 @@ public class ProcessingChain implements Listener protected Broadcaster mBroadcaster = new Broadcaster(); - public ProcessingChain( Channel channel, ResourceManager resourceManager ) + public ProcessingChainOld( Channel channel, ResourceManager resourceManager ) { mChannel = channel; mResourceManager = resourceManager; @@ -108,17 +99,17 @@ public class ProcessingChain implements Listener } } - public IAudioOutput getAudioOutput() - { - if( mDecoder != null ) - { - return mDecoder.getAudioOutput(); - } - - return null; - } +// public IAudioOutput getAudioOutput() +// { +// if( mDecoder != null ) +// { +// return mDecoder.getAudioOutput(); +// } +// +// return null; +// } - public Listener> getComplexReceiver() + public Listener> getComplexReceiver() { return mComplexReceiver; } @@ -140,9 +131,9 @@ public class ProcessingChain implements Listener public void dispose() { - if( mProcessorTask != null ) + if( mSampleProcessorTask != null ) { - mResourceManager.getThreadPoolManager().cancel( mProcessorTask ); + mResourceManager.getThreadPoolManager().cancel( mSampleProcessorTask ); } if( mChannelState != null ) @@ -206,7 +197,7 @@ public class ProcessingChain implements Listener /* Flush call events to the logger */ if( getChannel().getChannelType() == ChannelType.STANDARD ) { - getChannelState().getCallEventModel().flush(); +// getChannelState().getCallEventModel().flush(); } updateEventLogging(); @@ -225,14 +216,13 @@ public class ProcessingChain implements Listener return "Channel [" + mChannel.getName() + "] - "; } - public void updateSource() + private void releaseSource() { - /* Cleanup existing source */ if( mSource != null ) { if( mSource instanceof ComplexSource ) { - ((ComplexSource)mSource).removeListener( this.getComplexReceiver() ); +// ((ComplexSource)mSource).removeAttributeListener( this.getComplexReceiver() ); } else if( mSource instanceof RealSource ) { @@ -240,31 +230,38 @@ public class ProcessingChain implements Listener .removeListener( this.getRealReceiver() ); } - if( mProcessorTask != null ) + if( mSampleProcessorTask != null ) { - mResourceManager.getThreadPoolManager().cancel( mProcessorTask ); + mResourceManager.getThreadPoolManager().cancel( mSampleProcessorTask ); } mSource.dispose(); mSource = null; } + } + + private void obtainSource() + { + if( mSource != null ) + { + releaseSource(); + } - /* Get a new source if needed and start the sample processor thread */ if( mChannel.getEnabled() && mResourceManager != null ) { - try - { - mSource = mResourceManager.getSourceManager() - .getSource( ProcessingChain.this ); - } - catch ( SourceException e ) - { - mLog.error( "Couldn't obtain source for processing chain [" + - toString() + "]", e ); - - mSource = null; - } +// try +// { +// mSource = mResourceManager.getSourceManager() +// .getSource( ProcessingChainOld.this ); +// } +// catch ( SourceException e ) +// { +// mLog.error( "Couldn't obtain source for processing chain [" + +// toString() + "]", e ); +// +// mSource = null; +// } if( mSource != null ) { @@ -272,14 +269,14 @@ public class ProcessingChain implements Listener { try { - mProcessorTask = + mSampleProcessorTask = mResourceManager.getThreadPoolManager() .scheduleFixedRate( ThreadType.DECODER, new ComplexProcessor(), 50, TimeUnit.MILLISECONDS ); - ((ComplexSource)mSource) - .setListener( this.getComplexReceiver() ); +// ((ComplexSource)mSource) +// .setListener( this.getComplexReceiver() ); } catch( RejectedExecutionException ree ) { @@ -294,7 +291,7 @@ public class ProcessingChain implements Listener { try { - mProcessorTask = + mSampleProcessorTask = mResourceManager.getThreadPoolManager() .scheduleFixedRate( ThreadType.DECODER, new RealProcessor(), @@ -316,61 +313,67 @@ public class ProcessingChain implements Listener } } + public void updateSource() + { + /* Cleanup existing source */ + releaseSource(); + + /* Get a new source if needed and start the sample processor thread */ + obtainSource(); + } + public void updateDecoder() { if( mChannel.getEnabled() ) { - AliasList aliasList = null; - - if( mResourceManager != null ) + if( mDecoder != null ) { - aliasList = mResourceManager.getPlaylistManager() - .getPlayist().getAliasDirectory().getAliasList( - mChannel.getAliasListName() ); - } - - /* Setup the channel state */ - mChannelState = DecoderFactory.getChannelStateNew( this, aliasList ); - - if( mSource == null ) - { - mChannelState.setState( State.NO_TUNER ); + mDecoder.init(); } else { - mDecoder = DecoderFactory. - getDecoder( this, mSource.getSampleType(), aliasList ); - - if( mDecoder != null ) - { - /* Register to receive decoded messages and auxiliary messages */ - mDecoder.addMessageListener( ProcessingChain.this ); +// /* Setup the channel state */ +// mChannelState = DecoderFactory.getChannelStateNew( this, mAliasList ); - /* Establish two-way communication between the frequency - * correction controller and the tuner channel source */ - if( mSource instanceof TunerChannelSource && - mDecoder.hasFrequencyCorrectionControl() ) - { - TunerChannelSource tcs = (TunerChannelSource)mSource; - - FrequencyCorrectionControl fcc = - mDecoder.getFrequencyCorrectionControl(); - - if( fcc != null ) - { - tcs.addListener( fcc ); - fcc.setListener( tcs ); - } - } - - /* Register audio output to be controlled by the channel state */ - mChannelState.addListener( (SquelchListener)mDecoder.getAudioOutput() ); - mChannelState.setListener( (IAudioTypeListener)mDecoder.getAudioOutput() ); + if( mSource == null ) + { +// mChannelState.setState( State.NO_TUNER ); + } + else + { +// mDecoder = DecoderFactory. +// getDecoder( this, mSource.getSampleType(), mAliasList ); + + if( mDecoder != null ) + { + /* Register to receive decoded messages and auxiliary messages */ + mDecoder.addMessageListener( ProcessingChainOld.this ); + + /* Establish two-way communication between the frequency + * correction controller and the tuner channel source */ +// if( mSource instanceof TunerChannelSource && +// mDecoder.hasFrequencyCorrectionControl() ) +// { +// TunerChannelSource tcs = (TunerChannelSource)mSource; +// +// FrequencyCorrectionControl fcc = +// mDecoder.getFrequencyCorrectionController(); +// +// if( fcc != null ) +// { +// tcs.addListener( fcc ); +// fcc.setListener( tcs ); +// } + } + + /* Register audio output to be controlled by the channel state */ +// mChannelState.addListener( (SquelchListener)mDecoder.getAudioOutput() ); +// mChannelState.setListener( (IAudioTypeListener)mDecoder.getAudioOutput() ); + } } - } } - } +// } /** * Starts, stops or changes event logging @@ -405,8 +408,8 @@ public class ProcessingChain implements Listener logger = mResourceManager.getEventLogManager() .getLogger( this, type ); - getChannelState().getCallEventModel() - .addListener( (Listener)logger ); +// getChannelState().getCallEventModel() +// .addListener( (Listener)logger ); } else { @@ -468,14 +471,14 @@ public class ProcessingChain implements Listener mLog.info( getLogPrefix() + "- started audio recording [" + recorder.getFileName() + "]" ); - mDecoder.addRealSampleListener( (FloatWaveRecorder)recorder ); +// mDecoder.addRealSampleListener( (FloatWaveRecorder)recorder ); } else if( recorder instanceof ComplexWaveRecorder ) { mLog.info( getLogPrefix() + "- started baseband recording [" + recorder.getFileName() + "]" ); - mDecoder.addComplexListener( (ComplexWaveRecorder )recorder ); +// mDecoder.addComplexListener( (ComplexWaveRecorder )recorder ); } } @@ -565,7 +568,7 @@ public class ProcessingChain implements Listener { if( mDecoder != null ) { - mDecoder.addRealSampleListener( listener ); +// mDecoder.addRealSampleListener( listener ); } } @@ -576,7 +579,7 @@ public class ProcessingChain implements Listener { if( mDecoder != null ) { - mDecoder.removeRealListener( listener ); +// mDecoder.removeRealSampleListener( listener ); } } @@ -584,11 +587,11 @@ public class ProcessingChain implements Listener * Adds a listener to receive a copy of complex (I/Q) baseband samples * received by this processing chain */ - public void addComplexListener( Listener listener ) + public void addComplexListener( Listener listener ) { if( mDecoder != null ) { - mDecoder.addComplexListener( listener ); +// mDecoder.addComplexListener( listener ); } else { @@ -601,11 +604,11 @@ public class ProcessingChain implements Listener * Removes the listener from receiving a copy of complex (I/Q) baseband * samples received by this processing chain */ - public void removeComplexListener( Listener listener ) + public void removeComplexListener( Listener listener ) { if( mDecoder != null ) { - mDecoder.removeComplexListener( listener ); +// mDecoder.removeComplexListener( listener ); } else { @@ -663,7 +666,7 @@ public class ProcessingChain implements Listener for( float sample: samples ) { - mDecoder.getRealReceiver().receive( sample ); +// mDecoder.getRealReceiver().receive( sample ); } } @@ -689,18 +692,18 @@ public class ProcessingChain implements Listener { try { - List> sampleSets = - new ArrayList>(); + List> sampleSets = + new ArrayList>(); mComplexQueue.drainTo( sampleSets, 16 ); - for( List samples: sampleSets ) + for( List samples: sampleSets ) { - for( ComplexSample sample: samples ) + for( Complex sample: samples ) { if( sample != null && mDecoder != null ) { - mDecoder.getComplexReceiver().receive( sample ); +// mDecoder.getComplexReceiver().receive( sample ); } } @@ -720,10 +723,10 @@ public class ProcessingChain implements Listener * Internal listener to receive complex (I/Q) baseband samples. Places * received samples into the queue managed by the ComplexProcessor. */ - public class ComplexSampleReceiver implements Listener> + public class ComplexSampleReceiver implements Listener> { @Override - public void receive( List samples ) + public void receive( List samples ) { mComplexQueue.add( samples ); } diff --git a/src/decode/AuxDecodeComponentEditor.java b/src/module/decode/AuxDecodeComponentEditor.java similarity index 97% rename from src/decode/AuxDecodeComponentEditor.java rename to src/module/decode/AuxDecodeComponentEditor.java index 5013c8f0..209018f5 100644 --- a/src/decode/AuxDecodeComponentEditor.java +++ b/src/module/decode/AuxDecodeComponentEditor.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode; +package module.decode; import java.util.Collections; import java.util.HashMap; @@ -24,11 +24,11 @@ import java.util.List; import javax.swing.JCheckBox; import javax.swing.SwingUtilities; +import module.decode.config.AuxDecodeConfiguration; import controller.Editor; import controller.channel.AbstractChannelEditor; import controller.channel.ChannelNode; import controller.channel.ChannelValidationException; -import decode.config.AuxDecodeConfiguration; public class AuxDecodeComponentEditor extends AbstractChannelEditor { diff --git a/src/decode/DecodeComponentEditor.java b/src/module/decode/DecodeComponentEditor.java similarity index 88% rename from src/decode/DecodeComponentEditor.java rename to src/module/decode/DecodeComponentEditor.java index 8061cea9..6f35e569 100644 --- a/src/decode/DecodeComponentEditor.java +++ b/src/module/decode/DecodeComponentEditor.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode; +package module.decode; import java.awt.event.ActionEvent; @@ -23,14 +23,16 @@ import java.awt.event.ActionListener; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; +import javax.swing.JLabel; import javax.swing.SwingUtilities; +import net.miginfocom.swing.MigLayout; +import module.decode.config.DecodeConfigFactory; +import module.decode.config.DecodeConfiguration; import controller.Editor; import controller.channel.AbstractChannelEditor; import controller.channel.ChannelNode; import controller.channel.ChannelValidationException; -import decode.config.DecodeConfigFactory; -import decode.config.DecodeConfiguration; public class DecodeComponentEditor extends AbstractChannelEditor { @@ -44,6 +46,8 @@ public class DecodeComponentEditor extends AbstractChannelEditor { super( channelNode ); + setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][grow]" ) ); + /** * ComboBox: Decoders */ @@ -52,7 +56,7 @@ public class DecodeComponentEditor extends AbstractChannelEditor DefaultComboBoxModel model = new DefaultComboBoxModel(); - for( DecoderType type: DecoderType.getAvailableDecoders() ) + for( DecoderType type: DecoderType.getPrimaryDecoders() ) { model.addElement( type ); } @@ -91,15 +95,16 @@ public class DecodeComponentEditor extends AbstractChannelEditor } //Change to the new one - mEditor = DecodeEditorFactory.getPanel( config, mChannelNode ); + mEditor = DecoderFactory.getPanel( config, mChannelNode ); - add( mEditor, "span 2" ); + add( mEditor, "span 2,center" ); revalidate(); repaint(); } } }); + add( new JLabel( "Decoder:" ) ); add( mComboDecoders, "wrap" ); reset(); diff --git a/src/decode/DecodeEditor.java b/src/module/decode/DecodeEditor.java similarity index 91% rename from src/decode/DecodeEditor.java rename to src/module/decode/DecodeEditor.java index 09ef5340..a3e37865 100644 --- a/src/decode/DecodeEditor.java +++ b/src/module/decode/DecodeEditor.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode; +package module.decode; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -29,10 +29,11 @@ import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import net.miginfocom.swing.MigLayout; +import module.decode.config.DecodeConfiguration; import controller.Editor; import controller.channel.Channel; import controller.channel.ChannelValidationException; -import decode.config.DecodeConfiguration; public class DecodeEditor extends Editor { @@ -45,6 +46,8 @@ public class DecodeEditor extends Editor public DecodeEditor( DecodeConfiguration config ) { + setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][grow]" ) ); + mConfig = config; if( config.supportsAFC() ) @@ -93,8 +96,8 @@ public class DecodeEditor extends Editor } } ); - add( mAFCMaximumCorrection ); - add( new JLabel( "Correction Limit Hz" ), "growx, push" ); + add( new JLabel( "Correction Limit Hz:" ) ); + add( mAFCMaximumCorrection, "wrap" ); } } diff --git a/src/module/decode/Decoder.java b/src/module/decode/Decoder.java new file mode 100644 index 00000000..ea216ff5 --- /dev/null +++ b/src/module/decode/Decoder.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode; + +import message.IMessageProvider; +import message.Message; +import module.Module; +import module.decode.event.CallEvent; +import module.decode.event.ICallEventProvider; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.IDecoderStateEventListener; +import module.decode.state.IDecoderStateEventProvider; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Broadcaster; +import sample.Listener; + +public abstract class Decoder extends Module + implements ICallEventProvider, + IDecoderStateEventListener, + IDecoderStateEventProvider, + IMessageProvider +{ + private final static Logger mLog = LoggerFactory.getLogger( Decoder.class ); + + /** + * Broadcasts decoded messages both to the internal decoder state and any + * external registered message listeners + */ + protected Broadcaster mMessageBroadcaster = + new Broadcaster(); + + protected DecoderState mDecoderState; + + /** + * Decoder - parent class for all decoders, demodulators and components. + */ + public Decoder( DecoderState decoderState ) + { + assert( decoderState != null ); + + mDecoderState = decoderState; + mMessageBroadcaster.addListener( mDecoderState ); + } + + @Override + public void dispose() + { + mMessageBroadcaster.dispose(); + mMessageBroadcaster = null; + } + + /** + * Allows decoder to broadcast initialization messages/events so that + * external modules can be correctly configured. This method is invoked + * prior to processing start. + */ + public void init() + { + mDecoderState.init(); + } + + /** + * Identifies the decoder type (ie protocol) + */ + public abstract DecoderType getDecoderType(); + + + /** + * Adds a listener for receiving decoded messages from this decoder + */ + @Override + public void addMessageListener( Listener listener ) + { + mMessageBroadcaster.addListener( listener ); + } + + /** + * Removes the listener from receiving decoded messages from all attached + * decoders + */ + @Override + public void removeMessageListener( Listener listener ) + { + mMessageBroadcaster.removeListener( listener ); + } + + /** + * Decoder State - tracks the state of the decoder + */ + public DecoderState getDecoderState() + { + return mDecoderState; + } + + @Override + public Listener getDecoderStateListener() + { + return mDecoderState.getDecoderStateListener(); + } + + @Override + public void setDecoderStateListener( Listener listener ) + { + mDecoderState.setDecoderStateListener( listener ); + } + + @Override + public void removeDecoderStateListener() + { + mDecoderState.removeDecoderStateListener(); + } + + @Override + public void setCallEventListener( Listener listener ) + { + mDecoderState.setCallEventListener( listener ); + } + + @Override + public void removeCallEventListener() + { + mDecoderState.removeCallEventListener(); + } +} diff --git a/src/module/decode/DecoderFactory.java b/src/module/decode/DecoderFactory.java new file mode 100644 index 00000000..a02f4a99 --- /dev/null +++ b/src/module/decode/DecoderFactory.java @@ -0,0 +1,416 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import message.Message; +import message.MessageDirection; +import module.Module; +import module.decode.config.AuxDecodeConfiguration; +import module.decode.config.DecodeConfiguration; +import module.decode.fleetsync2.Fleetsync2Decoder; +import module.decode.fleetsync2.FleetsyncMessageFilter; +import module.decode.lj1200.LJ1200Decoder; +import module.decode.lj1200.LJ1200MessageFilter; +import module.decode.ltrnet.DecodeConfigLTRNet; +import module.decode.ltrnet.LTRNetDecoder; +import module.decode.ltrnet.LTRNetDecoderPanel; +import module.decode.ltrnet.LTRNetEditor; +import module.decode.ltrnet.LTRNetMessageFilter; +import module.decode.ltrstandard.DecodeConfigLTRStandard; +import module.decode.ltrstandard.LTRStandardDecoder; +import module.decode.ltrstandard.LTRStandardDecoderPanel; +import module.decode.ltrstandard.LTRStandardEditor; +import module.decode.ltrstandard.LTRStandardMessageFilter; +import module.decode.mdc1200.MDCDecoder; +import module.decode.mdc1200.MDCMessageFilter; +import module.decode.mpt1327.DecodeConfigMPT1327; +import module.decode.mpt1327.MPT1327ConfigEditor; +import module.decode.mpt1327.MPT1327Decoder; +import module.decode.mpt1327.MPT1327Decoder.Sync; +import module.decode.mpt1327.MPT1327DecoderPanel; +import module.decode.mpt1327.MPT1327MessageFilter; +import module.decode.nbfm.NBFMDecoder; +import module.decode.nbfm.NBFMDecoderPanel; +import module.decode.nbfm.NBFMEditor; +import module.decode.p25.DecodeConfigP25Phase1; +import module.decode.p25.P25Decoder; +import module.decode.p25.P25Decoder.Modulation; +import module.decode.p25.P25DecoderPanel; +import module.decode.p25.P25Editor; +import module.decode.p25.P25_C4FMDecoder; +import module.decode.p25.P25_LSMDecoder; +import module.decode.p25.audio.P25AudioModule; +import module.decode.p25.message.filter.P25MessageFilterSet; +import module.decode.passport.PassportEditor; +import module.decode.passport.PassportMessageFilter; +import module.decode.state.DecoderPanel; +import module.decode.state.TrafficChannelManager; +import module.decode.tait.Tait1200Decoder; +import module.demodulate.fm.FMDemodulatorModule; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import properties.SystemProperties; +import settings.SettingsManager; +import util.TimeStamp; +import alias.AliasList; +import audio.AudioModule; +import controller.ResourceManager; +import controller.channel.Channel.ChannelType; +import controller.channel.ChannelNode; +import controller.channel.map.ChannelMap; +import filter.AllPassFilter; +import filter.FilterSet; +import filter.IFilter; + +public class DecoderFactory +{ + private final static Logger mLog = LoggerFactory.getLogger( DecoderFactory.class ); + + public static final boolean NO_DC_REMOVAL = false; + public static final boolean REMOVE_DC = true; + + public static final int NO_FREQUENCY_CORRECTION = 0; + + /** + * Returns a list of one primary decoder and any auxiliary decoders, as + * specified in the configurations. + * + * @param config - primary decoder configuration + * @param auxConfig - auxiliary decoder(s) configuration + * + * @return list of configured decoders + */ + public static List getModules( ChannelType channelType, + ResourceManager resourceManager, + DecodeConfiguration config, + AuxDecodeConfiguration auxConfig, + AliasList aliasList ) + { + List modules = getPrimaryModules( channelType, config, + aliasList, resourceManager ); + + modules.addAll( getAuxiliaryDecoders( auxConfig, aliasList ) ); + + return modules; + } + + public static FMDemodulatorModule getFMDemodulator( DecodeConfiguration config, int pass, int stop, boolean removeDC ) + { + if( config.isAFCEnabled() ) + { + return new FMDemodulatorModule( pass, stop, config.getAFCMaximumCorrection(), removeDC ); + } + else + { + return new FMDemodulatorModule( pass, stop, NO_FREQUENCY_CORRECTION, removeDC ); + } + } + + /** + * Constructs a primary decoder as specified in the decode configuration + * @param config - primary decoder configuration + * @param aliasList - optional alias list + * @return configured decoder or null + */ + public static List getPrimaryModules( ChannelType channelType, + DecodeConfiguration config, + AliasList aliasList, + ResourceManager resourceManager ) + { + List modules = new ArrayList(); + + /* Baseband low-pass filter pass and stop frequencies */ + int pass = config.getDecoderType().getChannelBandwidth() / 2; + int stop = pass + 1250; + + switch( config.getDecoderType() ) + { + case AM: +// decoder = new AMDecoder(); + break; + case NBFM: + modules.add( new AudioModule() ); + modules.add( new NBFMDecoder( config ) ); + modules.add( getFMDemodulator( config, pass, stop, REMOVE_DC ) ); + break; + case LTR_STANDARD: + modules.add( new AudioModule( REMOVE_DC ) ); + MessageDirection direction = ((DecodeConfigLTRStandard)config).getMessageDirection(); + modules.add( new LTRStandardDecoder( aliasList, direction ) ); + modules.add( getFMDemodulator( config, pass, stop, NO_DC_REMOVAL ) ); + break; + case LTR_NET: + modules.add( new AudioModule( REMOVE_DC ) ); + modules.add( new LTRNetDecoder( (DecodeConfigLTRNet)config, aliasList ) ); +// modules.add( new ComplexBufferRecorderModule( getRecordingFilename( "decimated_baseband" ) ) ); + modules.add( getFMDemodulator( config, pass, stop, NO_DC_REMOVAL ) ); + break; + case MPT1327: + DecodeConfigMPT1327 mptConfig = (DecodeConfigMPT1327)config; + + modules.add( new AudioModule() ); + + ChannelMap channelMap = resourceManager.getPlaylistManager().getPlayist() + .getChannelMapList().getChannelMap( mptConfig.getChannelMapName() ); + Sync sync = mptConfig.getSync(); + modules.add( new MPT1327Decoder( channelMap, channelType, aliasList, sync ) ); + + if( channelType == ChannelType.STANDARD ) + { + long timeout = mptConfig.getCallTimeout() * 1000; //convert to milliseconds + + modules.add( new TrafficChannelManager( resourceManager, + config, timeout, + mptConfig.getTrafficChannelPoolSize() ) ); + } + +// modules.add( new RealBufferRecorderModule( getRecordingFilename( "demodulated_mpt1327" ) ) ); + modules.add( getFMDemodulator( config, pass, stop, REMOVE_DC ) ); + break; + case PASSPORT: +// decoder = new PassportDecoder( config, aliasList ); + break; + case P25_PHASE1: + DecodeConfigP25Phase1 p25Config = (DecodeConfigP25Phase1)config; + + Modulation modulation = p25Config.getModulation(); + + switch( modulation ) + { + case C4FM: + modules.add( getFMDemodulator( config, 6750, 7500, REMOVE_DC ) ); + modules.add( new P25_C4FMDecoder( aliasList, channelType ) ); + break; + case CQPSK: + modules.add( new P25_LSMDecoder( aliasList, channelType ) ); + break; + default: + throw new IllegalArgumentException( "Unrecognized P25 " + + "Phase 1 Modulation [" + modulation + "]" ); + } + + if( channelType == ChannelType.STANDARD ) + { + long timeout = p25Config.getCallTimeout() * 1000; //convert to milliseconds + + modules.add( new TrafficChannelManager( resourceManager, + config, timeout, + p25Config.getTrafficChannelPoolSize() ) ); + } + + modules.add( new P25AudioModule() ); + break; + default: + throw new IllegalArgumentException( + "Unknown decoder type [" + + config.getDecoderType().toString() + "]" ); + } + + return modules; + } + + /** + * Constructs a list of auxiliary decoders, as specified in the configuration + * + * @param config - auxiliary configuration + * @param aliasList - optional alias list + * @return - list of auxiliary decoders + */ + public static List getAuxiliaryDecoders( AuxDecodeConfiguration config, + AliasList aliasList ) + { + List decoders = new ArrayList(); + + for( DecoderType auxDecoder: config.getAuxDecoders() ) + { + switch( auxDecoder ) + { + case FLEETSYNC2: + decoders.add( new Fleetsync2Decoder( aliasList ) ); + break; + case MDC1200: + decoders.add( new MDCDecoder( aliasList ) ); + break; + case LJ_1200: + decoders.add( new LJ1200Decoder( aliasList ) ); + break; + case TAIT_1200: + decoders.add( new Tait1200Decoder( aliasList ) ); + break; + } + } + + return decoders; + } + + public static DecoderPanel getDecoderPanel( SettingsManager settingsManager, + Decoder decoder ) + { + switch( decoder.getDecoderType() ) + { +// case AM: +// break; +// case FLEETSYNC2: +// break; +// case LJ_1200: +// break; + case LTR_NET: + return new LTRNetDecoderPanel( settingsManager, (LTRNetDecoder)decoder ); + case LTR_STANDARD: + return new LTRStandardDecoderPanel( settingsManager, (LTRStandardDecoder)decoder ); +// case MDC1200: +// break; + case MPT1327: + return new MPT1327DecoderPanel( settingsManager, (MPT1327Decoder)decoder ); + case NBFM: + return new NBFMDecoderPanel( settingsManager, decoder ); + case P25_PHASE1: + return new P25DecoderPanel( settingsManager, (P25Decoder)decoder ); +// case PASSPORT: +// break; + case TAIT_1200: + break; + default: + throw new IllegalArgumentException( "Unrecognized decoder: " + + decoder.getDecoderType().getDisplayString() ); + } + + return null; + } + + /** + * Assembles a filter set containing filters for the primary channel + * decoder and each of the auxiliary decoders + */ + public static FilterSet getMessageFilters( List modules ) + { + FilterSet filterSet = new FilterSet<>(); + + for( Module module: modules ) + { + if( module instanceof Decoder ) + { + filterSet.addFilters( getMessageFilter( + ((Decoder)module).getDecoderType() ) ); + } + } + + /* If we don't have any filters, add an ALL-PASS filter */ + if( filterSet.getFilters().isEmpty() ) + { + filterSet.addFilter( new AllPassFilter() ); + } + + return filterSet; + } + + /** + * Returns a set of IMessageFilter objects (FilterSets or Filters) that + * can process all of the messages produced by the specified decoder type. + */ + public static List> getMessageFilter( DecoderType decoder ) + { + ArrayList> filters = new ArrayList>(); + + switch( decoder ) + { + case FLEETSYNC2: + filters.add( new FleetsyncMessageFilter() ); + break; + case LJ_1200: + filters.add( new LJ1200MessageFilter() ); + break; + case LTR_NET: + filters.add( new LTRNetMessageFilter() ); + break; + case LTR_STANDARD: + filters.add( new LTRStandardMessageFilter() ); + break; + case MDC1200: + filters.add( new MDCMessageFilter() ); + break; + case MPT1327: + filters.add( new MPT1327MessageFilter() ); + break; + case P25_PHASE1: + case P25_PHASE2: + filters.add( new P25MessageFilterSet() ); + break; + case PASSPORT: + filters.add( new PassportMessageFilter() ); + break; + default: + break; + } + + return filters; + } + + public static DecodeEditor getPanel( DecodeConfiguration config, + ChannelNode channelNode ) + { + DecodeEditor configuredPanel; + + switch( config.getDecoderType() ) + { + case NBFM: + configuredPanel = new NBFMEditor( config ); + break; + case LTR_STANDARD: + configuredPanel = new LTRStandardEditor( config ); + break; + case LTR_NET: + configuredPanel = new LTRNetEditor( config ); + break; + case MPT1327: + configuredPanel = new MPT1327ConfigEditor( config, channelNode ); + break; + case PASSPORT: + configuredPanel = new PassportEditor( config ); + break; + case P25_PHASE1: + configuredPanel = new P25Editor( config ); + break; + default: + configuredPanel = new DecodeEditor( config ); + break; + } + + return configuredPanel; + } + + public static String getRecordingFilename( String token ) + { + //Get the base recording filename + StringBuilder sb = new StringBuilder(); + sb.append( SystemProperties.getInstance() + .getApplicationFolder( "recordings" ) ); + sb.append( File.separator ); + sb.append( TimeStamp.getTimeStamp( "_" ) ); + sb.append( "_" ); + sb.append( token ); + + return sb.toString(); + } +} diff --git a/src/decode/DecoderType.java b/src/module/decode/DecoderType.java similarity index 96% rename from src/decode/DecoderType.java rename to src/module/decode/DecoderType.java index 98217436..ca7fee49 100644 --- a/src/decode/DecoderType.java +++ b/src/module/decode/DecoderType.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode; +package module.decode; import java.util.ArrayList; import java.util.EnumSet; @@ -33,7 +33,7 @@ public enum DecoderType FLEETSYNC2( "Fleetsync II", "Fsync2", "images/fm.png", 12500 ), LJ_1200( "LJ1200 173.075", "LJ1200", "images/lj1200.png", 12500 ), LTR_STANDARD( "LTR-Standard", "LTR", "images/ltr.png", 10000 ), - LTR_NET( "LTR-Net", "LTR-Net", "images/ltr_net.png", 10000 ), + LTR_NET( "LTR-Net", "LTR-Net", "images/ltr_net.png", 12500 ), MDC1200( "MDC1200", "MDC1200", "images/fm.png", 12500 ), MOTOROLA_TYPE_2( "MOTO T2", "MOTO T2", "images/motorola_type_2.png", 15000 ), MPT1327( "MPT1327", "MPT1327", "images/mpt1327.png", 12500 ), @@ -61,7 +61,7 @@ public enum DecoderType /** * Primary decoders */ - public static EnumSet getAvailableDecoders() + public static EnumSet getPrimaryDecoders() { return EnumSet.of( DecoderType.AM, DecoderType.LTR_NET, diff --git a/src/module/decode/PrimaryDecoder.java b/src/module/decode/PrimaryDecoder.java new file mode 100644 index 00000000..fa7f2c10 --- /dev/null +++ b/src/module/decode/PrimaryDecoder.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ + +package module.decode; + +import module.decode.config.DecodeConfiguration; +import module.decode.state.DecoderState; +import sample.Listener; +import audio.squelch.ISquelchStateProvider; +import audio.squelch.SquelchState; + +/** + * Primary decoder adds the following functionality over the basic decoder: + * + * - Provides audio squelch control + */ + +public abstract class PrimaryDecoder extends Decoder implements ISquelchStateProvider +{ + protected Listener mSquelchStateListener; + + protected DecodeConfiguration mDecodeConfiguration; + + public PrimaryDecoder( DecodeConfiguration config, DecoderState decoderState ) + { + super( decoderState ); + + mDecodeConfiguration = config; + } + + public DecodeConfiguration getDecodeConfiguration() + { + return mDecodeConfiguration; + } + + @Override + public void dispose() + { + super.dispose(); + + mSquelchStateListener = null; + mDecodeConfiguration = null; + } + + public void broadcast( SquelchState state ) + { + if( mSquelchStateListener != null ) + { + mSquelchStateListener.receive( state ); + } + } + + @Override + public void setSquelchStateListener( Listener listener ) + { + mSquelchStateListener = listener; + } + + @Override + public void removeSquelchStateListener() + { + mSquelchStateListener = null; + } +} diff --git a/src/decode/config/DecodeConfigMPT1327.java b/src/module/decode/am/AMDecoder.java similarity index 55% rename from src/decode/config/DecodeConfigMPT1327.java rename to src/module/decode/am/AMDecoder.java index fd68c706..e736215e 100644 --- a/src/decode/config/DecodeConfigMPT1327.java +++ b/src/module/decode/am/AMDecoder.java @@ -1,57 +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 - ******************************************************************************/ -package decode.config; - -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; - -import decode.DecoderType; -import decode.mpt1327.MPT1327Decoder.Sync; - -public class DecodeConfigMPT1327 extends DecodeConfiguration -{ - private String mChannelMapName; - private Sync mSync = Sync.NORMAL; - - public DecodeConfigMPT1327() - { - super( DecoderType.MPT1327 ); - } - - @XmlElement - public String getChannelMapName() - { - return mChannelMapName; - } - - public void setChannelMapName( String name ) - { - mChannelMapName = name; - } - - @XmlAttribute( name="sync" ) - public Sync getSync() - { - return mSync; - } - - public void setSync( Sync sync ) - { - mSync = sync; - } -} +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ + +package module.decode.am; + +import module.decode.DecoderType; +import module.decode.PrimaryDecoder; +import module.decode.config.DecodeConfiguration; +import module.decode.state.AlwaysUnsquelchedDecoderState; + +public class AMDecoder extends PrimaryDecoder +{ + public AMDecoder( DecodeConfiguration config ) + { + super( config, new AlwaysUnsquelchedDecoderState( "AM" ) ); + } + + @Override + public DecoderType getDecoderType() + { + return DecoderType.NBFM; + } +} diff --git a/src/decode/tait/Tait1200Panel.java b/src/module/decode/am/AMDecoderPanel.java similarity index 57% rename from src/decode/tait/Tait1200Panel.java rename to src/module/decode/am/AMDecoderPanel.java index 10db4912..1bb4f220 100644 --- a/src/decode/tait/Tait1200Panel.java +++ b/src/module/decode/am/AMDecoderPanel.java @@ -15,56 +15,41 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package module.decode.am; import javax.swing.JLabel; -import net.miginfocom.swing.MigLayout; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; import settings.ColorSetting; -import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingsManager; -import controller.state.AuxStatePanel; -import controller.state.ChannelState.ChangedAttribute; -public class Tait1200Panel extends AuxStatePanel +public class AMDecoderPanel extends DecoderPanel { private static final long serialVersionUID = 1L; - private static final String PROTOCOL = "TAIT-1200"; - private JLabel mProtocol = new JLabel( PROTOCOL ); + private JLabel mProtocol = new JLabel( "AM" ); - public Tait1200Panel( SettingsManager settingsManager, Tait1200ChannelState state ) + public AMDecoderPanel( SettingsManager settingsManager, AMDecoder decoder ) { - super( settingsManager, state ); + super( settingsManager, decoder ); init(); } - public void dispose() + public void init() { - super.dispose(); - } - - public Tait1200ChannelState getState() - { - return (Tait1200ChannelState)mAuxChannelState; - } - - private void init() - { - setOpaque( false ); //Use the parent panel's background color - - setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") ); + mProtocol.setFont( mFontDecoder ); - addLabel( mProtocol, false ); + add( mProtocol, "wrap" ); } @Override public void receive( final ChangedAttribute changedAttribute ) { } - + @Override public void settingChanged( Setting setting ) { @@ -72,12 +57,18 @@ public class Tait1200Panel extends AuxStatePanel if( setting instanceof ColorSetting ) { - ColorSetting color = (ColorSetting)setting; - - if( color.getColorSettingName() == - ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ) + ColorSetting colorSetting = (ColorSetting)setting; + + switch( colorSetting.getColorSettingName() ) { - mProtocol.setForeground( color.getColor() ); + case CHANNEL_STATE_LABEL_DECODER: + if( mProtocol != null ) + { + mProtocol.setForeground( mColorLabelDecoder ); + } + break; + default: + break; } } } diff --git a/src/decode/am/AMEditor.java b/src/module/decode/am/AMEditor.java similarity index 90% rename from src/decode/am/AMEditor.java rename to src/module/decode/am/AMEditor.java index 6380a97f..69561043 100644 --- a/src/decode/am/AMEditor.java +++ b/src/module/decode/am/AMEditor.java @@ -15,10 +15,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.am; +package module.decode.am; -import decode.DecodeEditor; -import decode.config.DecodeConfiguration; +import module.decode.DecodeEditor; +import module.decode.config.DecodeConfiguration; public class AMEditor extends DecodeEditor { diff --git a/src/decode/config/DecodeConfigAM.java b/src/module/decode/am/DecodeConfigAM.java similarity index 91% rename from src/decode/config/DecodeConfigAM.java rename to src/module/decode/am/DecodeConfigAM.java index 1ba26b68..844e2543 100644 --- a/src/decode/config/DecodeConfigAM.java +++ b/src/module/decode/am/DecodeConfigAM.java @@ -15,9 +15,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.am; -import decode.DecoderType; +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; public class DecodeConfigAM extends DecodeConfiguration { diff --git a/src/decode/config/AuxDecodeConfiguration.java b/src/module/decode/config/AuxDecodeConfiguration.java similarity index 96% rename from src/decode/config/AuxDecodeConfiguration.java rename to src/module/decode/config/AuxDecodeConfiguration.java index dcb31f40..044269b8 100644 --- a/src/decode/config/AuxDecodeConfiguration.java +++ b/src/module/decode/config/AuxDecodeConfiguration.java @@ -15,14 +15,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.config; import java.util.ArrayList; import javax.xml.bind.annotation.XmlElement; +import module.decode.DecoderType; import controller.config.Configuration; -import decode.DecoderType; public class AuxDecodeConfiguration extends Configuration { diff --git a/src/decode/config/DecodeConfigFactory.java b/src/module/decode/config/DecodeConfigFactory.java similarity index 82% rename from src/decode/config/DecodeConfigFactory.java rename to src/module/decode/config/DecodeConfigFactory.java index 449309d9..a4a88398 100644 --- a/src/decode/config/DecodeConfigFactory.java +++ b/src/module/decode/config/DecodeConfigFactory.java @@ -15,9 +15,16 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.config; -import decode.DecoderType; +import module.decode.DecoderType; +import module.decode.am.DecodeConfigAM; +import module.decode.ltrnet.DecodeConfigLTRNet; +import module.decode.ltrstandard.DecodeConfigLTRStandard; +import module.decode.mpt1327.DecodeConfigMPT1327; +import module.decode.nbfm.DecodeConfigNBFM; +import module.decode.p25.DecodeConfigP25Phase1; +import module.decode.passport.DecodeConfigPassport; public class DecodeConfigFactory { diff --git a/src/decode/config/DecodeConfiguration.java b/src/module/decode/config/DecodeConfiguration.java similarity index 74% rename from src/decode/config/DecodeConfiguration.java rename to src/module/decode/config/DecodeConfiguration.java index b5ec7bb8..b6b59f04 100644 --- a/src/decode/config/DecodeConfiguration.java +++ b/src/module/decode/config/DecodeConfiguration.java @@ -15,14 +15,21 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.config; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; +import module.decode.DecoderType; +import module.decode.am.DecodeConfigAM; +import module.decode.ltrnet.DecodeConfigLTRNet; +import module.decode.ltrstandard.DecodeConfigLTRStandard; +import module.decode.mpt1327.DecodeConfigMPT1327; +import module.decode.nbfm.DecodeConfigNBFM; +import module.decode.p25.DecodeConfigP25Phase1; +import module.decode.passport.DecodeConfigPassport; import controller.config.Configuration; -import decode.DecoderType; @XmlSeeAlso( { DecodeConfigAM.class, DecodeConfigNBFM.class, @@ -34,6 +41,14 @@ import decode.DecoderType; @XmlRootElement( name = "decode_configuration" ) public abstract class DecodeConfiguration extends Configuration { + public static final int DEFAULT_CALL_TIMEOUT_SECONDS = 45; + public static final int CALL_TIMEOUT_MINIMUM = 10; + public static final int CALL_TIMEOUT_MAXIMUM = 300; //5 minutes + + public static final int TRAFFIC_CHANNEL_LIMIT_DEFAULT = 3; + public static final int TRAFFIC_CHANNEL_LIMIT_MINIMUM = 0; + public static final int TRAFFIC_CHANNEL_LIMIT_MAXIMUM = 30; + private DecoderType mDecoderType = DecoderType.NBFM; private boolean mAFCEnabled = true; private int mAFCMaxCorrection = 3000; diff --git a/src/controller/activity/ActivitySummaryFrame.java b/src/module/decode/event/ActivitySummaryFrame.java similarity index 98% rename from src/controller/activity/ActivitySummaryFrame.java rename to src/module/decode/event/ActivitySummaryFrame.java index 8541ccb5..18188d2a 100644 --- a/src/controller/activity/ActivitySummaryFrame.java +++ b/src/module/decode/event/ActivitySummaryFrame.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; import java.awt.Component; import java.awt.EventQueue; diff --git a/src/controller/activity/ActivitySummaryProvider.java b/src/module/decode/event/ActivitySummaryProvider.java similarity index 93% rename from src/controller/activity/ActivitySummaryProvider.java rename to src/module/decode/event/ActivitySummaryProvider.java index 91e84253..e113c99c 100644 --- a/src/controller/activity/ActivitySummaryProvider.java +++ b/src/module/decode/event/ActivitySummaryProvider.java @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; import message.Message; import sample.Listener; public interface ActivitySummaryProvider extends Listener { - public String getSummary(); + public String getActivitySummary(); } diff --git a/src/controller/activity/CallEvent.java b/src/module/decode/event/CallEvent.java similarity index 84% rename from src/controller/activity/CallEvent.java rename to src/module/decode/event/CallEvent.java index 84e854f2..1cfcf9ca 100644 --- a/src/controller/activity/CallEvent.java +++ b/src/module/decode/event/CallEvent.java @@ -15,16 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; import java.text.SimpleDateFormat; import java.util.Date; +import module.decode.DecoderType; import alias.Alias; import alias.AliasList; -import decode.DecoderType; +import alias.MetadataType; -public abstract class CallEvent +public abstract class CallEvent implements Comparable { protected long mEventStart = System.currentTimeMillis(); protected long mEventEnd; @@ -38,6 +39,8 @@ public abstract class CallEvent protected String mToID; protected String mDetails; + private boolean mValid = true; + public CallEvent( DecoderType decoder, CallEventType callEventType, AliasList aliasList, @@ -53,11 +56,31 @@ public abstract class CallEvent mDetails = details; } + /** + * Indicates if this event is valid. Use this method to mark a previously + * dispatched event as invalid and resend it so that any downstream + * processing can remove the invalid event. + */ + public void setValid( boolean valid ) + { + mValid = valid; + } + + public boolean isValid() + { + return mValid; + } + public void setEnd( long end ) { mEventEnd = end; } + public void end() + { + setEnd( System.currentTimeMillis() ); + } + public DecoderType getDecoderType() { return mDecoderType; @@ -67,6 +90,11 @@ public abstract class CallEvent { return mCallEventType; } + + public void setCallEventType( CallEventType type ) + { + mCallEventType = type; + } public AliasList getAliasList() { @@ -103,6 +131,11 @@ public abstract class CallEvent return mFromID != null; } + public MetadataType getFromIDType() + { + return MetadataType.FROM_TALKGROUP; + } + public void setFromID( String id ) { mFromID = id; @@ -123,6 +156,11 @@ public abstract class CallEvent mToID = id; } + public MetadataType getToIDType() + { + return MetadataType.TO; + } + public String getDetails() { return mDetails; @@ -219,6 +257,7 @@ public abstract class CallEvent public enum CallEventType { + ANNOUNCEMENT( "Announcement" ), CALL( "Call" ), CALL_ALERT( "Call Alert" ), CALL_END( "Call End" ), @@ -241,6 +280,7 @@ public abstract class CallEvent REGISTER_ESN( "ESN" ), RESPONSE( "Response" ), SDM( "Short Data Message" ), + STATION_ID( "Station ID" ), STATUS( "Status" ), TELEPHONE_CALL( "Telephone Call" ), UNKNOWN( "Unknown" ), @@ -263,4 +303,10 @@ public abstract class CallEvent return mLabel; } } + + @Override + public int compareTo( CallEvent other ) + { + return Long.compare( mEventStart, other.getEventStartTime() ); + } } diff --git a/src/controller/activity/CallEventAliasCellRenderer.java b/src/module/decode/event/CallEventAliasCellRenderer.java similarity index 98% rename from src/controller/activity/CallEventAliasCellRenderer.java rename to src/module/decode/event/CallEventAliasCellRenderer.java index 88c35bd4..6ef081ee 100644 --- a/src/controller/activity/CallEventAliasCellRenderer.java +++ b/src/module/decode/event/CallEventAliasCellRenderer.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; import javax.swing.ImageIcon; import javax.swing.table.DefaultTableCellRenderer; diff --git a/src/controller/activity/CallEventModel.java b/src/module/decode/event/CallEventModel.java similarity index 52% rename from src/controller/activity/CallEventModel.java rename to src/module/decode/event/CallEventModel.java index 5ba9da80..38b5d40d 100644 --- a/src/controller/activity/CallEventModel.java +++ b/src/module/decode/event/CallEventModel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,33 +15,31 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; -import java.awt.EventQueue; import java.text.DecimalFormat; import java.text.SimpleDateFormat; -import java.util.LinkedList; +import java.util.ArrayList; +import java.util.List; import javax.swing.table.AbstractTableModel; +import module.decode.event.CallEvent.CallEventType; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Broadcaster; import sample.Listener; -import controller.activity.CallEvent.CallEventType; -public class CallEventModel extends AbstractTableModel +public class CallEventModel extends AbstractTableModel implements Listener { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( CallEventModel.class ); - + + private final static Logger mLog = LoggerFactory.getLogger( CallEventModel.class ); + private static DecimalFormat mFrequencyFormatter = new DecimalFormat( "0.000000" ); - private static final int DEFAULT_ICON_HEIGHT = 14; - public static final int TIME = 0; public static final int EVENT = 1; public static final int FROM_ID = 2; @@ -54,7 +52,7 @@ public class CallEventModel extends AbstractTableModel protected int mMaxMessages = 500; - protected LinkedList mEvents = new LinkedList(); + protected List mEvents = new ArrayList(); protected String[] mHeaders = new String[] { "Time", "Event", @@ -68,7 +66,6 @@ public class CallEventModel extends AbstractTableModel private SimpleDateFormat mSDFTime = new SimpleDateFormat( "HH:mm:ss" ); - private Broadcaster mBroadcaster = new Broadcaster(); private boolean mNewMessagesFirst = true; public CallEventModel() @@ -77,24 +74,14 @@ public class CallEventModel extends AbstractTableModel public void dispose() { - synchronized( mEvents ) - { - mEvents.clear(); - } - - mBroadcaster.dispose(); + mEvents.clear(); } - public void flush() + public void reset() { - /* Flush all events to listeners ( normally the call event logger ) */ - synchronized( mEvents ) - { - for( CallEvent event: mEvents ) - { - mBroadcaster.receive( event ); - } - } + mEvents.clear(); + + fireTableDataChanged(); } public int getMaxMessageCount() @@ -106,139 +93,57 @@ public class CallEventModel extends AbstractTableModel { mMaxMessages = count; } - - public void add( final CallEvent event ) - { - try - { - synchronized ( mEvents ) - { - mEvents.addFirst( event ); - } - fireTableRowsInserted( 0, 0 ); - - prune(); - } - catch( Exception e ) - { - e.printStackTrace(); - } - } - - public void remove( final CallEvent event ) + /** + * Adds, updates or deletes the event from the model. Producers can send + * the same call event multiple times to indicate that information in the + * event is updated. Producers can also mark the event as invalid and the + * event will be removed from the model. + */ + public void receive( final CallEvent event ) { - EventQueue.invokeLater( new Runnable() + //TODO: reverse this so that the new events go to the end and the oldest + //events are at the beginning ... use the getObjectAt() to control + //correct access to the ordering imposed. + + if( event.isValid() ) { - @Override - public void run() - { - synchronized ( mEvents ) - { - final int row = mEvents.indexOf( event ); - - mEvents.remove( event ); - - if( row != -1 ) - { - fireTableRowsDeleted( row, row ); - } - } - } - } ); - } - - public int indexOf( CallEvent event ) - { - return mEvents.indexOf( event ); - } - - public void setEnd( final CallEvent event ) - { - try - { - EventQueue.invokeAndWait( new Runnable() + if( !mEvents.contains( event ) ) { - @Override - public void run() - { - if( event != null ) - { - event.setEnd( System.currentTimeMillis() ); + mEvents.add( 0, event ); + + fireTableRowsInserted( 0, 0 ); - synchronized ( mEvents ) - { - int row = indexOf( event ); - - if( row != -1 ) - { - fireTableCellUpdated( row, TIME ); - } - } - } - else - { - mLog.error( "CallEventModel - couldn't log end time - event was null" ); - } - } - } ); + prune(); + } + else + { + int row = mEvents.indexOf( event ); + + fireTableRowsUpdated( row, row ); + } } - catch( Exception e ) + else { - e.printStackTrace(); + if( mEvents.contains( event ) ) + { + int row = mEvents.indexOf( event ); + + mEvents.remove( event ); + + fireTableRowsDeleted( row, row ); + } } } - public void setFromID( final CallEvent event, final String from ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - if( event != null ) - { - event.setFromID( from ); - - synchronized ( mEvents ) - { - int row = indexOf( event ); - - if( row != -1 ) - { - fireTableCellUpdated( row, FROM_ID ); - fireTableCellUpdated( row, FROM_ALIAS ); - } - else - { - mLog.error( "CallEventModel - tried to set from ID on " - + "call event - couldn't find index of event" ); - } - } - } - } - catch( Exception e ) - { - e.printStackTrace(); - } - } - } ); - } - private void prune() { - synchronized( mEvents ) + while( mEvents.size() > mMaxMessages ) { - while( mEvents.size() > mMaxMessages ) - { - CallEvent event = mEvents.removeLast(); - - mBroadcaster.receive( event ); - - fireTableRowsDeleted( mEvents.size(), mEvents.size() ); - } + int index = mEvents.size() - 1; + mEvents.remove( index ); + + fireTableRowsDeleted( index, index ); } } @@ -315,14 +220,4 @@ public class CallEventModel extends AbstractTableModel return null; } - - public void addListener( Listener listener ) - { - mBroadcaster.addListener( listener ); - } - - public void removeListener( Listener listener ) - { - mBroadcaster.removeListener( listener ); - } } diff --git a/src/controller/activity/CallEventPanel.java b/src/module/decode/event/CallEventPanel.java similarity index 74% rename from src/controller/activity/CallEventPanel.java rename to src/module/decode/event/CallEventPanel.java index 2611ad54..962a090e 100644 --- a/src/controller/activity/CallEventPanel.java +++ b/src/module/decode/event/CallEventPanel.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; import javax.swing.JPanel; import javax.swing.JScrollPane; @@ -45,6 +45,24 @@ public class CallEventPanel extends JPanel implements ChannelEventListener add( mEmptyScroller ); } +// if( mCallEventTable == null ) +// { +// mCallEventTable = new JTable( mCallEventModel ); +// mCallEventTable.setAutoCreateRowSorter( true ); +// mCallEventTable.setAutoResizeMode( JTable.AUTO_RESIZE_LAST_COLUMN ); +// +// CallEventAliasCellRenderer renderer = +// new CallEventAliasCellRenderer( mProcessingChain +// .getResourceManager().getSettingsManager() ); +// +// mCallEventTable.getColumnModel().getColumn( 3 ) +// .setCellRenderer( renderer ); +// +// mCallEventTable.getColumnModel().getColumn( 5 ) +// .setCellRenderer( renderer ); +// } +// +// return mCallEventTable; @Override public void channelChanged( ChannelEvent event ) @@ -58,9 +76,8 @@ public class CallEventPanel extends JPanel implements ChannelEventListener { removeAll(); - JScrollPane scroller = new JScrollPane( - event.getChannel().getProcessingChain().getChannelState() - .getCallEventTable() ); + JScrollPane scroller = new JScrollPane( new JTable( + event.getChannel().getCallEventModel() ) ); add( scroller ); @@ -70,8 +87,8 @@ public class CallEventPanel extends JPanel implements ChannelEventListener repaint(); } } - else if( event.getEvent() == Event.PROCESSING_STOPPED || - event.getEvent() == Event.CHANNEL_DELETED ) + else if( event.getEvent() == Event.CHANNEL_PROCESSING_STOPPED || + event.getEvent() == Event.CHANNEL_DISABLED ) { if( mDisplayedChannel != null && mDisplayedChannel == event.getChannel() ) diff --git a/src/module/decode/event/ICallEventProvider.java b/src/module/decode/event/ICallEventProvider.java new file mode 100644 index 00000000..f8645d36 --- /dev/null +++ b/src/module/decode/event/ICallEventProvider.java @@ -0,0 +1,9 @@ +package module.decode.event; + +import sample.Listener; + +public interface ICallEventProvider +{ + public void setCallEventListener( Listener listener ); + public void removeCallEventListener(); +} diff --git a/src/controller/activity/MessageActivityModel.java b/src/module/decode/event/MessageActivityModel.java similarity index 97% rename from src/controller/activity/MessageActivityModel.java rename to src/module/decode/event/MessageActivityModel.java index 7ee2e987..65850196 100644 --- a/src/controller/activity/MessageActivityModel.java +++ b/src/module/decode/event/MessageActivityModel.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; import java.awt.EventQueue; import java.text.SimpleDateFormat; @@ -122,7 +122,7 @@ public class MessageActivityModel extends AbstractTableModel public void receive( final Message message ) { - if( mMessageFilter.passes( message ) ) + if( message.isValid() && mMessageFilter.passes( message ) ) { EventQueue.invokeLater( new Runnable() { diff --git a/src/controller/activity/MessageActivityPanel.java b/src/module/decode/event/MessageActivityPanel.java similarity index 90% rename from src/controller/activity/MessageActivityPanel.java rename to src/module/decode/event/MessageActivityPanel.java index 6a216e1f..ae026b14 100644 --- a/src/controller/activity/MessageActivityPanel.java +++ b/src/module/decode/event/MessageActivityPanel.java @@ -15,11 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; - -import filter.FilterEditorPanel; -import filter.FilterSet; -import gui.SDRTrunk; +package module.decode.event; import java.awt.EventQueue; import java.awt.event.ActionEvent; @@ -52,12 +48,15 @@ import controller.channel.Channel; import controller.channel.ChannelEvent; import controller.channel.ChannelEvent.Event; import controller.channel.ChannelEventListener; +import filter.FilterEditorPanel; +import filter.FilterSet; public class MessageActivityPanel extends JPanel implements ChannelEventListener { private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class ); + private final static Logger mLog = + LoggerFactory.getLogger( MessageActivityPanel.class ); private static MessageActivityModel EMPTY_MODEL = new MessageActivityModel(); @@ -92,8 +91,7 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener { mDisplayedChannel = event.getChannel(); - mMessageTable.setModel( event.getChannel().getProcessingChain() - .getChannelState().getMessageActivityModel() ); + mMessageTable.setModel( event.getChannel().getMessageActivityModel() ); if( mDisplayedChannel != null ) { @@ -107,8 +105,8 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener changed = true; } } - else if( event.getEvent() == Event.PROCESSING_STOPPED || - event.getEvent() == Event.CHANNEL_DELETED ) + else if( event.getEvent() == Event.CHANNEL_PROCESSING_STOPPED || + event.getEvent() == Event.CHANNEL_DISABLED ) { if( mDisplayedChannel != null && mDisplayedChannel == event.getChannel() ) @@ -199,8 +197,7 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener if( mDisplayedChannel != null ) { - slider.setValue( mDisplayedChannel.getProcessingChain() - .getChannelState().getMessageActivityModel() + slider.setValue( mDisplayedChannel.getMessageActivityModel() .getMaxMessageCount() ); } else @@ -215,8 +212,7 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener { if( mDisplayedChannel != null ) { - mDisplayedChannel.getProcessingChain().getChannelState() - .getMessageActivityModel() + mDisplayedChannel.getMessageActivityModel() .setMaxMessageCount( slider.getValue() ); } } @@ -234,8 +230,7 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener { if( mDisplayedChannel != null ) { - mDisplayedChannel.getProcessingChain().getChannelState() - .getMessageActivityModel().clear(); + mDisplayedChannel.getMessageActivityModel().clear(); } } } ); @@ -274,9 +269,8 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener @SuppressWarnings( "unchecked" ) FilterSet filter = (FilterSet) - mDisplayedChannel - .getProcessingChain().getChannelState() - .getMessageActivityModel().getMessageFilter(); + mDisplayedChannel.getMessageActivityModel() + .getMessageFilter(); FilterEditorPanel panel = new FilterEditorPanel( filter ); diff --git a/src/controller/activity/MessageDetailsProvider.java b/src/module/decode/event/MessageDetailsProvider.java similarity index 97% rename from src/controller/activity/MessageDetailsProvider.java rename to src/module/decode/event/MessageDetailsProvider.java index 97672505..bfce825e 100644 --- a/src/controller/activity/MessageDetailsProvider.java +++ b/src/module/decode/event/MessageDetailsProvider.java @@ -15,14 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.activity; +package module.decode.event; import alias.Alias; /** * Getter methods to support the Message Activity Table/Model - * @author denny - * */ public interface MessageDetailsProvider { diff --git a/src/decode/fleetsync1/Fleetsync1Message.java b/src/module/decode/fleetsync1/Fleetsync1Message.java similarity index 99% rename from src/decode/fleetsync1/Fleetsync1Message.java rename to src/module/decode/fleetsync1/Fleetsync1Message.java index 80826562..73694103 100644 --- a/src/decode/fleetsync1/Fleetsync1Message.java +++ b/src/module/decode/fleetsync1/Fleetsync1Message.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync1; +package module.decode.fleetsync1; import java.text.SimpleDateFormat; import java.util.ArrayList; diff --git a/src/decode/fleetsync1/Fleetsync1MessageProcessor.java b/src/module/decode/fleetsync1/Fleetsync1MessageProcessor.java similarity index 98% rename from src/decode/fleetsync1/Fleetsync1MessageProcessor.java rename to src/module/decode/fleetsync1/Fleetsync1MessageProcessor.java index 8890d5a5..890bdd3d 100644 --- a/src/decode/fleetsync1/Fleetsync1MessageProcessor.java +++ b/src/module/decode/fleetsync1/Fleetsync1MessageProcessor.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync1; +package module.decode.fleetsync1; import message.Message; diff --git a/src/decode/fleetsync2/Fleetsync2Decoder.java b/src/module/decode/fleetsync2/Fleetsync2Decoder.java similarity index 68% rename from src/decode/fleetsync2/Fleetsync2Decoder.java rename to src/module/decode/fleetsync2/Fleetsync2Decoder.java index 98369eb5..f9c3bb44 100644 --- a/src/decode/fleetsync2/Fleetsync2Decoder.java +++ b/src/module/decode/fleetsync2/Fleetsync2Decoder.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,37 +15,36 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync2; +package module.decode.fleetsync2; import instrument.Instrumentable; import instrument.tap.Tap; import instrument.tap.stream.BinaryTap; -import instrument.tap.stream.FloatTap; +import instrument.tap.stream.FloatBufferTap; import java.util.ArrayList; import java.util.List; +import module.decode.Decoder; +import module.decode.DecoderType; import sample.Listener; -import sample.real.RealSampleListener; -import source.Source.SampleType; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; import alias.AliasList; -import audio.IAudioOutput; import bits.MessageFramer; import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; import dsp.filter.Filters; -import dsp.filter.FloatFIRFilter; -import dsp.filter.FloatHalfBandFilter; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; +import dsp.filter.halfband.real.HalfBandFilter_RB_RB; import dsp.fsk.FSK2Decoder; import dsp.fsk.FSK2Decoder.Output; /** * Fleetsync II Decoder - 1200 baud 2FSK decoder that can process 48k sample rate - * complex or floating point samples and output fully framed Fleetsync II - * messages + * floating point samples and output fully framed Fleetsync II messages */ -public class Fleetsync2Decoder extends Decoder implements Instrumentable +public class Fleetsync2Decoder extends Decoder implements IRealBufferListener, + Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ private static final int sDECIMATED_SAMPLE_RATE = 24000; @@ -58,29 +57,27 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable /* Instrumentation Taps */ private ArrayList mAvailableTaps; - private static final String INSTRUMENT_INPUT = - "Tap Point: Float Input"; private static final String INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD = "Tap Point: Bandpass Filter > < FSK2 Decoder"; private static final String INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER = "Tap Point: FSK2 Decoder > < Message Framer"; private FSK2Decoder mFSKDecoder; - private FloatHalfBandFilter mDecimationFilter; - private FloatFIRFilter mBandPassFilter; + private HalfBandFilter_RB_RB mDecimationFilter; + private RealFIRFilter_RB_RB mBandPassFilter; private MessageFramer mMessageFramer; private Fleetsync2MessageProcessor mMessageProcessor; public Fleetsync2Decoder( AliasList aliasList ) { - super( SampleType.REAL ); - - mDecimationFilter = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - addRealSampleListener( mDecimationFilter ); + super( new FleetsyncDecoderState( aliasList ) ); + + mDecimationFilter = new HalfBandFilter_RB_RB( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true ); + + mBandPassFilter = new RealFIRFilter_RB_RB( + Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02f ); - mBandPassFilter = new FloatFIRFilter( - Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02 ); mDecimationFilter.setListener( mBandPassFilter ); mFSKDecoder = new FSK2Decoder( sDECIMATED_SAMPLE_RATE, @@ -93,35 +90,28 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable mMessageProcessor = new Fleetsync2MessageProcessor( aliasList ); mMessageFramer.addMessageListener( mMessageProcessor ); - mMessageProcessor.addMessageListener( this ); + + mMessageProcessor.setMessageListener( mMessageBroadcaster ); } public void dispose() { super.dispose(); - mDecimationFilter.dispose(); mBandPassFilter.dispose(); + mDecimationFilter.dispose(); + mDecoderState.dispose(); mFSKDecoder.dispose(); mMessageFramer.dispose(); mMessageProcessor.dispose(); } @Override - public DecoderType getType() - { - return DecoderType.FLEETSYNC2; - } - - /** - * Returns a float listener interface for connecting this decoder to a - * float stream provider - */ - public RealSampleListener getRealReceiver() + public DecoderType getDecoderType() { - return (RealSampleListener)mDecimationFilter; + return DecoderType.FLEETSYNC2; } - + @Override public List getTaps() { @@ -129,8 +119,7 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable { mAvailableTaps = new ArrayList(); - mAvailableTaps.add( new FloatTap( INSTRUMENT_INPUT, 0, 1.0f ) ); - mAvailableTaps.add( new FloatTap( + mAvailableTaps.add( new FloatBufferTap( INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD, 0, 0.5f ) ); mAvailableTaps.addAll( mFSKDecoder.getTaps() ); mAvailableTaps.add( new BinaryTap( @@ -147,12 +136,8 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - addRealSampleListener( inputTap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: - FloatTap bpTap = (FloatTap)tap; + FloatBufferTap bpTap = (FloatBufferTap)tap; mBandPassFilter.setListener( bpTap ); bpTap.setListener( mFSKDecoder ); break; @@ -171,10 +156,6 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - removeRealListener( inputTap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: mBandPassFilter.setListener( mFSKDecoder ); break; @@ -185,15 +166,8 @@ public class Fleetsync2Decoder extends Decoder implements Instrumentable } @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - //Not implemented - } - - @Override - public IAudioOutput getAudioOutput() + public Listener getRealBufferListener() { - //Not implemented - return null; + return mDecimationFilter; } } diff --git a/src/module/decode/fleetsync2/Fleetsync2DecoderPanel.java b/src/module/decode/fleetsync2/Fleetsync2DecoderPanel.java new file mode 100644 index 00000000..1da74da3 --- /dev/null +++ b/src/module/decode/fleetsync2/Fleetsync2DecoderPanel.java @@ -0,0 +1,189 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.fleetsync2; + +import java.awt.EventQueue; + +import javax.swing.JLabel; + +import alias.Alias; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; +import net.miginfocom.swing.MigLayout; +import settings.ColorSetting; +import settings.ColorSetting.ColorSettingName; +import settings.Setting; +import settings.SettingsManager; + +public class Fleetsync2DecoderPanel extends DecoderPanel +{ + private static final long serialVersionUID = 1L; + private static final String PROTOCOL = "FSync II"; + + private JLabel mFromLabel = new JLabel( " " ); + private JLabel mFrom = new JLabel(); + private JLabel mFromAlias = new JLabel(); + + private JLabel mToLabel = new JLabel(); + private JLabel mTo = new JLabel( " " ); + private JLabel mToAlias = new JLabel(); + + private JLabel mProtocol = new JLabel( PROTOCOL ); + private JLabel mMessage = new JLabel(); + private JLabel mMessageType = new JLabel(); + + public Fleetsync2DecoderPanel( SettingsManager settingsManager, + Fleetsync2Decoder decoder ) + { + super( settingsManager, decoder ); + + init(); + } + + private FleetsyncDecoderState getState() + { + return (FleetsyncDecoderState)mDecoder.getDecoderState(); + } + + public void dispose() + { + super.dispose(); + } + + protected void init() + { + /* Calling super init will get and broadcast color settings to properly + * setup the jlabel colors */ + super.init(); + + setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") ); + + mFromLabel.setFont( mFontAuxDecoder ); + add( mFromLabel ); + mFrom.setFont( mFontAuxDecoder ); + add( mFrom ); + mFromAlias.setFont( mFontAuxDecoder ); + add( mFromAlias, "wrap" ); + + mToLabel.setFont( mFontAuxDecoder ); + add( mToLabel ); + mTo.setFont( mFontAuxDecoder ); + add( mTo ); + mToAlias.setFont( mFontAuxDecoder ); + add( mToAlias, "wrap" ); + + mProtocol.setFont( mFontAuxDecoder ); + add( mProtocol ); + mMessageType.setFont( mFontAuxDecoder ); + add( mMessageType ); + mMessage.setFont( mFontAuxDecoder ); + add( mMessage, "wrap" ); + } + + @Override + public void receive( final ChangedAttribute changedAttribute ) + { + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + switch( changedAttribute ) + { + case FROM_TALKGROUP: + String from = getState().getFromID(); + + if( from != null ) + { + mFromLabel.setText( "FM:" ); + } + else + { + mFromLabel.setText( " " ); + } + mFrom.setText( from ); + break; + case FROM_TALKGROUP_ALIAS: + Alias fromAlias = getState().getFromIDAlias(); + + if( fromAlias != null ) + { + mFromAlias.setText( fromAlias.getName() ); + } + break; + case TO_TALKGROUP: + String to = getState().getToID(); + + if( to != null ) + { + mToLabel.setText( "TO:" ); + } + else + { + mToLabel.setText( " " ); + } + mTo.setText( to ); + break; + case TO_TALKGROUP_ALIAS: + Alias toAlias = getState().getToIDAlias(); + + if( toAlias != null ) + { + mToAlias.setText( toAlias.getName() ); + } + break; + case MESSAGE: + mMessage.setText( getState().getMessage() ); + break; + case MESSAGE_TYPE: + mMessageType.setText( getState().getMessageType() ); + break; + default: + break; + } + + repaint(); + } + } ); + } + + @Override + public void settingChanged( Setting setting ) + { + super.settingChanged( setting ); + + if( setting instanceof ColorSetting ) + { + ColorSetting color = (ColorSetting)setting; + + if( color.getColorSettingName() == + ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ) + { + mFromLabel.setForeground( color.getColor() ); + mFrom.setForeground( color.getColor() ); + mFromAlias.setForeground( color.getColor() ); + mToLabel.setForeground( color.getColor() ); + mTo.setForeground( color.getColor() ); + mToAlias.setForeground( color.getColor() ); + mProtocol.setForeground( color.getColor() ); + mMessageType.setForeground( color.getColor() ); + mMessage.setForeground( color.getColor() ); + } + } + } +} diff --git a/src/decode/fleetsync2/Fleetsync2MessageProcessor.java b/src/module/decode/fleetsync2/Fleetsync2MessageProcessor.java similarity index 77% rename from src/decode/fleetsync2/Fleetsync2MessageProcessor.java rename to src/module/decode/fleetsync2/Fleetsync2MessageProcessor.java index 5bf85d02..23c74653 100644 --- a/src/decode/fleetsync2/Fleetsync2MessageProcessor.java +++ b/src/module/decode/fleetsync2/Fleetsync2MessageProcessor.java @@ -15,17 +15,16 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync2; +package module.decode.fleetsync2; import message.Message; -import sample.Broadcaster; import sample.Listener; import alias.AliasList; import bits.BinaryMessage; public class Fleetsync2MessageProcessor implements Listener { - private Broadcaster mBroadcaster = new Broadcaster(); + private Listener mMessageListener; private AliasList mAliasList; @@ -36,7 +35,8 @@ public class Fleetsync2MessageProcessor implements Listener public void dispose() { - mBroadcaster.dispose(); + mMessageListener = null; + mAliasList = null; } @Override @@ -44,16 +44,19 @@ public class Fleetsync2MessageProcessor implements Listener { FleetsyncMessage message = new FleetsyncMessage( buffer, mAliasList ); - mBroadcaster.receive( message ); + if( mMessageListener != null ) + { + mMessageListener.receive( message ); + } } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } } diff --git a/src/decode/fleetsync2/FleetsyncCallEvent.java b/src/module/decode/fleetsync2/FleetsyncCallEvent.java similarity index 98% rename from src/decode/fleetsync2/FleetsyncCallEvent.java rename to src/module/decode/fleetsync2/FleetsyncCallEvent.java index 1c7dd85e..4b7b2121 100644 --- a/src/decode/fleetsync2/FleetsyncCallEvent.java +++ b/src/module/decode/fleetsync2/FleetsyncCallEvent.java @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync2; +package module.decode.fleetsync2; +import module.decode.DecoderType; +import module.decode.event.CallEvent; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; public class FleetsyncCallEvent extends CallEvent { diff --git a/src/module/decode/fleetsync2/FleetsyncDecoderState.java b/src/module/decode/fleetsync2/FleetsyncDecoderState.java new file mode 100644 index 00000000..ae40ecb0 --- /dev/null +++ b/src/module/decode/fleetsync2/FleetsyncDecoderState.java @@ -0,0 +1,328 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.fleetsync2; + +import java.util.Iterator; +import java.util.TreeSet; + +import message.Message; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.DecoderStateEvent.Event; +import module.decode.state.State; +import alias.Alias; +import alias.AliasList; + +public class FleetsyncDecoderState extends DecoderState +{ + private TreeSet mIdents = new TreeSet(); + private TreeSet mEmergencyIdents = new TreeSet(); + + private String mFrom; + private Alias mFromAlias; + + private String mTo; + private Alias mToAlias; + + private String mMessage; + private String mMessageType; + + public FleetsyncDecoderState( AliasList aliasList ) + { + super( aliasList ); + } + + @Override + public void dispose() + { + super.dispose(); + + + } + + @Override + public void init() + { + + } + + /** + * Resets the overall decoder state and clears any accumulated event details + */ + @Override + public void reset() + { + mIdents.clear(); + mEmergencyIdents.clear(); + + resetState(); + } + + /** + * Resets this decoder state + */ + public void resetState() + { + setFromID( null ); + setFromIDAlias( null ); + setToID( null ); + setToIDAlias( null ); + setMessage( null ); + setMessageType( null ); + } + + @Override + public void receive( Message message ) + { + if( message instanceof FleetsyncMessage ) + { + FleetsyncMessage fleetsync = (FleetsyncMessage)message; + + if( fleetsync.isValid() ) + { + setFromID( fleetsync.getFromID() ); + setFromIDAlias( fleetsync.getFromIDAlias() ); + mIdents.add( fleetsync.getFromID() ); + + FleetsyncMessageType type = fleetsync.getMessageType(); + + if( type != FleetsyncMessageType.ANI ) + { + setToID( fleetsync.getToID() ); + setToIDAlias( fleetsync.getToIDAlias() ); + mIdents.add( fleetsync.getToID() ); + } + + setMessageType( type.getLabel() ); + + switch( type ) + { + case GPS: + setMessage( fleetsync.getGPSLocation() ); + break; + case STATUS: + StringBuilder sb = new StringBuilder(); + sb.append( fleetsync.getStatus() ); + + Alias status = fleetsync.getStatusAlias(); + + if( status != null ) + { + sb.append( "/" ); + sb.append( status.getName() ); + } + + setMessage( sb.toString() ); + break; + case EMERGENCY: + case LONE_WORKER_EMERGENCY: + mEmergencyIdents.add( fleetsync.getFromID() ); + break; + default: + break; + } + + FleetsyncCallEvent fsCallEvent = + FleetsyncCallEvent.getFleetsync2Event( fleetsync ); + + fsCallEvent.setAliasList( getAliasList() ); + + broadcast( fsCallEvent ); + + /* Broadcast decode event. If the primary state is not already + * in a call state, then we'll use a default decoded data state */ + broadcast( new DecoderStateEvent( this, Event.DECODE, State.DATA ) ); + } + } + } + + public String getFromID() + { + return mFrom; + } + + public void setFromID( String from ) + { + mFrom = from; + broadcast( ChangedAttribute.FROM_TALKGROUP ); + } + + public Alias getFromIDAlias() + { + return mFromAlias; + } + + public void setFromIDAlias( Alias alias ) + { + mFromAlias = alias; + broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + } + + public String getToID() + { + return mTo; + } + + public void setToID( String to ) + { + mTo = to; + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + + public Alias getToIDAlias() + { + return mToAlias; + } + + public void setToIDAlias( Alias alias ) + { + mToAlias = alias; + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + } + + public String getMessage() + { + return mMessage; + } + + public void setMessage( String message ) + { + mMessage = message; + broadcast( ChangedAttribute.MESSAGE ); + } + + public String getMessageType() + { + return mMessageType; + } + + public void setMessageType( String type ) + { + mMessageType = type; + broadcast( ChangedAttribute.MESSAGE_TYPE ); + } + + /** + * Responds to reset events issued by the channel state + */ + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + switch( event.getEvent() ) + { + case RESET: + resetState(); + break; + default: + break; + } + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "=============================\n" ); + sb.append( "Decoder:\tFleetsync II\n\n" ); + sb.append( "Fleetsync Idents\n" ); + + if( mIdents.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mIdents.iterator(); + + while( it.hasNext() ) + { + String ident = it.next(); + + sb.append( " " ); + sb.append( formatIdent( ident ) ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getFleetsyncAlias( ident ); + + if( alias != null ) + { + sb.append( " " ); + sb.append( alias.getName() ); + } + } + + sb.append( "\n" ); + } + } + + sb.append( "\nFleetsync Emergency Activations\n" ); + + if( mEmergencyIdents.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mEmergencyIdents.iterator(); + + while( it.hasNext() ) + { + String ident = it.next(); + + sb.append( " " ); + sb.append( formatIdent( ident ) ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getFleetsyncAlias( ident ); + + if( alias != null ) + { + sb.append( " " ); + sb.append( alias.getName() ); + } + + sb.append( "\n" ); + } + } + } + + return sb.toString(); + } + + public static String formatIdent( String ident ) + { + StringBuilder sb = new StringBuilder(); + + if( ident.length() == 7 ) + { + sb.append( ident.substring( 0, 3 ) ); + sb.append( "-" ); + sb.append( ident.substring( 3, 7 ) ); + + return sb.toString(); + } + else + { + return ident; + } + } +} diff --git a/src/decode/fleetsync2/FleetsyncMessage.java b/src/module/decode/fleetsync2/FleetsyncMessage.java similarity index 99% rename from src/decode/fleetsync2/FleetsyncMessage.java rename to src/module/decode/fleetsync2/FleetsyncMessage.java index 190e0a1c..85a47cf7 100644 --- a/src/decode/fleetsync2/FleetsyncMessage.java +++ b/src/module/decode/fleetsync2/FleetsyncMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync2; +package module.decode.fleetsync2; import java.text.DecimalFormat; import java.text.SimpleDateFormat; diff --git a/src/decode/fleetsync2/FleetsyncMessageFilter.java b/src/module/decode/fleetsync2/FleetsyncMessageFilter.java similarity index 97% rename from src/decode/fleetsync2/FleetsyncMessageFilter.java rename to src/module/decode/fleetsync2/FleetsyncMessageFilter.java index 8c04a308..a9a61c5f 100644 --- a/src/decode/fleetsync2/FleetsyncMessageFilter.java +++ b/src/module/decode/fleetsync2/FleetsyncMessageFilter.java @@ -1,4 +1,4 @@ -package decode.fleetsync2; +package module.decode.fleetsync2; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/decode/fleetsync2/FleetsyncMessageType.java b/src/module/decode/fleetsync2/FleetsyncMessageType.java similarity index 97% rename from src/decode/fleetsync2/FleetsyncMessageType.java rename to src/module/decode/fleetsync2/FleetsyncMessageType.java index 1d3a09a5..6f3730b0 100644 --- a/src/decode/fleetsync2/FleetsyncMessageType.java +++ b/src/module/decode/fleetsync2/FleetsyncMessageType.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync2; +package module.decode.fleetsync2; public enum FleetsyncMessageType { diff --git a/src/decode/lj1200/LJ1200CallEvent.java b/src/module/decode/lj1200/LJ1200CallEvent.java similarity index 95% rename from src/decode/lj1200/LJ1200CallEvent.java rename to src/module/decode/lj1200/LJ1200CallEvent.java index a6f52608..5be56feb 100644 --- a/src/decode/lj1200/LJ1200CallEvent.java +++ b/src/module/decode/lj1200/LJ1200CallEvent.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.lj1200; +package module.decode.lj1200; +import module.decode.DecoderType; +import module.decode.event.CallEvent; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; public class LJ1200CallEvent extends CallEvent { diff --git a/src/decode/lj1200/LJ1200Decoder.java b/src/module/decode/lj1200/LJ1200Decoder.java similarity index 76% rename from src/decode/lj1200/LJ1200Decoder.java rename to src/module/decode/lj1200/LJ1200Decoder.java index cf4367b8..2454657c 100644 --- a/src/decode/lj1200/LJ1200Decoder.java +++ b/src/module/decode/lj1200/LJ1200Decoder.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,35 +15,38 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.lj1200; +package module.decode.lj1200; import instrument.Instrumentable; import instrument.tap.Tap; import instrument.tap.stream.BinaryTap; +import instrument.tap.stream.FloatBufferTap; import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; +import module.decode.Decoder; +import module.decode.DecoderType; import sample.Broadcaster; +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; import sample.real.RealSampleListener; -import source.Source.SampleType; import alias.AliasList; -import audio.IAudioOutput; import bits.MessageFramer; import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; import dsp.filter.Filters; -import dsp.filter.FloatFIRFilter; -import dsp.filter.FloatHalfBandFilter; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; +import dsp.filter.halfband.real.HalfBandFilter_RB_RB; import dsp.fsk.FSK2Decoder; import dsp.fsk.FSK2Decoder.Output; /** * LJ1200 - 1200 baud 2FSK decoder */ -public class LJ1200Decoder extends Decoder implements Instrumentable +public class LJ1200Decoder extends Decoder implements IRealBufferListener, + Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ private static final int DECIMATED_SAMPLE_RATE = 24000; @@ -56,8 +59,6 @@ public class LJ1200Decoder extends Decoder implements Instrumentable /* Instrumentation Taps */ private ArrayList mAvailableTaps; - private static final String INSTRUMENT_INPUT = - "Tap Point: Float Input"; private static final String INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD = "Tap Point: Bandpass Filter > < FSK2 Decoder"; private static final String INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER = @@ -67,22 +68,21 @@ public class LJ1200Decoder extends Decoder implements Instrumentable private Broadcaster mFSKDecoderBroadcaster = new Broadcaster(); - private FloatHalfBandFilter mDecimationFilter; - private FloatFIRFilter mBandPassFilter; + private HalfBandFilter_RB_RB mDecimationFilter; + private RealFIRFilter_RB_RB mBandPassFilter; private MessageFramer mTowerMessageFramer; private MessageFramer mTransponderMessageFramer; private LJ1200MessageProcessor mMessageProcessor; public LJ1200Decoder( AliasList aliasList ) { - super( SampleType.REAL ); + super( new LJ1200DecoderState( aliasList ) ); - mDecimationFilter = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - addRealSampleListener( mDecimationFilter ); + mDecimationFilter = new HalfBandFilter_RB_RB( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true ); - mBandPassFilter = new FloatFIRFilter( - Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02 ); + mBandPassFilter = new RealFIRFilter_RB_RB( + Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02f ); mDecimationFilter.setListener( mBandPassFilter ); mFSKDecoder = new FSK2Decoder( DECIMATED_SAMPLE_RATE, @@ -101,10 +101,16 @@ public class LJ1200Decoder extends Decoder implements Instrumentable mMessageProcessor = new LJ1200MessageProcessor( aliasList ); mTowerMessageFramer.addMessageListener( mMessageProcessor ); mTransponderMessageFramer.addMessageListener( mMessageProcessor ); - mMessageProcessor.addMessageListener( this ); + mMessageProcessor.setMessageListener( mMessageBroadcaster ); } - public void dispose() + @Override + public DecoderType getDecoderType() + { + return DecoderType.LJ_1200; + } + + public void dispose() { super.dispose(); @@ -115,12 +121,6 @@ public class LJ1200Decoder extends Decoder implements Instrumentable mMessageProcessor.dispose(); } - @Override - public DecoderType getType() - { - return DecoderType.FLEETSYNC2; - } - /** * Returns a float listener interface for connecting this decoder to a * float stream provider @@ -137,7 +137,6 @@ public class LJ1200Decoder extends Decoder implements Instrumentable { mAvailableTaps = new ArrayList(); - mAvailableTaps.add( new FloatTap( INSTRUMENT_INPUT, 0, 1.0f ) ); mAvailableTaps.add( new FloatTap( INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD, 0, 0.5f ) ); mAvailableTaps.addAll( mFSKDecoder.getTaps() ); @@ -155,12 +154,8 @@ public class LJ1200Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - addRealSampleListener( inputTap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: - FloatTap bpTap = (FloatTap)tap; + FloatBufferTap bpTap = (FloatBufferTap)tap; mBandPassFilter.setListener( bpTap ); bpTap.setListener( mFSKDecoder ); break; @@ -179,10 +174,6 @@ public class LJ1200Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - removeRealListener( inputTap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: mBandPassFilter.setListener( mFSKDecoder ); break; @@ -193,15 +184,8 @@ public class LJ1200Decoder extends Decoder implements Instrumentable } @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - //Not implemented - } - - @Override - public IAudioOutput getAudioOutput() + public Listener getRealBufferListener() { - //Not implemented - return null; + return mDecimationFilter; } } diff --git a/src/module/decode/lj1200/LJ1200DecoderState.java b/src/module/decode/lj1200/LJ1200DecoderState.java new file mode 100644 index 00000000..3c3ab3c6 --- /dev/null +++ b/src/module/decode/lj1200/LJ1200DecoderState.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.lj1200; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import message.Message; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.DecoderStateEvent.Event; +import module.decode.state.State; +import alias.Alias; +import alias.AliasList; + +public class LJ1200DecoderState extends DecoderState +{ + private Set mAddresses = new TreeSet(); + + private String mAddress; + private Alias mAddressAlias; + + public LJ1200DecoderState( AliasList aliasList ) + { + super( aliasList ); + } + + public String getAddress() + { + return mAddress; + } + + public Alias getAddressAlias() + { + return mAddressAlias; + } + + @Override + public void receive( Message message ) + { + if( message instanceof LJ1200Message ) + { + LJ1200Message lj = (LJ1200Message)message; + + if( lj.isValid() ) + { + setAddress( lj.getAddress() ); + setAddressAlias( lj.getAddressAlias() ); + + mAddresses.add( mAddress ); + + broadcast( LJ1200CallEvent.getLJ1200Event( lj ) ); + + broadcast( new DecoderStateEvent( this, Event.DECODE, State.DATA ) ); + } + } + else if( message instanceof LJ1200TransponderMessage ) + { + + } + } + + private void setAddress( String address ) + { + mAddress = address; + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + + private void setAddressAlias( Alias alias ) + { + mAddressAlias = alias; + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "=============================\n" ); + sb.append( "Decoder:\tLJ-1200I\n\n" ); + + if( !mAddresses.isEmpty() ) + { + sb.append( "Transponder Addresses:\n" ); + + List addresses = new ArrayList( mAddresses ); + + Collections.sort( addresses ); + + for( String address: addresses ) + { + sb.append( "\t" ); + sb.append( address ); + sb.append( "\n" ); + } + } + + return sb.toString(); + } + + @Override + public void init() + { + /* No initialization steps */ + } + + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + if( event.getEvent() == Event.RESET ) + { + + } + } + + @Override + public void reset() + { + mAddresses.clear(); + + setAddress( null ); + setAddressAlias( null ); + } +} diff --git a/src/decode/lj1200/LJ1200Message.java b/src/module/decode/lj1200/LJ1200Message.java similarity index 97% rename from src/decode/lj1200/LJ1200Message.java rename to src/module/decode/lj1200/LJ1200Message.java index 385ac24f..d174e2f2 100644 --- a/src/decode/lj1200/LJ1200Message.java +++ b/src/module/decode/lj1200/LJ1200Message.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.lj1200; +package module.decode.lj1200; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -320,12 +320,12 @@ public class LJ1200Message extends Message F4_ACTIVATE( "4-ACTIVATE" ), F5_TRANSPONDER_TEST_REPLY( "5-TRANSPONDER TEST REPLY" ), F6_TRANSPONDER_TEST_COMMAND( "6-TRANSPONDER TEST COMMAND" ), - F7_UNKNOWN( "7-UNKNOWN" ), + F7_QUIET_COMMAND( "7-QUIET COMMAND" ), F8_UNKNOWN( "8-UNKNOWN" ), F9_UNKNOWN( "9-UNKNOWN" ), FA_UNKNOWN( "A-UNKNOWN" ), FB_UNKNOWN( "B-UNKNOWN" ), - FC_UNKNOWN( "C-UNKNOWN" ), + FC_ALT_DEACTIVATE( "C-ALT-DEACTIVATE" ), FD_UNKNOWN( "D-UNKNOWN" ), FE_UNKNOWN( "E-UNKNOWN" ), FF_TRACK_PULSE( "F-TRACK PULSE" ), @@ -375,7 +375,7 @@ public class LJ1200Message extends Message case 6: return Function.F6_TRANSPONDER_TEST_COMMAND; case 7: - return Function.F7_UNKNOWN; + return Function.F7_QUIET_COMMAND; case 8: return Function.F8_UNKNOWN; case 9: @@ -385,7 +385,7 @@ public class LJ1200Message extends Message case 11: return Function.FB_UNKNOWN; case 12: - return Function.FC_UNKNOWN; + return Function.FC_ALT_DEACTIVATE; case 13: return Function.FD_UNKNOWN; case 14: diff --git a/src/module/decode/lj1200/LJ1200MessageFilter.java b/src/module/decode/lj1200/LJ1200MessageFilter.java new file mode 100644 index 00000000..7f6257d7 --- /dev/null +++ b/src/module/decode/lj1200/LJ1200MessageFilter.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.lj1200; + +import java.util.Collections; +import java.util.List; + +import message.Message; +import filter.Filter; +import filter.FilterElement; + +public class LJ1200MessageFilter extends Filter +{ + + public LJ1200MessageFilter() + { + super( "LJ-1200 Message Filter" ); + } + + @Override + public boolean passes( Message message ) + { + return mEnabled && canProcess( message ); + } + + @Override + public boolean canProcess( Message message ) + { + return message instanceof LJ1200Message || + message instanceof LJ1200TransponderMessage; + } + + @Override + public List> getFilterElements() + { + return Collections.EMPTY_LIST; + } +} diff --git a/src/decode/lj1200/LJ1200MessageProcessor.java b/src/module/decode/lj1200/LJ1200MessageProcessor.java similarity index 71% rename from src/decode/lj1200/LJ1200MessageProcessor.java rename to src/module/decode/lj1200/LJ1200MessageProcessor.java index 0d4378f2..fc11279c 100644 --- a/src/decode/lj1200/LJ1200MessageProcessor.java +++ b/src/module/decode/lj1200/LJ1200MessageProcessor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,14 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.lj1200; +package module.decode.lj1200; import message.Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Broadcaster; import sample.Listener; import alias.AliasList; import bits.BinaryMessage; @@ -37,7 +36,7 @@ public class LJ1200MessageProcessor implements Listener public static int SYNC_TOWER = 0x550F; public static int SYNC_TRANSPONDER = 0x2AD5; - private Broadcaster mBroadcaster = new Broadcaster(); + private Listener mMessageListener; private AliasList mAliasList; @@ -48,7 +47,8 @@ public class LJ1200MessageProcessor implements Listener public void dispose() { - mBroadcaster.dispose(); + mAliasList = null; + mMessageListener = null; } @Override @@ -58,25 +58,27 @@ public class LJ1200MessageProcessor implements Listener if( sync == SYNC_TOWER ) { - LJ1200Message towerMessage = new LJ1200Message( buffer, mAliasList ); - mBroadcaster.receive( towerMessage ); + if( mMessageListener != null ) + { + mMessageListener.receive( new LJ1200Message( buffer, mAliasList ) ); + } } else if( sync == SYNC_TRANSPONDER ) { - LJ1200TransponderMessage transponderMessage = - new LJ1200TransponderMessage( buffer, mAliasList ); - - mBroadcaster.receive( transponderMessage ); + if( mMessageListener != null ) + { + mMessageListener.receive( new LJ1200TransponderMessage( buffer, mAliasList ) ); + } } } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } } diff --git a/src/module/decode/lj1200/LJ1200Panel.java b/src/module/decode/lj1200/LJ1200Panel.java new file mode 100644 index 00000000..49e76030 --- /dev/null +++ b/src/module/decode/lj1200/LJ1200Panel.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.lj1200; + +import javax.swing.JLabel; + +import alias.Alias; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; +import net.miginfocom.swing.MigLayout; +import settings.ColorSetting; +import settings.ColorSetting.ColorSettingName; +import settings.Setting; +import settings.SettingsManager; + +public class LJ1200Panel extends DecoderPanel +{ + private static final long serialVersionUID = 1L; + private static final String PROTOCOL = "LJ-1200"; + + private JLabel mProtocol = new JLabel( PROTOCOL ); + private JLabel mTo = new JLabel(); + private JLabel mToAlias = new JLabel(); + + public LJ1200Panel( SettingsManager settingsManager, LJ1200Decoder decoder ) + { + super( settingsManager, decoder ); + + init(); + } + + public void dispose() + { + super.dispose(); + } + + protected void init() + { + /* Calling super init will get and broadcast color settings to properly + * setup the jlabel colors */ + super.init(); + + setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "" ) ); + + add( mProtocol ); + add( mTo ); + add( mToAlias, "wrap" ); + } + + private LJ1200DecoderState getState() + { + return (LJ1200DecoderState)getDecoder().getDecoderState(); + } + + @Override + public void receive( final ChangedAttribute changedAttribute ) + { + switch( changedAttribute ) + { + case TO_TALKGROUP: + mTo.setText( getState().getAddress() ); + repaint(); + break; + case FROM_TALKGROUP_ALIAS: + Alias alias = getState().getAddressAlias(); + + if( alias != null ) + { + mToAlias.setText( alias.getName() ); + repaint(); + } + break; + default: + break; + } + } + + @Override + public void settingChanged( Setting setting ) + { + super.settingChanged( setting ); + + if( setting instanceof ColorSetting ) + { + ColorSetting color = (ColorSetting)setting; + + if( color.getColorSettingName() == + ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ) + { + mProtocol.setForeground( color.getColor() ); + mTo.setForeground( color.getColor() ); + mToAlias.setForeground( color.getColor() ); + } + } + } +} diff --git a/src/decode/lj1200/LJ1200TransponderMessage.java b/src/module/decode/lj1200/LJ1200TransponderMessage.java similarity index 98% rename from src/decode/lj1200/LJ1200TransponderMessage.java rename to src/module/decode/lj1200/LJ1200TransponderMessage.java index 9ba9b7c8..fecfc8fd 100644 --- a/src/decode/lj1200/LJ1200TransponderMessage.java +++ b/src/module/decode/lj1200/LJ1200TransponderMessage.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.lj1200; +package module.decode.lj1200; import java.text.SimpleDateFormat; diff --git a/src/decode/config/DecodeConfigLTRNet.java b/src/module/decode/ltrnet/DecodeConfigLTRNet.java similarity index 92% rename from src/decode/config/DecodeConfigLTRNet.java rename to src/module/decode/ltrnet/DecodeConfigLTRNet.java index 6c62e6ff..574785b5 100644 --- a/src/decode/config/DecodeConfigLTRNet.java +++ b/src/module/decode/ltrnet/DecodeConfigLTRNet.java @@ -15,12 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.ltrnet; import javax.xml.bind.annotation.XmlAttribute; import message.MessageDirection; -import decode.DecoderType; +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; public class DecodeConfigLTRNet extends DecodeConfiguration { diff --git a/src/decode/ltrnet/LTRCallEvent.java b/src/module/decode/ltrnet/LTRCallEvent.java similarity index 89% rename from src/decode/ltrnet/LTRCallEvent.java rename to src/module/decode/ltrnet/LTRCallEvent.java index 3368d26d..bfd1b8ed 100644 --- a/src/decode/ltrnet/LTRCallEvent.java +++ b/src/module/decode/ltrnet/LTRCallEvent.java @@ -15,13 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrnet; +package module.decode.ltrnet; +import module.decode.DecoderType; +import module.decode.event.CallEvent; +import module.decode.ltrstandard.LTRStandardMessage; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; -import decode.ltrstandard.LTRStandardMessage; public class LTRCallEvent extends CallEvent { @@ -64,11 +64,19 @@ public class LTRCallEvent extends CallEvent return false; } - public boolean addMessage( LTRNetMessage message ) + /** + * Checks if the message's talkgroup matches this call event. Counts the + * number of messages that match this call event to use in determining if + * a call event is valid or an isolated (errant) message. + * + * See: isValid() + */ + public boolean isMatchingTalkgroup( LTRNetMessage message ) { String talkgroup = message.getTalkgroupID(); - if( talkgroup.contentEquals( getToID() ) ) + if( talkgroup != null && getToID() != null && + talkgroup.contentEquals( getToID() ) ) { mValidCallMessages++; diff --git a/src/module/decode/ltrnet/LTRNetDecoder.java b/src/module/decode/ltrnet/LTRNetDecoder.java new file mode 100644 index 00000000..47636daa --- /dev/null +++ b/src/module/decode/ltrnet/LTRNetDecoder.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.ltrnet; + +import instrument.Instrumentable; +import instrument.tap.Tap; + +import java.util.ArrayList; +import java.util.List; + +import message.MessageDirection; +import module.decode.Decoder; +import module.decode.DecoderType; +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; +import alias.AliasList; +import bits.MessageFramer; +import bits.SyncPattern; +import dsp.fsk.LTRFSKDecoder; + +public class LTRNetDecoder extends Decoder + implements IRealBufferListener, Instrumentable +{ + public static final int LTR_STANDARD_MESSAGE_LENGTH = 40; + private LTRFSKDecoder mLTRFSKDecoder; + private MessageFramer mLTRMessageFramer; + private LTRNetMessageProcessor mLTRMessageProcessor; + + private List mAvailableTaps; + + public LTRNetDecoder( DecodeConfigLTRNet config, AliasList aliasList ) + { + super( new LTRNetDecoderState( aliasList ) ); + + mLTRFSKDecoder = new LTRFSKDecoder(); + + + if( config.getMessageDirection() == MessageDirection.OSW ) + { + mLTRMessageFramer = + new MessageFramer( SyncPattern.LTR_STANDARD_OSW.getPattern(), + LTR_STANDARD_MESSAGE_LENGTH ); + } + else + { + mLTRMessageFramer = + new MessageFramer( SyncPattern.LTR_STANDARD_ISW.getPattern(), + LTR_STANDARD_MESSAGE_LENGTH ); + } + + mLTRFSKDecoder.addListener( mLTRMessageFramer ); + + mLTRMessageProcessor = new LTRNetMessageProcessor( + config.getMessageDirection(), aliasList ); + + mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); + + mLTRMessageProcessor.setMessageListener( mMessageBroadcaster ); + } + + @Override + public DecoderType getDecoderType() + { + return DecoderType.LTR_NET; + } + + @Override + public Listener getRealBufferListener() + { + return mLTRFSKDecoder; + } + + @Override + public List getTaps() + { + if( mAvailableTaps == null ) + { + mAvailableTaps = new ArrayList(); + + mAvailableTaps.addAll( mLTRFSKDecoder.getTaps() ); + } + + return mAvailableTaps; + } + + @Override + public void addTap( Tap tap ) + { + mLTRFSKDecoder.addTap( tap ); + } + + @Override + public void removeTap( Tap tap ) + { + mLTRFSKDecoder.removeTap( tap ); + } +} diff --git a/src/decode/ltrnet/LTRNetPanel.java b/src/module/decode/ltrnet/LTRNetDecoderPanel.java similarity index 66% rename from src/decode/ltrnet/LTRNetPanel.java rename to src/module/decode/ltrnet/LTRNetDecoderPanel.java index 276acf48..3f4e4a8d 100644 --- a/src/decode/ltrnet/LTRNetPanel.java +++ b/src/module/decode/ltrnet/LTRNetDecoderPanel.java @@ -15,27 +15,24 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrnet; +package module.decode.ltrnet; import java.awt.EventQueue; import javax.swing.JLabel; +import net.miginfocom.swing.MigLayout; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; import settings.ColorSetting; import settings.Setting; +import settings.SettingsManager; import alias.Alias; -import controller.channel.Channel; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelStatePanel; -public class LTRNetPanel extends ChannelStatePanel +public class LTRNetDecoderPanel extends DecoderPanel { private static final long serialVersionUID = 1L; - private JLabel mStateLabel; - private JLabel mSourceLabel; - private JLabel mChannelLabel; - private JLabel mProtocol = new JLabel( "LTR-Net" ); private JLabel mTalkgroup = new JLabel(); private JLabel mTalkgroupAlias = new JLabel(); @@ -43,28 +40,19 @@ public class LTRNetPanel extends ChannelStatePanel private JLabel mLCN = new JLabel( "LCN:" ); private JLabel mDescription = new JLabel(); - public LTRNetPanel( Channel channel ) + public LTRNetDecoderPanel( SettingsManager settingsManager, LTRNetDecoder decoder ) { - super( channel ); + super( settingsManager, decoder ); init(); } public void init() { - mStateLabel = new JLabel( mChannel.getProcessingChain(). - getChannelState().getState().getDisplayValue() ); - mStateLabel.setFont( mFontDecoder ); - mStateLabel.setForeground( mColorLabelDecoder ); + super.init(); - mSourceLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() ); - mSourceLabel.setFont( mFontDetails ); - mSourceLabel.setForeground( mColorLabelDetails ); + setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]0[]") ); - mChannelLabel = new JLabel( mChannel.getChannelDisplayName() ); - mChannelLabel.setFont( mFontDetails ); - mChannelLabel.setForeground( mColorLabelDetails ); - mProtocol.setFont( mFontDecoder ); mProtocol.setForeground( mColorLabelDecoder ); @@ -80,10 +68,6 @@ public class LTRNetPanel extends ChannelStatePanel mDescription.setFont( mFontDecoder ); mDescription.setForeground( mColorLabelDecoder ); - add( mStateLabel ); - add( mSourceLabel ); - add( mChannelLabel, "wrap" ); - add( mProtocol ); add( mTalkgroup ); add( mTalkgroupAlias, "wrap" ); @@ -91,6 +75,11 @@ public class LTRNetPanel extends ChannelStatePanel add( mLCN ); add( mDescription, "span,wrap" ); } + + private LTRNetDecoderState getDecoderState() + { + return (LTRNetDecoderState)getDecoder().getDecoderState(); + } @Override public void receive( final ChangedAttribute changedAttribute ) @@ -100,23 +89,10 @@ public class LTRNetPanel extends ChannelStatePanel @Override public void run() { - LTRNetChannelState state = (LTRNetChannelState)mChannel - .getProcessingChain().getChannelState(); + LTRNetDecoderState state = getDecoderState(); switch( changedAttribute ) { - case CHANNEL_STATE: - mStateLabel.setText( state.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; case TO_TALKGROUP: mTalkgroup.setText( state.getToTalkgroup() ); break; @@ -127,7 +103,7 @@ public class LTRNetPanel extends ChannelStatePanel { mTalkgroupAlias.setText( tgAlias.getName() ); String iconNameString = tgAlias.getIconName(); - mTalkgroupAlias.setIcon( getSettingsManager() + mTalkgroupAlias.setIcon( mSettingsManager .getImageIcon( iconNameString, 12 ) ); } else @@ -163,10 +139,6 @@ public class LTRNetPanel extends ChannelStatePanel switch( colorSetting.getColorSettingName() ) { case CHANNEL_STATE_LABEL_DECODER: - if( mStateLabel != null ) - { - mStateLabel.setForeground( mColorLabelDecoder ); - } if( mProtocol != null ) { mProtocol.setForeground( mColorLabelDecoder ); @@ -188,16 +160,6 @@ public class LTRNetPanel extends ChannelStatePanel mDescription.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/module/decode/ltrnet/LTRNetDecoderState.java b/src/module/decode/ltrnet/LTRNetDecoderState.java new file mode 100644 index 00000000..cecfb04d --- /dev/null +++ b/src/module/decode/ltrnet/LTRNetDecoderState.java @@ -0,0 +1,707 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.ltrnet; + +import java.text.DecimalFormat; +import java.util.HashMap; +import java.util.Iterator; +import java.util.TreeSet; + +import message.Message; +import module.decode.event.CallEvent; +import module.decode.event.CallEvent.CallEventType; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.DecoderStateEvent.Event; +import module.decode.state.State; +import alias.Alias; +import alias.AliasList; + +public class LTRNetDecoderState extends DecoderState +{ + private DecimalFormat mDecimalFormatter = new DecimalFormat( "0.00000" ); + + private TreeSet mTalkgroups = new TreeSet(); + private TreeSet mTalkgroupsFirstHeard = new TreeSet(); + private TreeSet mESNs = new TreeSet(); + private TreeSet mUniqueIDs = new TreeSet(); + private TreeSet mNeighborIDs = new TreeSet(); + private TreeSet mSiteIDs = new TreeSet(); + private HashMap mReceiveFrequencies = + new HashMap(); + private HashMap mTransmitFrequencies = + new HashMap(); + private HashMap mActiveCalls = new HashMap(); + + private String mTalkgroup; + private Alias mTalkgroupAlias; + private String mDescription; + private int mChannelNumber; + private long mFrequency = 0; + + public LTRNetDecoderState( AliasList aliasList ) + { + super( aliasList ); + } + + @Override + public void receive( Message message ) + { + if( message.isValid() ) + { + if( message instanceof LTRNetOSWMessage ) + { + LTRNetOSWMessage ltr = (LTRNetOSWMessage)message; + + switch( ltr.getMessageType() ) + { + case CA_STRT: + case CA_ENDD: + if( mChannelNumber == 0 ) + { + mChannelNumber = ltr.getChannel(); + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + + if( ltr.getChannel() == 31 ) + { + processCallEndMessage( ltr ); + } + else if( ltr.getChannel() == mChannelNumber ) + { + processCallMessage( ltr ); + } + else + { + processCallDetectMessage( ltr ); + } + break; + case SY_IDLE: + if( mChannelNumber != ltr.getChannel() ) + { + mChannelNumber = ltr.getChannel(); + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + break; + case MA_CHNH: + break; + case MA_CHNL: + break; + case FQ_RXHI: + case FQ_RXLO: + if( ltr.getFrequency() > 0 ) + { + mReceiveFrequencies.put( ltr.getHomeRepeater(), + ltr.getFrequency() ); + } + break; + case FQ_TXHI: + case FQ_TXLO: + if( ltr.getFrequency() > 0 ) + { + mTransmitFrequencies.put( ltr.getHomeRepeater(), + ltr.getFrequency() ); + } + break; + case ID_NBOR: + String neighborID = ltr.getNeighborID(); + + if( neighborID != null ) + { + mNeighborIDs.add( neighborID ); + } + break; + case ID_UNIQ: + int uniqueID = ltr.getRadioUniqueID(); + + if( uniqueID != LTRNetOSWMessage.INT_NULL_VALUE ) + { + mUniqueIDs.add( uniqueID ); + } + + if( getCurrentLTRCallEvent() == null ) + { + mCurrentCallEvent = new LTRCallEvent.Builder( CallEventType.REGISTER ) + .aliasList( getAliasList() ) + .channel( String.valueOf( mChannelNumber ) ) + .frequency( mFrequency ) + .from( String.valueOf( uniqueID ) ) + .build(); + } + else + { + mCurrentCallEvent.setFromID( + String.valueOf( ltr.getRadioUniqueID() ) ); + mCurrentCallEvent.setDetails( "Unique ID" ); + + broadcast( mCurrentCallEvent ); + } + + mTalkgroup = String.valueOf( uniqueID ); + broadcast( ChangedAttribute.TO_TALKGROUP ); + + mTalkgroupAlias = ltr.getRadioUniqueIDAlias(); + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + break; + case ID_SITE: + String siteID = ltr.getSiteID(); + + if( siteID != null ) + { + mSiteIDs.add( siteID ); + } + break; + default: + break; + } + } + else if( message instanceof LTRNetISWMessage ) + { + LTRNetISWMessage ltr = ((LTRNetISWMessage)message); + + switch( ltr.getMessageType() ) + { + case CA_STRT: + processCallMessage( ltr ); + break; + case CA_ENDD: + processCallEndMessage( ltr ); + break; + case ID_ESNH: + case ID_ESNL: + String esn = ltr.getESN(); + + if( !esn.contains( "xxxx" ) ) + { + mESNs.add( ltr.getESN() ); + } + + mDescription = "ESN"; + broadcast( ChangedAttribute.DESCRIPTION ); + + mTalkgroup = ltr.getESN(); + broadcast( ChangedAttribute.TO_TALKGROUP ); + + mTalkgroupAlias = ltr.getESNAlias(); + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + + broadcast( new DecoderStateEvent( this, Event.DECODE, State.DATA ) ); + + if( mCurrentCallEvent == null ) + { + mCurrentCallEvent = new LTRCallEvent.Builder( CallEventType.REGISTER_ESN ) + .aliasList( getAliasList() ) + .details( "ESN:" + ltr.getESN() ) + .frequency( mFrequency ) + .from( ltr.getESN() ) + .build(); + + broadcast( mCurrentCallEvent ); + } + + break; + case ID_UNIQ: + int uniqueid = ltr.getRadioUniqueID(); + + if( uniqueid != LTRNetISWMessage.INT_NULL_VALUE ) + { + mUniqueIDs.add( uniqueid ); + + mDescription = "REGISTER UID"; + broadcast( ChangedAttribute.DESCRIPTION ); + + mTalkgroup = String.valueOf( uniqueid ); + broadcast( ChangedAttribute.TO_TALKGROUP ); + + mTalkgroupAlias = ltr.getRadioUniqueIDAlias(); + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + + if( getCurrentLTRCallEvent() == null ) + { + mCurrentCallEvent = new LTRCallEvent.Builder( CallEventType.REGISTER ) + .aliasList( getAliasList() ) + .channel( String.valueOf( mChannelNumber ) ) + .frequency( mFrequency ) + .from( String.valueOf( uniqueid ) ) + .build(); + } + else + { + mCurrentCallEvent.setFromID( + String.valueOf( ltr.getRadioUniqueID() ) ); + mCurrentCallEvent.setDetails( "Unique ID" ); + + broadcast( mCurrentCallEvent ); + } + } + break; + default: + break; + } + } + } + } + + public LTRCallEvent getCurrentLTRCallEvent() + { + if( mCurrentCallEvent != null ) + { + return (LTRCallEvent)mCurrentCallEvent; + } + + return null; + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "Activity Summary\n" ); + sb.append( "Decoder:\tLTR-Net\n\n" ); + + if( mSiteIDs.isEmpty() ) + { + sb.append( "Site:\tUnknown\n" ); + } + else + { + Iterator it = mSiteIDs.iterator(); + + while( it.hasNext() ) + { + sb.append( "Site:\t" ); + + String siteID = it.next(); + + sb.append( siteID ); + + if( hasAliasList() ) + { + Alias siteAlias = getAliasList().getSiteID( String.valueOf( siteID ) ); + + if( siteAlias != null ) + { + sb.append( " " ); + sb.append( siteAlias.getName() ); + } + } + + sb.append( "\n" ); + } + } + + sb.append( "\nLCNs (transmit | receive)\n" ); + + if( mReceiveFrequencies.isEmpty() && mTransmitFrequencies.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + for( int x = 1; x < 21; x++ ) + { + long rcv = 0; + + if( mReceiveFrequencies.containsKey( x ) ) + { + rcv = mReceiveFrequencies.get( x ); + } + + long xmt = 0; + + if( mTransmitFrequencies.containsKey( x ) ) + { + xmt = mTransmitFrequencies.get( x ); + } + + if( rcv > 0 || xmt > 0 ) + { + if( x < 10 ) + { + sb.append( " " ); + } + + sb.append( x ); + sb.append( ": " ); + + if( xmt == 0 ) + { + sb.append( "---.-----" ); + } + else + { + sb.append( mDecimalFormatter.format( (double)xmt/1E6d ) ); + } + sb.append( " | " ); + + if( rcv == 0 ) + { + sb.append( "---.-----" ); + } + else + { + sb.append( mDecimalFormatter.format( (double)rcv/1E6d ) ); + } + + if( x == mChannelNumber ) + { + sb.append( " **" ); + } + + sb.append( "\n" ); + } + } + } + + sb.append( "\nTalkgroups\n" ); + + if( mTalkgroups.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mTalkgroups.iterator(); + + while( it.hasNext() ) + { + String tgid = it.next(); + + sb.append( " " ); + sb.append( tgid ); + sb.append( " " ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getTalkgroupAlias( tgid ); + + if( alias != null ) + { + sb.append( alias.getName() ); + } + } + + sb.append( "\n" ); + } + } + + sb.append( "\nRadio Unique IDs\n" ); + + if( mUniqueIDs.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mUniqueIDs.iterator(); + + while( it.hasNext() ) + { + int uid = it.next(); + + sb.append( " " ); + sb.append( uid ); + sb.append( " " ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getUniqueID( uid ); + + if( alias != null ) + { + sb.append( alias.getName() ); + } + } + + sb.append( "\n" ); + } + } + + sb.append( "\nESNs\n" ); + + if( mESNs.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mESNs.iterator(); + + while( it.hasNext() ) + { + String esn = it.next(); + + sb.append( " " ); + sb.append( esn ); + sb.append( " " ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getESNAlias( esn ); + + if( alias != null ) + { + sb.append( alias.getName() ); + } + } + + sb.append( "\n" ); + } + } + + sb.append( "\nNeighbor Sites\n" ); + + if( mNeighborIDs.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mNeighborIDs.iterator(); + + while( it.hasNext() ) + { + String neighbor = it.next(); + + sb.append( " " ); + sb.append( neighbor ); + sb.append( " " ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getSiteID( String.valueOf( neighbor ) ); + + if( alias != null ) + { + sb.append( alias.getName() ); + } + } + + sb.append( "\n" ); + } + } + + return sb.toString(); + } + + /** + * Call Detect - current channel messages indicate a call on another channel + */ + private void processCallDetectMessage( LTRNetMessage message ) + { + if( !mActiveCalls.containsKey( message.getChannel() ) || + !mActiveCalls.get( message.getChannel() ).contentEquals( message.getTalkgroupID() ) ) + { + mActiveCalls.put( message.getChannel(), message.getTalkgroupID() ); + + int channel = message.getChannel(); + + long frequency = 0; + + if( mTransmitFrequencies.containsKey( channel ) ) + { + frequency = mTransmitFrequencies.get( channel ); + } + + broadcast( new LTRCallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( String.valueOf( message.getChannel() ) ) + .frequency( frequency ) + .to( message.getTalkgroupID() ) + .build() ); + } + } + + private void processCallMessage( LTRNetMessage message ) + { + LTRCallEvent current = getCurrentLTRCallEvent(); + + int group = message.getGroup(); + + /* If this is a new call or the talkgroup is different from the current + * call, create a new call event */ + if( current == null || !current.isMatchingTalkgroup( message ) ) + { + /* Invalidate the current call */ + if( current != null ) + { + current.setValid( false ); + broadcast( current ); + } + + mTalkgroup = message.getTalkgroupID(); + broadcast( ChangedAttribute.TO_TALKGROUP ); + + /* A talkgroup must be seen at least once before it will be added + * to the mTalkgroups list that is used in the activity summary, + * so that we don't pollute the summary with one-off error talkgroups */ + if( mTalkgroupsFirstHeard.contains( mTalkgroup ) ) + { + mTalkgroups.add( mTalkgroup ); + } + else + { + mTalkgroupsFirstHeard.add( mTalkgroup ); + } + + mTalkgroupAlias = message.getTalkgroupIDAlias(); + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + + CallEventType type = CallEventType.CALL; + + if( group == 253 ) + { + type = CallEventType.REGISTER; + + mDescription = "REGISTER"; + broadcast( ChangedAttribute.DESCRIPTION ); + } + else if( group == 254 ) + { + type = CallEventType.STATION_ID; + + mDescription = "STATION ID"; + broadcast( ChangedAttribute.DESCRIPTION ); + } + + mCurrentCallEvent = new LTRCallEvent.Builder( type ) + .aliasList( getAliasList() ) + .channel( String.valueOf( message.getChannel() ) ) + .frequency( mFrequency ) + .to( message.getTalkgroupID() ) + .build(); + + broadcast( mCurrentCallEvent ); + + broadcast( new DecoderStateEvent( this, Event.START, + ( type == CallEventType.CALL ) ? State.CALL : State.DATA ) ); + } + else + { + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, + ( group == 253 || group == 254 ) ? State.DATA : State.CALL ) ); + } + } + + private void processCallEndMessage( LTRNetMessage message ) + { + if( mCurrentCallEvent != null && + mCurrentCallEvent.getCallEventType() == CallEventType.CALL ) + { + mCurrentCallEvent.setEnd( System.currentTimeMillis() ); + broadcast( mCurrentCallEvent ); + + } + + mCurrentCallEvent = null; + + broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) ); + } + + /** + * Resets the decoder state for re-use on another channel + */ + public void reset() + { + mChannelNumber = 0; + + mActiveCalls.clear(); + mESNs.clear(); + mNeighborIDs.clear(); + mReceiveFrequencies.clear(); + mSiteIDs.clear(); + mTalkgroups.clear(); + mTransmitFrequencies.clear(); + mUniqueIDs.clear(); + + resetState(); + } + + /** + * Resets the decoder state after a call event + */ + private void resetState() + { + if( mCurrentCallEvent != null && mCurrentCallEvent + .getCallEventType() == CallEventType.CALL ) + { + mCurrentCallEvent.setEnd( System.currentTimeMillis() ); + broadcast( mCurrentCallEvent ); + } + + mCurrentCallEvent = null; + + mTalkgroup = null; + broadcast( ChangedAttribute.TO_TALKGROUP ); + + mTalkgroupAlias = null; + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + + mDescription = null; + broadcast( ChangedAttribute.DESCRIPTION ); + } + + public String getToTalkgroup() + { + return mTalkgroup; + } + + public Alias getToTalkgroupAlias() + { + return mTalkgroupAlias; + } + + public String getDescription() + { + return mDescription; + } + + public boolean hasChannelNumber() + { + return mChannelNumber != 0; + } + + public int getChannelNumber() + { + return mChannelNumber; + } + + @Override + public void init() + { + /* No initialization steps required */ + } + + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + switch( event.getEvent() ) + { + case RESET: + resetState(); + break; + case SOURCE_FREQUENCY: + mFrequency = event.getFrequency(); + break; + default: + break; + } + } +} diff --git a/src/decode/ltrnet/LTRNetEditor.java b/src/module/decode/ltrnet/LTRNetEditor.java similarity index 90% rename from src/decode/ltrnet/LTRNetEditor.java rename to src/module/decode/ltrnet/LTRNetEditor.java index 1c3dc760..f35bb2ce 100644 --- a/src/decode/ltrnet/LTRNetEditor.java +++ b/src/module/decode/ltrnet/LTRNetEditor.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrnet; +package module.decode.ltrnet; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -23,11 +23,11 @@ import java.awt.event.ActionListener; import javax.swing.DefaultComboBoxModel; import javax.swing.JCheckBox; import javax.swing.JComboBox; +import javax.swing.JLabel; import message.MessageDirection; -import decode.DecodeEditor; -import decode.config.DecodeConfigLTRNet; -import decode.config.DecodeConfiguration; +import module.decode.DecodeEditor; +import module.decode.config.DecodeConfiguration; public class LTRNetEditor extends DecodeEditor { @@ -66,6 +66,7 @@ public class LTRNetEditor extends DecodeEditor } } ); - add( mComboDirection, "span 2,wrap" ); + add( new JLabel( "Format:" ) ); + add( mComboDirection, "wrap" ); } } diff --git a/src/decode/ltrnet/LTRNetISWMessage.java b/src/module/decode/ltrnet/LTRNetISWMessage.java similarity index 99% rename from src/decode/ltrnet/LTRNetISWMessage.java rename to src/module/decode/ltrnet/LTRNetISWMessage.java index d643bc4e..2567b5d1 100644 --- a/src/decode/ltrnet/LTRNetISWMessage.java +++ b/src/module/decode/ltrnet/LTRNetISWMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrnet; +package module.decode.ltrnet; import java.util.ArrayList; import java.util.Date; @@ -221,7 +221,7 @@ public class LTRNetISWMessage extends LTRNetMessage public int getRadioUniqueID() { - int retVal = sINT_NULL_VALUE; + int retVal = INT_NULL_VALUE; if( mMessageType == MessageType.ID_UNIQ ) { diff --git a/src/decode/ltrnet/LTRNetMessage.java b/src/module/decode/ltrnet/LTRNetMessage.java similarity index 98% rename from src/decode/ltrnet/LTRNetMessage.java rename to src/module/decode/ltrnet/LTRNetMessage.java index 05f57150..75d3d468 100644 --- a/src/decode/ltrnet/LTRNetMessage.java +++ b/src/module/decode/ltrnet/LTRNetMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrnet; +package module.decode.ltrnet; import java.text.DecimalFormat; import java.text.SimpleDateFormat; @@ -23,17 +23,17 @@ import java.text.SimpleDateFormat; import message.Message; import message.MessageDirection; import message.MessageType; +import module.decode.DecoderType; import alias.Alias; import alias.AliasList; import bits.BinaryMessage; -import decode.DecoderType; import edac.CRC; import edac.CRCLTR; public abstract class LTRNetMessage extends Message { protected static final String sUNKNOWN = "**UNKNOWN**"; - protected static final int sINT_NULL_VALUE = -1; + protected static final int INT_NULL_VALUE = -1; protected static final double sDOUBLE_NULL_VALUE = -1.0D; protected SimpleDateFormat mDatestampFormatter = new SimpleDateFormat( "yyyyMMdd HHmmss" ); diff --git a/src/decode/ltrnet/LTRNetMessageFilter.java b/src/module/decode/ltrnet/LTRNetMessageFilter.java similarity index 98% rename from src/decode/ltrnet/LTRNetMessageFilter.java rename to src/module/decode/ltrnet/LTRNetMessageFilter.java index 1f5a0f02..3d73884b 100644 --- a/src/decode/ltrnet/LTRNetMessageFilter.java +++ b/src/module/decode/ltrnet/LTRNetMessageFilter.java @@ -1,4 +1,4 @@ -package decode.ltrnet; +package module.decode.ltrnet; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/module/decode/ltrnet/LTRNetMessageProcessor.java b/src/module/decode/ltrnet/LTRNetMessageProcessor.java new file mode 100644 index 00000000..bd7aa512 --- /dev/null +++ b/src/module/decode/ltrnet/LTRNetMessageProcessor.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.ltrnet; + +import java.util.HashMap; + +import message.Message; +import message.MessageDirection; +import message.MessageType; +import sample.Listener; +import alias.AliasList; +import bits.BinaryMessage; + +public class LTRNetMessageProcessor implements Listener +{ + private Listener mMessageListener; + + private HashMap mReceiveHighAuxMessages = + new HashMap(); + private HashMap mReceiveLowAuxMessages = + new HashMap(); + + private HashMap mTransmitHighAuxMessages = + new HashMap(); + private HashMap mTransmitLowAuxMessages = + new HashMap(); + + private LTRNetISWMessage mESNHighMessage; + private LTRNetISWMessage mESNLowMessage; + + private MessageDirection mDirection; + private AliasList mAliasList; + + public LTRNetMessageProcessor( MessageDirection direction, + AliasList list ) + { + mDirection = direction; + mAliasList = list; + } + + @Override + public void receive( BinaryMessage buffer ) + { + if( mMessageListener != null ) + { + LTRNetMessage message; + + if( mDirection == MessageDirection.OSW ) + { + message = new LTRNetOSWMessage( buffer, mAliasList ); + + if( message.isValid() ) + { + if( message.getMessageType() == MessageType.FQ_TXHI ) + { + mTransmitHighAuxMessages.put( message.getHomeRepeater(), + (LTRNetOSWMessage)message ); + + message.setAuxiliaryMessage( mTransmitLowAuxMessages.get( + message.getHomeRepeater() ) ); + } + else if( message.getMessageType() == MessageType.FQ_TXLO ) + { + mTransmitLowAuxMessages.put( message.getHomeRepeater(), + (LTRNetOSWMessage)message ); + + message.setAuxiliaryMessage( mTransmitHighAuxMessages.get( + message.getHomeRepeater() ) ); + } + else if( message.getMessageType() == MessageType.FQ_RXHI ) + { + mReceiveHighAuxMessages.put( message.getHomeRepeater(), + (LTRNetOSWMessage)message ); + + message.setAuxiliaryMessage( mReceiveLowAuxMessages.get( + message.getHomeRepeater() ) ); + } + else if( message.getMessageType() == MessageType.FQ_RXLO ) + { + mReceiveLowAuxMessages.put( message.getHomeRepeater(), + (LTRNetOSWMessage)message ); + message.setAuxiliaryMessage( mReceiveHighAuxMessages.get( + message.getHomeRepeater() ) ); + } + } + } + else + { + buffer.flip( 0, 40 ); + + message = new LTRNetISWMessage( buffer, mAliasList ); + + if( message.isValid() ) + { + /** + * Catch ESN High messages to marry up with ESN Low messages. Purge + * the ESN high message after 2 messages, so that it doesn't get + * paired with the wrong esn low message + */ + if( message.getMessageType() == MessageType.ID_ESNH ) + { + mESNHighMessage = (LTRNetISWMessage)message; + + if( mESNLowMessage != null ) + { + /* Check that the stored message is not older than + * 5 seconds */ + if( System.currentTimeMillis() - + mESNLowMessage.getTimeReceived() < 5000 ) + { + ((LTRNetISWMessage)message).setAuxiliaryMessage( mESNLowMessage ); + } + else + { + mESNLowMessage = null; + } + } + } + else if( message.getMessageType() == MessageType.ID_ESNL ) + { + mESNLowMessage = (LTRNetISWMessage)message; + + if( mESNHighMessage != null ) + { + /* Check that the stored message is not older than 5 seconds */ + if( System.currentTimeMillis() - mESNHighMessage.getTimeReceived() < 5000 ) + { + ((LTRNetISWMessage)message).setAuxiliaryMessage( mESNHighMessage ); + } + else + { + mESNHighMessage = null; + } + } + } + } + } + + mMessageListener.receive( message ); + } + } + + public void setMessageListener( Listener listener ) + { + mMessageListener = listener; + } + + public void removeMessageListener() + { + mMessageListener = null; + } +} diff --git a/src/decode/ltrnet/LTRNetOSWMessage.java b/src/module/decode/ltrnet/LTRNetOSWMessage.java similarity index 83% rename from src/decode/ltrnet/LTRNetOSWMessage.java rename to src/module/decode/ltrnet/LTRNetOSWMessage.java index 93e276da..5e90fd99 100644 --- a/src/decode/ltrnet/LTRNetOSWMessage.java +++ b/src/module/decode/ltrnet/LTRNetOSWMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrnet; +package module.decode.ltrnet; import java.util.ArrayList; import java.util.Date; @@ -30,27 +30,27 @@ import bits.BinaryMessage; public class LTRNetOSWMessage extends LTRNetMessage { - private static final int sCHANNEL_FREQUENCY_MESSAGE_TYPE_BIT = 20; - private static final int sCHANNEL_MAP_MESSAGE_TYPE_BIT = 17; + private static final int CHANNEL_FREQUENCY_MESSAGE_TYPE_BIT = 20; + private static final int CHANNEL_MAP_MESSAGE_TYPE_BIT = 17; - private static final int sCHIU_UNIQUE_ID = 17; - private static final int sCHIU_SITE_ID = 18; - private static final int sCHIU_TRANSMIT_FREQUENCY = 24; - private static final int sCHIU_RECEIVE_FREQUENCY = 25; - private static final int sCHIU_NEIGHBOR = 26; - private static final int sCHIU_MAP = 28; - private static final int sCHIU_CALL_END = 31; + private static final int CHIU_UNIQUE_ID = 17; + private static final int CHIU_SITE_ID = 18; + private static final int CHIU_TRANSMIT_FREQUENCY = 24; + private static final int CHIU_RECEIVE_FREQUENCY = 25; + private static final int CHIU_NEIGHBOR = 26; + private static final int CHIU_MAP = 28; + private static final int CHIU_CALL_END = 31; - private static final int sHOME_DIRECTED_GROUP_CALL = 29; - private static final int sHOME_CHANNEL_IN_USE_MUTE_AUDIO = 30; + private static final int HOME_DIRECTED_GROUP_CALL = 29; + private static final int HOME_CHANNEL_IN_USE_MUTE_AUDIO = 30; - private static final int sGROUP_DO_NOTHING = 253; - private static final int sGROUP_CWID = 254; - private static final int sGROUP_CHANNEL_IDLE = 255; + private static final int GROUP_DO_NOTHING = 253; + private static final int GROUP_CWID = 254; + private static final int GROUP_CHANNEL_IDLE = 255; - private static final int sFREE_ALL_CHANNELS_BUSY = 0; - private static final int sFREE_LTR_GROUP_CALL = 30; - private static final int sFREE_LTRNET_GROUP_CALL = 31; + private static final int FREE_ALL_CHANNELS_BUSY = 0; + private static final int FREE_LTR_GROUP_CALL = 30; + private static final int FREE_LTRNET_GROUP_CALL = 31; private LTRNetOSWMessage mAuxMessage; @@ -74,7 +74,7 @@ public class LTRNetOSWMessage extends LTRNetMessage int radioId = getRadioUniqueID(); - if( radioId != sINT_NULL_VALUE ) + if( radioId != INT_NULL_VALUE ) { sb.append( " RADIO UNIQUE ID: " ); sb.append( radioId ); @@ -154,10 +154,10 @@ public class LTRNetOSWMessage extends LTRNetMessage sb.append( "CHAN RX LOW LCN:" ); sb.append( format( getHomeRepeater(), 2 ) ); sb.append( " = " ); - double rxFreqLo = getFrequency(); - if( rxFreqLo != sDOUBLE_NULL_VALUE ) + long rxFreqLo = getFrequency(); + if( rxFreqLo > 0 ) { - sb.append( mDecimalFormatter.format( rxFreqLo ) ); + sb.append( mDecimalFormatter.format( (double)rxFreqLo / 1E6d ) ); } else { @@ -169,10 +169,10 @@ public class LTRNetOSWMessage extends LTRNetMessage sb.append( "CHAN RX HIGH LCN:" ); sb.append( format( getHomeRepeater(), 2 ) ); sb.append( " = " ); - double rxfreqhi = getFrequency(); - if( rxfreqhi != sDOUBLE_NULL_VALUE ) + long rxfreqhi = getFrequency(); + if( rxfreqhi > 0 ) { - sb.append( mDecimalFormatter.format( rxfreqhi ) ); + sb.append( mDecimalFormatter.format( (double)rxfreqhi / 1E6d ) ); } else { @@ -184,10 +184,10 @@ public class LTRNetOSWMessage extends LTRNetMessage sb.append( "CHAN TX LOW LCN:" ); sb.append( format( getHomeRepeater(), 2 ) ); sb.append( " = " ); - double txfreq = getFrequency(); - if( txfreq != sDOUBLE_NULL_VALUE ) + long txfreq = getFrequency(); + if( txfreq > 0 ) { - sb.append( mDecimalFormatter.format( txfreq ) ); + sb.append( mDecimalFormatter.format( (double)txfreq / 1E6d ) ); } else { @@ -199,10 +199,10 @@ public class LTRNetOSWMessage extends LTRNetMessage sb.append( "CHAN TX HIGH LCN:" ); sb.append( format( getHomeRepeater(), 2 ) ); sb.append( " = " ); - double txFreqLo = getFrequency(); - if( txFreqLo != sDOUBLE_NULL_VALUE ) + long txFreqLo = getFrequency(); + if( txFreqLo > 0 ) { - sb.append( mDecimalFormatter.format( txFreqLo ) ); + sb.append( mDecimalFormatter.format( (double)txFreqLo / 1E6d ) ); } else { @@ -224,7 +224,7 @@ public class LTRNetOSWMessage extends LTRNetMessage int neighborRank = getNeighborRank(); String neighborId = getNeighborID(); - if( neighborRank != sINT_NULL_VALUE && + if( neighborRank != INT_NULL_VALUE && neighborId != null ) { sb.append( "NEIGHBOR SITE " ); @@ -335,7 +335,7 @@ public class LTRNetOSWMessage extends LTRNetMessage public int getRadioUniqueID() { - int retVal = sINT_NULL_VALUE; + int retVal = INT_NULL_VALUE; if( mMessageType == MessageType.ID_UNIQ ) { @@ -379,7 +379,12 @@ public class LTRNetOSWMessage extends LTRNetMessage { if( mMessageType == MessageType.ID_NBOR ) { - return String.valueOf( mMessage.getInt( 23, 32 ) ); + int id = mMessage.getInt( 23, 32 ); + + if( id > 0 ) + { + return String.valueOf( id ); + } } return null; @@ -397,7 +402,7 @@ public class LTRNetOSWMessage extends LTRNetMessage public int getNeighborRank() { - int retVal = sINT_NULL_VALUE; + int retVal = INT_NULL_VALUE; if( mMessageType == MessageType.ID_NBOR ) { @@ -407,9 +412,9 @@ public class LTRNetOSWMessage extends LTRNetMessage return retVal; } - public double getFrequency() + public long getFrequency() { - double retVal = sDOUBLE_NULL_VALUE; + long retVal = 0; if( mAuxMessage != null ) { @@ -429,9 +434,9 @@ public class LTRNetOSWMessage extends LTRNetMessage low = getLowChannelUnits(); } - if( high != sINT_NULL_VALUE && low != sINT_NULL_VALUE ) + if( high != INT_NULL_VALUE && low != INT_NULL_VALUE ) { - retVal = ( high + low ) * .00125 + 150.0; + retVal = ( high + low ) * 1250 + 150000000; } } @@ -445,7 +450,7 @@ public class LTRNetOSWMessage extends LTRNetMessage */ public int getHighChannelUnits() { - int retVal = sINT_NULL_VALUE; + int retVal = INT_NULL_VALUE; if( mAuxMessage != null && ( mMessageType == MessageType.FQ_RXHI || @@ -466,7 +471,7 @@ public class LTRNetOSWMessage extends LTRNetMessage */ public int getLowChannelUnits() { - int retVal = sINT_NULL_VALUE; + int retVal = INT_NULL_VALUE; if( mMessageType == MessageType.FQ_RXLO || mMessageType == MessageType.FQ_TXLO ) @@ -490,20 +495,20 @@ public class LTRNetOSWMessage extends LTRNetMessage { switch( channel ) { - case sCHIU_UNIQUE_ID: + case CHIU_UNIQUE_ID: retVal = MessageType.ID_UNIQ; break; - case sCHIU_SITE_ID: + case CHIU_SITE_ID: retVal = MessageType.ID_SITE; break; - case sCHIU_TRANSMIT_FREQUENCY: + case CHIU_TRANSMIT_FREQUENCY: if( getHomeRepeater() == 24 ) { retVal = MessageType.ID_UNIQ; } else { - if( mMessage.get( sCHANNEL_FREQUENCY_MESSAGE_TYPE_BIT ) ) + if( mMessage.get( CHANNEL_FREQUENCY_MESSAGE_TYPE_BIT ) ) { retVal = MessageType.FQ_TXHI; } @@ -513,8 +518,8 @@ public class LTRNetOSWMessage extends LTRNetMessage } } break; - case sCHIU_RECEIVE_FREQUENCY: - if( mMessage.get( sCHANNEL_FREQUENCY_MESSAGE_TYPE_BIT ) ) + case CHIU_RECEIVE_FREQUENCY: + if( mMessage.get( CHANNEL_FREQUENCY_MESSAGE_TYPE_BIT ) ) { retVal = MessageType.FQ_RXHI; } @@ -523,11 +528,11 @@ public class LTRNetOSWMessage extends LTRNetMessage retVal = MessageType.FQ_RXLO; } break; - case sCHIU_NEIGHBOR: + case CHIU_NEIGHBOR: retVal = MessageType.ID_NBOR; break; - case sCHIU_MAP: - if( mMessage.get( sCHANNEL_MAP_MESSAGE_TYPE_BIT ) ) + case CHIU_MAP: + if( mMessage.get( CHANNEL_MAP_MESSAGE_TYPE_BIT ) ) { retVal = MessageType.MA_CHNH; } @@ -536,7 +541,7 @@ public class LTRNetOSWMessage extends LTRNetMessage retVal = MessageType.MA_CHNL; } break; - case sCHIU_CALL_END: + case CHIU_CALL_END: retVal = MessageType.CA_ENDD; } } @@ -544,7 +549,7 @@ public class LTRNetOSWMessage extends LTRNetMessage { int group = getGroup(); - if( group == sGROUP_CHANNEL_IDLE ) + if( group == GROUP_CHANNEL_IDLE ) { retVal = MessageType.SY_IDLE; } diff --git a/src/decode/config/DecodeConfigLTRStandard.java b/src/module/decode/ltrstandard/DecodeConfigLTRStandard.java similarity index 92% rename from src/decode/config/DecodeConfigLTRStandard.java rename to src/module/decode/ltrstandard/DecodeConfigLTRStandard.java index 62118f0f..702ceff1 100644 --- a/src/decode/config/DecodeConfigLTRStandard.java +++ b/src/module/decode/ltrstandard/DecodeConfigLTRStandard.java @@ -15,12 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.ltrstandard; import javax.xml.bind.annotation.XmlAttribute; import message.MessageDirection; -import decode.DecoderType; +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; public class DecodeConfigLTRStandard extends DecodeConfiguration { diff --git a/src/module/decode/ltrstandard/LTRStandardDecoder.java b/src/module/decode/ltrstandard/LTRStandardDecoder.java new file mode 100644 index 00000000..25620a4c --- /dev/null +++ b/src/module/decode/ltrstandard/LTRStandardDecoder.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.ltrstandard; + +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; +import message.MessageDirection; +import module.decode.Decoder; +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; +import alias.AliasList; +import bits.MessageFramer; +import bits.SyncPattern; +import dsp.fsk.LTRFSKDecoder; + +public class LTRStandardDecoder extends Decoder implements IRealBufferListener +{ + public static final int LTR_STANDARD_MESSAGE_LENGTH = 40; + + private LTRFSKDecoder mLTRFSKDecoder; + private MessageFramer mLTRMessageFramer; + private LTRStandardMessageProcessor mLTRMessageProcessor; + + public LTRStandardDecoder( AliasList aliasList, + MessageDirection direction ) + { + super( new LTRStandardDecoderState( aliasList ) ); + + mLTRFSKDecoder = new LTRFSKDecoder(); + + if( direction == MessageDirection.OSW ) + { + mLTRMessageFramer = + new MessageFramer( SyncPattern.LTR_STANDARD_OSW.getPattern(), + LTR_STANDARD_MESSAGE_LENGTH ); + } + else + { + mLTRMessageFramer = + new MessageFramer( SyncPattern.LTR_STANDARD_ISW.getPattern(), + LTR_STANDARD_MESSAGE_LENGTH ); + } + + mLTRFSKDecoder.addListener( mLTRMessageFramer ); + + mLTRMessageProcessor = new LTRStandardMessageProcessor( direction, aliasList ); + mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); + mLTRMessageProcessor.setMessageListener( mMessageBroadcaster ); + } + + @Override + public DecoderType getDecoderType() + { + return DecoderType.LTR_STANDARD; + } + + @Override + public Listener getRealBufferListener() + { + return mLTRFSKDecoder; + } +} diff --git a/src/decode/ltrstandard/LTRPanel.java b/src/module/decode/ltrstandard/LTRStandardDecoderPanel.java similarity index 62% rename from src/decode/ltrstandard/LTRPanel.java rename to src/module/decode/ltrstandard/LTRStandardDecoderPanel.java index 921f9c4a..81a5a4c3 100644 --- a/src/decode/ltrstandard/LTRPanel.java +++ b/src/module/decode/ltrstandard/LTRStandardDecoderPanel.java @@ -15,67 +15,43 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrstandard; +package module.decode.ltrstandard; import java.awt.EventQueue; import javax.swing.JLabel; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; import settings.ColorSetting; import settings.Setting; import settings.SettingsManager; import alias.Alias; -import controller.channel.Channel; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelStatePanel; -public class LTRPanel extends ChannelStatePanel +public class LTRStandardDecoderPanel extends DecoderPanel { private static final long serialVersionUID = 1L; - private JLabel mStateLabel; - private JLabel mSourceLabel; - private JLabel mChannelLabel; - private JLabel mProtocol = new JLabel( "LTR Standard" ); private JLabel mTalkgroup = new JLabel(); private JLabel mTalkgroupAlias = new JLabel(); private JLabel mLCN = new JLabel( "LCN:" ); - private SettingsManager mSettingsManager; - - public LTRPanel( SettingsManager settingsManager, Channel channel ) + public LTRStandardDecoderPanel( SettingsManager settingsManager, LTRStandardDecoder decoder ) { - super( channel ); + super( settingsManager, decoder ); - channel.getProcessingChain().getChannelState().addListener( this ); - - mSettingsManager = settingsManager; init(); } public void dispose() { super.dispose(); - mSettingsManager = null; } 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 ); @@ -88,16 +64,17 @@ public class LTRPanel extends ChannelStatePanel mLCN.setFont( mFontDecoder ); mLCN.setForeground( mColorLabelDecoder ); - add( mStateLabel ); - add( mSourceLabel ); - add( mChannelLabel, "wrap" ); - add( mProtocol ); add( mTalkgroup ); add( mTalkgroupAlias, "wrap" ); add( mLCN, "wrap" ); } + + private LTRStandardDecoderState getDecoderState() + { + return (LTRStandardDecoderState)getDecoder().getDecoderState(); + } @Override public void receive( final ChangedAttribute attribute ) @@ -107,8 +84,7 @@ public class LTRPanel extends ChannelStatePanel @Override public void run() { - LTRChannelState state = (LTRChannelState)mChannel - .getProcessingChain().getChannelState(); + LTRStandardDecoderState state = getDecoderState(); switch( attribute ) { @@ -118,10 +94,10 @@ public class LTRPanel extends ChannelStatePanel mLCN.setText( "LCN: " + state.getChannelNumber() ); } break; - case FROM_TALKGROUP: + case TO_TALKGROUP: mTalkgroup.setText( state.getTalkgroup() ); /* fall-through */ - case FROM_TALKGROUP_ALIAS: + case TO_TALKGROUP_ALIAS: Alias alias = state.getTalkgroupAlias(); if( alias != null ) @@ -135,18 +111,6 @@ public class LTRPanel extends ChannelStatePanel mTalkgroupAlias.setText( null ); mTalkgroupAlias.setIcon( null ); } - case CHANNEL_STATE: - mStateLabel.setText( state.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; default: break; } @@ -168,10 +132,6 @@ public class LTRPanel extends ChannelStatePanel switch( colorSetting.getColorSettingName() ) { case CHANNEL_STATE_LABEL_DECODER: - if( mStateLabel != null ) - { - mStateLabel.setForeground( mColorLabelDecoder ); - } if( mProtocol != null ) { mProtocol.setForeground( mColorLabelDecoder ); @@ -189,15 +149,7 @@ public class LTRPanel extends ChannelStatePanel mLCN.setForeground( mColorLabelDecoder ); } break; - case CHANNEL_STATE_LABEL_DETAILS: - if( mSourceLabel != null ) - { - mSourceLabel.setForeground( mColorLabelDetails ); - } - if( mChannelLabel != null ) - { - mChannelLabel.setForeground( mColorLabelDetails ); - } + default: break; } } diff --git a/src/module/decode/ltrstandard/LTRStandardDecoderState.java b/src/module/decode/ltrstandard/LTRStandardDecoderState.java new file mode 100644 index 00000000..bfb26bcb --- /dev/null +++ b/src/module/decode/ltrstandard/LTRStandardDecoderState.java @@ -0,0 +1,398 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.ltrstandard; + +import java.text.DecimalFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.TreeSet; + +import message.Message; +import module.decode.event.CallEvent.CallEventType; +import module.decode.ltrnet.LTRCallEvent; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.DecoderStateEvent.Event; +import module.decode.state.State; +import alias.Alias; +import alias.AliasList; + +public class LTRStandardDecoderState extends DecoderState +{ + private static final int sINT_NULL_VALUE = -1; + private DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000"); + + private HashMap mActiveCalls = new HashMap(); + private HashSet mTalkgroupsFirstHeard = new HashSet(); + private TreeSet mTalkgroups = new TreeSet(); + private TreeSet mActiveLCNs = new TreeSet(); + private int mMonitoredLCN; + + private String mTalkgroup; + private Alias mTalkgroupAlias; + private int mChannelNumber; + + private long mFrequency; + + public LTRStandardDecoderState( AliasList aliasList ) + { + super( aliasList ); + } + + private LTRCallEvent getCurrentCallEvent() + { + return (LTRCallEvent)mCurrentCallEvent; + } + + @Override + public void receive( Message message ) + { + if( message.isValid() ) + { + if( message instanceof LTRStandardOSWMessage ) + { + LTRStandardOSWMessage ltr = ((LTRStandardOSWMessage)message); + + switch( ltr.getMessageType() ) + { + case CA_STRT: + if( 0 < ltr.getChannel() && ltr.getChannel() <=20 ) + { + /* If we haven't captured the LCN yet ... */ + if( mChannelNumber == 0 ) + { + mChannelNumber = ltr.getChannel(); + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + + /* If this call event is for this channel, then update + * talkgroup and alias */ + if( ltr.getChannel() == mChannelNumber ) + { + mTalkgroup = ltr.getTalkgroupID(); + broadcast( ChangedAttribute.TO_TALKGROUP ); + + mTalkgroupAlias = ltr.getTalkgroupIDAlias(); + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + + LTRCallEvent current = getCurrentCallEvent(); + + /* If we have an ongoing call, then add this message + * to the current event - if addMessage returns + * false, then we have a different talkgroup and a + * new call event, so close out the old one. */ + if( current != null && !current.addMessage( ltr ) ) + { + mCurrentCallEvent.setEnd( System.currentTimeMillis() ); + broadcast( mCurrentCallEvent ); + mCurrentCallEvent = null; + } + + if( current == null ) + { + mCurrentCallEvent = + new LTRCallEvent.Builder( CallEventType.CALL ) + .to( ltr.getTalkgroupID() ) + .aliasList( getAliasList() ) + .channel( String.valueOf( ltr.getChannel() ) ) + .frequency( mFrequency ) + .build(); + + broadcast( mCurrentCallEvent ); + + broadcast( new DecoderStateEvent( this, Event.START, State.CALL ) ); + } + else + { + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CALL ) ); + } + } + } + break; + case CA_ENDD: + mTalkgroup = ltr.getTalkgroupID(); + broadcast( ChangedAttribute.FROM_TALKGROUP ); + + mTalkgroupAlias = ltr.getTalkgroupIDAlias(); + broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + + if( mCurrentCallEvent != null ) + { + mCurrentCallEvent.setEnd( System.currentTimeMillis() ); + broadcast( mCurrentCallEvent ); + broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) ); + mCurrentCallEvent = null; + } + + int home = ltr.getHomeRepeater(); + + if( 0 < home && home <= 20 ) + { + String talkgroup = ltr.getTalkgroupID( false ); + + if( mTalkgroupsFirstHeard.contains( talkgroup ) ) + { + mTalkgroups.add( talkgroup ); + } + else + { + mTalkgroupsFirstHeard.add( talkgroup ); + } + } + + int channel = ltr.getChannel(); + + if( 0 < channel && channel <= 20 ) + { + if( channel == home ) + { + if( mMonitoredLCN != channel ) + { + mMonitoredLCN = channel; + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + + mActiveLCNs.add( channel ); + } + + int free = ltr.getFree(); + + if( 0 < free && free <= 20 ) + { + mActiveLCNs.add( free ); + } + } + + break; + case SY_IDLE: + int idleChannel = ltr.getChannel(); + + if( idleChannel == ltr.getHomeRepeater() && + idleChannel == ltr.getFree() ) + { + if( mMonitoredLCN != idleChannel ) + { + mMonitoredLCN = idleChannel; + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + + mActiveLCNs.add( idleChannel ); + } + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.IDLE ) ); + break; + case UN_KNWN: + default: + break; + } + } + } + } + +// /** +// * Intercept the fade event so that we can generate a call end event +// */ +// @Override +// public void fade( final CallEventType type ) +// { +// /* We can receive multiple call tear-down messages -- only respond to +// * the message that can change the state to fade */ +// if( getState().canChangeTo( State.FADE ) ) +// { +// mActiveCalls.clear(); +// +// LTRCallEvent current = getCurrentLTRCallEvent(); +// +// if( current != null ) +// { +// /* Close out the call event. If we only received 1 call +// * message, then flag it as noise and remove the call event */ +// if( current.isValid() ) +// { +// mCallEventModel.setEnd( current ); +// } +// else +// { +// mCallEventModel.remove( current ); +// } +// } +// +// setCurrentCallEvent( null ); +// } +// +// super.fade( type ); +// } + + public void reset() + { + resetState(); + + if( mCurrentCallEvent != null ) + { + mCurrentCallEvent.setEnd( System.currentTimeMillis() ); + broadcast( mCurrentCallEvent ); + mCurrentCallEvent = null; + } + + } + + private void resetState() + { + mTalkgroup = null; + broadcast( ChangedAttribute.FROM_TALKGROUP ); + + mTalkgroupAlias = null; + broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + } + + public String getTalkgroup() + { + return mTalkgroup; + } + + public Alias getTalkgroupAlias() + { + return mTalkgroupAlias; + } + + public boolean hasChannelNumber() + { + return mChannelNumber != 0; + } + + public int getChannelNumber() + { + return mChannelNumber; + } + + @Override + public void init() + { + + } + + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + switch( event.getEvent() ) + { + case RESET: + resetState(); + break; + case SOURCE_FREQUENCY: + mFrequency = event.getFrequency(); + break; + default: + break; + } + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "Activity Summary\n\n" ); + + sb.append( "Decoder:\tLTR-Standard\n" ); + + sb.append( "Monitored LCN: " ); + + if( mMonitoredLCN > 0 ) + { + sb.append( mMonitoredLCN ); + } + else + { + sb.append( "unknown" ); + } + + sb.append( "\n" ); + + Integer[] lcns = mActiveLCNs.toArray( new Integer[0] ); + + sb.append( "Active LCNs:\t" ); + + if( lcns.length > 0 ) + { + sb.append( Arrays.toString( lcns ) ); + } + else + { + sb.append( "none" ); + } + sb.append( "\n\n" ); + + sb.append( "Talkgroups\n" ); + + if( mTalkgroups.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mTalkgroups.iterator(); + + while( it.hasNext() ) + { + String tgid = it.next(); + + sb.append( " " ); + sb.append( formatTalkgroup( tgid ) ); + sb.append( " " ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getTalkgroupAlias( tgid ); + + if( alias != null ) + { + sb.append( alias.getName() ); + } + } + + sb.append( "\n" ); + } + } + + return sb.toString(); + } + + public static String formatTalkgroup( String talkgroup ) + { + StringBuilder sb = new StringBuilder(); + + if( talkgroup.length() == 6 ) + { + sb.append( talkgroup.substring( 0, 1 ) ); + sb.append( "-" ); + sb.append( talkgroup.substring( 1, 3 ) ); + sb.append( "-" ); + sb.append( talkgroup.substring( 3, 6 ) ); + + return sb.toString(); + } + else + { + return talkgroup; + } + } + +} diff --git a/src/decode/ltrstandard/LTRStandardEditor.java b/src/module/decode/ltrstandard/LTRStandardEditor.java similarity index 90% rename from src/decode/ltrstandard/LTRStandardEditor.java rename to src/module/decode/ltrstandard/LTRStandardEditor.java index 57a7de7c..35f0e107 100644 --- a/src/decode/ltrstandard/LTRStandardEditor.java +++ b/src/module/decode/ltrstandard/LTRStandardEditor.java @@ -15,18 +15,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrstandard; +package module.decode.ltrstandard; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; +import javax.swing.JLabel; import message.MessageDirection; -import decode.DecodeEditor; -import decode.config.DecodeConfigLTRStandard; -import decode.config.DecodeConfiguration; +import module.decode.DecodeEditor; +import module.decode.config.DecodeConfiguration; public class LTRStandardEditor extends DecodeEditor { @@ -64,7 +64,8 @@ public class LTRStandardEditor extends DecodeEditor } } } ); - - add( mComboDirection, "span 2" ); + + add( new JLabel( "Format:" ) ); + add( mComboDirection, "wrap" ); } } diff --git a/src/decode/ltrstandard/LTRStandardISWMessage.java b/src/module/decode/ltrstandard/LTRStandardISWMessage.java similarity index 99% rename from src/decode/ltrstandard/LTRStandardISWMessage.java rename to src/module/decode/ltrstandard/LTRStandardISWMessage.java index 44d44385..8f16ac1f 100644 --- a/src/decode/ltrstandard/LTRStandardISWMessage.java +++ b/src/module/decode/ltrstandard/LTRStandardISWMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrstandard; +package module.decode.ltrstandard; import java.util.ArrayList; import java.util.Date; diff --git a/src/decode/ltrstandard/LTRStandardMessage.java b/src/module/decode/ltrstandard/LTRStandardMessage.java similarity index 98% rename from src/decode/ltrstandard/LTRStandardMessage.java rename to src/module/decode/ltrstandard/LTRStandardMessage.java index b598d58c..408c7bd9 100644 --- a/src/decode/ltrstandard/LTRStandardMessage.java +++ b/src/module/decode/ltrstandard/LTRStandardMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrstandard; +package module.decode.ltrstandard; import java.text.DecimalFormat; import java.text.SimpleDateFormat; @@ -23,10 +23,10 @@ import java.text.SimpleDateFormat; import message.Message; import message.MessageDirection; import message.MessageType; +import module.decode.DecoderType; import alias.Alias; import alias.AliasList; import bits.BinaryMessage; -import decode.DecoderType; import edac.CRC; import edac.CRCLTR; diff --git a/src/decode/ltrstandard/LTRStandardMessageFilter.java b/src/module/decode/ltrstandard/LTRStandardMessageFilter.java similarity index 97% rename from src/decode/ltrstandard/LTRStandardMessageFilter.java rename to src/module/decode/ltrstandard/LTRStandardMessageFilter.java index 8054fd3a..e51eb243 100644 --- a/src/decode/ltrstandard/LTRStandardMessageFilter.java +++ b/src/module/decode/ltrstandard/LTRStandardMessageFilter.java @@ -1,4 +1,4 @@ -package decode.ltrstandard; +package module.decode.ltrstandard; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/decode/ltrstandard/LTRStandardMessageProcessor.java b/src/module/decode/ltrstandard/LTRStandardMessageProcessor.java similarity index 67% rename from src/decode/ltrstandard/LTRStandardMessageProcessor.java rename to src/module/decode/ltrstandard/LTRStandardMessageProcessor.java index 510eac6c..ecfb5d12 100644 --- a/src/decode/ltrstandard/LTRStandardMessageProcessor.java +++ b/src/module/decode/ltrstandard/LTRStandardMessageProcessor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,11 +15,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrstandard; +package module.decode.ltrstandard; import message.Message; import message.MessageDirection; -import sample.Broadcaster; import sample.Listener; import alias.AliasList; import bits.BinaryMessage; @@ -27,8 +26,8 @@ import bits.BinaryMessage; public class LTRStandardMessageProcessor implements Listener { private MessageDirection mDirection; - private Broadcaster mBroadcaster = new Broadcaster(); private AliasList mAliasList; + private Listener mMessageListener; public LTRStandardMessageProcessor( MessageDirection direction, AliasList list ) { @@ -39,29 +38,32 @@ public class LTRStandardMessageProcessor implements Listener @Override public void receive( BinaryMessage buffer ) { - LTRStandardMessage message; - - if( mDirection == MessageDirection.OSW ) + if( mMessageListener != null ) { - message = new LTRStandardOSWMessage( buffer, mAliasList ); - } - else - { - buffer.flip( 0, 40 ); + LTRStandardMessage message; + + if( mDirection == MessageDirection.OSW ) + { + message = new LTRStandardOSWMessage( buffer, mAliasList ); + } + else + { + buffer.flip( 0, 40 ); - message = new LTRStandardISWMessage( buffer, mAliasList ); + message = new LTRStandardISWMessage( buffer, mAliasList ); + } + + mMessageListener.receive( message ); } - - mBroadcaster.receive( message ); } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } } diff --git a/src/decode/ltrstandard/LTRStandardOSWMessage.java b/src/module/decode/ltrstandard/LTRStandardOSWMessage.java similarity index 99% rename from src/decode/ltrstandard/LTRStandardOSWMessage.java rename to src/module/decode/ltrstandard/LTRStandardOSWMessage.java index e29bb9d1..df3bde82 100644 --- a/src/decode/ltrstandard/LTRStandardOSWMessage.java +++ b/src/module/decode/ltrstandard/LTRStandardOSWMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.ltrstandard; +package module.decode.ltrstandard; import java.util.ArrayList; import java.util.Date; diff --git a/src/decode/mdc1200/MDCCallEvent.java b/src/module/decode/mdc1200/MDCCallEvent.java similarity index 96% rename from src/decode/mdc1200/MDCCallEvent.java rename to src/module/decode/mdc1200/MDCCallEvent.java index 25f8bb8d..e2fc1dcd 100644 --- a/src/decode/mdc1200/MDCCallEvent.java +++ b/src/module/decode/mdc1200/MDCCallEvent.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mdc1200; +package module.decode.mdc1200; +import module.decode.DecoderType; +import module.decode.event.CallEvent; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; public class MDCCallEvent extends CallEvent { diff --git a/src/decode/mdc1200/MDCDecoder.java b/src/module/decode/mdc1200/MDCDecoder.java similarity index 79% rename from src/decode/mdc1200/MDCDecoder.java rename to src/module/decode/mdc1200/MDCDecoder.java index 79b4d09c..55bf1e68 100644 --- a/src/decode/mdc1200/MDCDecoder.java +++ b/src/module/decode/mdc1200/MDCDecoder.java @@ -15,28 +15,29 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mdc1200; +package module.decode.mdc1200; import instrument.Instrumentable; import instrument.tap.Tap; import instrument.tap.stream.BinaryTap; +import instrument.tap.stream.FloatBufferTap; import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; -import sample.real.RealSampleListener; -import source.Source.SampleType; +import module.decode.Decoder; +import module.decode.DecoderType; +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; import alias.AliasList; -import audio.IAudioOutput; import bits.MessageFramer; import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; import dsp.NRZDecoder; import dsp.filter.Filters; -import dsp.filter.FloatFIRFilter; -import dsp.filter.FloatHalfBandFilter; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; +import dsp.filter.halfband.real.HalfBandFilter_RB_RB; import dsp.fsk.FSK2Decoder; import dsp.fsk.FSK2Decoder.Output; @@ -44,7 +45,8 @@ import dsp.fsk.FSK2Decoder.Output; * MDC1200 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate * complex or floating point samples and output fully framed MDC1200 messages */ -public class MDCDecoder extends Decoder implements Instrumentable +public class MDCDecoder extends Decoder implements IRealBufferListener, + Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ private static final int sDECIMATED_SAMPLE_RATE = 24000; @@ -57,8 +59,6 @@ public class MDCDecoder extends Decoder implements Instrumentable /* Instrumentation Taps */ private ArrayList mAvailableTaps; - private static final String INSTRUMENT_INPUT = - "Tap Point: Float Input"; private static final String INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD = "Tap Point: Bandpass Filter > < FSK2 Decoder"; private static final String INSTRUMENT_FSK2_DECODER_TO_NRZ_DECODER = @@ -67,24 +67,23 @@ public class MDCDecoder extends Decoder implements Instrumentable "Tap Point: NRZ Decoder > < Message Framer"; private FSK2Decoder mFSKDecoder; - private FloatHalfBandFilter mDecimationFilter; - private FloatFIRFilter mBandPassFilter; + private HalfBandFilter_RB_RB mDecimationFilter; + private RealFIRFilter_RB_RB mBandPassFilter; private NRZDecoder mNRZDecoder; private MessageFramer mMessageFramer; private MDCMessageProcessor mMessageProcessor; public MDCDecoder( AliasList aliasList ) { - super( SampleType.REAL ); - + super( new MDCDecoderState( aliasList ) ); + /* Decimation filter: 48000 / 2 = 24000 */ - mDecimationFilter = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - addRealSampleListener( mDecimationFilter ); + mDecimationFilter = new HalfBandFilter_RB_RB( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true ); /* Bandpass filter - predefined */ - mBandPassFilter = new FloatFIRFilter( - Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02 ); + mBandPassFilter = new RealFIRFilter_RB_RB( + Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02f ); mDecimationFilter.setListener( mBandPassFilter ); /* 2FSK Decoder */ @@ -103,8 +102,8 @@ public class MDCDecoder extends Decoder implements Instrumentable /* Message Processor */ mMessageProcessor = new MDCMessageProcessor( aliasList ); - mMessageProcessor.addMessageListener( this ); mMessageFramer.addMessageListener( mMessageProcessor ); + mMessageProcessor.addMessageListener( mMessageBroadcaster ); } public void dispose() @@ -119,12 +118,6 @@ public class MDCDecoder extends Decoder implements Instrumentable mMessageProcessor.dispose(); } - @Override - public DecoderType getType() - { - return DecoderType.MDC1200; - } - /* Instrumentation */ @Override public List getTaps() @@ -133,8 +126,6 @@ public class MDCDecoder extends Decoder implements Instrumentable { mAvailableTaps = new ArrayList(); - mAvailableTaps.add( - new FloatTap( INSTRUMENT_INPUT, 0, 1.0f ) ); mAvailableTaps.add( new FloatTap( INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD, 0, 0.5f ) ); mAvailableTaps.addAll( mFSKDecoder.getTaps() ); @@ -154,12 +145,9 @@ public class MDCDecoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - addRealSampleListener( (FloatTap)tap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: /* wire the tap between the bandpass filter and the demod */ - FloatTap floatTap = (FloatTap)tap; + FloatBufferTap floatTap = (FloatBufferTap)tap; mBandPassFilter.setListener( floatTap ); floatTap.setListener( mFSKDecoder ); break; @@ -186,9 +174,6 @@ public class MDCDecoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - removeRealListener( (FloatTap)tap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: mBandPassFilter.setListener( mFSKDecoder ); break; @@ -203,15 +188,14 @@ public class MDCDecoder extends Decoder implements Instrumentable } @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - //Not implemented - } - - @Override - public IAudioOutput getAudioOutput() + public DecoderType getDecoderType() { - // Not implemented - return null; + return DecoderType.MDC1200; + } + + @Override + public Listener getRealBufferListener() + { + return mDecimationFilter; } } diff --git a/src/module/decode/mdc1200/MDCDecoderState.java b/src/module/decode/mdc1200/MDCDecoderState.java new file mode 100644 index 00000000..ea2823f0 --- /dev/null +++ b/src/module/decode/mdc1200/MDCDecoderState.java @@ -0,0 +1,288 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.mdc1200; + +import java.util.Iterator; +import java.util.TreeSet; + +import message.Message; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.State; +import module.decode.state.DecoderStateEvent.Event; +import alias.Alias; +import alias.AliasList; + +public class MDCDecoderState extends DecoderState +{ + private TreeSet mIdents = new TreeSet(); + private TreeSet mEmergencyIdents = new TreeSet(); + + private String mFrom; + private Alias mFromAlias; + private String mTo; + private Alias mToAlias; + private String mMessage; + private String mMessageType; + + public MDCDecoderState( AliasList aliasList ) + { + super( aliasList ); + } + + @Override + public void reset() + { + mIdents.clear(); + mEmergencyIdents.clear(); + + resetState(); + } + + private void resetState() + { + setFrom( null ); + setFromAlias( null ); + setTo( null); + setToAlias( null ); + setMessage( null ); + setMessageType( null ); + } + + @Override + public void receive( Message message ) + { + if( message instanceof MDCMessage ) + { + MDCMessage mdc = (MDCMessage)message; + + mIdents.add( mdc.getUnitID() ); + + if( mdc.isEmergency() ) + { + mEmergencyIdents.add( mdc.getUnitID() ); + } + + setFrom( mdc.getFromID() ); + setFromAlias( mdc.getFromIDAlias() ); + + MDCMessageType type = mdc.getMessageType(); + + setMessageType( type.getLabel() ); + + StringBuilder sb = new StringBuilder(); + + sb.append( "OPCODE " ); + sb.append( String.valueOf( mdc.getOpcode() ) ); + + if( mdc.isBOT() ) + { + sb.append( " TYPE:BOT" ); + } + + if( mdc.isEOT() ) + { + sb.append( " TYPE:EOT" ); + } + + setMessage( sb.toString() ); + + MDCCallEvent event = MDCCallEvent.getMDCCallEvent( mdc ); + event.setAliasList( getAliasList() ); + broadcast( event ); + + switch( type ) + { + case ANI: + broadcast( new DecoderStateEvent( this, Event.DECODE, State.CALL ) ); + break; + case ACKNOWLEDGE: + case EMERGENCY: + case PAGING: + case STATUS: + default: + broadcast( new DecoderStateEvent( this, Event.DECODE, State.DATA ) ); + break; + } + + } + } + + public String getFrom() + { + return mFrom; + } + + public void setFrom( String from ) + { + mFrom = from; + broadcast( ChangedAttribute.FROM_TALKGROUP ); + } + + public Alias getFromAlias() + { + return mFromAlias; + } + + public void setFromAlias( Alias alias ) + { + mFromAlias = alias; + broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + } + + public String getTo() + { + return mTo; + } + + public void setTo( String to ) + { + mTo = to; + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + + public Alias getToAlias() + { + return mToAlias; + } + + public void setToAlias( Alias alias ) + { + mToAlias = alias; + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + } + + public String getMessage() + { + return mMessage; + } + + public void setMessage( String message ) + { + mMessage = message; + broadcast( ChangedAttribute.MESSAGE ); + } + + public String getMessageType() + { + return mMessageType; + } + + public void setMessageType( String type ) + { + mMessageType = type; + broadcast( ChangedAttribute.MESSAGE_TYPE ); + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "=============================\n" ); + sb.append( "Decoder:\tMDC-1200\n\n" ); + sb.append( "MDC-1200 Idents\n" ); + + if( mIdents.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mIdents.iterator(); + + while( it.hasNext() ) + { + String ident = it.next(); + + sb.append( " " ); + sb.append( ident ); + sb.append( " " ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getMDC1200Alias( ident ); + + if( alias != null ) + { + sb.append( alias.getName() ); + } + } + + sb.append( "\n" ); + } + } + + sb.append( "MDC-1200 Emergency Idents\n" ); + + if( mEmergencyIdents.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mEmergencyIdents.iterator(); + + while( it.hasNext() ) + { + String ident = it.next(); + + sb.append( " " ); + sb.append( ident ); + sb.append( " " ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getMDC1200Alias( ident ); + + if( alias != null ) + { + sb.append( alias.getName() ); + } + } + + sb.append( "\n" ); + } + } + + return sb.toString(); + } + + @Override + public void init() + { + /* No initialization required */ + } + + /** + * Responds to reset events issued by the channel state + */ + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + switch( event.getEvent() ) + { + case RESET: + resetState(); + break; + default: + break; + } + } +} diff --git a/src/decode/mdc1200/MDCMessage.java b/src/module/decode/mdc1200/MDCMessage.java similarity index 99% rename from src/decode/mdc1200/MDCMessage.java rename to src/module/decode/mdc1200/MDCMessage.java index 4ba2738b..adb809a3 100644 --- a/src/decode/mdc1200/MDCMessage.java +++ b/src/module/decode/mdc1200/MDCMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mdc1200; +package module.decode.mdc1200; import java.text.SimpleDateFormat; import java.util.ArrayList; diff --git a/src/decode/mdc1200/MDCMessageFilter.java b/src/module/decode/mdc1200/MDCMessageFilter.java similarity index 97% rename from src/decode/mdc1200/MDCMessageFilter.java rename to src/module/decode/mdc1200/MDCMessageFilter.java index 729c7756..9da68f8a 100644 --- a/src/decode/mdc1200/MDCMessageFilter.java +++ b/src/module/decode/mdc1200/MDCMessageFilter.java @@ -1,4 +1,4 @@ -package decode.mdc1200; +package module.decode.mdc1200; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/decode/mdc1200/MDCMessageProcessor.java b/src/module/decode/mdc1200/MDCMessageProcessor.java similarity index 99% rename from src/decode/mdc1200/MDCMessageProcessor.java rename to src/module/decode/mdc1200/MDCMessageProcessor.java index a5070666..a1992092 100644 --- a/src/decode/mdc1200/MDCMessageProcessor.java +++ b/src/module/decode/mdc1200/MDCMessageProcessor.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mdc1200; +package module.decode.mdc1200; import message.Message; import sample.Broadcaster; diff --git a/src/decode/mdc1200/MDCMessageType.java b/src/module/decode/mdc1200/MDCMessageType.java similarity index 97% rename from src/decode/mdc1200/MDCMessageType.java rename to src/module/decode/mdc1200/MDCMessageType.java index 084d34ff..feae9aed 100644 --- a/src/decode/mdc1200/MDCMessageType.java +++ b/src/module/decode/mdc1200/MDCMessageType.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mdc1200; +package module.decode.mdc1200; public enum MDCMessageType { diff --git a/src/decode/mdc1200/MDCPanel.java b/src/module/decode/mdc1200/MDCPanel.java similarity index 75% rename from src/decode/mdc1200/MDCPanel.java rename to src/module/decode/mdc1200/MDCPanel.java index 22226a57..4f87897b 100644 --- a/src/decode/mdc1200/MDCPanel.java +++ b/src/module/decode/mdc1200/MDCPanel.java @@ -15,21 +15,22 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mdc1200; +package module.decode.mdc1200; import java.awt.EventQueue; import javax.swing.JLabel; +import alias.Alias; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; import net.miginfocom.swing.MigLayout; import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingsManager; -import controller.state.AuxStatePanel; -import controller.state.ChannelState.ChangedAttribute; -public class MDCPanel extends AuxStatePanel +public class MDCPanel extends DecoderPanel { private static final long serialVersionUID = 1L; private static final String sPROTOCOL = "MDC-1200"; @@ -46,35 +47,37 @@ public class MDCPanel extends AuxStatePanel private JLabel mMessage = new JLabel(); private JLabel mMessageType = new JLabel(); - public MDCPanel( SettingsManager settingsManager, MDCChannelState state ) + public MDCPanel( SettingsManager settingsManager, MDCDecoder decoder ) { - super( settingsManager, state ); + super( settingsManager, decoder ); init(); } - public MDCChannelState getState() + public MDCDecoderState getState() { - return (MDCChannelState)mAuxChannelState; + return (MDCDecoderState)getDecoder().getDecoderState(); } - private void init() + protected void init() { - setOpaque( false ); //Use the parent panel's background color + /* Calling super init will get and broadcast color settings to properly + * setup the jlabel colors */ + super.init(); setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") ); - addLabel( mFromLabel, false ); - addLabel( mFrom, false ); - addLabel( mFromAlias, true ); + add( mFromLabel ); + add( mFrom ); + add( mFromAlias, "wrap" ); - addLabel( mToLabel, false ); - addLabel( mTo, false ); - addLabel( mToAlias, true ); + add( mToLabel ); + add( mTo ); + add( mToAlias, "wrap" ); - addLabel( mProtocol, false ); - addLabel( mMessageType, false ); - addLabel( mMessage, true ); + add( mProtocol ); + add( mMessageType ); + add( mMessage, "wrap" ); } @Override @@ -101,8 +104,16 @@ public class MDCPanel extends AuxStatePanel mFrom.setText( from ); break; case FROM_TALKGROUP_ALIAS: - updateAliasLabel( mFromAlias, - getState().getFromAlias() ); + Alias fromAlias = getState().getFromAlias(); + + if( fromAlias != null ) + { + mFromAlias.setText( fromAlias.getName() ); + } + else + { + mFromAlias.setText( null ); + } break; case TO_TALKGROUP: String to = getState().getTo(); @@ -118,8 +129,16 @@ public class MDCPanel extends AuxStatePanel mTo.setText( to ); break; case TO_TALKGROUP_ALIAS: - updateAliasLabel( mToAlias, - getState().getToAlias() ); + Alias toAlias = getState().getToAlias(); + + if( toAlias != null ) + { + mToAlias.setText( toAlias.getName() ); + } + else + { + mToAlias.setText( null ); + } break; case MESSAGE: mMessage.setText( getState().getMessage() ); @@ -127,6 +146,8 @@ public class MDCPanel extends AuxStatePanel case MESSAGE_TYPE: mMessageType.setText( getState().getMessageType() ); break; + default: + break; } repaint(); diff --git a/src/module/decode/mpt1327/DecodeConfigMPT1327.java b/src/module/decode/mpt1327/DecodeConfigMPT1327.java new file mode 100644 index 00000000..926272a3 --- /dev/null +++ b/src/module/decode/mpt1327/DecodeConfigMPT1327.java @@ -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 + ******************************************************************************/ +package module.decode.mpt1327; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; + +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; +import module.decode.mpt1327.MPT1327Decoder.Sync; + +public class DecodeConfigMPT1327 extends DecodeConfiguration +{ + private String mChannelMapName; + private Sync mSync = Sync.NORMAL; + + private int mCallTimeout = DEFAULT_CALL_TIMEOUT_SECONDS; + private int mTrafficChannelPoolSize = TRAFFIC_CHANNEL_LIMIT_DEFAULT; + + public DecodeConfigMPT1327() + { + super( DecoderType.MPT1327 ); + } + + @XmlElement + public String getChannelMapName() + { + return mChannelMapName; + } + + public void setChannelMapName( String name ) + { + mChannelMapName = name; + } + + @XmlAttribute( name="sync" ) + public Sync getSync() + { + return mSync; + } + + public void setSync( Sync sync ) + { + mSync = sync; + } + + @XmlAttribute( name="call_timeout" ) + public int getCallTimeout() + { + return mCallTimeout; + } + + /** + * Sets the call timeout value in seconds ( 10 - 600 ); + * @param timeout + */ + public void setCallTimeout( int timeout ) + { + if( CALL_TIMEOUT_MINIMUM <= timeout && timeout <= CALL_TIMEOUT_MAXIMUM ) + { + mCallTimeout = timeout; + } + else + { + mCallTimeout = DEFAULT_CALL_TIMEOUT_SECONDS; + } + } + + @XmlAttribute( name="traffic_channel_pool_size" ) + public int getTrafficChannelPoolSize() + { + return mTrafficChannelPoolSize; + } + + /** + * Sets the traffic channel pool size which is the maximum number of + * simultaneous traffic channels that can be allocated. + * + * This limits the maximum calls so that busy systems won't cause more + * traffic channels to be allocated than the decoder/software/host computer + * can support. + */ + public void setTrafficChannelPoolSize( int size ) + { + mTrafficChannelPoolSize = size; + } +} diff --git a/src/decode/mpt1327/MPT1327CallEvent.java b/src/module/decode/mpt1327/MPT1327CallEvent.java similarity index 96% rename from src/decode/mpt1327/MPT1327CallEvent.java rename to src/module/decode/mpt1327/MPT1327CallEvent.java index 24ed009b..abeec4be 100644 --- a/src/decode/mpt1327/MPT1327CallEvent.java +++ b/src/module/decode/mpt1327/MPT1327CallEvent.java @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mpt1327; +package module.decode.mpt1327; +import module.decode.DecoderType; +import module.decode.event.CallEvent; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; public class MPT1327CallEvent extends CallEvent { diff --git a/src/decode/mpt1327/MPT1327Editor.java b/src/module/decode/mpt1327/MPT1327ConfigEditor.java similarity index 50% rename from src/decode/mpt1327/MPT1327Editor.java rename to src/module/decode/mpt1327/MPT1327ConfigEditor.java index e3289311..17c11938 100644 --- a/src/decode/mpt1327/MPT1327Editor.java +++ b/src/module/decode/mpt1327/MPT1327ConfigEditor.java @@ -15,30 +15,36 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mpt1327; +package module.decode.mpt1327; import java.util.ArrayList; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.JLabel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; -import controller.channel.ChannelMap; +import module.decode.DecodeEditor; +import module.decode.config.DecodeConfiguration; +import module.decode.mpt1327.MPT1327Decoder.Sync; import controller.channel.ChannelNode; -import decode.DecodeEditor; -import decode.config.DecodeConfigMPT1327; -import decode.config.DecodeConfiguration; -import decode.mpt1327.MPT1327Decoder.Sync; +import controller.channel.map.ChannelMap; -public class MPT1327Editor extends DecodeEditor +public class MPT1327ConfigEditor extends DecodeEditor { private static final long serialVersionUID = 1L; private JComboBox mComboChannelMaps; private JComboBox mComboSync; + private JLabel mCallTimeoutLabel; + private JSlider mCallTimeout; + private JLabel mTrafficChannelPoolSizeLabel; + private JSlider mTrafficChannelPoolSize; private ChannelNode mChannelNode; - public MPT1327Editor( DecodeConfiguration config, ChannelNode channelNode ) + public MPT1327ConfigEditor( DecodeConfiguration config, ChannelNode channelNode ) { super( config ); @@ -47,6 +53,11 @@ public class MPT1327Editor extends DecodeEditor initGUI(); } + private DecodeConfigMPT1327 getMPTConfig() + { + return (DecodeConfigMPT1327)mConfig; + } + private void initGUI() { /** @@ -61,22 +72,74 @@ public class MPT1327Editor extends DecodeEditor mComboChannelMaps.setModel( new DefaultComboBoxModel( mapArray ) ); - add( new JLabel( "Channel Map" ), "wrap" ); + add( new JLabel( "Channel Map:" ) ); add( mComboChannelMaps, "wrap" ); mComboSync = new JComboBox(); mComboSync.setModel( new DefaultComboBoxModel( Sync.values() ) ); - add( new JLabel( "Sync" ), "wrap" ); + add( new JLabel( "Sync:" ) ); add( mComboSync, "wrap" ); + mCallTimeout = new JSlider( JSlider.HORIZONTAL, + DecodeConfiguration.CALL_TIMEOUT_MINIMUM, + DecodeConfiguration.CALL_TIMEOUT_MAXIMUM, + DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS ); + + mCallTimeout.setMajorTickSpacing( 90 ); + mCallTimeout.setMinorTickSpacing( 10 ); + mCallTimeout.setPaintTicks( true ); + + mCallTimeout.setLabelTable( mCallTimeout.createStandardLabels( 100, 100 ) ); + mCallTimeout.setPaintLabels( true ); + + mCallTimeoutLabel = new JLabel( "Call Timeout: " + mCallTimeout.getValue() + " " ); + + mCallTimeout.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mCallTimeoutLabel.setText( "Call Timeout: " + mCallTimeout.getValue() ); + } + } ); + + add( mCallTimeoutLabel ); + add( mCallTimeout, "wrap,grow" ); + + mTrafficChannelPoolSize = new JSlider( JSlider.HORIZONTAL, + DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MINIMUM, + DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MAXIMUM, + DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT ); + + mTrafficChannelPoolSize.setMajorTickSpacing( 10 ); + mTrafficChannelPoolSize.setMinorTickSpacing( 1 ); + mTrafficChannelPoolSize.setPaintTicks( true ); + + mTrafficChannelPoolSize.setLabelTable( mTrafficChannelPoolSize.createStandardLabels( 10, 10 ) ); + mTrafficChannelPoolSize.setPaintLabels( true ); + + mTrafficChannelPoolSizeLabel = new JLabel( "Traffic Channel Pool Size: " + mTrafficChannelPoolSize.getValue() + " " ); + + mTrafficChannelPoolSize.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mTrafficChannelPoolSizeLabel.setText( "Traffic Channel Pool Size: " + mTrafficChannelPoolSize.getValue() ); + } + } ); + + add( mTrafficChannelPoolSizeLabel ); + add( mTrafficChannelPoolSize, "wrap,grow" ); + reset(); } @Override public void save() { - DecodeConfigMPT1327 config = (DecodeConfigMPT1327)mConfig; + DecodeConfigMPT1327 config = getMPTConfig(); ChannelMap selected = (ChannelMap)mComboChannelMaps.getSelectedItem(); @@ -91,6 +154,10 @@ public class MPT1327Editor extends DecodeEditor { config.setSync( selectedSync ); } + + config.setCallTimeout( mCallTimeout.getValue() ); + + config.setTrafficChannelPoolSize( mTrafficChannelPoolSize.getValue() ); } @Override @@ -120,5 +187,9 @@ public class MPT1327Editor extends DecodeEditor mComboChannelMaps.requestFocusInWindow(); mComboSync.setSelectedItem( config.getSync() ); + + mCallTimeout.setValue( config.getCallTimeout() ); + + mTrafficChannelPoolSize.setValue( config.getTrafficChannelPoolSize() ); } } diff --git a/src/decode/mpt1327/MPT1327Decoder.java b/src/module/decode/mpt1327/MPT1327Decoder.java similarity index 70% rename from src/decode/mpt1327/MPT1327Decoder.java rename to src/module/decode/mpt1327/MPT1327Decoder.java index 7fabfbbe..1ef00a4d 100644 --- a/src/decode/mpt1327/MPT1327Decoder.java +++ b/src/module/decode/mpt1327/MPT1327Decoder.java @@ -15,47 +15,45 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mpt1327; +package module.decode.mpt1327; import instrument.Instrumentable; import instrument.tap.Tap; import instrument.tap.stream.BinaryTap; +import instrument.tap.stream.FloatBufferTap; import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; +import module.decode.Decoder; +import module.decode.DecoderType; import sample.Broadcaster; -import sample.real.RealSampleListener; -import source.Source.SampleType; +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; import alias.AliasList; -import audio.AudioOutputImpl; -import audio.IAudioOutput; import bits.MessageFramer; import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; -import decode.config.DecodeConfiguration; -import dsp.filter.DCRemovalFilter; +import controller.channel.Channel.ChannelType; +import controller.channel.map.ChannelMap; import dsp.filter.FilterFactory; import dsp.filter.Filters; -import dsp.filter.FloatFIRFilter; -import dsp.filter.FloatHalfBandFilter; import dsp.filter.Window.WindowType; +import dsp.filter.dc.DCRemovalFilterLyons; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; +import dsp.filter.halfband.real.HalfBandFilter_RB_RB; import dsp.fsk.FSK2Decoder; import dsp.fsk.FSK2Decoder.Output; -import dsp.nbfm.NBFMDemodulator; /** * MPT1327 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate * complex or floating point samples and output fully framed MPT1327 control and * traffic messages. */ -public class MPT1327Decoder extends Decoder implements Instrumentable +public class MPT1327Decoder extends Decoder + implements IRealBufferListener, Instrumentable { - /* Determines how quickly the DC remove filter responds */ - private static final double sDC_REMOVAL_RATIO = 0.95; - /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ private static final int sDECIMATED_SAMPLE_RATE = 24000; @@ -67,7 +65,6 @@ public class MPT1327Decoder extends Decoder implements Instrumentable private static final int sMESSAGE_LENGTH = 350; /* Instrumentation Taps */ - private static final String INSTRUMENT_INPUT = "Tap Point: Input"; private static final String INSTRUMENT_HB1_FILTER_TO_LOW_PASS = "Tap Point: Half-band Decimation Filter > < Low Pass Filter"; private static final String INSTRUMENT_LOW_PASS_TO_DECODER = @@ -76,61 +73,35 @@ public class MPT1327Decoder extends Decoder implements Instrumentable "Tap Point: Decoder > < Sync Detect/Message Framer"; private List mAvailableTaps; - private NBFMDemodulator mNBFMDemodulator; - private FloatHalfBandFilter mDecimationFilter; - private FloatFIRFilter mLowPassFilter; - private DCRemovalFilter mDCRemovalFilter; + private HalfBandFilter_RB_RB mDecimationFilter; + private RealFIRFilter_RB_RB mLowPassFilter; + private DCRemovalFilterLyons mDCRemovalFilter; private FSK2Decoder mFSKDecoder; private Broadcaster mSymbolBroadcaster; private MessageFramer mControlMessageFramer; private MessageFramer mTrafficMessageFramer; private MPT1327MessageProcessor mMessageProcessor; - private AudioOutputImpl mAudioOutput = new AudioOutputImpl( "MPT1327 Decoder Audio Output" ); - public MPT1327Decoder( DecodeConfiguration config, - SampleType sampleType, + public MPT1327Decoder( ChannelMap channelMap, + ChannelType channelType, AliasList aliasList, Sync sync ) { - super( config, sampleType ); - - /* If we're receiving complex samples, do FM demodulation and DC removal - * and feed the output back to this decoder */ - if( mSourceSampleType == SampleType.COMPLEX ) - { - /* I/Q low pass filtering narrow band FM demodulator */ - mNBFMDemodulator = new NBFMDemodulator( - FilterFactory.getLowPass( 48000, 4000, 73, WindowType.HAMMING ), - 1.0002, true ); - - this.addComplexListener( mNBFMDemodulator ); - - /* DC removal filter */ - mDCRemovalFilter = new DCRemovalFilter( sDC_REMOVAL_RATIO ); - - if( mNBFMDemodulator != null ) - { - mNBFMDemodulator.addListener( mDCRemovalFilter ); - mDCRemovalFilter.setListener( this.getRealReceiver() ); - } - } - - /* Demodulated float sample processing */ + super( new MPT1327DecoderState( aliasList, channelMap, channelType ) ); /* Decimation filter - 48000 / 2 = 24000 output */ - mDecimationFilter = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - addRealSampleListener( mDecimationFilter ); + mDecimationFilter = new HalfBandFilter_RB_RB( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0002f, true ); /* Low pass filter: 2kHz to pass the 1200 & 1800 Hz FSK */ - mLowPassFilter = new FloatFIRFilter( + mLowPassFilter = new RealFIRFilter_RB_RB( FilterFactory.getLowPass( 24000, //sampleRate, 2000, //passFrequency, 4000, //stopFrequency, 60, //attenuation, WindowType.HANNING, //windowType, true ) //forceOddLength - , 2.0 ); //Gain + , 2.0f ); //Gain mDecimationFilter.setListener( mLowPassFilter ); /** @@ -168,26 +139,23 @@ public class MPT1327Decoder extends Decoder implements Instrumentable /* Fully decoded and framed messages processor */ mMessageProcessor = new MPT1327MessageProcessor( aliasList ); - mMessageProcessor.addMessageListener( this ); + mMessageProcessor.setMessageListener( mMessageBroadcaster ); mControlMessageFramer.addMessageListener( mMessageProcessor ); mTrafficMessageFramer.addMessageListener( mMessageProcessor ); } @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - if( mNBFMDemodulator != null ) - { - mNBFMDemodulator.addListener( listener ); - } - } + public Listener getRealBufferListener() + { + return mDecimationFilter; + } @Override - public DecoderType getType() - { - return DecoderType.MPT1327; - } - + public DecoderType getDecoderType() + { + return DecoderType.MPT1327; + } + /** * Cleanup method */ @@ -197,8 +165,6 @@ public class MPT1327Decoder extends Decoder implements Instrumentable mSymbolBroadcaster.dispose(); - mAudioOutput.dispose(); - mControlMessageFramer.dispose(); if( mDCRemovalFilter != null ) @@ -206,11 +172,6 @@ public class MPT1327Decoder extends Decoder implements Instrumentable mDCRemovalFilter.dispose(); } - if( mNBFMDemodulator != null ) - { - mNBFMDemodulator.dispose(); - } - mFSKDecoder.dispose(); mDecimationFilter.dispose(); @@ -230,8 +191,6 @@ public class MPT1327Decoder extends Decoder implements Instrumentable { mAvailableTaps = new ArrayList(); - mAvailableTaps.add( - new FloatTap( INSTRUMENT_INPUT, 0, 1.0f ) ); mAvailableTaps.add( new FloatTap( INSTRUMENT_HB1_FILTER_TO_LOW_PASS, 0, 0.5f ) ); mAvailableTaps.add( @@ -254,24 +213,13 @@ public class MPT1327Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - if( mNBFMDemodulator != null ) - { - mNBFMDemodulator.addListener( inputTap ); - } - else - { - addRealSampleListener( inputTap ); - } - break; case INSTRUMENT_HB1_FILTER_TO_LOW_PASS: - FloatTap hb1Tap = (FloatTap)tap; + FloatBufferTap hb1Tap = (FloatBufferTap)tap; mDecimationFilter.setListener( hb1Tap ); hb1Tap.setListener( mLowPassFilter ); break; case INSTRUMENT_LOW_PASS_TO_DECODER: - FloatTap lowTap = (FloatTap)tap; + FloatBufferTap lowTap = (FloatBufferTap)tap; mLowPassFilter.setListener( lowTap ); lowTap.setListener( mFSKDecoder ); break; @@ -290,17 +238,6 @@ public class MPT1327Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - if( mNBFMDemodulator != null ) - { - mNBFMDemodulator.removeListener( inputTap ); - } - else - { - removeRealListener( inputTap ); - } - break; case INSTRUMENT_HB1_FILTER_TO_LOW_PASS: mDecimationFilter.setListener( mLowPassFilter ); break; @@ -355,10 +292,4 @@ public class MPT1327Decoder extends Decoder implements Instrumentable return getLabel(); } } - - @Override - public IAudioOutput getAudioOutput() - { - return mAudioOutput; - } } diff --git a/src/module/decode/mpt1327/MPT1327DecoderPanel.java b/src/module/decode/mpt1327/MPT1327DecoderPanel.java new file mode 100644 index 00000000..da4f6a5f --- /dev/null +++ b/src/module/decode/mpt1327/MPT1327DecoderPanel.java @@ -0,0 +1,222 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.mpt1327; + +import java.awt.EventQueue; + +import javax.swing.JLabel; + +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; +import net.miginfocom.swing.MigLayout; +import settings.ColorSetting; +import settings.Setting; +import settings.SettingsManager; +import alias.Alias; +import controller.channel.Channel.ChannelType; + +public class MPT1327DecoderPanel extends DecoderPanel +{ + private static final long serialVersionUID = 1L; + + private JLabel mProtocol = new JLabel( "" ); + private JLabel mSiteOrFromTalkgroup = new JLabel( "" ); + private JLabel mSiteOrFromTalkgroupAlias = new JLabel( "" ); + + private JLabel mToTalkgroupLabel = new JLabel( "" ); + private JLabel mToTalkgroup = new JLabel( "" ); + private JLabel mToTalkgroupAliasLabel = new JLabel( "" ); + + public MPT1327DecoderPanel( SettingsManager settingsManager, MPT1327Decoder decoder ) + { + super( settingsManager, decoder ); + + init(); + } + + private MPT1327DecoderState getDecoderState() + { + return (MPT1327DecoderState)getDecoder().getDecoderState(); + } + + public void dispose() + { + super.dispose(); + } + + public void init() + { + super.init(); + + setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]0[]") ); + + mProtocol.setText( "MPT1327" ); + mProtocol.setFont( mFontDecoder ); + mProtocol.setForeground( mColorLabelDecoder ); + + MPT1327DecoderState mptState = getDecoderState(); + + mSiteOrFromTalkgroup.setFont( mFontDecoder ); + mSiteOrFromTalkgroup.setForeground( mColorLabelDecoder ); + + mSiteOrFromTalkgroupAlias.setFont( mFontDecoder ); + mSiteOrFromTalkgroupAlias.setForeground( mColorLabelDecoder ); + + mToTalkgroupLabel.setFont( mFontDetails ); + mToTalkgroupLabel.setForeground( mColorLabelDetails ); + + mToTalkgroup.setFont( mFontDecoder ); + mToTalkgroup.setForeground( mColorLabelDecoder ); + + mToTalkgroupAliasLabel.setFont( mFontDecoder ); + mToTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); + + + switch( mptState.getChannelType() ) + { + case STANDARD: + mSiteOrFromTalkgroup.setText( mptState.getSite() ); + setAliasLabel( mSiteOrFromTalkgroupAlias, mptState.getSiteAlias() ); + break; + case TRAFFIC: + mSiteOrFromTalkgroup.setText( mptState.getFromTalkgroup() ); + setAliasLabel( mSiteOrFromTalkgroupAlias, mptState.getFromTalkgroupAlias() ); + + mToTalkgroupLabel.setText( "To:" ); + mToTalkgroup.setText( getDecoderState().getToTalkgroup() ); + setAliasLabel( mToTalkgroupAliasLabel, mptState.getToTalkgroupAlias() ); + break; + default: + throw new IllegalArgumentException( "Unrecognized channel " + + "type in MPT1327 Decoder [" + getDecoderState() + .getChannelType().name() + "]" ); + } + + add( mProtocol ); + add( mSiteOrFromTalkgroup ); + add( mSiteOrFromTalkgroupAlias, "wrap" ); + + if( mptState.getChannelType() == ChannelType.TRAFFIC ) + { + add( mToTalkgroupLabel ); + add( mToTalkgroup ); + add( mToTalkgroupAliasLabel, "wrap" ); + } + } + + private void setAliasLabel( final JLabel label, final Alias alias ) + { + if( alias != null ) + { + label.setText( alias.getName() ); + + String iconName = alias.getIconName(); + + if( iconName != null ) + { + label.setIcon( mSettingsManager.getImageIcon( iconName, 12 ) ); + } + else + { + label.setIcon( null ); + } + } + else + { + label.setText( "" ); + label.setIcon( null ); + } + } + + @Override + public void receive( final ChangedAttribute changedAttribute ) + { + final MPT1327DecoderState state = getDecoderState(); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + switch( changedAttribute ) + { + case CHANNEL_SITE_NUMBER: + mSiteOrFromTalkgroup.setText( String.valueOf( state.getSite() ) ); + setAliasLabel( mSiteOrFromTalkgroupAlias, state.getSiteAlias() ); + repaint(); + break; + case FROM_TALKGROUP: + mSiteOrFromTalkgroup.setText( state.getFromTalkgroup() ); + setAliasLabel( mSiteOrFromTalkgroupAlias, state.getFromTalkgroupAlias() ); + repaint(); + break; + case TO_TALKGROUP: + mToTalkgroup.setText( state.getToTalkgroup() ); + setAliasLabel( mToTalkgroupAliasLabel, state.getToTalkgroupAlias() ); + repaint(); + break; + default: + break; + } + } + } ); + } + + @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( mProtocol != null ) + { + mProtocol.setForeground( mColorLabelDecoder ); + } + if( mSiteOrFromTalkgroup != null ) + { + mSiteOrFromTalkgroup.setForeground( mColorLabelDecoder ); + } + if( mSiteOrFromTalkgroupAlias != null ) + { + mSiteOrFromTalkgroupAlias.setForeground( mColorLabelDecoder ); + } + if( mToTalkgroup != null ) + { + mToTalkgroup.setForeground( mColorLabelDecoder ); + } + if( mToTalkgroupAliasLabel != null ) + { + mToTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); + } + break; + case CHANNEL_STATE_LABEL_DETAILS: + if( mToTalkgroupLabel != null ) + { + mToTalkgroupLabel.setForeground( mColorLabelDetails ); + } + break; + } + } + } +} diff --git a/src/module/decode/mpt1327/MPT1327DecoderState.java b/src/module/decode/mpt1327/MPT1327DecoderState.java new file mode 100644 index 00000000..32461577 --- /dev/null +++ b/src/module/decode/mpt1327/MPT1327DecoderState.java @@ -0,0 +1,572 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.mpt1327; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.TreeSet; + +import message.Message; +import module.decode.event.CallEvent; +import module.decode.event.CallEvent.CallEventType; +import module.decode.mpt1327.MPT1327Message.IdentType; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.DecoderStateEvent.Event; +import module.decode.state.State; +import module.decode.state.TrafficChannelAllocationEvent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import alias.Alias; +import alias.AliasList; +import controller.channel.Channel.ChannelType; +import controller.channel.map.ChannelMap; + +public class MPT1327DecoderState extends DecoderState +{ + private final static Logger mLog = LoggerFactory.getLogger( MPT1327DecoderState.class ); + + private TreeSet mIdents = new TreeSet(); + private HashMap> mGroups = new HashMap>(); + + private String mSite; + private String mFromTalkgroup; + private String mToTalkgroup; + + private int mChannelNumber; + private ChannelType mChannelType; + private ChannelMap mChannelMap; + + private long mFrequency = 0; + + public MPT1327DecoderState( AliasList aliasList, ChannelMap channelMap, + ChannelType channelType ) + { + super( aliasList ); + + mChannelMap = channelMap; + + mChannelType = channelType; + } + + public ChannelType getChannelType() + { + return mChannelType; + } + + public void dispose() + { + super.dispose(); + } + + @Override + public void receive( Message message ) + { + if( message.isValid() ) + { + if( message instanceof MPT1327Message ) + { + MPT1327Message mpt = ((MPT1327Message)message); + + switch( mpt.getMessageType() ) + { + case ACK: + mIdents.add( mpt.getFromID() ); + + IdentType identType = mpt.getIdent1Type(); + + if( identType == IdentType.REGI ) + { + broadcast( new MPT1327CallEvent.Builder( CallEventType.REGISTER ) + .aliasList( getAliasList() ) + .channel( String.valueOf( mChannelNumber ) ) + .details( "REGISTERED ON NETWORK" ) + .frequency( mFrequency ) + .from( mpt.getToID() ) + .to( mpt.getFromID() ) + .build() ); + } + else + { + broadcast( new MPT1327CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( getAliasList() ) + .channel( String.valueOf( mChannelNumber ) ) + .details( "ACK " + identType.getLabel() ) + .frequency( mFrequency ) + .from( mpt.getFromID() ) + .to( mpt.getToID() ) + .build() ); + } + + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) ); + break; + case ACKI: + mIdents.add( mpt.getFromID() ); + mIdents.add( mpt.getToID() ); + + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) ); + break; + case AHYC: + mIdents.add( mpt.getToID() ); + + broadcast( new MPT1327CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .channel( String.valueOf( mChannelNumber ) ) + .details( ( (MPT1327Message) message ).getRequestString() ) + .frequency( mFrequency ) + .from( mpt.getFromID() ) + .to( mpt.getToID() ) + .build() ); + + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) ); + break; + case AHYQ: + broadcast( new MPT1327CallEvent.Builder( CallEventType.STATUS ) + .aliasList( getAliasList() ) + .channel( String.valueOf( mChannelNumber ) ) + .details( mpt.getStatusMessage() ) + .frequency( mFrequency ) + .from( mpt.getFromID() ) + .to( mpt.getToID() ) + .build() ); + + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) ); + break; + case ALH: + String site = mpt.getSiteID(); + + if( site != null && ( mSite == null || !site.contentEquals( mSite ) ) ) + { + mSite = site; + broadcast( ChangedAttribute.CHANNEL_SITE_NUMBER ); + } + mSite = mpt.getSiteID(); + + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) ); + break; + case GTC: + String from = mpt.getFromID(); + String to = mpt.getToID(); + + if( from != null ) + { + mIdents.add( from ); + } + + if( to != null ) + { + mIdents.add( to ); + } + + /* Capture the idents that talk to each group */ + if( from != null && to != null ) + { + if( mGroups.containsKey( to ) ) + { + ArrayList talkgroups = mGroups.get( to ); + + if( !talkgroups.contains( from ) ) + { + talkgroups.add( from ); + } + } + else + { + ArrayList talkgroups = new ArrayList(); + talkgroups.add( from ); + + mGroups.put( to, talkgroups ); + } + } + + int channel = mpt.getChannel(); + + long frequency = 0; + + if( getChannelMap() != null ) + { + frequency = getChannelMap().getFrequency( channel ); + } + + CallEvent event = new MPT1327CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( String.valueOf( channel ) ) + .details( "GTC" ) + .frequency( frequency ) + .from( mpt.getFromID() ) + .to( mpt.getToID() ) + .build(); + + broadcast( new TrafficChannelAllocationEvent( this, event ) ); + break; + case HEAD_PLUS1: + case HEAD_PLUS2: + case HEAD_PLUS3: + case HEAD_PLUS4: + broadcast( new MPT1327CallEvent.Builder( CallEventType.SDM ) + .aliasList( getAliasList() ) + .details( mpt.getMessage() ) + .from( mpt.getFromID() ) + .to( mpt.getToID() ) + .build() ); + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) ); + break; + + /* Traffic Channel Events */ + case CLEAR: + case MAINT: + broadcast( new DecoderStateEvent( this, Event.END, State.END ) ); + break; + } + } + } + } + +// /** +// * Intercept the fade event so that we can generate a call end event +// */ +// @Override +// public void fade( final CallEventType type ) +// { +// /* +// * We can receive multiple call tear-down messages -- only post a call +// * end event for the one that can change the state to fade +// */ +// if( getState().canChangeTo( State.FADE ) ) +// { +// CallEvent current = getCurrentCallEvent(); +// +// if( current != null ) +// { +// mCallEventModel.setEnd( current ); +// } +// else +// { +// mCallEventModel.add( +// new MPT1327CallEvent.Builder( type ) +// .aliasList( getAliasList() ) +// .channel( String.valueOf( mChannelNumber ) ) +// .frequency( mChannelMap.getFrequency( mChannelNumber ) ) +// .from( mFromTalkgroup ) +// .to( mToTalkgroup ) +// .build() ); +// } +// +// setCurrentCall( null ); +// } +// +// super.fade( type ); +// } + + public void reset() + { + mIdents.clear(); + + resetState(); + } + + private void resetState() + { + mFromTalkgroup = null; + broadcast( ChangedAttribute.FROM_TALKGROUP ); + + mToTalkgroup = null; + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + + public String getSite() + { + return mSite; + } + + public Alias getSiteAlias() + { + if( hasAliasList() ) + { + return getAliasList().getSiteID( mSite ); + } + + return null; + } + + public ChannelMap getChannelMap() + { + return mChannelMap; + } + + public void setChannelMap( ChannelMap channelMap ) + { + mChannelMap = channelMap; + } + + public String getFromTalkgroup() + { + return mFromTalkgroup; + } + + /** + * Set the talkgroup. This is used primarily for traffic channels since + * the talkgroup will already have been identified prior to the traffic + * channel being created. + */ + public void setFromTalkgroup( String fromTalkgroup ) + { + mFromTalkgroup = fromTalkgroup; + + broadcast( ChangedAttribute.FROM_TALKGROUP ); + } + + public Alias getFromTalkgroupAlias() + { + if( hasAliasList() ) + { + return getAliasList().getTalkgroupAlias( mFromTalkgroup ); + } + + return null; + } + + public String getToTalkgroup() + { + return mToTalkgroup; + } + + /** + * Set the talkgroup. This is used primarily for traffic channels since + * the talkgroup will already have been identified prior to the traffic + * channel being created. + */ + public void setToTalkgroup( String toTalkgroup ) + { + mToTalkgroup = toTalkgroup; + + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + + public Alias getToTalkgroupAlias() + { + if( hasAliasList() ) + { + return getAliasList().getTalkgroupAlias( mToTalkgroup ); + } + + return null; + } + + + public int getChannelNumber() + { + return mChannelNumber; + } + /** + * Set the channel number. This is used primarily for traffic channels since + * the channel will already have been identified prior to the traffic + * channel being created. + */ + public void setChannelNumber( int channel ) + { + mChannelNumber = channel; + + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "Activity Summary\n" ); + sb.append( "Decoder:\tMPT-1327\n" ); + sb.append( "Site:\t" ); + + if( mSite != null ) + { + sb.append( mSite ); + + if( hasAliasList() ) + { + Alias siteAlias = getAliasList().getSiteID( mSite ); + + if( siteAlias != null ) + { + sb.append( " " ); + sb.append( siteAlias.getName() ); + sb.append( "\n" ); + } + } + } + else + { + sb.append( "Unknown\n" ); + } + + sb.append( "\n\n" ); + + sb.append( "Talkgroups\n\n" ); + + if( mGroups.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + List talkgroups = new ArrayList( mGroups.keySet() ); + Collections.sort( talkgroups ); + + for( String talkgroup: talkgroups ) + { + sb.append( " " ); + sb.append( talkgroup ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getMPT1327Alias( talkgroup ); + + if( alias != null ) + { + sb.append( " " ); + sb.append( alias.getName() ); + } + } + + sb.append( "\n" ); + + ArrayList members = mGroups.get( talkgroup ); + Collections.sort( members ); + + for( String member: members ) + { + sb.append( " >" ); + sb.append( member ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getMPT1327Alias( member ); + + if( alias != null ) + { + sb.append( " " ); + sb.append( alias.getName() ); + } + } + + sb.append( "\n" ); + } + + sb.append( "\n" ); + } + } + + sb.append( "\n" ); + + sb.append( "Individual Idents\n" ); + if( mIdents.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mIdents.iterator(); + + while( it.hasNext() ) + { + String ident = it.next(); + + sb.append( " " ); + sb.append( ident ); + sb.append( " " ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getMPT1327Alias( ident ); + + if( alias != null ) + { + sb.append( alias.getName() ); + } + } + + sb.append( "\n" ); + } + } + + return sb.toString(); + } + + @Override + public void init() + { + /* No initialization required */ + } + + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + switch( event.getEvent() ) + { + case RESET: + resetState(); + break; + case SOURCE_FREQUENCY: + mFrequency = event.getFrequency(); + break; + case TRAFFIC_CHANNEL_ALLOCATION: + if( event.getSource() != MPT1327DecoderState.this ) + { + if( event instanceof TrafficChannelAllocationEvent ) + { + TrafficChannelAllocationEvent allocationEvent = + (TrafficChannelAllocationEvent)event; + + String channel = allocationEvent.getCallEvent().getChannel(); + + if( channel != null ) + { + try + { + mChannelNumber = Integer.valueOf( channel ); + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + catch( Exception e ) + { + //Do nothing, we couldn't parse the channel number + } + } + + mFrequency = allocationEvent.getCallEvent().getFrequency(); + broadcast( ChangedAttribute.SOURCE ); + + mFromTalkgroup = allocationEvent.getCallEvent().getFromID(); + broadcast( ChangedAttribute.FROM_TALKGROUP ); + + mToTalkgroup = allocationEvent.getCallEvent().getToID(); + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + } + break; + default: + break; + } + } +} diff --git a/src/decode/mpt1327/MPT1327Message.java b/src/module/decode/mpt1327/MPT1327Message.java similarity index 99% rename from src/decode/mpt1327/MPT1327Message.java rename to src/module/decode/mpt1327/MPT1327Message.java index 6038cef5..206fafa9 100644 --- a/src/decode/mpt1327/MPT1327Message.java +++ b/src/module/decode/mpt1327/MPT1327Message.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mpt1327; +package module.decode.mpt1327; import java.text.DecimalFormat; import java.text.SimpleDateFormat; diff --git a/src/decode/mpt1327/MPT1327MessageFilter.java b/src/module/decode/mpt1327/MPT1327MessageFilter.java similarity index 93% rename from src/decode/mpt1327/MPT1327MessageFilter.java rename to src/module/decode/mpt1327/MPT1327MessageFilter.java index e98f89b9..6ad9be50 100644 --- a/src/decode/mpt1327/MPT1327MessageFilter.java +++ b/src/module/decode/mpt1327/MPT1327MessageFilter.java @@ -1,11 +1,11 @@ -package decode.mpt1327; +package module.decode.mpt1327; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import message.Message; -import decode.mpt1327.MPT1327Message.MPTMessageType; +import module.decode.mpt1327.MPT1327Message.MPTMessageType; import filter.Filter; import filter.FilterElement; diff --git a/src/decode/mpt1327/MPT1327MessageProcessor.java b/src/module/decode/mpt1327/MPT1327MessageProcessor.java similarity index 66% rename from src/decode/mpt1327/MPT1327MessageProcessor.java rename to src/module/decode/mpt1327/MPT1327MessageProcessor.java index 1c73216a..1d1d01e2 100644 --- a/src/decode/mpt1327/MPT1327MessageProcessor.java +++ b/src/module/decode/mpt1327/MPT1327MessageProcessor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,17 +15,22 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.mpt1327; +package module.decode.mpt1327; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import message.Message; -import sample.Broadcaster; +import module.decode.DecoderFactory; import sample.Listener; import alias.AliasList; import bits.BinaryMessage; public class MPT1327MessageProcessor implements Listener { - private Broadcaster mBroadcaster = new Broadcaster(); + private final static Logger mLog = LoggerFactory.getLogger( MPT1327MessageProcessor.class ); + + private Listener mMessageListener; private AliasList mAliasList; @@ -36,24 +41,27 @@ public class MPT1327MessageProcessor implements Listener public void dispose() { - mBroadcaster.dispose(); + mMessageListener = null; } @Override public void receive( BinaryMessage buffer ) { - MPT1327Message message = new MPT1327Message( buffer, mAliasList ); - - mBroadcaster.receive( message ); + if( mMessageListener != null ) + { + MPT1327Message message = new MPT1327Message( buffer, mAliasList ); + + mMessageListener.receive( message ); + } } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } } diff --git a/src/module/decode/mpt1327/MPT1327TrafficPanel.java b/src/module/decode/mpt1327/MPT1327TrafficPanel.java new file mode 100644 index 00000000..f52d47fc --- /dev/null +++ b/src/module/decode/mpt1327/MPT1327TrafficPanel.java @@ -0,0 +1,177 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.mpt1327; + +import java.awt.EventQueue; + +import javax.swing.JLabel; + +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; +import settings.ColorSetting; +import settings.Setting; +import settings.SettingsManager; +import alias.Alias; + +public class MPT1327TrafficPanel extends DecoderPanel +{ + private static final long serialVersionUID = 1L; + + private JLabel mProtocolLabel; + private JLabel mFromTalkgroupLabel; + private JLabel mFromTalkgroupAliasLabel; + + private JLabel mToLabel; + private JLabel mToTalkgroupLabel; + private JLabel mToTalkgroupAliasLabel; + + public MPT1327TrafficPanel( SettingsManager settingsManager, MPT1327Decoder decoder ) + { + super( settingsManager, decoder ); + + init(); + } + + private MPT1327DecoderState getDecoderState() + { + return (MPT1327DecoderState)getDecoder().getDecoderState(); + } + + public void dispose() + { + super.dispose(); + } + + public void init() + { + mProtocolLabel = new JLabel( "MPT1327" ); + mProtocolLabel.setFont( mFontDecoder ); + mProtocolLabel.setForeground( mColorLabelDecoder ); + + mFromTalkgroupLabel = new JLabel(); + mFromTalkgroupLabel.setFont( mFontDecoder ); + mFromTalkgroupLabel.setForeground( mColorLabelDecoder ); + + mFromTalkgroupAliasLabel = new JLabel(); + mFromTalkgroupAliasLabel.setFont( mFontDecoder ); + mFromTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); + + mToLabel = new JLabel( "TO:" ); + mToLabel.setFont( mFontDetails ); + mToLabel.setForeground( mColorLabelDetails ); + + mToTalkgroupLabel = new JLabel( getDecoderState().getFromTalkgroup() ); + mToTalkgroupLabel.setFont( mFontDecoder ); + mToTalkgroupLabel.setForeground( mColorLabelDecoder ); + + mToTalkgroupAliasLabel = new JLabel(); + mToTalkgroupAliasLabel.setFont( mFontDecoder ); + mToTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); + + add( mProtocolLabel ); + add( mFromTalkgroupLabel ); + add( mFromTalkgroupAliasLabel, "wrap" ); + + add( mToLabel ); + add( mToTalkgroupLabel ); + add( mToTalkgroupAliasLabel, "wrap" ); + } + + @Override + public void receive( final ChangedAttribute changedAttribute ) + { + final MPT1327DecoderState state = getDecoderState(); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + switch( changedAttribute ) + { + case FROM_TALKGROUP: + String talkgroup = state.getFromTalkgroup(); + + mFromTalkgroupLabel.setText( talkgroup ); + + Alias alias = state.getFromTalkgroupAlias(); + + if( alias != null ) + { + mFromTalkgroupAliasLabel.setText( alias.getName() ); + + String icon = alias.getIconName(); + + mFromTalkgroupAliasLabel.setIcon( mSettingsManager + .getImageIcon( icon, 12 ) ); + } + break; + case TO_TALKGROUP: + String toTalkgroup = state.getToTalkgroup(); + + mToTalkgroupLabel.setText( toTalkgroup ); + + Alias toAlias = state.getToTalkgroupAlias(); + + if( toAlias != null ) + { + mToTalkgroupAliasLabel.setText( toAlias.getName() ); + + String icon = toAlias.getIconName(); + + if( icon != null ) + { + mToTalkgroupAliasLabel.setIcon( mSettingsManager + .getImageIcon( icon, 12 ) ); + } + } + break; + default: + 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: + mProtocolLabel.setForeground( mColorLabelDecoder ); + mFromTalkgroupLabel.setForeground( mColorLabelDecoder ); + mFromTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); + mToTalkgroupLabel.setForeground( mColorLabelDecoder ); + mToTalkgroupAliasLabel.setForeground( mColorLabelDecoder ); + break; + case CHANNEL_STATE_LABEL_DETAILS: + mToLabel.setForeground( mColorLabelDetails ); + break; + } + } + } +} diff --git a/src/decode/config/DecodeConfigNBFM.java b/src/module/decode/nbfm/DecodeConfigNBFM.java similarity index 90% rename from src/decode/config/DecodeConfigNBFM.java rename to src/module/decode/nbfm/DecodeConfigNBFM.java index 625ffffc..8d1b5b90 100644 --- a/src/decode/config/DecodeConfigNBFM.java +++ b/src/module/decode/nbfm/DecodeConfigNBFM.java @@ -15,9 +15,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.nbfm; -import decode.DecoderType; +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; public class DecodeConfigNBFM extends DecodeConfiguration { diff --git a/src/module/decode/nbfm/NBFMDecoder.java b/src/module/decode/nbfm/NBFMDecoder.java new file mode 100644 index 00000000..6a90fb80 --- /dev/null +++ b/src/module/decode/nbfm/NBFMDecoder.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.nbfm; + +import module.decode.DecoderType; +import module.decode.PrimaryDecoder; +import module.decode.config.DecodeConfiguration; +import module.decode.state.AlwaysUnsquelchedDecoderState; + +public class NBFMDecoder extends PrimaryDecoder +{ + public NBFMDecoder( DecodeConfiguration config ) + { + super( config, new AlwaysUnsquelchedDecoderState( "NBFM" ) ); + } + + @Override + public DecoderType getDecoderType() + { + return DecoderType.NBFM; + } +} diff --git a/src/decode/lj1200/LJ1200Panel.java b/src/module/decode/nbfm/NBFMDecoderPanel.java similarity index 62% rename from src/decode/lj1200/LJ1200Panel.java rename to src/module/decode/nbfm/NBFMDecoderPanel.java index d63d49d3..ab39d039 100644 --- a/src/decode/lj1200/LJ1200Panel.java +++ b/src/module/decode/nbfm/NBFMDecoderPanel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,55 +15,49 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.lj1200; +package module.decode.nbfm; import javax.swing.JLabel; import net.miginfocom.swing.MigLayout; +import module.decode.Decoder; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; import settings.ColorSetting; -import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingsManager; -import controller.state.AuxStatePanel; -import controller.state.ChannelState.ChangedAttribute; +import settings.ColorSetting.ColorSettingName; -public class LJ1200Panel extends AuxStatePanel +public class NBFMDecoderPanel extends DecoderPanel { private static final long serialVersionUID = 1L; - private static final String PROTOCOL = "LJ-1200"; - private JLabel mProtocol = new JLabel( PROTOCOL ); + private JLabel mProtocol = new JLabel( "NBFM" ); - public LJ1200Panel( SettingsManager settingsManager, LJ1200ChannelState state ) + public NBFMDecoderPanel( SettingsManager settingsManager, Decoder decoder ) { - super( settingsManager, state ); + super( settingsManager, decoder ); init(); } - public void dispose() + protected void init() { - super.dispose(); - } - - public LJ1200ChannelState getState() - { - return (LJ1200ChannelState)mAuxChannelState; - } - - private void init() - { - setOpaque( false ); //Use the parent panel's background color - - setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") ); + super.init(); - addLabel( mProtocol, false ); + setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]0[]") ); + + mProtocol.setFont( mFontDecoder ); + mProtocol.setForeground( mColorLabelDecoder ); + + add( mProtocol, "wrap" ); } @Override - public void receive( final ChangedAttribute changedAttribute ) - { - } + public void receive( ChangedAttribute t ) + { + /* Not implemented */ + } @Override public void settingChanged( Setting setting ) @@ -75,7 +69,7 @@ public class LJ1200Panel extends AuxStatePanel ColorSetting color = (ColorSetting)setting; if( color.getColorSettingName() == - ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ) + ColorSettingName.CHANNEL_STATE_LABEL_DECODER ) { mProtocol.setForeground( color.getColor() ); } diff --git a/src/decode/nbfm/NBFMEditor.java b/src/module/decode/nbfm/NBFMEditor.java similarity index 91% rename from src/decode/nbfm/NBFMEditor.java rename to src/module/decode/nbfm/NBFMEditor.java index e5be3f75..d80e099a 100644 --- a/src/decode/nbfm/NBFMEditor.java +++ b/src/module/decode/nbfm/NBFMEditor.java @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.nbfm; +package module.decode.nbfm; import javax.swing.JCheckBox; -import decode.DecodeEditor; -import decode.config.DecodeConfiguration; +import module.decode.DecodeEditor; +import module.decode.config.DecodeConfiguration; public class NBFMEditor extends DecodeEditor { diff --git a/src/decode/p25/C4FMFrameListener.java b/src/module/decode/p25/C4FMFrameListener.java similarity index 92% rename from src/decode/p25/C4FMFrameListener.java rename to src/module/decode/p25/C4FMFrameListener.java index ce4767c1..befb0468 100644 --- a/src/decode/p25/C4FMFrameListener.java +++ b/src/module/decode/p25/C4FMFrameListener.java @@ -1,4 +1,4 @@ -package decode.p25; +package module.decode.p25; import bits.BinaryMessage; diff --git a/src/decode/p25/C4FMSlicer.java b/src/module/decode/p25/C4FMSlicer.java similarity index 98% rename from src/decode/p25/C4FMSlicer.java rename to src/module/decode/p25/C4FMSlicer.java index 720c290e..6a7d92e3 100644 --- a/src/decode/p25/C4FMSlicer.java +++ b/src/module/decode/p25/C4FMSlicer.java @@ -1,4 +1,4 @@ -package decode.p25; +package module.decode.p25; import dsp.symbol.Dibit; import sample.Broadcaster; diff --git a/src/decode/p25/C4FMSymbolFilter.java b/src/module/decode/p25/C4FMSymbolFilter.java similarity index 99% rename from src/decode/p25/C4FMSymbolFilter.java rename to src/module/decode/p25/C4FMSymbolFilter.java index bafff11f..339be4ff 100644 --- a/src/decode/p25/C4FMSymbolFilter.java +++ b/src/module/decode/p25/C4FMSymbolFilter.java @@ -1,4 +1,4 @@ -package decode.p25; +package module.decode.p25; import instrument.Instrumentable; import instrument.tap.Tap; @@ -232,6 +232,7 @@ public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, @Override public void receive( float sample ) { + //TODO: limit frequency corrections to once per buffer !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!11 if( mReset ) { mGainController.reset(); diff --git a/src/module/decode/p25/DecodeConfigP25Phase1.java b/src/module/decode/p25/DecodeConfigP25Phase1.java new file mode 100644 index 00000000..aa19583f --- /dev/null +++ b/src/module/decode/p25/DecodeConfigP25Phase1.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.p25; + +import javax.xml.bind.annotation.XmlAttribute; + +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; +import module.decode.p25.P25Decoder.Modulation; + +public class DecodeConfigP25Phase1 extends DecodeConfiguration +{ + private P25_LSMDecoder.Modulation mModulation = Modulation.C4FM; + + private int mCallTimeout = DEFAULT_CALL_TIMEOUT_SECONDS; + private int mTrafficChannelPoolSize = TRAFFIC_CHANNEL_LIMIT_DEFAULT; + + public DecodeConfigP25Phase1() + { + super( DecoderType.P25_PHASE1 ); + + setAFC( false ); + } + + public boolean supportsAFC() + { + return false; + } + + @XmlAttribute( name = "modulation" ) + public P25_LSMDecoder.Modulation getModulation() + { + return mModulation; + } + + public void setModulation( P25_LSMDecoder.Modulation modulation ) + { + mModulation = modulation; + } + + @XmlAttribute( name="call_timeout" ) + public int getCallTimeout() + { + return mCallTimeout; + } + + /** + * Sets the call timeout value in seconds ( 10 - 600 ); + * @param timeout + */ + public void setCallTimeout( int timeout ) + { + if( CALL_TIMEOUT_MINIMUM <= timeout && timeout <= CALL_TIMEOUT_MAXIMUM ) + { + mCallTimeout = timeout; + } + else + { + mCallTimeout = DEFAULT_CALL_TIMEOUT_SECONDS; + } + } + + + @XmlAttribute( name="traffic_channel_pool_size" ) + public int getTrafficChannelPoolSize() + { + return mTrafficChannelPoolSize; + } + + /** + * Sets the traffic channel pool size which is the maximum number of + * simultaneous traffic channels that can be allocated. + * + * This limits the maximum calls so that busy systems won't cause more + * traffic channels to be allocated than the decoder/software/host computer + * can support. + */ + public void setTrafficChannelPoolSize( int size ) + { + mTrafficChannelPoolSize = size; + } +} diff --git a/src/decode/p25/P25CallEvent.java b/src/module/decode/p25/P25CallEvent.java similarity index 95% rename from src/decode/p25/P25CallEvent.java rename to src/module/decode/p25/P25CallEvent.java index 1796a88f..3813d46b 100644 --- a/src/decode/p25/P25CallEvent.java +++ b/src/module/decode/p25/P25CallEvent.java @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.p25; +package module.decode.p25; +import module.decode.DecoderType; +import module.decode.event.CallEvent; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; public class P25CallEvent extends CallEvent { @@ -35,7 +35,7 @@ public class P25CallEvent extends CallEvent long frequency, String details ) { - super( DecoderType.MPT1327, callEventType, + super( DecoderType.P25_PHASE1, callEventType, aliasList, fromID, toID, details ); mChannel = channel; diff --git a/src/module/decode/p25/P25Decoder.java b/src/module/decode/p25/P25Decoder.java new file mode 100644 index 00000000..26ccce3b --- /dev/null +++ b/src/module/decode/p25/P25Decoder.java @@ -0,0 +1,65 @@ +package module.decode.p25; + +import instrument.Instrumentable; +import module.decode.Decoder; +import module.decode.DecoderType; +import source.tuner.frequency.IFrequencyCorrectionController; +import alias.AliasList; +import controller.channel.Channel.ChannelType; + +public abstract class P25Decoder extends Decoder + implements IFrequencyCorrectionController, Instrumentable +{ + private P25MessageProcessor mMessageProcessor; + + public P25Decoder( AliasList aliasList, ChannelType channelType ) + { + super( new P25DecoderState( aliasList, channelType ) ); + + mMessageProcessor = new P25MessageProcessor( aliasList ); + mMessageProcessor.setMessageListener( mMessageBroadcaster ); + } + + @Override + public DecoderType getDecoderType() + { + return DecoderType.P25_PHASE1; + } + + protected P25MessageProcessor getMessageProcessor() + { + return mMessageProcessor; + } + + public abstract Modulation getModulation(); + + public enum Modulation + { + CQPSK( "Simulcast (LSM)", "LSM" ), + C4FM( "Normal (C4FM)", "C4FM" ); + + private String mLabel; + private String mShortLabel; + + private Modulation( String label, String shortLabel ) + { + mLabel = label; + mShortLabel = shortLabel; + } + + public String getLabel() + { + return mLabel; + } + + public String getShortLabel() + { + return mShortLabel; + } + + public String toString() + { + return getLabel(); + } + } +} diff --git a/src/decode/p25/P25Panel.java b/src/module/decode/p25/P25DecoderPanel.java similarity index 57% rename from src/decode/p25/P25Panel.java rename to src/module/decode/p25/P25DecoderPanel.java index 6bd67b6e..97701a17 100644 --- a/src/decode/p25/P25Panel.java +++ b/src/module/decode/p25/P25DecoderPanel.java @@ -15,29 +15,25 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.p25; +package module.decode.p25; import java.awt.EventQueue; import javax.swing.JLabel; +import net.miginfocom.swing.MigLayout; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; +import module.decode.state.State; import settings.ColorSetting; import settings.Setting; +import settings.SettingsManager; import alias.Alias; -import controller.channel.Channel; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelState.State; -import controller.state.ChannelStatePanel; -import decode.config.DecodeConfigP25Phase1; -public class P25Panel extends ChannelStatePanel +public class P25DecoderPanel extends DecoderPanel { private static final long serialVersionUID = 1L; - private JLabel mStateLabel; - private JLabel mSourceLabel; - private JLabel mChannelLabel; - private JLabel mProtocol; private JLabel mFrom = new JLabel( " " ); private JLabel mFromAlias = new JLabel( " " ); @@ -51,35 +47,22 @@ public class P25Panel extends ChannelStatePanel private JLabel mSiteAlias = new JLabel( "" ); - public P25Panel( Channel channel ) + public P25DecoderPanel( SettingsManager settingsManager, P25Decoder decoder ) { - super( channel ); + super( settingsManager, decoder ); - DecodeConfigP25Phase1 p25Config = - (DecodeConfigP25Phase1)channel.getDecodeConfiguration(); - - mProtocol = new JLabel( "P25-1 " + - p25Config.getModulation().getShortLabel() ); + mProtocol = new JLabel( "P25-1 " + decoder.getModulation().getShortLabel() ); init(); } public void init() { - mStateLabel = new JLabel( mChannel.getProcessingChain(). - getChannelState().getState().getDisplayValue() ); - mStateLabel.setFont( mFontDecoder ); - mStateLabel.setForeground( mColorLabelDecoder ); + super.init(); - mSourceLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() ); - mSourceLabel.setFont( mFontDetails ); - mSourceLabel.setForeground( mColorLabelDetails ); - - mChannelLabel = new JLabel( mChannel.getChannelDisplayName() ); - mChannelLabel.setFont( mFontDetails ); - mChannelLabel.setForeground( mColorLabelDetails ); + setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]0[]") ); - mProtocol.setFont( mFontDecoder ); + mProtocol.setFont( mFontDecoder ); mProtocol.setForeground( mColorLabelDecoder ); mFrom.setFont( mFontDecoder ); @@ -106,10 +89,6 @@ public class P25Panel extends ChannelStatePanel mSite.setFont( mFontDetails ); mSite.setForeground( mColorLabelDecoder ); - add( mStateLabel ); - add( mSourceLabel ); - add( mChannelLabel, "wrap" ); - add( mProtocol ); add( mFrom ); add( mFromAlias, "wrap" ); @@ -122,66 +101,47 @@ public class P25Panel extends ChannelStatePanel add( mSite ); add( mSiteAlias, "wrap" ); } + + private P25DecoderState getDecoderState() + { + return (P25DecoderState)getDecoder().getDecoderState(); + } @Override public void receive( final ChangedAttribute changedAttribute ) { + final P25DecoderState state = getDecoderState(); + EventQueue.invokeLater( new Runnable() { @Override public void run() { - final P25ChannelState channelState = (P25ChannelState)mChannel. - getProcessingChain().getChannelState(); switch( changedAttribute ) { - case CHANNEL_STATE: - State state = channelState.getState(); - - mStateLabel.setText( state.getDisplayValue() ); - - if( state == State.IDLE ) - { - mFrom.setText( null ); - mFromAlias.setText( null ); - mFromAlias.setIcon( null ); - mTo.setText( null ); - mToAlias.setText( null ); - mToAlias.setIcon( null ); - } - break; - case SOURCE: - mSourceLabel.setText( mChannel.getSourceConfiguration() - .getDescription() ); - break; - case CHANNEL_NAME: - case SITE_NAME: - case SYSTEM_NAME: - mChannelLabel.setText( mChannel.getChannelDisplayName() ); - break; case NAC: - mNAC.setText( "NAC:" + channelState.getNAC() ); + mNAC.setText( "NAC:" + state.getNAC() ); break; case SYSTEM: - mSystem.setText( "SYS:" + channelState.getSystem() ); + mSystem.setText( "SYS:" + state.getSystem() ); break; case SITE: - mSite.setText( "SITE:" + channelState.getSite() ); + mSite.setText( "SITE:" + state.getSite() ); break; case SITE_ALIAS: - mSiteAlias.setText( channelState.getSiteAlias() ); + mSiteAlias.setText( state.getSiteAlias() ); break; case FROM_TALKGROUP: - mFrom.setText( channelState.getFromTalkgroup() ); + mFrom.setText( state.getFromTalkgroup() ); break; case FROM_TALKGROUP_ALIAS: - Alias from = channelState.getFromAlias(); + Alias from = state.getFromAlias(); if( from != null ) { mFromAlias.setText( from.getName() ); - mFromAlias.setIcon( getSettingsManager() + mFromAlias.setIcon( mSettingsManager .getImageIcon( from.getIconName(), 12 ) ); } else @@ -191,15 +151,15 @@ public class P25Panel extends ChannelStatePanel } break; case TO_TALKGROUP: - mTo.setText( channelState.getToTalkgroup() ); + mTo.setText( state.getToTalkgroup() ); break; case TO_TALKGROUP_ALIAS: - Alias to = channelState.getToAlias(); + Alias to = state.getToAlias(); if( to != null ) { mToAlias.setText( to.getName() ); - mToAlias.setIcon( getSettingsManager() + mToAlias.setIcon( mSettingsManager .getImageIcon( to.getIconName(), 12 ) ); } else @@ -227,24 +187,12 @@ public class P25Panel extends ChannelStatePanel 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 ); - } + default: break; } } diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java new file mode 100644 index 00000000..61de6752 --- /dev/null +++ b/src/module/decode/p25/P25DecoderState.java @@ -0,0 +1,2932 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.p25; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import message.Message; +import module.decode.event.CallEvent; +import module.decode.event.CallEvent.CallEventType; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.P25Message; +import module.decode.p25.message.hdu.HDUMessage; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.message.ldu.LDUMessage; +import module.decode.p25.message.ldu.lc.CallTermination; +import module.decode.p25.message.ldu.lc.TelephoneInterconnectVoiceChannelUser; +import module.decode.p25.message.ldu.lc.UnitToUnitVoiceChannelUser; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.message.pdu.confirmed.PDUConfirmedMessage; +import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextAccept; +import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextReject; +import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextRequest; +import module.decode.p25.message.pdu.confirmed.SNDCPDeactivateTDSContext; +import module.decode.p25.message.pdu.confirmed.SNDCPUserData; +import module.decode.p25.message.pdu.osp.control.AdjacentStatusBroadcastExtended; +import module.decode.p25.message.pdu.osp.control.CallAlertExtended; +import module.decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended; +import module.decode.p25.message.pdu.osp.control.GroupAffiliationResponseExtended; +import module.decode.p25.message.pdu.osp.control.MessageUpdateExtended; +import module.decode.p25.message.pdu.osp.control.NetworkStatusBroadcastExtended; +import module.decode.p25.message.pdu.osp.control.ProtectionParameterBroadcast; +import module.decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended; +import module.decode.p25.message.pdu.osp.control.RoamingAddressUpdateExtended; +import module.decode.p25.message.pdu.osp.control.StatusQueryExtended; +import module.decode.p25.message.pdu.osp.control.StatusUpdateExtended; +import module.decode.p25.message.pdu.osp.control.UnitRegistrationResponseExtended; +import module.decode.p25.message.pdu.osp.data.GroupDataChannelGrantExtended; +import module.decode.p25.message.pdu.osp.data.IndividualDataChannelGrantExtended; +import module.decode.p25.message.pdu.osp.voice.GroupVoiceChannelGrantExplicit; +import module.decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantExplicit; +import module.decode.p25.message.pdu.osp.voice.UnitToUnitAnswerRequestExplicit; +import module.decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantExtended; +import module.decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantUpdateExtended; +import module.decode.p25.message.tdu.TDUMessage; +import module.decode.p25.message.tdu.lc.AdjacentSiteStatusBroadcast; +import module.decode.p25.message.tdu.lc.GroupVoiceChannelUpdate; +import module.decode.p25.message.tdu.lc.GroupVoiceChannelUpdateExplicit; +import module.decode.p25.message.tdu.lc.NetworkStatusBroadcast; +import module.decode.p25.message.tdu.lc.NetworkStatusBroadcastExplicit; +import module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcast; +import module.decode.p25.message.tdu.lc.SecondaryControlChannelBroadcastExplicit; +import module.decode.p25.message.tdu.lc.TDULinkControlMessage; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.message.tsbk.osp.control.AcknowledgeResponse; +import module.decode.p25.message.tsbk.osp.control.AdjacentStatusBroadcast; +import module.decode.p25.message.tsbk.osp.control.AuthenticationCommand; +import module.decode.p25.message.tsbk.osp.control.CallAlert; +import module.decode.p25.message.tsbk.osp.control.DenyResponse; +import module.decode.p25.message.tsbk.osp.control.ExtendedFunctionCommand; +import module.decode.p25.message.tsbk.osp.control.GroupAffiliationQuery; +import module.decode.p25.message.tsbk.osp.control.GroupAffiliationResponse; +import module.decode.p25.message.tsbk.osp.control.IdentifierUpdate; +import module.decode.p25.message.tsbk.osp.control.LocationRegistrationResponse; +import module.decode.p25.message.tsbk.osp.control.MessageUpdate; +import module.decode.p25.message.tsbk.osp.control.ProtectionParameterUpdate; +import module.decode.p25.message.tsbk.osp.control.QueuedResponse; +import module.decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast; +import module.decode.p25.message.tsbk.osp.control.RadioUnitMonitorCommand; +import module.decode.p25.message.tsbk.osp.control.RoamingAddressCommand; +import module.decode.p25.message.tsbk.osp.control.StatusQuery; +import module.decode.p25.message.tsbk.osp.control.StatusUpdate; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge; +import module.decode.p25.message.tsbk.osp.control.UnitRegistrationCommand; +import module.decode.p25.message.tsbk.osp.control.UnitRegistrationResponse; +import module.decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncement; +import module.decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncementExplicit; +import module.decode.p25.message.tsbk.osp.data.GroupDataChannelGrant; +import module.decode.p25.message.tsbk.osp.data.IndividualDataChannelGrant; +import module.decode.p25.message.tsbk.osp.data.SNDCPDataChannelAnnouncementExplicit; +import module.decode.p25.message.tsbk.osp.data.SNDCPDataChannelGrant; +import module.decode.p25.message.tsbk.osp.data.SNDCPDataPageRequest; +import module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant; +import module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate; +import module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit; +import module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectAnswerRequest; +import module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrant; +import module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrantUpdate; +import module.decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest; +import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrant; +import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate; +import module.decode.p25.reference.IPProtocol; +import module.decode.p25.reference.Response; +import module.decode.p25.reference.Service; +import module.decode.p25.reference.Vendor; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import module.decode.state.DecoderStateEvent.Event; +import module.decode.state.State; +import module.decode.state.TrafficChannelAllocationEvent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import alias.Alias; +import alias.AliasList; +import controller.channel.Channel.ChannelType; + +public class P25DecoderState extends DecoderState +{ + private final static Logger mLog = LoggerFactory.getLogger( P25DecoderState.class ); + private static final DecimalFormat mFrequencyFormatter = + new DecimalFormat( "0.000000" ); + + private module.decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast mNetworkStatus; + private NetworkStatusBroadcastExtended mNetworkStatusExtended; + private ProtectionParameterBroadcast mProtectionParameterBroadcast; + private RFSSStatusBroadcast mRFSSStatusMessage; + private RFSSStatusBroadcastExtended mRFSSStatusMessageExtended; + private SNDCPDataChannelAnnouncementExplicit mSNDCPDataChannel; + private HashMap mNeighborMap = + new HashMap(); + + private Set mSecondaryControlChannels = + new TreeSet<>(); + + private HashMap mBands = + new HashMap(); + + private HashMap mRegistrations = new HashMap(); + private String mLastCommandEventID; + private String mLastPageEventID; + private String mLastQueryEventID; + private String mLastRegistrationEventID; + private String mLastResponseEventID; + + private String mNAC; + private String mSystem; + private String mSite; + private String mSiteAlias; + private String mFromTalkgroup; + private Alias mFromAlias; + private String mToTalkgroup; + private Alias mToAlias; + private String mCurrentChannel = "CURRENT"; + private long mCurrentChannelFrequency = 0; + + private P25CallEvent mCurrentCallEvent; + private ChannelType mChannelType; + + public P25DecoderState( AliasList aliasList, ChannelType channelType ) + { + super( aliasList ); + + mChannelType = channelType; + } + + /** + * Performs a full reset to prepare this object for reuse on a new channel + */ + @Override + public void reset() + { + resetState(); + + updateNAC( null ); + updateSite( null ); + updateSystem( null ); + } + + /** + * Resets any temporal state details + */ + private void resetState() + { + updateFrom( null ); + updateTo( null ); + } + + /** + * Performs any initialization operations to prepare for use + */ + @Override + public void init() + { + } + + /** + * Primary message processing method. + */ + public void receive( Message message ) + { + if( message instanceof P25Message ) + { + updateNAC( ( (P25Message)message ).getNAC() ); + + /* Voice Vocoder messages */ + if( message instanceof LDUMessage ) + { + processLDU( (LDUMessage)message ); + } + + /* Terminator Data Unit with Link Control Message */ + else if( message instanceof TDULinkControlMessage ) + { + processTDULC( (TDULinkControlMessage)message ); + } + + /* Trunking Signalling Messages */ + else if( message instanceof TSBKMessage ) + { + processTSBK( (TSBKMessage)message ); + } + + /* Header Data Unit Message - preceeds voice LDUs */ + else if( message instanceof HDUMessage ) + { + processHDU( (HDUMessage)message ); + } + + /* Packet Data Unit Messages */ + else if( message instanceof PDUMessage ) + { + processPDU( (PDUMessage)message ); + } + + /* Terminator Data Unit, or default message if CRC failed */ + else if( message instanceof TDUMessage ) + { + processTDU( (TDUMessage)message ); + } + } + } + + /** + * Terminator Data Unit, or default message if the CRC failed for a longer + * message. We process these as continuation events. + */ + private void processTDU( TDUMessage tdu ) + { + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) ); + } + + /** + * Header Data Unit - first message in a call sequence. + */ + private void processHDU( HDUMessage hdu ) + { + if( hdu.isValid() ) + { + State state = State.CALL; + + if( hdu.isEncrypted() ) + { + state = State.ENCRYPTED; + } + + broadcast( new DecoderStateEvent( this, Event.START, state ) ); + + updateTo( hdu.getToID() ); + + if( mChannelType == ChannelType.STANDARD ) + { + StringBuilder sb = new StringBuilder(); + + CallEventType type = CallEventType.CALL; + + if( hdu.isEncrypted() ) + { + type = CallEventType.ENCRYPTED_CALL; + + sb.append( "ENCRYPTED WITH " ); + sb.append( hdu.getEncryption().name() ); + sb.append( " KEY:" ); + sb.append( hdu.getKeyID() ); + } + + P25CallEvent event = new P25CallEvent.Builder( type ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "Trigger:HDU" ) + .frequency( mCurrentChannelFrequency ) + .to( hdu.getToID() ) + .details( sb.toString() ) + .build(); + + mCurrentCallEvent = event; + + broadcast( event ); + } + } + } + + /** + * Terminator Data Unit with Link Control - indicates an end of the call + * sequences with embedded link control messages + */ + private void processTDULC( TDULinkControlMessage tdulc ) + { + broadcast( new DecoderStateEvent( this, Event.END, State.END ) ); + + switch( tdulc.getOpcode() ) + { + case ADJACENT_SITE_STATUS_BROADCAST: + if( tdulc instanceof AdjacentSiteStatusBroadcast ) + { + AdjacentSiteStatusBroadcast assb = + (AdjacentSiteStatusBroadcast)tdulc; + + updateSystem( assb.getSystemID() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: + /* This message doesn't provide anything we need for channel state */ + break; + case CALL_ALERT: + if( tdulc instanceof module.decode.p25.message.tdu.lc.CallAlert ) + { + module.decode.p25.message.tdu.lc.CallAlert ca = + (module.decode.p25.message.tdu.lc.CallAlert)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .from( ca.getSourceAddress() ) + .to( ca.getTargetAddress() ) + .details( "CALL ALERT" ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case CALL_TERMINATION_OR_CANCELLATION: + break; + case CHANNEL_IDENTIFIER_UPDATE: + //TODO: does the activity summary need this message? + + /* This message is handled by the P25MessageProcessor and + * inserted into any channels needing frequency band info */ + break; + case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT: + //TODO: does the activity summary need this message? + + /* This message is handled by the P25MessageProcessor and + * inserted into any channels needing frequency band info */ + break; + case EXTENDED_FUNCTION_COMMAND: + if( tdulc instanceof module.decode.p25.message.tdu.lc.ExtendedFunctionCommand ) + { + module.decode.p25.message.tdu.lc.ExtendedFunctionCommand efc = + (module.decode.p25.message.tdu.lc.ExtendedFunctionCommand)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .to( efc.getTargetAddress() ) + .details( "FUNCTION:" + efc.getExtendedFunction().getLabel() + + " ARG:" + efc.getArgument() ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case GROUP_AFFILIATION_QUERY: + if( tdulc instanceof module.decode.p25.message.tdu.lc.GroupAffiliationQuery ) + { + module.decode.p25.message.tdu.lc.GroupAffiliationQuery gaq = + (module.decode.p25.message.tdu.lc.GroupAffiliationQuery)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .details( "GROUP AFFILIATION QUERY" ) + .from( gaq.getSourceAddress() ) + .to( gaq.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case GROUP_VOICE_CHANNEL_UPDATE: + /* Reflects other call activity on the system: CALL DETECT */ + if( mChannelType == ChannelType.STANDARD && + tdulc instanceof GroupVoiceChannelUpdate ) + { + GroupVoiceChannelUpdate gvcu = (GroupVoiceChannelUpdate)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( gvcu.getChannelA() ) + .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) + .frequency( gvcu.getDownlinkFrequencyA() ) + .from( gvcu.getGroupAddressA() ) + .to( gvcu.getGroupAddressB() ) + .build() ); + } + break; + case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: + /* Reflects other call activity on the system: CALL DETECT */ + if( mChannelType == ChannelType.STANDARD && + tdulc instanceof GroupVoiceChannelUpdateExplicit ) + { + GroupVoiceChannelUpdateExplicit gvcue = + (GroupVoiceChannelUpdateExplicit)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( gvcue.getTransmitChannel() ) + .details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) ) + .frequency( gvcue.getDownlinkFrequency() ) + .to( gvcue.getGroupAddress() ) + .build() ); + } + break; + case GROUP_VOICE_CHANNEL_USER: + if( mChannelType == ChannelType.STANDARD && + tdulc instanceof module.decode.p25.message.tdu.lc.GroupVoiceChannelUser ) + { + module.decode.p25.message.tdu.lc.GroupVoiceChannelUser gvcuser = + (module.decode.p25.message.tdu.lc.GroupVoiceChannelUser)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( ( gvcuser.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcuser.isEmergency() ? " EMERGENCY" : "") ) + .frequency( mCurrentChannelFrequency ) + .from( gvcuser.getSourceAddress() ) + .to( gvcuser.getGroupAddress() ) + .build() ); + } + break; + case MESSAGE_UPDATE: + if( tdulc instanceof module.decode.p25.message.tdu.lc.MessageUpdate ) + { + module.decode.p25.message.tdu.lc.MessageUpdate mu = + (module.decode.p25.message.tdu.lc.MessageUpdate)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.SDM ) + .aliasList( getAliasList() ) + .from( mu.getSourceAddress() ) + .to( mu.getTargetAddress() ) + .details( "MSG: " + mu.getShortDataMessage() ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case NETWORK_STATUS_BROADCAST: + if( tdulc instanceof NetworkStatusBroadcast ) + { + updateSystem( ((NetworkStatusBroadcast)tdulc).getSystem() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case NETWORK_STATUS_BROADCAST_EXPLICIT: + if( tdulc instanceof NetworkStatusBroadcastExplicit ) + { + updateSystem( ((NetworkStatusBroadcastExplicit)tdulc).getSystem() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case PROTECTION_PARAMETER_BROADCAST: + if( tdulc instanceof module.decode.p25.message.tdu.lc.ProtectionParameterBroadcast ) + { + module.decode.p25.message.tdu.lc.ProtectionParameterBroadcast ppb = + (module.decode.p25.message.tdu.lc.ProtectionParameterBroadcast)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .to( ppb.getTargetAddress() ) + .details( "ENCRYPTION: " + + ppb.getEncryption().name() + " KEY:" + + ppb.getEncryptionKey() ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case RFSS_STATUS_BROADCAST: + if( tdulc instanceof module.decode.p25.message.tdu.lc.RFSSStatusBroadcast ) + { + module.decode.p25.message.tdu.lc.RFSSStatusBroadcast rfsssb = + (module.decode.p25.message.tdu.lc.RFSSStatusBroadcast)tdulc; + + updateSystem( rfsssb.getSystem() ); + + String site = rfsssb.getRFSubsystemID() + "-" + + rfsssb.getSiteID(); + + updateSite( site ); + + if( mCurrentChannel == null || + !mCurrentChannel.contentEquals( rfsssb.getChannel() ) ) + { + mCurrentChannel = rfsssb.getChannel(); + mCurrentChannelFrequency = rfsssb.getDownlinkFrequency(); + } + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case RFSS_STATUS_BROADCAST_EXPLICIT: + if( tdulc instanceof module.decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit ) + { + module.decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit rfsssbe = + (module.decode.p25.message.tdu.lc.RFSSStatusBroadcastExplicit)tdulc; + + String site = rfsssbe.getRFSubsystemID() + "-" + + rfsssbe.getSiteID(); + + updateSite( site ); + + if( mCurrentChannel == null || + !mCurrentChannel.contentEquals( rfsssbe.getTransmitChannel() ) ) + { + mCurrentChannel = rfsssbe.getTransmitChannel(); + mCurrentChannelFrequency = rfsssbe.getDownlinkFrequency(); + } + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case SECONDARY_CONTROL_CHANNEL_BROADCAST: + if( tdulc instanceof SecondaryControlChannelBroadcast ) + { + SecondaryControlChannelBroadcast sccb = + (SecondaryControlChannelBroadcast)tdulc; + + String site = sccb.getRFSubsystemID() + "-" + + sccb.getSiteID(); + + updateSite( site ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: + if( tdulc instanceof SecondaryControlChannelBroadcastExplicit ) + { + SecondaryControlChannelBroadcastExplicit sccb = + (SecondaryControlChannelBroadcastExplicit)tdulc; + + String site = sccb.getRFSubsystemID() + "-" + + sccb.getSiteID(); + + updateSite( site ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case STATUS_QUERY: + if( tdulc instanceof module.decode.p25.message.tdu.lc.StatusQuery ) + { + module.decode.p25.message.tdu.lc.StatusQuery sq = + (module.decode.p25.message.tdu.lc.StatusQuery)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .details( "STATUS QUERY" ) + .from( sq.getSourceAddress() ) + .to( sq.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case STATUS_UPDATE: + if( tdulc instanceof module.decode.p25.message.tdu.lc.StatusUpdate ) + { + module.decode.p25.message.tdu.lc.StatusUpdate su = + (module.decode.p25.message.tdu.lc.StatusUpdate)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.STATUS ) + .aliasList( getAliasList() ) + .details( "STATUS UNIT:" + su.getUnitStatus() + + " USER:" + su.getUserStatus() ) + .from( su.getSourceAddress() ) + .to( su.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case SYSTEM_SERVICE_BROADCAST: + /* This message doesn't provide anything we need for channel state */ + break; + case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: + if( tdulc instanceof module.decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest ) + { + module.decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest tiar = + (module.decode.p25.message.tdu.lc.TelephoneInterconnectAnswerRequest)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .from( tiar.getTelephoneNumber() ) + .to( tiar.getTargetAddress() ) + .details( "TELEPHONE CALL ALERT" ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER: + if( mChannelType == ChannelType.STANDARD && + tdulc instanceof module.decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser ) + { + module.decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser tivcu = + (module.decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "TELEPHONE INTERCONNECT " + + ( tivcu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( tivcu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( mCurrentChannelFrequency ) + .from( null ) + .to( tivcu.getAddress() ) + .build() ); + } + break; + case UNIT_AUTHENTICATION_COMMAND: + if( tdulc instanceof module.decode.p25.message.tdu.lc.UnitAuthenticationCommand ) + { + module.decode.p25.message.tdu.lc.UnitAuthenticationCommand uac = + (module.decode.p25.message.tdu.lc.UnitAuthenticationCommand)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .to( uac.getCompleteTargetAddress() ) + .details( "AUTHENTICATE" ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case UNIT_REGISTRATION_COMMAND: + if( tdulc instanceof module.decode.p25.message.tdu.lc.UnitRegistrationCommand ) + { + module.decode.p25.message.tdu.lc.UnitRegistrationCommand urc = + (module.decode.p25.message.tdu.lc.UnitRegistrationCommand)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .to( urc.getCompleteTargetAddress() ) + .details( "REGISTER" ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case UNIT_TO_UNIT_ANSWER_REQUEST: + if( tdulc instanceof module.decode.p25.message.tdu.lc.UnitToUnitAnswerRequest ) + { + module.decode.p25.message.tdu.lc.UnitToUnitAnswerRequest uuar = + (module.decode.p25.message.tdu.lc.UnitToUnitAnswerRequest)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .from( uuar.getSourceAddress() ) + .to( uuar.getTargetAddress() ) + .details( "UNIT TO UNIT CALL ALERT" ) + .build() ); + } + else + { + logAlternateVendorMessage( tdulc ); + } + break; + case UNIT_TO_UNIT_VOICE_CHANNEL_USER: + if( mChannelType == ChannelType.STANDARD && + tdulc instanceof module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser ) + { + module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser uuvcu = + (module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser)tdulc; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( ( uuvcu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( uuvcu.isEmergency() ? " EMERGENCY" : "" ) ) + .frequency( mCurrentChannelFrequency ) + .from( uuvcu.getSourceAddress() ) + .to( uuvcu.getTargetAddress() ) + .build() ); + } + break; + default: + break; + } + } + + /** + * Log optional vendor format messages that we don't yet support so that we + * can understand those messages and eventually add support. + */ + private void logAlternateVendorMessage( P25Message p25 ) + { + if( p25 instanceof TDULinkControlMessage ) + { + TDULinkControlMessage tdulc = (TDULinkControlMessage)p25; + + mLog.debug( "TDULC - VENDOR [" + tdulc.getVendor() + + "] SPECIFIC FORMAT FOR " + tdulc.getVendorOpcode().getLabel() + + " " + tdulc.getBinaryMessage() ); + } + else if( p25 instanceof LDU1Message ) + { + LDU1Message ldu1 = (LDU1Message)p25; + + mLog.debug( "LDU1 - VENDOR [" + ldu1.getVendor() + + "] SPECIFIC FORMAT FOR " + ldu1.getVendorOpcode().getLabel() + + " " + ldu1.getBinaryMessage() ); + } + else if( p25 instanceof PDUMessage ) + { + PDUMessage pdu = (PDUMessage)p25; + + mLog.debug( "PDU - VENDOR [" + pdu.getVendor() + + "] SPECIFIC FORMAT FOR " + pdu.getVendorOpcode().getLabel() + + " " + pdu.getBinaryMessage() ); + } + else + { + mLog.debug( "P25 - ALTERNATE VENDOR MESSAGE [" + p25.getClass() + "]" ); + } + } + + private void processLDU( LDUMessage ldu ) + { + State state = ( ldu.isValid() && ldu.isEncrypted() ) ? + State.ENCRYPTED : State.CALL; + + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, state ) ); + + if( ldu instanceof LDU1Message ) + { + switch( ((LDU1Message)ldu).getOpcode() ) + { + case ADJACENT_SITE_STATUS_BROADCAST: + if( ldu instanceof module.decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcast ) + { + module.decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcast assb = + (module.decode.p25.message.ldu.lc.AdjacentSiteStatusBroadcast)ldu; + + updateSystem( assb.getSystemID() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + + break; + case ADJACENT_SITE_STATUS_BROADCAST_EXPLICIT: + /* This message doesn't provide anything we need for channel state */ + break; + case CALL_ALERT: + if( ldu instanceof module.decode.p25.message.ldu.lc.CallAlert ) + { + module.decode.p25.message.ldu.lc.CallAlert ca = + (module.decode.p25.message.ldu.lc.CallAlert)ldu; + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .from( ca.getSourceAddress() ) + .to( ca.getTargetAddress() ) + .details( "CALL ALERT" ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case CALL_TERMINATION_OR_CANCELLATION: + broadcast( new DecoderStateEvent( this, Event.END, State.END ) ); + mCurrentCallEvent = null; + + if( !( ldu instanceof CallTermination ) ) + { + logAlternateVendorMessage( ldu ); + } + break; + case CHANNEL_IDENTIFIER_UPDATE: + //TODO: do we need this for the activity summary? + + /* This message is handled by the P25MessageProcessor and + * inserted into any channels needing frequency band info */ + break; + case CHANNEL_IDENTIFIER_UPDATE_EXPLICIT: + //TODO: do we need this for the activity summary? + + /* This message is handled by the P25MessageProcessor and + * inserted into any channels needing frequency band info */ + break; + case EXTENDED_FUNCTION_COMMAND: + if( ldu instanceof module.decode.p25.message.ldu.lc.ExtendedFunctionCommand ) + { + module.decode.p25.message.ldu.lc.ExtendedFunctionCommand efc = + (module.decode.p25.message.ldu.lc.ExtendedFunctionCommand)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .to( efc.getTargetAddress() ) + .details( "FUNCTION:" + efc.getExtendedFunction().getLabel() + + " ARG:" + efc.getArgument() ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case GROUP_AFFILIATION_QUERY: + if( mChannelType == ChannelType.STANDARD && + ldu instanceof module.decode.p25.message.ldu.lc.GroupAffiliationQuery ) + { + module.decode.p25.message.ldu.lc.GroupAffiliationQuery gaq = + (module.decode.p25.message.ldu.lc.GroupAffiliationQuery)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .details( "GROUP AFFILIATION QUERY" ) + .from( gaq.getSourceAddress() ) + .to( gaq.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case GROUP_VOICE_CHANNEL_UPDATE: + if( mChannelType == ChannelType.STANDARD && + ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate ) + { + module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate gvcu = + (module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( gvcu.getChannelA() ) + .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) + .frequency( gvcu.getDownlinkFrequencyA() ) + .to( gvcu.getGroupAddressA() ) + .build() ); + + String userB = gvcu.getGroupAddressB(); + + if( userB != null && !userB.contentEquals( "0000" ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( gvcu.getChannelB() ) + .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) + .frequency( gvcu.getDownlinkFrequencyB() ) + .to( userB ) + .build() ); + } + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: + if( mChannelType == ChannelType.STANDARD && + ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit ) + { + module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit gvcue = + (module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( gvcue.getTransmitChannel() ) + .details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) ) + .frequency( gvcue.getDownlinkFrequency() ) + .to( gvcue.getGroupAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case GROUP_VOICE_CHANNEL_USER: + /* Indicates the current user of the current channel */ + if( mChannelType == ChannelType.STANDARD && + ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUser ) + { + module.decode.p25.message.ldu.lc.GroupVoiceChannelUser gvcuser = + (module.decode.p25.message.ldu.lc.GroupVoiceChannelUser)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "Trigger:LDU-GROUP_VOICE_CHANNEL_USER " + + ( gvcuser.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcuser.isEmergency() ? " EMERGENCY" : "") ) + .frequency( mCurrentChannelFrequency ) + .from( gvcuser.getSourceAddress() ) + .to( gvcuser.getGroupAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case MESSAGE_UPDATE: + if( ldu instanceof module.decode.p25.message.ldu.lc.MessageUpdate ) + { + module.decode.p25.message.ldu.lc.MessageUpdate mu = + (module.decode.p25.message.ldu.lc.MessageUpdate)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.SDM ) + .aliasList( getAliasList() ) + .from( mu.getSourceAddress() ) + .to( mu.getTargetAddress() ) + .details( "MSG: " + mu.getShortDataMessage() ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case NETWORK_STATUS_BROADCAST: + if( ldu instanceof module.decode.p25.message.ldu.lc.NetworkStatusBroadcast ) + { + updateSystem( ((module.decode.p25.message.ldu.lc.NetworkStatusBroadcast)ldu).getSystem() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case NETWORK_STATUS_BROADCAST_EXPLICIT: + if( ldu instanceof module.decode.p25.message.ldu.lc.NetworkStatusBroadcastExplicit ) + { + updateSystem( ((module.decode.p25.message.ldu.lc.NetworkStatusBroadcastExplicit)ldu).getSystem() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case PROTECTION_PARAMETER_BROADCAST: + if( ldu instanceof module.decode.p25.message.ldu.lc.ProtectionParameterBroadcast ) + { + module.decode.p25.message.ldu.lc.ProtectionParameterBroadcast ppb = + (module.decode.p25.message.ldu.lc.ProtectionParameterBroadcast)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .to( ppb.getTargetAddress() ) + .details( "ENCRYPTION: " + + ppb.getEncryption().name() + " KEY:" + + ppb.getEncryptionKey() ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case RFSS_STATUS_BROADCAST: + if( ldu instanceof module.decode.p25.message.ldu.lc.RFSSStatusBroadcast ) + { + module.decode.p25.message.ldu.lc.RFSSStatusBroadcast rfsssb = + (module.decode.p25.message.ldu.lc.RFSSStatusBroadcast)ldu; + + updateSystem( rfsssb.getSystem() ); + + String site = rfsssb.getRFSubsystemID() + "-" + + rfsssb.getSiteID(); + + updateSite( site ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case RFSS_STATUS_BROADCAST_EXPLICIT: + if( ldu instanceof module.decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit ) + { + module.decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit rfsssbe = + (module.decode.p25.message.ldu.lc.RFSSStatusBroadcastExplicit)ldu; + + String site = rfsssbe.getRFSubsystemID() + "-" + + rfsssbe.getSiteID(); + + updateSite( site ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case SECONDARY_CONTROL_CHANNEL_BROADCAST: + if( ldu instanceof module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast ) + { + module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast sccb = + (module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcast)ldu; + + String site = sccb.getRFSubsystemID() + "-" + + sccb.getSiteID(); + + updateSite( site ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case SECONDARY_CONTROL_CHANNEL_BROADCAST_EXPLICIT: + if( ldu instanceof module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit ) + { + module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit sccb = + (module.decode.p25.message.ldu.lc.SecondaryControlChannelBroadcastExplicit)ldu; + + String site = sccb.getRFSubsystemID() + "-" + + sccb.getSiteID(); + + updateSite( site ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case STATUS_QUERY: + if( ldu instanceof module.decode.p25.message.ldu.lc.StatusQuery ) + { + module.decode.p25.message.ldu.lc.StatusQuery sq = + (module.decode.p25.message.ldu.lc.StatusQuery)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .details( "STATUS QUERY" ) + .from( sq.getSourceAddress() ) + .to( sq.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case STATUS_UPDATE: + if( ldu instanceof module.decode.p25.message.ldu.lc.StatusUpdate ) + { + module.decode.p25.message.ldu.lc.StatusUpdate su = + (module.decode.p25.message.ldu.lc.StatusUpdate)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.STATUS ) + .aliasList( getAliasList() ) + .details( "STATUS UNIT:" + su.getUnitStatus() + + " USER:" + su.getUserStatus() ) + .from( su.getSourceAddress() ) + .to( su.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case SYSTEM_SERVICE_BROADCAST: + /* This message doesn't provide anything we need for channel state */ + break; + case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: + if( ldu instanceof module.decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest ) + { + module.decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest tiar = + (module.decode.p25.message.ldu.lc.TelephoneInterconnectAnswerRequest)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .from( tiar.getTelephoneNumber() ) + .to( tiar.getTargetAddress() ) + .details( "TELEPHONE CALL ALERT" ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_USER: + /* Indicates the current user of the current channel */ + if( mChannelType == ChannelType.STANDARD && + ldu instanceof TelephoneInterconnectVoiceChannelUser ) + { + TelephoneInterconnectVoiceChannelUser tivcu = + (TelephoneInterconnectVoiceChannelUser)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "Trigger: LDU-TEL_INT_VOIC_CHAN_USER " + + "TELEPHONE INTERCONNECT " + + ( tivcu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( tivcu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( mCurrentChannelFrequency ) + .from( null ) + .to( tivcu.getAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case UNIT_AUTHENTICATION_COMMAND: + if( ldu instanceof module.decode.p25.message.ldu.lc.UnitAuthenticationCommand ) + { + module.decode.p25.message.ldu.lc.UnitAuthenticationCommand uac = + (module.decode.p25.message.ldu.lc.UnitAuthenticationCommand)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .to( uac.getCompleteTargetAddress() ) + .details( "AUTHENTICATE" ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case UNIT_REGISTRATION_COMMAND: + if( ldu instanceof module.decode.p25.message.ldu.lc.UnitRegistrationCommand ) + { + module.decode.p25.message.ldu.lc.UnitRegistrationCommand urc = + (module.decode.p25.message.ldu.lc.UnitRegistrationCommand)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .to( urc.getCompleteTargetAddress() ) + .details( "REGISTER" ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case UNIT_TO_UNIT_ANSWER_REQUEST: + if( ldu instanceof module.decode.p25.message.ldu.lc.UnitToUnitAnswerRequest ) + { + module.decode.p25.message.ldu.lc.UnitToUnitAnswerRequest uuar = + (module.decode.p25.message.ldu.lc.UnitToUnitAnswerRequest)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .from( uuar.getSourceAddress() ) + .to( uuar.getTargetAddress() ) + .details( "UNIT TO UNIT CALL ALERT" ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + case UNIT_TO_UNIT_VOICE_CHANNEL_USER: + if( mChannelType == ChannelType.STANDARD && + ldu instanceof UnitToUnitVoiceChannelUser ) + { + UnitToUnitVoiceChannelUser uuvcu = + (UnitToUnitVoiceChannelUser)ldu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "Trigger:LDU-U2U_VOICE_CHAN_USER " + + ( uuvcu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( uuvcu.isEmergency() ? " EMERGENCY" : "" ) ) + .frequency( mCurrentChannelFrequency ) + .from( uuvcu.getSourceAddress() ) + .to( uuvcu.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( ldu ); + } + break; + default: + break; + } + } + } + + /** + * Process a Trunking Signalling Block message + */ + private void processTSBK( TSBKMessage tsbk ) + { + /* Trunking Signalling Block Messages - indicates Control Channel */ + broadcast( new DecoderStateEvent( this, Event.START, State.CONTROL ) ); + + if( tsbk.getVendor() == Vendor.STANDARD ) + { + switch( tsbk.getOpcode() ) + { + case ADJACENT_STATUS_BROADCAST: + AdjacentStatusBroadcast neighborMessage = + (AdjacentStatusBroadcast)tsbk; + + mNeighborMap.put( neighborMessage.getUniqueID(), neighborMessage ); + break; + case ACKNOWLEDGE_RESPONSE: + processTSBKResponse( tsbk ); + break; + case AUTHENTICATION_COMMAND: + processTSBKCommand( tsbk ); + break; + case CALL_ALERT: + processTSBKPage( tsbk ); + break; + case DENY_RESPONSE: + processTSBKResponse( tsbk ); + break; + case EXTENDED_FUNCTION_COMMAND: + processTSBKCommand( tsbk ); + break; + case GROUP_AFFILIATION_QUERY: + processTSBKQuery( tsbk ); + break; + case GROUP_AFFILIATION_RESPONSE: + processTSBKResponse( tsbk ); + break; + case GROUP_DATA_CHANNEL_ANNOUNCEMENT: + case GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: + processTSBKDataChannelAnnouncement( tsbk); + break; + case GROUP_DATA_CHANNEL_GRANT: + case GROUP_VOICE_CHANNEL_GRANT: + case GROUP_VOICE_CHANNEL_GRANT_UPDATE: + case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: + case INDIVIDUAL_DATA_CHANNEL_GRANT: + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: + processTSBKChannelGrant( tsbk ); + break; + case IDENTIFIER_UPDATE_NON_VUHF: + case IDENTIFIER_UPDATE_VHF_UHF_BANDS: + IdentifierUpdate iu = (IdentifierUpdate)tsbk; + + if( !mBands.containsKey( iu.getIdentifier() ) ) + { + mBands.put( iu.getIdentifier(), iu ); + } + break; + case LOCATION_REGISTRATION_RESPONSE: + case UNIT_DEREGISTRATION_ACKNOWLEDGE: + processTSBKResponse( tsbk ); + break; + case MESSAGE_UPDATE: + processTSBKMessage( tsbk ); + break; + case NETWORK_STATUS_BROADCAST: + mNetworkStatus = (module.decode.p25.message.tsbk.osp + .control.NetworkStatusBroadcast)tsbk; + break; + case PROTECTION_PARAMETER_UPDATE: + processTSBKResponse( tsbk ); + break; + case QUEUED_RESPONSE: + processTSBKResponse( tsbk ); + break; + case RADIO_UNIT_MONITOR_COMMAND: + processTSBKCommand( tsbk ); + break; + case RFSS_STATUS_BROADCAST: + processTSBKRFSSStatus( (RFSSStatusBroadcast)tsbk ); + break; + case ROAMING_ADDRESS_COMMAND: + processTSBKCommand( tsbk ); + break; + case SECONDARY_CONTROL_CHANNEL_BROADCAST: + module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast sccb = + (module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast)tsbk; + + if( sccb.getDownlinkFrequency1() > 0 ) + { + mSecondaryControlChannels.add( sccb ); + } + break; + case SNDCP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: + mSNDCPDataChannel = (SNDCPDataChannelAnnouncementExplicit)tsbk; + break; + case SNDCP_DATA_CHANNEL_GRANT: + processTSBKChannelGrant( tsbk ); + break; + case STATUS_QUERY: + processTSBKQuery( tsbk ); + break; + case STATUS_UPDATE: + processTSBKResponse( tsbk ); + break; + case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: + case UNIT_TO_UNIT_ANSWER_REQUEST: + processTSBKPage( tsbk ); + break; + case UNIT_REGISTRATION_COMMAND: + processTSBKCommand( tsbk ); + break; + case UNIT_REGISTRATION_RESPONSE: + processTSBKResponse( tsbk ); + break; + default: + break; + } + } + } + + /** + * Process a Packet Data Unit message + */ + private void processPDU( PDUMessage pdu ) + { + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.DATA ) ); + + if( pdu instanceof NetworkStatusBroadcastExtended ) + { + mNetworkStatusExtended = (NetworkStatusBroadcastExtended)pdu; + } + else if( pdu instanceof ProtectionParameterBroadcast ) + { + mProtectionParameterBroadcast = + (ProtectionParameterBroadcast)pdu; + } + else if( pdu instanceof RFSSStatusBroadcastExtended ) + { + mRFSSStatusMessageExtended = (RFSSStatusBroadcastExtended)pdu; + } + + if( pdu instanceof PDUConfirmedMessage ) + { + PDUConfirmedMessage pduc = (PDUConfirmedMessage)pdu; + + switch( pduc.getPDUType() ) + { + case SN_ACTIVATE_TDS_CONTEXT_ACCEPT: + SNDCPActivateTDSContextAccept satca = + (SNDCPActivateTDSContextAccept)pduc; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "ACTIVATE SNDCP USE IP:" + satca.getIPAddress() ) + .frequency( mCurrentChannelFrequency ) + .to( satca.getLogicalLinkID() ) + .build() ); + break; + case SN_ACTIVATE_TDS_CONTEXT_REJECT: + SNDCPActivateTDSContextReject satcr = + (SNDCPActivateTDSContextReject)pduc; + + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "REJECT: SNDCP CONTEXT ACTIVATION " + + "REASON:" + satcr.getReason().getLabel() ) + .frequency( mCurrentChannelFrequency ) + .to( satcr.getLogicalLinkID() ) + .build() ); + break; + case SN_ACTIVATE_TDS_CONTEXT_REQUEST: + SNDCPActivateTDSContextRequest satcreq = + (SNDCPActivateTDSContextRequest)pduc; + + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "REQUEST SNDCP USE IP:" + satcreq.getIPAddress() ) + .frequency( mCurrentChannelFrequency ) + .from( satcreq.getLogicalLinkID() ) + .build() ); + break; + case SN_DEACTIVATE_TDS_CONTEXT_ACCEPT: + SNDCPDeactivateTDSContext sdtca = + (SNDCPDeactivateTDSContext)pduc; + + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "ACCEPT DEACTIVATE SNDCP CONTEXT" ) + .frequency( mCurrentChannelFrequency ) + .from( sdtca.getLogicalLinkID() ) + .build() ); + break; + case SN_DEACTIVATE_TDS_CONTEXT_REQUEST: + SNDCPDeactivateTDSContext sdtcreq = + (SNDCPDeactivateTDSContext)pduc; + + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "REQUEST DEACTIVATE SNDCP CONTEXT" ) + .frequency( mCurrentChannelFrequency ) + .from( sdtcreq.getLogicalLinkID() ) + .build() ); + break; + case SN_RF_CONFIRMED_DATA: + SNDCPUserData sud = (SNDCPUserData)pduc; + + StringBuilder sbFrom = new StringBuilder(); + StringBuilder sbTo = new StringBuilder(); + + sbFrom.append( sud.getSourceIPAddress() ); + sbTo.append( sud.getDestinationIPAddress() ); + + if( sud.getIPProtocol() == IPProtocol.UDP ) + { + sbFrom.append( ":" ); + sbFrom.append( sud.getUDPSourcePort() ); + sbTo.append( ":" ); + sbTo.append( sud.getUDPDestinationPort() ); + } + + broadcast( new DecoderStateEvent( this, Event.START, State.DATA ) ); + + broadcast( new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( getAliasList() ) + .channel( mCurrentChannel ) + .details( "DATA: " + sud.getPayload() + + " RADIO IP:" + sbTo.toString() ) + .frequency( mCurrentChannelFrequency ) + .from( sbFrom.toString() ) + .to( pduc.getLogicalLinkID() ) + .build() ); + break; + default: + mLog.debug( "PDUC - Unrecognized Message: " + pduc.toString() ); + break; + } + } + else + { + switch( pdu.getOpcode() ) + { + case ADJACENT_STATUS_BROADCAST: + AdjacentStatusBroadcastExtended asbe = + (AdjacentStatusBroadcastExtended)pdu; + + updateSystem( asbe.getSystemID() ); + break; + case CALL_ALERT: + if( pdu instanceof CallAlertExtended ) + { + CallAlertExtended ca = (CallAlertExtended)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .from( ca.getWACN() + "-" + ca.getSystemID() + "-" + + ca.getSourceID() ) + .to( ca.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case GROUP_AFFILIATION_QUERY: + if( pdu instanceof GroupAffiliationQueryExtended ) + { + GroupAffiliationQueryExtended gaqe = + (GroupAffiliationQueryExtended)pdu; + + if( mLastQueryEventID == null || !gaqe.getTargetAddress() + .contentEquals( mLastQueryEventID ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .details( "GROUP AFFILIATION" ) + .from( gaqe.getWACN() + "-" + gaqe.getSystemID() + + "-" + gaqe.getSourceID() ) + .to( gaqe.getTargetAddress() ) + .build() ); + + mLastQueryEventID = gaqe.getToID(); + } + } + break; + case GROUP_AFFILIATION_RESPONSE: + if( pdu instanceof GroupAffiliationResponseExtended ) + { + GroupAffiliationResponseExtended gar = + (GroupAffiliationResponseExtended)pdu; + + if( mLastResponseEventID == null || !gar.getTargetAddress() + .contentEquals( mLastResponseEventID ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( getAliasList() ) + .details( "AFFILIATION:" + gar.getResponse().name() + + " FOR GROUP:" + gar.getGroupWACN() + "-" + + gar.getGroupSystemID() + "-" + + gar.getGroupID() + " ANNOUNCEMENT GROUP:" + + gar.getAnnouncementGroupID() ) + .from( gar.getSourceWACN() + "-" + + gar.getSourceSystemID() + "-" + + gar.getSourceID() ) + .to( gar.getTargetAddress() ) + .build() ); + + mLastResponseEventID = gar.getTargetAddress(); + } + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case GROUP_DATA_CHANNEL_GRANT: + if( pdu instanceof GroupDataChannelGrantExtended ) + { + GroupDataChannelGrantExtended gdcge = + (GroupDataChannelGrantExtended)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( getAliasList() ) + .channel( gdcge.getTransmitChannel() ) + .details( ( gdcge.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gdcge.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gdcge.getDownlinkFrequency() ) + .from( gdcge.getSourceAddress() ) + .to( gdcge.getGroupAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case GROUP_VOICE_CHANNEL_GRANT: + if( pdu instanceof GroupVoiceChannelGrantExplicit ) + { + GroupVoiceChannelGrantExplicit gvcge = + (GroupVoiceChannelGrantExplicit)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( gvcge.getTransmitChannel() ) + .details( "Trigger: PDU-GRP_V_CHAN_USER" + + ( gvcge.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcge.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcge.getDownlinkFrequency() ) + .from( gvcge.getSourceAddress() ) + .to( gvcge.getGroupAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case INDIVIDUAL_DATA_CHANNEL_GRANT: + if( pdu instanceof IndividualDataChannelGrantExtended ) + { + IndividualDataChannelGrantExtended idcge = + (IndividualDataChannelGrantExtended)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( getAliasList() ) + .channel( idcge.getTransmitChannel() ) + .details( "Trigger:PDU-IND_DATA_CHAN_GRNT " + + ( idcge.isEncrypted() ? "ENCRYPTED" : "" ) + + ( idcge.isEmergency() ? " EMERGENCY" : "") ) + .frequency( idcge.getDownlinkFrequency() ) + .from( idcge.getSourceWACN() + "-" + + idcge.getSourceSystemID() + "-" + + idcge.getSourceAddress() ) + .to( idcge.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case MESSAGE_UPDATE: + if( pdu instanceof MessageUpdateExtended ) + { + MessageUpdateExtended mu = (MessageUpdateExtended)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.SDM ) + .aliasList( getAliasList() ) + .details( "MESSAGE: " + mu.getMessage() ) + .from( mu.getSourceWACN() + "-" + mu.getSourceSystemID() + + "-" + mu.getSourceID() ) + .to( mu.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case RFSS_STATUS_BROADCAST: + if( pdu instanceof RFSSStatusBroadcastExtended ) + { + RFSSStatusBroadcastExtended rsbe = (RFSSStatusBroadcastExtended)pdu; + + updateNAC( rsbe.getNAC() ); + updateSystem( rsbe.getSystemID() ); + updateSite( rsbe.getRFSubsystemID() + "-" + rsbe.getSiteID() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case ROAMING_ADDRESS_UPDATE: + if( pdu instanceof RoamingAddressUpdateExtended ) + { + RoamingAddressUpdateExtended raue = + (RoamingAddressUpdateExtended)pdu; + + StringBuilder sb = new StringBuilder(); + sb.append( "ROAMING ADDRESS STACK A:" ); + sb.append( raue.getWACNA() + "-" + raue.getSystemIDA() ); + + if( raue.isFormat2() ) + { + sb.append( " B:" ); + sb.append( raue.getWACNB() + "-" + raue.getSystemIDB() ); + sb.append( " C:" ); + sb.append( raue.getWACNC() + "-" + raue.getSystemIDC() ); + sb.append( " D:" ); + sb.append( raue.getWACND() + "-" + raue.getSystemIDD() ); + } + + if( raue.isFormat3() ) + { + sb.append( " E:" ); + sb.append( raue.getWACNE() + "-" + raue.getSystemIDE() ); + sb.append( " F:" ); + sb.append( raue.getWACNF() + "-" + raue.getSystemIDF() ); + sb.append( " G:" ); + sb.append( raue.getWACNG() + "-" + raue.getSystemIDG() ); + } + + broadcast( new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( getAliasList() ) + .details( sb.toString() ) + .from( raue.getSourceID() ) + .to( raue.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case STATUS_QUERY: + if( pdu instanceof StatusQueryExtended ) + { + StatusQueryExtended sq = (StatusQueryExtended)pdu; + + if( mLastQueryEventID == null || !sq.getTargetAddress() + .contentEquals( mLastQueryEventID ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .details( "STATUS QUERY" ) + .from( sq.getSourceWACN() + "-" + + sq.getSourceSystemID() + "-" + + sq.getSourceID() ) + .to( sq.getTargetAddress() ) + .build() ); + + mLastQueryEventID = sq.getToID(); + } + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case STATUS_UPDATE: + if( pdu instanceof StatusUpdateExtended ) + { + StatusUpdateExtended su = (StatusUpdateExtended)pdu; + + if( mLastResponseEventID == null || !mLastResponseEventID + .contentEquals( su.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( getAliasList() ) + .details( "STATUS USER: " + su.getUserStatus() + + " UNIT: " + su.getUnitStatus() ) + .from( su.getSourceWACN() + "-" + + su.getSourceSystemID() + "-" + + su.getSourceID() ) + .to( su.getTargetAddress() ) + .build() ); + + mLastResponseEventID = su.getTargetAddress(); + } + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + TelephoneInterconnectChannelGrantExplicit ticge = + (TelephoneInterconnectChannelGrantExplicit)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( ticge.getTransmitChannel() ) + .details( "Trigger: PDU-TEL_INT_V_CH_GRNT " + + "TELEPHONE CALL " + + ( ticge.isEncrypted() ? "ENCRYPTED" : "" ) + + ( ticge.isEmergency() ? " EMERGENCY" : "") + + " CALL TIMER:" + ticge.getCallTimer() ) + .frequency( ticge.getDownlinkFrequency() ) + .from( ticge.getAddress() ) + .build() ); + break; + case UNIT_REGISTRATION_RESPONSE: + if( pdu instanceof UnitRegistrationResponseExtended ) + { + UnitRegistrationResponseExtended urr = + (UnitRegistrationResponseExtended)pdu; + + if( urr.getResponse() == Response.ACCEPT ) + { + mRegistrations.put( urr.getAssignedSourceAddress(), + System.currentTimeMillis() ); + } + + if( mLastRegistrationEventID == null || !mLastRegistrationEventID + .contentEquals( urr.getAssignedSourceAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.REGISTER ) + .aliasList( getAliasList() ) + .details( "REGISTRATION:" + urr.getResponse().name() + + " FOR EXTERNAL SYSTEM ADDRESS: " + + urr.getWACN() + "-" + + urr.getSystemID() + "-" + + urr.getSourceAddress() + + " SOURCE ID: " + urr.getSourceID() ) + .from( urr.getAssignedSourceAddress() ) + .build() ); + + mLastRegistrationEventID = urr.getAssignedSourceAddress(); + } + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case UNIT_TO_UNIT_ANSWER_REQUEST: + if( pdu instanceof UnitToUnitAnswerRequestExplicit ) + { + UnitToUnitAnswerRequestExplicit utuare = + (UnitToUnitAnswerRequestExplicit)pdu; + + if( mLastPageEventID == null || !mLastPageEventID + .contentEquals( utuare.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .details( ( utuare.isEmergency() ? "EMERGENCY" : "" ) ) + .from( utuare.getWACN() + "-" + + utuare.getSystemID() + "-" + + utuare.getSourceID() ) + .to( utuare.getTargetAddress() ) + .build() ); + + mLastPageEventID = utuare.getTargetAddress(); + } + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + if( pdu instanceof UnitToUnitVoiceChannelGrantExtended ) + { + UnitToUnitVoiceChannelGrantExtended uuvcge = + (UnitToUnitVoiceChannelGrantExtended)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( uuvcge.getTransmitChannel() ) + .details( "Trigger:PDU-U2U_V-CH_GRNT " + + ( uuvcge.isEncrypted() ? "ENCRYPTED" : "" ) + + ( uuvcge.isEmergency() ? " EMERGENCY" : "") ) + .frequency( uuvcge.getDownlinkFrequency() ) + .from( uuvcge.getSourceWACN() + "-" + + uuvcge.getSourceSystemID() + "-" + + uuvcge.getSourceID() ) + .to( uuvcge.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: + if( pdu instanceof UnitToUnitVoiceChannelGrantUpdateExtended ) + { + UnitToUnitVoiceChannelGrantUpdateExtended uuvcgue = + (UnitToUnitVoiceChannelGrantUpdateExtended)pdu; + + broadcast( new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( uuvcgue.getTransmitChannel() ) + .details( "Trigger: PDU-U2U_V_CH_G_U " + + ( uuvcgue.isEncrypted() ? "ENCRYPTED" : "" ) + + ( uuvcgue.isEmergency() ? " EMERGENCY" : "") ) + .frequency( uuvcgue.getDownlinkFrequency() ) + .from( uuvcgue.getSourceWACN() + "-" + + uuvcgue.getSourceSystemID() + "-" + + uuvcgue.getSourceID() ) + .to( uuvcgue.getTargetAddress() ) + .build() ); + } + else + { + logAlternateVendorMessage( pdu ); + } + break; + default: + break; + } + } + } + + private void processTSBKCommand( TSBKMessage message ) + { + switch( message.getOpcode() ) + { + case AUTHENTICATION_COMMAND: + AuthenticationCommand ac = (AuthenticationCommand)message; + + if( mLastCommandEventID == null || !mLastCommandEventID + .contentEquals( ac.getFullTargetID() )) + { + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .details( "AUTHENTICATE" ) + .to( ac.getWACN() + "-" + ac.getSystemID() + "-" + + ac.getTargetID() ) + .build() ); + + mLastCommandEventID = ac.getFullTargetID(); + } + break; + case EXTENDED_FUNCTION_COMMAND: + ExtendedFunctionCommand efc = (ExtendedFunctionCommand)message; + + if( mLastCommandEventID == null || !mLastCommandEventID + .contentEquals( efc.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.FUNCTION ) + .aliasList( getAliasList() ) + .details( "EXTENDED FUNCTION: " + + efc.getExtendedFunction().getLabel() ) + .from( efc.getSourceAddress() ) + .to( efc.getTargetAddress() ) + .build() ); + + mLastCommandEventID = efc.getTargetAddress(); + } + break; + case RADIO_UNIT_MONITOR_COMMAND: + RadioUnitMonitorCommand rumc = (RadioUnitMonitorCommand)message; + + if( mLastCommandEventID == null || !mLastCommandEventID + .contentEquals( rumc.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .details( "RADIO UNIT MONITOR" ) + .from( rumc.getSourceAddress() ) + .to( rumc.getTargetAddress() ) + .build() ); + + mLastCommandEventID = rumc.getTargetAddress(); + } + break; + case ROAMING_ADDRESS_COMMAND: + RoamingAddressCommand rac = (RoamingAddressCommand)message; + + if( mLastCommandEventID == null || !mLastCommandEventID + .contentEquals( rac.getTargetID() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .details( rac.getStackOperation().name() + + " ROAMING ADDRESS " + rac.getWACN() + "-" + + rac.getSystemID() ) + .to( rac.getTargetID() ) + .build() ); + + mLastCommandEventID = rac.getTargetID(); + } + break; + case UNIT_REGISTRATION_COMMAND: + UnitRegistrationCommand urc = (UnitRegistrationCommand)message; + + if( mLastCommandEventID == null || !mLastCommandEventID + .contentEquals( urc.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.COMMAND ) + .aliasList( getAliasList() ) + .details( "REGISTER" ) + .from( urc.getSourceAddress() ) + .to( urc.getTargetAddress() ) + .build() ); + + mLastCommandEventID = urc.getTargetAddress(); + } + break; + default: + break; + } + } + + private void processTSBKMessage( TSBKMessage message ) + { + MessageUpdate mu = (MessageUpdate)message; + + broadcast( new P25CallEvent.Builder( CallEventType.SDM ) + .aliasList( getAliasList() ) + .details( "MESSAGE: " + mu.getMessage() ) + .from( mu.getSourceAddress() ) + .to( mu.getTargetAddress() ) + .build() ); + } + + private void processTSBKQuery( TSBKMessage message ) + { + switch( message.getOpcode() ) + { + case GROUP_AFFILIATION_QUERY: + GroupAffiliationQuery gaq = (GroupAffiliationQuery)message; + + if( mLastQueryEventID == null || !mLastQueryEventID + .contentEquals( gaq.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .details( "GROUP AFFILIATION" ) + .from( gaq.getSourceAddress() ) + .to( gaq.getTargetAddress() ) + .build() ); + } + break; + case STATUS_QUERY: + StatusQuery sq = (StatusQuery)message; + + if( mLastQueryEventID == null || !mLastQueryEventID + .contentEquals( sq.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.QUERY ) + .aliasList( getAliasList() ) + .details( "STATUS QUERY" ) + .from( sq.getSourceAddress() ) + .to( sq.getTargetAddress() ) + .build() ); + } + break; + default: + break; + } + } + + private void processTSBKResponse( TSBKMessage message ) + { + CallEvent event = null; + + switch( message.getOpcode() ) + { + case ACKNOWLEDGE_RESPONSE: + AcknowledgeResponse ar = (AcknowledgeResponse)message; + + if( mLastResponseEventID == null || !ar.getTargetAddress() + .contentEquals( mLastResponseEventID ) ) + { + String to = ar.getTargetAddress(); + + if( ar.hasAdditionalInformation() && ar.hasExtendedAddress() ) + { + to = ar.getWACN() + "-" + ar.getSystemID() + "-" + + ar.getTargetAddress(); + } + + String from = null; + + if( ar.hasAdditionalInformation() && !ar.hasExtendedAddress() ) + { + from = ar.getFromID(); + } + + broadcast( new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( getAliasList() ) + .details( "ACKNOWLEDGE" ) + .from( from ) + .to( to ) + .build() ); + + mLastResponseEventID = ar.getTargetAddress(); + } + break; + case DENY_RESPONSE: + DenyResponse dr = (DenyResponse)message; + + if( mLastResponseEventID == null || !dr.getTargetAddress() + .contentEquals( mLastResponseEventID ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( getAliasList() ) + .details( "DENY REASON: " + dr.getReason().name() + + " REQUESTED: " + dr.getServiceType().name() ) + .from( dr.getSourceAddress() ) + .to( dr.getTargetAddress() ) + .build() ); + + mLastResponseEventID = dr.getTargetAddress(); + } + break; + case GROUP_AFFILIATION_RESPONSE: + GroupAffiliationResponse gar = (GroupAffiliationResponse)message; + + if( mLastResponseEventID == null || !gar.getTargetAddress() + .contentEquals( mLastResponseEventID ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( getAliasList() ) + .details( "AFFILIATION:" + gar.getResponse().name() + + " FOR " + gar.getAffiliationScope() + + " GROUP:" + gar.getGroupAddress() + + " ANNOUNCEMENT GROUP:" + + gar.getAnnouncementGroupAddress() ) + .to( gar.getTargetAddress() ) + .build() ); + + mLastResponseEventID = gar.getTargetAddress(); + } + break; + case LOCATION_REGISTRATION_RESPONSE: + LocationRegistrationResponse lrr = + (LocationRegistrationResponse)message; + + if( lrr.getResponse() == Response.ACCEPT ) + { + mRegistrations.put( lrr.getTargetAddress(), + System.currentTimeMillis() ); + } + + if( mLastRegistrationEventID == null || + !mLastRegistrationEventID.contentEquals( lrr.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.REGISTER ) + .aliasList( getAliasList() ) + .details( "REGISTRATION:" + lrr.getResponse().name() + + " SITE: " + lrr.getRFSSID() + "-" + + lrr.getSiteID() ) + .from( lrr.getTargetAddress() ) + .to( lrr.getGroupAddress() ) + .build() ); + + mLastRegistrationEventID = lrr.getTargetAddress(); + } + break; + case PROTECTION_PARAMETER_UPDATE: + ProtectionParameterUpdate ppu = (ProtectionParameterUpdate)message; + + if( mLastResponseEventID == null || !ppu.getTargetAddress() + .contentEquals( mLastResponseEventID ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( getAliasList() ) + .details( "USE ENCRYPTION ALGORITHM:" + + ppu.getAlgorithm().name() + " KEY:" + + ppu.getKeyID() ) + .to( ppu.getTargetAddress() ) + .build() ); + + mLastResponseEventID = ppu.getTargetAddress(); + } + break; + case QUEUED_RESPONSE: + QueuedResponse qr = (QueuedResponse)message; + + if( mLastResponseEventID == null || !qr.getTargetAddress() + .contentEquals( mLastResponseEventID ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( getAliasList() ) + .details( "QUEUED REASON: " + qr.getReason().name() + + " REQUESTED: " + qr.getServiceType().name() ) + .from( qr.getSourceAddress() ) + .to( qr.getTargetAddress() ) + .build() ); + + mLastResponseEventID = qr.getTargetAddress(); + } + break; + case STATUS_UPDATE: + StatusUpdate su = (StatusUpdate)message; + + if( mLastResponseEventID == null || !su.getTargetAddress() + .contentEquals( mLastResponseEventID ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.RESPONSE ) + .aliasList( getAliasList() ) + .details( "STATUS USER: " + su.getUserStatus() + + " UNIT: " + su.getUnitStatus() ) + .from( su.getSourceAddress() ) + .to( su.getTargetAddress() ) + .build() ); + + mLastResponseEventID = su.getTargetAddress(); + } + + break; + case UNIT_REGISTRATION_RESPONSE: + UnitRegistrationResponse urr = (UnitRegistrationResponse)message; + + if( urr.getResponse() == Response.ACCEPT ) + { + mRegistrations.put( urr.getSourceAddress(), + System.currentTimeMillis() ); + } + + if( mLastRegistrationEventID == null || + !mLastRegistrationEventID.contentEquals( urr.getSourceAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.REGISTER ) + .aliasList( getAliasList() ) + .details( "REGISTRATION:" + urr.getResponse().name() + + " SYSTEM: " + urr.getSystemID() + + " SOURCE ID: " + urr.getSourceID() ) + .from( urr.getSourceAddress() ) + .build() ); + + mLastRegistrationEventID = urr.getSourceAddress(); + } + break; + case UNIT_DEREGISTRATION_ACKNOWLEDGE: + UnitDeregistrationAcknowledge udr = + (UnitDeregistrationAcknowledge)message; + + if( mLastRegistrationEventID == null || + !mLastRegistrationEventID.contentEquals( udr.getSourceID() ) ) + { + + broadcast( new P25CallEvent.Builder( CallEventType.DEREGISTER ) + .aliasList( getAliasList() ) + .from( udr.getSourceID() ) + .build() ); + + List keysToRemove = new ArrayList(); + + /* Remove this radio from the registrations set */ + for( String key: mRegistrations.keySet() ) + { + if( key.startsWith( udr.getSourceID() ) ) + { + keysToRemove.add( key ); + } + } + + for( String key: keysToRemove ) + { + mRegistrations.remove( key ); + } + + mLastRegistrationEventID = udr.getSourceID(); + } + break; + default: + break; + } + } + + private void processTSBKRFSSStatus( RFSSStatusBroadcast message ) + { + mRFSSStatusMessage = message; + + updateNAC( message.getNAC() ); + + updateSystem( message.getSystemID() ); + + updateSite( message.getRFSubsystemID() + "-" + message.getSiteID() ); + } + + private void processTSBKDataChannelAnnouncement( TSBKMessage message ) + { + switch( message.getOpcode() ) + { + case GROUP_DATA_CHANNEL_ANNOUNCEMENT: + GroupDataChannelAnnouncement gdca = (GroupDataChannelAnnouncement)message; + + broadcast( new P25CallEvent.Builder( CallEventType.ANNOUNCEMENT ) + .aliasList( getAliasList() ) + .channel( gdca.getChannel1() ) + .details( ( gdca.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gdca.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gdca.getDownlinkFrequency1() ) + .to( gdca.getGroupAddress1() ) + .build() ); + + if( gdca.hasChannelNumber2() ) + { + broadcast( new P25CallEvent.Builder( CallEventType.ANNOUNCEMENT ) + .aliasList( getAliasList() ) + .channel( gdca.getChannel2() ) + .details( ( gdca.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gdca.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gdca.getDownlinkFrequency2() ) + .to( gdca.getGroupAddress2() ) + .build() ); + } + break; + case GROUP_DATA_CHANNEL_ANNOUNCEMENT_EXPLICIT: + GroupDataChannelAnnouncementExplicit gdcae = + (GroupDataChannelAnnouncementExplicit)message; + + broadcast( new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( getAliasList() ) + .channel( gdcae.getTransmitChannel() ) + .details( ( gdcae.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gdcae.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gdcae.getDownlinkFrequency() ) + .to( gdcae.getGroupAddress() ) + .build() ); + break; + } + } + + /** + * Process a traffic channel allocation message + */ + private void processTSBKChannelGrant( TSBKMessage message ) + { + P25CallEvent event = null; + + switch( message.getOpcode() ) + { + case GROUP_DATA_CHANNEL_GRANT: + GroupDataChannelGrant gdcg = (GroupDataChannelGrant)message; + + event = new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( getAliasList() ) + .channel( gdcg.getChannel() ) + .details( ( gdcg.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gdcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gdcg.getDownlinkFrequency() ) + .from( gdcg.getSourceAddress() ) + .to( gdcg.getGroupAddress() ) + .build(); + break; + case GROUP_VOICE_CHANNEL_GRANT: + GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)message; + + event = new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( gvcg.getChannel() ) + .details( "Trigger: TSBK-G_V_CH_GRNT " + + ( gvcg.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcg.getDownlinkFrequency() ) + .from( gvcg.getSourceAddress() ) + .to( gvcg.getGroupAddress() ) + .build(); + break; + case GROUP_VOICE_CHANNEL_GRANT_UPDATE: + GroupVoiceChannelGrantUpdate gvcgu = + (GroupVoiceChannelGrantUpdate)message; + + event = new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( gvcgu.getChannel1() ) + .details( "Trigger: TSBK-G_V_CH_GRNT_UP " + + ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcgu.getDownlinkFrequency1() ) + .to( gvcgu.getGroupAddress1() ) + .build(); + + if( gvcgu.hasChannelNumber2() ) + { + /* We handle this channel grant independent of the first event */ + P25CallEvent event2 = new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( gvcgu.getChannel2() ) + .details( "Trigger: TSBK-G_V_CH_GRNT_UP#2 " + + ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcgu.getDownlinkFrequency2() ) + .to( gvcgu.getGroupAddress2() ) + .build(); + + broadcast( new TrafficChannelAllocationEvent( this, event2 ) ); + } + break; + case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT: + GroupVoiceChannelGrantUpdateExplicit gvcgue = + (GroupVoiceChannelGrantUpdateExplicit)message; + + event = new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( gvcgue.getTransmitChannelIdentifier() + "-" + + gvcgue.getTransmitChannelNumber() ) + .details( "Trigger: TSBK-G_V_CH_GRNT_U_E " + + ( gvcgue.isEncrypted() ? "ENCRYPTED" : "" ) + + ( gvcgue.isEmergency() ? " EMERGENCY" : "") ) + .frequency( gvcgue.getDownlinkFrequency() ) + .to( gvcgue.getGroupAddress() ) + .build(); + break; + case INDIVIDUAL_DATA_CHANNEL_GRANT: + IndividualDataChannelGrant idcg = (IndividualDataChannelGrant)message; + + event = new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( getAliasList() ) + .channel( idcg.getChannel() ) + .details( "Trigger: TSBK-IND_D_CH_GRNT " + + ( idcg.isEncrypted() ? "ENCRYPTED" : "" ) + + ( idcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( idcg.getDownlinkFrequency() ) + .from( idcg.getSourceAddress() ) + .to( idcg.getTargetAddress() ) + .build(); + break; + case SNDCP_DATA_CHANNEL_GRANT: + SNDCPDataChannelGrant sdcg = (SNDCPDataChannelGrant)message; + + event = new P25CallEvent.Builder( CallEventType.DATA_CALL ) + .aliasList( getAliasList() ) + .channel( sdcg.getTransmitChannel() ) + .details( "Trigger: TSBK-SN_D_CH_GRNT " + + "SNDCP DATA NSAPI:" + sdcg.getNSAPI() ) + .frequency( sdcg.getDownlinkFrequency() ) + .to( sdcg.getTargetAddress() ) + .build(); + break; + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT: + TelephoneInterconnectVoiceChannelGrant tivcg = + (TelephoneInterconnectVoiceChannelGrant)message; + + event = new P25CallEvent.Builder( CallEventType.TELEPHONE_CALL ) + .aliasList( getAliasList() ) + .channel( tivcg.getChannel() ) + .details( "Trigger: TSBK-T_INT_V_CH_GRNT " + + ( tivcg.isEncrypted() ? "ENCRYPTED" : "" ) + + ( tivcg.isEmergency() ? " EMERGENCY" : "") + + " CALL TIMER:" + tivcg.getCallTimer() ) + .frequency( tivcg.getDownlinkFrequency() ) + .from( tivcg.getAddress() ) + .build(); + break; + case TELEPHONE_INTERCONNECT_VOICE_CHANNEL_GRANT_UPDATE: + TelephoneInterconnectVoiceChannelGrantUpdate tivcgu = + (TelephoneInterconnectVoiceChannelGrantUpdate)message; + + event = new P25CallEvent.Builder( CallEventType.TELEPHONE_CALL ) + .aliasList( getAliasList() ) + .channel( tivcgu.getChannelIdentifier() + "-" + + tivcgu.getChannelNumber() ) + .details( "Trigger: TSBK-T_INT_V_CH_G_UP " + + ( tivcgu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( tivcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( tivcgu.getDownlinkFrequency() ) + .from( tivcgu.getAddress() ) + .build(); + break; + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT: + UnitToUnitVoiceChannelGrant uuvcg = + (UnitToUnitVoiceChannelGrant)message; + + event = new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( uuvcg.getChannelIdentifier() + "-" + + uuvcg.getChannelNumber() ) + .details( "Trigger: TSBK-U2U_V_CH_GRNT " + + ( uuvcg.isEncrypted() ? "ENCRYPTED" : "" ) + + ( uuvcg.isEmergency() ? " EMERGENCY" : "") ) + .frequency( uuvcg.getDownlinkFrequency() ) + .from( uuvcg.getSourceAddress() ) + .to( uuvcg.getTargetAddress() ) + .build(); + break; + case UNIT_TO_UNIT_VOICE_CHANNEL_GRANT_UPDATE: + UnitToUnitVoiceChannelGrantUpdate uuvcgu = + (UnitToUnitVoiceChannelGrantUpdate)message; + + event = new P25CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( uuvcgu.getChannelIdentifier() + "-" + + uuvcgu.getChannelNumber() ) + .details( "Trigger: TSBK-U2U_V_CH_G_UP " + + ( uuvcgu.isEncrypted() ? "ENCRYPTED" : "" ) + + ( uuvcgu.isEmergency() ? " EMERGENCY" : "") ) + .frequency( uuvcgu.getDownlinkFrequency() ) + .from( uuvcgu.getSourceAddress() ) + .to( uuvcgu.getTargetAddress() ) + .build(); + break; + default: + break; + } + + if( event != null ) + { + broadcast( new TrafficChannelAllocationEvent( this, event ) ); + } + } + + /** + * Broadcasts an update for the TO group or unit address + */ + private void updateTo( String to ) + { + if( to != null && + !to.contentEquals( "0000" ) && + !to.contentEquals( "000000" ) && + ( mToTalkgroup == null || !mToTalkgroup.contentEquals( to ) ) ) + { + mToTalkgroup = to; + broadcast( ChangedAttribute.TO_TALKGROUP ); + + if( hasAliasList() ) + { + mToAlias = getAliasList().getTalkgroupAlias( to ); + } + else + { + mToAlias = null; + } + + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + } + } + + /** + * Broadcasts an update for the FROM group or unit address + */ + private void updateFrom( String from ) + { + if( from != null && + !from.contentEquals( "0000" ) && + !from.contentEquals( "000000" ) && + ( mFromTalkgroup == null || !mFromTalkgroup.contentEquals( from ) ) ) + { + mFromTalkgroup = from; + + broadcast( ChangedAttribute.FROM_TALKGROUP ); + + if( hasAliasList() ) + { + mFromAlias = getAliasList().getTalkgroupAlias( from ); + } + else + { + mFromAlias = null; + } + + broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + } + } + + /** + * Broadcasts an update to the NAC + */ + private void updateNAC( String nac ) + { + if( mNAC == null || ( nac != null && !mNAC.contentEquals( nac ) ) ) + { + mNAC = nac; + + broadcast( ChangedAttribute.NAC ); + } + } + + /** + * Updates the site information + */ + private void updateSite( String site ) + { + if( mSite == null || ( site != null && !mSite.contentEquals( site ) ) ) + { + mSite = site; + + broadcast( ChangedAttribute.SITE ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getSiteID( mSite ); + + if( alias != null ) + { + mSiteAlias = alias.getName(); + } + else + { + mSiteAlias = null; + } + } + + broadcast( ChangedAttribute.SITE_ALIAS ); + } + } + + private void updateSystem( String system ) + { + if( mSystem == null || ( system != null && !mSystem.contentEquals( system ) ) ) + { + mSystem = system; + + broadcast( ChangedAttribute.SYSTEM ); + } + } + + /** + * Process a unit paging event message + */ + private void processTSBKPage( TSBKMessage message ) + { + switch( message.getOpcode() ) + { + case CALL_ALERT: + CallAlert ca = (CallAlert)message; + + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .from( ca.getSourceID() ) + .to( ca.getTargetAddress() ) + .build() ); + break; + case UNIT_TO_UNIT_ANSWER_REQUEST: + UnitToUnitAnswerRequest utuar = (UnitToUnitAnswerRequest)message; + + if( mLastPageEventID == null || !mLastPageEventID + .contentEquals( utuar.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .details( ( utuar.isEmergency() ? "EMERGENCY" : "" ) ) + .from( utuar.getSourceAddress() ) + .to( utuar.getTargetAddress() ) + .build() ); + + mLastPageEventID = utuar.getTargetAddress(); + } + break; + case SNDCP_DATA_PAGE_REQUEST: + SNDCPDataPageRequest sdpr = (SNDCPDataPageRequest)message; + + if( mLastPageEventID == null || !mLastPageEventID + .contentEquals( sdpr.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .details( "SNDCP DATA DAC: " + + sdpr.getDataAccessControl() + + " NSAPI:" + sdpr.getNSAPI() ) + .to( sdpr.getTargetAddress() ) + .build() ); + + mLastPageEventID = sdpr.getTargetAddress(); + } + break; + case TELEPHONE_INTERCONNECT_ANSWER_REQUEST: + TelephoneInterconnectAnswerRequest tiar = + (TelephoneInterconnectAnswerRequest)message; + + if( mLastPageEventID == null || !mLastPageEventID + .contentEquals( tiar.getTargetAddress() ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.PAGE ) + .aliasList( getAliasList() ) + .details( ( "TELEPHONE INTERCONNECT" ) ) + .from( tiar.getTelephoneNumber() ) + .to( tiar.getTargetAddress() ) + .build() ); + + mLastPageEventID = tiar.getTargetAddress(); + } + break; + default: + break; + } + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "Activity Summary\n" ); + sb.append( "Decoder:\tP25\n" ); + sb.append( "===================== THIS SITE ======================" ); + + if( mNetworkStatus != null ) + { + sb.append( "\nNAC:\t" + mNetworkStatus.getNAC() ); + sb.append( "\nWACN-SYS:\t" + mNetworkStatus.getWACN() ); + sb.append( "-" + mNetworkStatus.getSystemID() ); + sb.append( " [" ); + sb.append( mNetworkStatus.getNetworkCallsign() ); + sb.append( "]" ); + sb.append( "\nLRA:\t" + mNetworkStatus.getLocationRegistrationArea() ); + } + else if( mNetworkStatusExtended != null ) + { + sb.append( "\nNAC:\t" + mNetworkStatusExtended.getNAC() ); + sb.append( "\nWACN-SYS:\t" + mNetworkStatusExtended.getWACN() ); + sb.append( "-" + mNetworkStatusExtended.getSystemID() ); + sb.append( " [" ); + sb.append( mNetworkStatusExtended.getNetworkCallsign() ); + sb.append( "]" ); + sb.append( "\nLRA:\t" + mNetworkStatusExtended.getLocationRegistrationArea() ); + } + + String site = null; + + if( mRFSSStatusMessage != null ) + { + site = mRFSSStatusMessage.getRFSubsystemID() + "-" + mRFSSStatusMessage.getSiteID(); + } + else if( mRFSSStatusMessageExtended != null ) + { + site = mRFSSStatusMessageExtended.getRFSubsystemID() + "-" + + mRFSSStatusMessageExtended.getSiteID(); + } + + sb.append( "\nRFSS-SITE:\t" + site ); + + if( hasAliasList() ) + { + Alias siteAlias = getAliasList().getSiteID( site ); + + if( siteAlias != null ) + { + sb.append( " " + siteAlias.getName() ); + } + } + + if( mNetworkStatus != null ) + { + sb.append( "\nSERVICES:\t" + SystemService.toString( + mNetworkStatus.getSystemServiceClass() ) ); + sb.append( "\nPCCH:\tDNLINK " + mFrequencyFormatter.format( + (double)mNetworkStatus.getDownlinkFrequency() / 1E6d ) + + " [" + mNetworkStatus.getIdentifier() + "-" + + mNetworkStatus.getChannel() + "]\n" ); + sb.append( "\tUPLINK " + mFrequencyFormatter.format( + (double)mNetworkStatus.getUplinkFrequency() / 1E6d ) + " [" + + mNetworkStatus.getIdentifier() + "-" + + mNetworkStatus.getChannel() + "]\n" ); + } + else if( mNetworkStatusExtended != null ) + { + sb.append( "\nSERVICES:\t" + SystemService.toString( + mNetworkStatusExtended.getSystemServiceClass() ) ); + sb.append( "\nPCCH:\tDNLINK " + mFrequencyFormatter.format( + (double)mNetworkStatusExtended.getDownlinkFrequency() / 1E6d ) + + " [" + mNetworkStatusExtended.getTransmitIdentifier() + "-" + + mNetworkStatusExtended.getTransmitChannel() + "]\n" ); + sb.append( "\tUPLINK " + mFrequencyFormatter.format( + (double)mNetworkStatusExtended.getUplinkFrequency() / 1E6d ) + + " [" + mNetworkStatusExtended.getReceiveIdentifier() + "-" + + mNetworkStatusExtended.getReceiveChannel() + "]" ); + } + + if( mSecondaryControlChannels.isEmpty() ) + { + sb.append( "\nSCCH:\tNONE" ); + } + else + { + for( module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast + sec: mSecondaryControlChannels ) + { + sb.append( "\nSCCH:\tDNLINK " + mFrequencyFormatter.format( + (double)sec.getDownlinkFrequency1() / 1E6d ) + + " [" + sec.getIdentifier1() + "-" + sec.getChannel1() + "]\n" ); + sb.append( "\tUPLINK " + mFrequencyFormatter.format( + (double)sec.getUplinkFrequency1() / 1E6d ) + " [" + + sec.getIdentifier1() + "-" + sec.getChannel1() + "]\n" ); + + if( sec.hasChannel2() ) + { + sb.append( "\nSCCH:\tDNLINK " + mFrequencyFormatter.format( + (double)sec.getDownlinkFrequency2() / 1E6d ) + + " [" + sec.getIdentifier2() + "-" + sec.getChannel2() + "]\n" ); + sb.append( "\tUPLINK " + mFrequencyFormatter.format( + (double)sec.getUplinkFrequency2() / 1E6d ) + " [" + + sec.getIdentifier2() + "-" + sec.getChannel2() + "]" ); + } + } + } + + if( mSNDCPDataChannel != null ) + { + sb.append( "\nSNDCP:" ); + sb.append( "\tDNLINK " + mFrequencyFormatter.format( + (double)mSNDCPDataChannel.getDownlinkFrequency()/1E6D ) + + " [" + mSNDCPDataChannel.getTransmitChannel() + "]" ); + sb.append( "\tUPLINK " + mFrequencyFormatter.format( + (double)mSNDCPDataChannel.getUplinkFrequency()/1E6D ) + + " [" + mSNDCPDataChannel.getReceiveChannel() + "]" ); + } + + if( mProtectionParameterBroadcast != null ) + { + sb.append( "\nENCRYPTION:" ); + sb.append( "\nTYPE:\t" + mProtectionParameterBroadcast + .getEncryptionType().name() ); + sb.append( "\nALGORITHM:\t" + mProtectionParameterBroadcast + .getAlgorithmID() ); + sb.append( "\nKEY:\t" + mProtectionParameterBroadcast.getKeyID() ); + sb.append( "\nINBOUND IV:\t" + mProtectionParameterBroadcast + .getInboundInitializationVector() ); + sb.append( "\nOUTBOUND IV:\t" + mProtectionParameterBroadcast + .getOutboundInitializationVector() ); + } + + List identifiers = new ArrayList<>( mBands.keySet() ); + + Collections.sort( identifiers ); + + sb.append( "\nFREQUENCY BANDS:" ); + for( Integer id: identifiers ) + { + IBandIdentifier band = mBands.get( id ); + + sb.append( "\n\t" + id ); + sb.append( "- BASE: " + mFrequencyFormatter.format( + (double)band.getBaseFrequency() / 1E6d ) ); + sb.append( " CHANNEL SIZE: " + mFrequencyFormatter.format( + (double)band.getChannelSpacing() / 1E6d ) ); + sb.append( " UPLINK OFFSET: " + mFrequencyFormatter.format( + (double)band.getTransmitOffset() / 1E6D ) ); + } + + sb.append( "\n\n=================== NEIGHBORS ======================" ); + + if( mNeighborMap.isEmpty() ) + { + sb.append( "\n\tNONE\n" ); + sb.append( "\n----------------------------------------------------" ); + } + else + { + for( AdjacentStatusBroadcast neighbor: mNeighborMap.values() ) + { + sb.append( "\nNAC:\t" + neighbor.getNAC() ); + sb.append( "\nSYSTEM:\t" + neighbor.getSystemID() ); + sb.append( "\nLRA:\t" + neighbor.getLocationRegistrationArea() ); + + String neighborID = neighbor.getRFSS() + "-" + neighbor.getSiteID(); + sb.append( "\nRFSS-SITE:\t" + neighborID ); + + if( hasAliasList() ) + { + Alias siteAlias = getAliasList().getSiteID( neighborID ); + + if( siteAlias != null ) + { + sb.append( " " + siteAlias.getName() ); + } + } + + sb.append( "\nPCCH:\tDNLINK " + mFrequencyFormatter.format( + (double)neighbor.getDownlinkFrequency() / 1E6d ) + + " [" + neighbor.getIdentifier() + "-" + neighbor.getChannel() + "]" ); + sb.append( "\n\tUPLINK:" + mFrequencyFormatter.format( + (double)neighbor.getUplinkFrequency() / 1E6d ) + "\n" ); + sb.append( "\nSERVICES:\t" + Service.getServices( neighbor.getSystemServiceClass() ) ); + sb.append( "\n----------------------------------------------------" ); + } + } + + return sb.toString(); + } + + public String getNAC() + { + return mNAC; + } + + public String getSystem() + { + return mSystem; + } + + public String getSiteAlias() + { + return mSiteAlias; + } + + public String getSite() + { + return mSite; + } + + public String getFromTalkgroup() + { + return mFromTalkgroup; + } + + public Alias getFromAlias() + { + return mFromAlias; + } + + public String getToTalkgroup() + { + return mToTalkgroup; + } + + public Alias getToAlias() + { + return mToAlias; + } + + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + switch( event.getEvent() ) + { + case RESET: + resetState(); + break; + case SOURCE_FREQUENCY: + mCurrentChannelFrequency = event.getFrequency(); + break; + case TRAFFIC_CHANNEL_ALLOCATION: + if( event.getSource() != P25DecoderState.this ) + { + if( event instanceof TrafficChannelAllocationEvent ) + { + TrafficChannelAllocationEvent allocationEvent = + (TrafficChannelAllocationEvent)event; + + mCurrentChannel = allocationEvent.getCallEvent().getChannel(); + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + + mCurrentChannelFrequency = allocationEvent.getCallEvent().getFrequency(); + broadcast( ChangedAttribute.SOURCE ); + + mFromTalkgroup = allocationEvent.getCallEvent().getFromID(); + broadcast( ChangedAttribute.FROM_TALKGROUP ); + + mToTalkgroup = allocationEvent.getCallEvent().getToID(); + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + } + break; + default: + break; + } + } +} diff --git a/src/decode/p25/P25Editor.java b/src/module/decode/p25/P25Editor.java similarity index 81% rename from src/decode/p25/P25Editor.java rename to src/module/decode/p25/P25Editor.java index f9e9f86e..347b235f 100644 --- a/src/decode/p25/P25Editor.java +++ b/src/module/decode/p25/P25Editor.java @@ -15,13 +15,18 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.p25; +package module.decode.p25; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; +import javax.swing.JLabel; + +import module.decode.DecodeEditor; +import module.decode.config.DecodeConfiguration; +import module.decode.p25.P25Decoder.Modulation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,10 +35,6 @@ import source.SourceEditor; import source.tuner.TunerEditor; import controller.Editor; import controller.channel.ChannelValidationException; -import decode.DecodeEditor; -import decode.config.DecodeConfigP25Phase1; -import decode.config.DecodeConfiguration; -import decode.p25.P25Decoder.Modulation; public class P25Editor extends DecodeEditor { @@ -41,7 +42,7 @@ public class P25Editor extends DecodeEditor private static final long serialVersionUID = 1L; - private JComboBox mComboModulation; + private JComboBox mComboModulation; public P25Editor( DecodeConfiguration config ) { @@ -52,10 +53,10 @@ public class P25Editor extends DecodeEditor private void initGUI() { - mComboModulation = new JComboBox(); + mComboModulation = new JComboBox(); - mComboModulation.setModel( new DefaultComboBoxModel( - P25Decoder.Modulation.values() ) ); + mComboModulation.setModel( new DefaultComboBoxModel( + P25_LSMDecoder.Modulation.values() ) ); mComboModulation.setSelectedItem( ((DecodeConfigP25Phase1)mConfig).getModulation() ); @@ -64,7 +65,7 @@ public class P25Editor extends DecodeEditor @Override public void actionPerformed( ActionEvent e ) { - P25Decoder.Modulation selected = mComboModulation + P25_LSMDecoder.Modulation selected = mComboModulation .getItemAt( mComboModulation.getSelectedIndex() ); if( selected != null ) @@ -74,7 +75,8 @@ public class P25Editor extends DecodeEditor } } ); - add( mComboModulation, "span 2,wrap" ); + add( new JLabel( "Modulation:" ) ); + add( mComboModulation, "wrap" ); } /** diff --git a/src/decode/p25/P25Interleave.java b/src/module/decode/p25/P25Interleave.java similarity index 99% rename from src/decode/p25/P25Interleave.java rename to src/module/decode/p25/P25Interleave.java index 60da95eb..6c6ac746 100644 --- a/src/decode/p25/P25Interleave.java +++ b/src/module/decode/p25/P25Interleave.java @@ -1,4 +1,4 @@ -package decode.p25; +package module.decode.p25; import java.util.BitSet; diff --git a/src/decode/p25/P25MessageFramer.java b/src/module/decode/p25/P25MessageFramer.java similarity index 92% rename from src/decode/p25/P25MessageFramer.java rename to src/module/decode/p25/P25MessageFramer.java index fcb90263..32e41b21 100644 --- a/src/decode/p25/P25MessageFramer.java +++ b/src/module/decode/p25/P25MessageFramer.java @@ -1,8 +1,23 @@ -package decode.p25; +package module.decode.p25; import java.util.ArrayList; import message.Message; +import module.decode.p25.message.P25Message; +import module.decode.p25.message.hdu.HDUMessage; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.message.ldu.LDU2Message; +import module.decode.p25.message.ldu.lc.LDULCMessageFactory; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.message.pdu.PDUMessageFactory; +import module.decode.p25.message.pdu.confirmed.PDUConfirmedMessage; +import module.decode.p25.message.pdu.confirmed.PDUConfirmedMessageFactory; +import module.decode.p25.message.tdu.TDUMessage; +import module.decode.p25.message.tdu.lc.TDULCMessageFactory; +import module.decode.p25.message.tdu.lc.TDULinkControlMessage; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.message.tsbk.TSBKMessageFactory; +import module.decode.p25.reference.DataUnitID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,21 +30,6 @@ import bits.ISyncDetectListener; import bits.MultiSyncPatternMatcher; import bits.SoftSyncDetector; import bits.SyncDetector; -import decode.p25.message.P25Message; -import decode.p25.message.hdu.HDUMessage; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.message.ldu.LDU2Message; -import decode.p25.message.ldu.lc.LDULCMessageFactory; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.message.pdu.PDUMessageFactory; -import decode.p25.message.pdu.confirmed.PDUConfirmedMessage; -import decode.p25.message.pdu.confirmed.PDUConfirmedMessageFactory; -import decode.p25.message.tdu.TDUMessage; -import decode.p25.message.tdu.lc.TDULCMessageFactory; -import decode.p25.message.tdu.lc.TDULinkControlMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.message.tsbk.TSBKMessageFactory; -import decode.p25.reference.DataUnitID; import dsp.psk.CQPSKDemodulator; import dsp.psk.CQPSKDemodulator2; import dsp.symbol.Dibit; @@ -128,16 +128,19 @@ public class P25MessageFramer implements Listener { this( aliasList ); - /* For CQPSK, we include 3 additional sync detectors to watch for and - * correction +/-90 and 180 degree costas loop phase lock errors */ - mMatcher.add( new CostasPhaseErrorDetector( FrameSync.P25_PHASE1_ERROR_90_CCW, - demodulator, PHASE_CORRECTION_90_DEGREES ) ); + if( demodulator != null ) + { + /* For CQPSK, we include 3 additional sync detectors to watch for and + * correction +/-90 and 180 degree costas loop phase lock errors */ + mMatcher.add( new CostasPhaseErrorDetector( FrameSync.P25_PHASE1_ERROR_90_CCW, + demodulator, PHASE_CORRECTION_90_DEGREES ) ); - mMatcher.add( new CostasPhaseErrorDetector( FrameSync.P25_PHASE1_ERROR_90_CW, - demodulator, -PHASE_CORRECTION_90_DEGREES ) ); + mMatcher.add( new CostasPhaseErrorDetector( FrameSync.P25_PHASE1_ERROR_90_CW, + demodulator, -PHASE_CORRECTION_90_DEGREES ) ); - mMatcher.add( new CostasPhaseErrorDetector( FrameSync.P25_PHASE1_ERROR_180, - demodulator, PHASE_CORRECTION_180_DEGREES ) ); + mMatcher.add( new CostasPhaseErrorDetector( FrameSync.P25_PHASE1_ERROR_180, + demodulator, PHASE_CORRECTION_180_DEGREES ) ); + } } private void dispatch( Message message ) diff --git a/src/decode/p25/P25MessageProcessor.java b/src/module/decode/p25/P25MessageProcessor.java similarity index 78% rename from src/decode/p25/P25MessageProcessor.java rename to src/module/decode/p25/P25MessageProcessor.java index f23c064b..c7c8e597 100644 --- a/src/decode/p25/P25MessageProcessor.java +++ b/src/module/decode/p25/P25MessageProcessor.java @@ -1,8 +1,11 @@ -package decode.p25; +package module.decode.p25; import java.util.HashMap; import message.Message; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDUMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,16 +13,13 @@ import org.slf4j.LoggerFactory; import sample.Broadcaster; import sample.Listener; import alias.AliasList; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDUMessage; public class P25MessageProcessor implements Listener { private final static Logger mLog = LoggerFactory.getLogger( P25MessageProcessor.class ); - private Broadcaster mBroadcaster = new Broadcaster(); + private Listener mMessageListener; /* Map of up to 16 band identifiers per RFSS. These identifier update * messages are inserted into any message that conveys channel information @@ -74,25 +74,25 @@ public class P25MessageProcessor implements Listener * Broadcast all valid messages and any LDU voice messages regardless if * they are valid or not, so that we don't miss any voice frames */ - if( message.isValid() || message instanceof LDUMessage ) + if( mMessageListener != null && message.isValid() || message instanceof LDUMessage ) { - mBroadcaster.broadcast( message ); + mMessageListener.receive( message ); } } public void dispose() { mBandIdentifierMap.clear(); - mBroadcaster.dispose(); + mMessageListener = null; } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } } diff --git a/src/module/decode/p25/P25_C4FMDecoder.java b/src/module/decode/p25/P25_C4FMDecoder.java new file mode 100644 index 00000000..4e425d86 --- /dev/null +++ b/src/module/decode/p25/P25_C4FMDecoder.java @@ -0,0 +1,239 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.p25; + +import instrument.tap.Tap; +import instrument.tap.stream.DibitTap; +import instrument.tap.stream.FloatTap; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; +import sample.real.RealBufferToStreamConverter; +import source.tuner.frequency.FrequencyCorrectionControl; +import alias.AliasList; +import controller.channel.Channel.ChannelType; +import dsp.filter.FilterFactory; +import dsp.filter.Window.WindowType; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; +import dsp.gain.DirectGainControl; + +public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener +{ + private final static Logger mLog = LoggerFactory.getLogger( P25_C4FMDecoder.class ); + + /* Instrumentation Taps */ + private static final String INSTRUMENT_FILTER_OUTPUT = "Tap Point: Pre-Filter Output"; + private static final String INSTRUMENT_DGC_OUTPUT = "Tap Point: DGC Output"; + private static final String INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT = "Tap Point: Symbol Filter Output"; + private static final String INSTRUMENT_C4FM_SLICER_OUTPUT = "Tap Point: C4FM Slicer Output"; + + private final static int MAXIMUM_FREQUENCY_CORRECTION = 3000; //Hertz, +/- + + private List mAvailableTaps; + private RealFIRFilter_RB_RB mC4FMPreFilter; + private DirectGainControl mDGC; + private RealBufferToStreamConverter mStreamConverter = + new RealBufferToStreamConverter(); + private FrequencyCorrectionControl mFrequencyCorrectionControl; + private C4FMSymbolFilter mSymbolFilter; + private C4FMSlicer mC4FMSlicer; + private P25MessageFramer mMessageFramer; + + /** + * P25 Phase 1 C4FM Decoder processes real buffers of un-filtered, + * demodulated audio and produces decoded P25 Phase 1 Messages. + * + * Provides Frequency Control to steer external tuner source and incorporates + * internal gain control. + * + * Note: use the P25AudioModule to convert the decoded messages into audio. + * + * @param aliasList - optional (can be null) list of alias values for network + * infrastructure and subscriber identities that will be included in each + * decoded message + */ + public P25_C4FMDecoder( AliasList aliasList, ChannelType channelType ) + { + super( aliasList, channelType ); + + /* Filter demodulated sample buffers */ + mC4FMPreFilter = new RealFIRFilter_RB_RB( FilterFactory.getLowPass( + 48000, 6750, 7500, 60, WindowType.HANNING, true ), 1.0f ); + + /* Convert buffers to a sample stream so that the symbol filter can + * provide fine-grained gain control over individual samples */ + mC4FMPreFilter.setListener( mStreamConverter ); + + /* Apply gain remotely controlled by the downstream symbol filter */ + mDGC = new DirectGainControl( 15.0f, 0.1f, 35.0f, 0.3f ); + mStreamConverter.setListener( mDGC ); + + /* Issue tuned frequency correction commands, remotely controlled by the + * downstream symbol filter */ + mFrequencyCorrectionControl = new FrequencyCorrectionControl( + MAXIMUM_FREQUENCY_CORRECTION ); + + /* Shape gain and frequency offsets to optimize sample stream */ + mSymbolFilter = new C4FMSymbolFilter( mDGC, mFrequencyCorrectionControl ); + mDGC.setListener( mSymbolFilter ); + + /* Convert samples to symbols */ + mC4FMSlicer = new C4FMSlicer(); + mSymbolFilter.setListener( mC4FMSlicer ); + + /* Sync pattern detection and message construction */ + mMessageFramer = new P25MessageFramer( aliasList ); + mC4FMSlicer.addListener( mMessageFramer ); + + /* Process and broadcast messages */ + mMessageFramer.setListener( getMessageProcessor() ); + } + + public Modulation getModulation() + { + return Modulation.C4FM; + } + + @Override + public boolean hasFrequencyCorrectionControl() + { + return mFrequencyCorrectionControl != null; + } + + @Override + public FrequencyCorrectionControl getFrequencyCorrectionControl() + { + return mFrequencyCorrectionControl; + } + + @Override + public Listener getRealBufferListener() + { + return mC4FMPreFilter; + } + + /** + * Instrumentation taps for monitoring internal processing + */ + @Override + public List getTaps() + { + if( mAvailableTaps == null ) + { + mAvailableTaps = new ArrayList(); + + mAvailableTaps.add( new FloatTap( INSTRUMENT_FILTER_OUTPUT, 0, 1.0f ) ); + mAvailableTaps.add( new FloatTap( INSTRUMENT_DGC_OUTPUT, 0, 1.0f ) ); + + if( mSymbolFilter != null ) + { + mAvailableTaps.addAll( mSymbolFilter.getTaps() ); + } + + mAvailableTaps.add( new FloatTap( INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT, 0, 0.1f ) ); + mAvailableTaps.add( new DibitTap( INSTRUMENT_C4FM_SLICER_OUTPUT, 0, 0.1f ) ); + } + + return mAvailableTaps; + } + + /** + * Adds an instrumentation tap to monitor internal processing. + */ + @Override + public void addTap( Tap tap ) + { + if( mSymbolFilter != null ) + { + mSymbolFilter.addTap( tap ); + } + + switch( tap.getName() ) + { + case INSTRUMENT_FILTER_OUTPUT: + FloatTap filterOutput = (FloatTap)tap; + mStreamConverter.setListener( filterOutput ); + filterOutput.setListener( mDGC ); + break; + case INSTRUMENT_DGC_OUTPUT: + FloatTap dgcTap = (FloatTap)tap; + mDGC.setListener( dgcTap ); + dgcTap.setListener( mSymbolFilter ); + break; + case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT: + FloatTap symbolTap = (FloatTap)tap; + mSymbolFilter.setListener( symbolTap ); + symbolTap.setListener( mC4FMSlicer ); + break; + case INSTRUMENT_C4FM_SLICER_OUTPUT: + DibitTap slicerTap = (DibitTap)tap; + + if( mC4FMSlicer != null ) + { + mC4FMSlicer.addListener( slicerTap ); + } + break; + default: + throw new IllegalArgumentException( "Unrecognized tap: " + + tap.getName() ); + } + } + + /** + * Removes the instrumentation tap. + */ + @Override + public void removeTap( Tap tap ) + { + if( mSymbolFilter != null ) + { + mSymbolFilter.removeTap( tap ); + } + + switch( tap.getName() ) + { + case INSTRUMENT_FILTER_OUTPUT: + mStreamConverter.setListener( mDGC ); + break; + case INSTRUMENT_DGC_OUTPUT: + mDGC.setListener( mSymbolFilter ); + break; + case INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT: + mSymbolFilter.setListener( mC4FMSlicer ); + break; + case INSTRUMENT_C4FM_SLICER_OUTPUT: + DibitTap slicerTap = (DibitTap)tap; + + if( mC4FMSlicer != null ) + { + mC4FMSlicer.removeListener( slicerTap ); + } + break; + default: + throw new IllegalArgumentException( "Unrecognized tap: " + + tap.getName() ); + } + } +} diff --git a/src/module/decode/p25/P25_LSMDecoder.java b/src/module/decode/p25/P25_LSMDecoder.java new file mode 100644 index 00000000..60999e97 --- /dev/null +++ b/src/module/decode/p25/P25_LSMDecoder.java @@ -0,0 +1,205 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.p25; + +import instrument.tap.Tap; +import instrument.tap.stream.ComplexSampleTap; +import instrument.tap.stream.ComplexTap; +import instrument.tap.stream.DibitTap; +import instrument.tap.stream.FloatTap; +import instrument.tap.stream.QPSKTap; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import controller.channel.Channel.ChannelType; +import sample.Listener; +import sample.complex.ComplexBuffer; +import sample.complex.ComplexBufferToStreamConverter; +import sample.complex.IComplexBufferListener; +import source.tuner.frequency.FrequencyCorrectionControl; +import alias.AliasList; +import dsp.filter.FilterFactory; +import dsp.filter.Window.WindowType; +import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; +import dsp.gain.ComplexFeedForwardGainControl; +import dsp.psk.CQPSKDemodulator; +import dsp.psk.QPSKPolarSlicer; + +public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener +{ + private final static Logger mLog = LoggerFactory.getLogger( P25_LSMDecoder.class ); + + /* Instrumentation Taps */ + private static final String INSTRUMENT_BASEBAND_FILTER_OUTPUT = "Tap Point: Baseband Filter Output"; + private static final String INSTRUMENT_AGC_OUTPUT = "Tap Point: AGC Output"; + private static final String INSTRUMENT_QPSK_DEMODULATOR_OUTPUT = "Tap Point: CQPSK Demodulator Output"; + private static final String INSTRUMENT_CQPSK_SLICER_OUTPUT = "Tap Point: CQPSK Slicer Output"; + + private List mAvailableTaps; + + private ComplexFIRFilter_CB_CB mBasebandFilter; + private ComplexBufferToStreamConverter mStreamConverter = + new ComplexBufferToStreamConverter(); + private ComplexFeedForwardGainControl mAGC = + new ComplexFeedForwardGainControl( 32 ); + private CQPSKDemodulator mCQPSKDemodulator = new CQPSKDemodulator(); + private QPSKPolarSlicer mCQPSKSlicer = new QPSKPolarSlicer(); + private P25MessageFramer mMessageFramer; + + public P25_LSMDecoder( AliasList aliasList, ChannelType channelType ) + { + super( aliasList, channelType ); + + mBasebandFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass( + 48000, 7250, 8000, 60, WindowType.HANNING, true ), 1.0f ); + + mBasebandFilter.setListener( mStreamConverter ); + + mStreamConverter.setListener( mAGC ); + + mAGC.setListener( mCQPSKDemodulator ); + + mCQPSKDemodulator.setSymbolListener( mCQPSKSlicer ); + + mCQPSKSlicer.addListener( mMessageFramer ); + + mMessageFramer = new P25MessageFramer( aliasList, mCQPSKDemodulator ); + mCQPSKSlicer.addListener( mMessageFramer ); + + mMessageFramer.setListener( getMessageProcessor() ); + } + + @Override + public Listener getComplexBufferListener() + { + return mBasebandFilter; + } + + public Modulation getModulation() + { + return Modulation.CQPSK; + } + + @Override + public boolean hasFrequencyCorrectionControl() + { + return false; + } + + @Override + public FrequencyCorrectionControl getFrequencyCorrectionControl() + { + return null; + } + + /** + * Provides a list of instrumentation taps for monitoring internal processing + */ + @Override + public List getTaps() + { + if( mAvailableTaps == null ) + { + mAvailableTaps = new ArrayList(); + + mAvailableTaps.add( new ComplexTap( INSTRUMENT_BASEBAND_FILTER_OUTPUT, 0, 1.0f ) ); + mAvailableTaps.add( new ComplexTap( INSTRUMENT_AGC_OUTPUT, 0, 1.0f ) ); + mAvailableTaps.add( new QPSKTap( INSTRUMENT_QPSK_DEMODULATOR_OUTPUT, 0, 1.0f ) ); + mAvailableTaps.add( new DibitTap( INSTRUMENT_CQPSK_SLICER_OUTPUT, 0, 0.1f ) ); + + if( mCQPSKDemodulator != null ) + { + mAvailableTaps.addAll( mCQPSKDemodulator.getTaps() ); + } + } + + return mAvailableTaps; + } + + /** + * Adds the instrumentation tap + */ + @Override + public void addTap( Tap tap ) + { + if( mCQPSKDemodulator != null ) + { + mCQPSKDemodulator.addTap( tap ); + } + + switch( tap.getName() ) + { + case INSTRUMENT_BASEBAND_FILTER_OUTPUT: + ComplexSampleTap baseband = (ComplexSampleTap)tap; + mStreamConverter.setListener( baseband ); + baseband.setListener( mAGC ); + break; + case INSTRUMENT_AGC_OUTPUT: + ComplexSampleTap agcSymbol = (ComplexSampleTap)tap; + mAGC.setListener( agcSymbol ); + agcSymbol.setListener( mCQPSKDemodulator ); + break; + case INSTRUMENT_QPSK_DEMODULATOR_OUTPUT: + QPSKTap qpsk = (QPSKTap)tap; + mCQPSKDemodulator.setSymbolListener( qpsk ); + qpsk.setListener( mCQPSKSlicer ); + break; + case INSTRUMENT_CQPSK_SLICER_OUTPUT: + mCQPSKSlicer.addListener( (DibitTap)tap ); + break; + default: + throw new IllegalArgumentException( "Unrecognized tap: " + + tap.getName() ); + } + } + + /** + * Removes the instrumentation tap + */ + @Override + public void removeTap( Tap tap ) + { + if( mCQPSKDemodulator != null ) + { + mCQPSKDemodulator.removeTap( tap ); + } + + switch( tap.getName() ) + { + case INSTRUMENT_BASEBAND_FILTER_OUTPUT: + mStreamConverter.setListener( mAGC ); + break; + case INSTRUMENT_AGC_OUTPUT: + mAGC.setListener( mCQPSKDemodulator ); + break; + case INSTRUMENT_QPSK_DEMODULATOR_OUTPUT: + mCQPSKDemodulator.setSymbolListener( mCQPSKSlicer ); + break; + case INSTRUMENT_CQPSK_SLICER_OUTPUT: + mCQPSKSlicer.removeListener( (DibitTap)tap ); + break; + default: + throw new IllegalArgumentException( "Unrecognized tap: " + + tap.getName() ); + } + } +} diff --git a/src/decode/p25/Trellis_1_2_Rate.java b/src/module/decode/p25/Trellis_1_2_Rate.java similarity index 99% rename from src/decode/p25/Trellis_1_2_Rate.java rename to src/module/decode/p25/Trellis_1_2_Rate.java index 3bf37f63..ee35c5ad 100644 --- a/src/decode/p25/Trellis_1_2_Rate.java +++ b/src/module/decode/p25/Trellis_1_2_Rate.java @@ -1,4 +1,4 @@ -package decode.p25; +package module.decode.p25; import java.util.ArrayList; import java.util.Random; diff --git a/src/decode/p25/Trellis_3_4_Rate.java b/src/module/decode/p25/Trellis_3_4_Rate.java similarity index 99% rename from src/decode/p25/Trellis_3_4_Rate.java rename to src/module/decode/p25/Trellis_3_4_Rate.java index 0f1ed03a..4cca9cf7 100644 --- a/src/decode/p25/Trellis_3_4_Rate.java +++ b/src/module/decode/p25/Trellis_3_4_Rate.java @@ -1,4 +1,4 @@ -package decode.p25; +package module.decode.p25; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/module/decode/p25/audio/P25AudioModule.java b/src/module/decode/p25/audio/P25AudioModule.java new file mode 100644 index 00000000..140ee98b --- /dev/null +++ b/src/module/decode/p25/audio/P25AudioModule.java @@ -0,0 +1,220 @@ +package module.decode.p25.audio; + +import jmbe.iface.AudioConversionLibrary; +import jmbe.iface.AudioConverter; +import message.IMessageListener; +import message.Message; +import module.Module; +import module.decode.p25.message.ldu.LDUMessage; +import module.decode.p25.message.tdu.TDUMessage; +import module.decode.p25.message.tdu.lc.TDULinkControlMessage; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import controller.channel.ChannelEvent; +import controller.channel.IChannelEventListener; +import sample.Listener; +import alias.Metadata; +import audio.AudioFormats; +import audio.AudioPacket; +import audio.IAudioPacketProvider; +import audio.metadata.AudioMetadata; +import audio.metadata.IMetadataListener; + +public class P25AudioModule extends Module implements Listener, + IAudioPacketProvider, IMessageListener, IChannelEventListener, IMetadataListener +{ + private final static Logger mLog = LoggerFactory.getLogger( P25AudioModule.class ); + + private static final String IMBE_CODEC = "IMBE"; + + /* Provides a unique identifier for this audio module instance to use as a + * source identifier for all audio packets */ + private static int UNIQUE_ID = 0; + private int mSourceID; + private static boolean mLibraryLoadStatusLogged = false; + private boolean mCanConvertAudio = false; + private boolean mEncryptedAudio = false; + private AudioConverter mAudioConverter; + private Listener mAudioPacketListener; + private AudioMetadata mAudioMetadata; + private ChannelEventListener mChannelEventListener = new ChannelEventListener(); + + public P25AudioModule() + { + mSourceID = ++UNIQUE_ID; + mAudioMetadata = new AudioMetadata( mSourceID ); + } + + @Override + public Listener getMessageListener() + { + return this; + } + + @Override + public void dispose() + { + mAudioConverter = null; + } + + @Override + public void init() + { + loadConverter(); + } + + /** + * Primary inject point for p25 imbe audio frame messages. Each LDU audio + * message contains 9 imbe voice frames. If the audio is not encrypted, + * we insert each audio frame asynchronously into the audio converter. + */ + public void receive( Message message ) + { + if( mCanConvertAudio && mAudioPacketListener != null ) + { + if( message instanceof LDUMessage ) + { + LDUMessage ldu = (LDUMessage)message; + + /* If we detect a valid LDU message with the encryption flag + * set to true, we toggle the encrypted audio state until we + * receive the first TDU or TDULC message, then reset it. */ + if( ldu.isValid() && ldu.isEncrypted() ) + { + mEncryptedAudio = true; + } + + if( !mEncryptedAudio && mAudioConverter != null ) + { + for( byte[] frame: ((LDUMessage)message).getIMBEFrames() ) + { + float[] audio = mAudioConverter.convert( frame ); + + mAudioPacketListener.receive( + new AudioPacket( audio, mAudioMetadata ) ); + } + } + } + else if( message instanceof TDUMessage || message instanceof TDULinkControlMessage ) + { + mEncryptedAudio = false; + } + } + } + + /** + * Loads audio frame processing chain. Constructs an imbe targetdataline + * to receive the raw imbe frames. Adds an IMBE to 8k PCM format conversion + * stream wrapper. Finally, adds an upsampling (8k to 48k) stream wrapper. + */ + private void loadConverter() + { + AudioConversionLibrary library = null; + + try + { + @SuppressWarnings( "rawtypes" ) + Class temp = Class.forName( "jmbe.JMBEAudioLibrary" ); + + library = (AudioConversionLibrary)temp.newInstance(); + + mAudioConverter = library.getAudioConverter( IMBE_CODEC, + AudioFormats.PCM_SIGNED_48KHZ_16BITS ); + + if( mAudioConverter != null ) + { + mCanConvertAudio = true; + + if( !mLibraryLoadStatusLogged ) + { + mLog.info( "JMBE audio conversion library successfully loaded" + + " - P25 audio will be available" ); + + mLibraryLoadStatusLogged = true; + } + } + else + { + if( !mLibraryLoadStatusLogged ) + { + mLog.info( "JMBE audio conversion library NOT FOUND" ); + mLibraryLoadStatusLogged = true; + } + } + } + catch ( ClassNotFoundException e1 ) + { + if( !mLibraryLoadStatusLogged ) + { + mLog.error( "Couldn't find/load JMBE audio conversion library" ); + mLibraryLoadStatusLogged = true; + } + } + catch ( InstantiationException e1 ) + { + if( !mLibraryLoadStatusLogged ) + { + mLog.error( "Couldn't instantiate JMBE audio conversion library class" ); + mLibraryLoadStatusLogged = true; + } + } + catch ( IllegalAccessException e1 ) + { + if( !mLibraryLoadStatusLogged ) + { + mLog.error( "Couldn't load JMBE audio conversion library due to " + + "security restrictions" ); + mLibraryLoadStatusLogged = true; + } + } + } + + @Override + public void setAudioPacketListener( Listener listener ) + { + mAudioPacketListener = listener; + } + + @Override + public void removeAudioPacketListener() + { + mAudioPacketListener = null; + } + + @Override + public Listener getMetadataListener() + { + return mAudioMetadata; + } + + @Override + public Listener getChannelEventListener() + { + return mChannelEventListener; + } + + /** + * Wrapper for channel event listener. Responds to channel state reset + * events to remove/cleanup current audio metadata + */ + public class ChannelEventListener implements Listener + { + @Override + public void receive( ChannelEvent event ) + { + switch( event.getEvent() ) + { + case CHANNEL_STATE_RESET: + mAudioMetadata.reset(); + break; + case CHANGE_SELECTED: + mAudioMetadata.setSelected( event.getChannel().isSelected() ); + break; + default: + break; + } + } + } +} diff --git a/src/decode/p25/message/IBandIdentifier.java b/src/module/decode/p25/message/IBandIdentifier.java similarity index 92% rename from src/decode/p25/message/IBandIdentifier.java rename to src/module/decode/p25/message/IBandIdentifier.java index ad995510..a2588c42 100644 --- a/src/decode/p25/message/IBandIdentifier.java +++ b/src/module/decode/p25/message/IBandIdentifier.java @@ -1,4 +1,4 @@ -package decode.p25.message; +package module.decode.p25.message; public interface IBandIdentifier { diff --git a/src/decode/p25/message/IdentifierReceiver.java b/src/module/decode/p25/message/IdentifierReceiver.java similarity index 91% rename from src/decode/p25/message/IdentifierReceiver.java rename to src/module/decode/p25/message/IdentifierReceiver.java index 0438ae1b..a1c65c63 100644 --- a/src/decode/p25/message/IdentifierReceiver.java +++ b/src/module/decode/p25/message/IdentifierReceiver.java @@ -1,4 +1,4 @@ -package decode.p25.message; +package module.decode.p25.message; /** diff --git a/src/decode/p25/message/P25Message.java b/src/module/decode/p25/message/P25Message.java similarity index 97% rename from src/decode/p25/message/P25Message.java rename to src/module/decode/p25/message/P25Message.java index 69f20486..e8421211 100644 --- a/src/decode/p25/message/P25Message.java +++ b/src/module/decode/p25/message/P25Message.java @@ -1,13 +1,13 @@ -package decode.p25.message; +package module.decode.p25.message; import java.text.SimpleDateFormat; import map.Plottable; import message.Message; +import module.decode.p25.reference.DataUnitID; import alias.Alias; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; import edac.CRC; public class P25Message extends Message diff --git a/src/decode/p25/message/filter/HDUMessageFilter.java b/src/module/decode/p25/message/filter/HDUMessageFilter.java similarity index 79% rename from src/decode/p25/message/filter/HDUMessageFilter.java rename to src/module/decode/p25/message/filter/HDUMessageFilter.java index 78c5deee..0f716691 100644 --- a/src/decode/p25/message/filter/HDUMessageFilter.java +++ b/src/module/decode/p25/message/filter/HDUMessageFilter.java @@ -1,11 +1,11 @@ -package decode.p25.message.filter; +package module.decode.p25.message.filter; import java.util.Collections; import java.util.List; import message.Message; -import decode.p25.message.hdu.HDUMessage; -import decode.p25.message.tdu.lc.TDULinkControlMessage; +import module.decode.p25.message.hdu.HDUMessage; +import module.decode.p25.message.tdu.lc.TDULinkControlMessage; import filter.Filter; import filter.FilterElement; diff --git a/src/decode/p25/message/filter/LDUMessageFilter.java b/src/module/decode/p25/message/filter/LDUMessageFilter.java similarity index 86% rename from src/decode/p25/message/filter/LDUMessageFilter.java rename to src/module/decode/p25/message/filter/LDUMessageFilter.java index 85a95a1f..d0fcfb52 100644 --- a/src/decode/p25/message/filter/LDUMessageFilter.java +++ b/src/module/decode/p25/message/filter/LDUMessageFilter.java @@ -1,10 +1,10 @@ -package decode.p25.message.filter; +package module.decode.p25.message.filter; import java.util.Collections; import java.util.List; import message.Message; -import decode.p25.message.ldu.LDUMessage; +import module.decode.p25.message.ldu.LDUMessage; import filter.Filter; import filter.FilterElement; diff --git a/src/decode/p25/message/filter/P25MessageFilterSet.java b/src/module/decode/p25/message/filter/P25MessageFilterSet.java similarity index 86% rename from src/decode/p25/message/filter/P25MessageFilterSet.java rename to src/module/decode/p25/message/filter/P25MessageFilterSet.java index cb93aaba..f4f72258 100644 --- a/src/decode/p25/message/filter/P25MessageFilterSet.java +++ b/src/module/decode/p25/message/filter/P25MessageFilterSet.java @@ -1,7 +1,7 @@ -package decode.p25.message.filter; +package module.decode.p25.message.filter; import message.Message; -import decode.p25.message.P25Message; +import module.decode.p25.message.P25Message; import filter.FilterSet; public class P25MessageFilterSet extends FilterSet diff --git a/src/decode/p25/message/filter/PDUMessageFilter.java b/src/module/decode/p25/message/filter/PDUMessageFilter.java similarity index 89% rename from src/decode/p25/message/filter/PDUMessageFilter.java rename to src/module/decode/p25/message/filter/PDUMessageFilter.java index 42fdd002..a18bd47d 100644 --- a/src/decode/p25/message/filter/PDUMessageFilter.java +++ b/src/module/decode/p25/message/filter/PDUMessageFilter.java @@ -1,12 +1,12 @@ -package decode.p25.message.filter; +package module.decode.p25.message.filter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import message.Message; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.Opcode; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.Opcode; import filter.Filter; import filter.FilterElement; diff --git a/src/decode/p25/message/filter/TDULCMessageFilter.java b/src/module/decode/p25/message/filter/TDULCMessageFilter.java similarity index 80% rename from src/decode/p25/message/filter/TDULCMessageFilter.java rename to src/module/decode/p25/message/filter/TDULCMessageFilter.java index 43473a3f..dbf38811 100644 --- a/src/decode/p25/message/filter/TDULCMessageFilter.java +++ b/src/module/decode/p25/message/filter/TDULCMessageFilter.java @@ -1,11 +1,11 @@ -package decode.p25.message.filter; +package module.decode.p25.message.filter; import java.util.Collections; import java.util.List; import message.Message; -import decode.p25.message.tdu.TDUMessage; -import decode.p25.message.tdu.lc.TDULinkControlMessage; +import module.decode.p25.message.tdu.TDUMessage; +import module.decode.p25.message.tdu.lc.TDULinkControlMessage; import filter.Filter; import filter.FilterElement; diff --git a/src/decode/p25/message/filter/TDUMessageFilter.java b/src/module/decode/p25/message/filter/TDUMessageFilter.java similarity index 86% rename from src/decode/p25/message/filter/TDUMessageFilter.java rename to src/module/decode/p25/message/filter/TDUMessageFilter.java index d7f2a20b..f87dc835 100644 --- a/src/decode/p25/message/filter/TDUMessageFilter.java +++ b/src/module/decode/p25/message/filter/TDUMessageFilter.java @@ -1,10 +1,10 @@ -package decode.p25.message.filter; +package module.decode.p25.message.filter; import java.util.Collections; import java.util.List; import message.Message; -import decode.p25.message.tdu.TDUMessage; +import module.decode.p25.message.tdu.TDUMessage; import filter.Filter; import filter.FilterElement; diff --git a/src/decode/p25/message/filter/TSBKMessageFilterSet.java b/src/module/decode/p25/message/filter/TSBKMessageFilterSet.java similarity index 93% rename from src/decode/p25/message/filter/TSBKMessageFilterSet.java rename to src/module/decode/p25/message/filter/TSBKMessageFilterSet.java index 1b90bb54..337c0fb0 100644 --- a/src/decode/p25/message/filter/TSBKMessageFilterSet.java +++ b/src/module/decode/p25/message/filter/TSBKMessageFilterSet.java @@ -1,15 +1,15 @@ -package decode.p25.message.filter; +package module.decode.p25.message.filter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import message.Message; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.message.tsbk.motorola.MotorolaOpcode; -import decode.p25.message.tsbk.vendor.VendorOpcode; -import decode.p25.reference.Opcode; -import decode.p25.reference.Vendor; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.message.tsbk.motorola.MotorolaOpcode; +import module.decode.p25.message.tsbk.vendor.VendorOpcode; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Vendor; import filter.Filter; import filter.FilterElement; import filter.FilterSet; diff --git a/src/decode/p25/message/hdu/HDUMessage.java b/src/module/decode/p25/message/hdu/HDUMessage.java similarity index 98% rename from src/decode/p25/message/hdu/HDUMessage.java rename to src/module/decode/p25/message/hdu/HDUMessage.java index 857658f2..2e7d5da3 100644 --- a/src/decode/p25/message/hdu/HDUMessage.java +++ b/src/module/decode/p25/message/hdu/HDUMessage.java @@ -1,13 +1,14 @@ -package decode.p25.message.hdu; +package module.decode.p25.message.hdu; + +import module.decode.p25.message.P25Message; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Encryption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.P25Message; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Encryption; import edac.CRC; import edac.Golay18; import edac.ReedSolomon_63_47_17; diff --git a/src/decode/p25/message/ldu/LDU1Message.java b/src/module/decode/p25/message/ldu/LDU1Message.java similarity index 97% rename from src/decode/p25/message/ldu/LDU1Message.java rename to src/module/decode/p25/message/ldu/LDU1Message.java index b9a19f4c..929bac1e 100644 --- a/src/decode/p25/message/ldu/LDU1Message.java +++ b/src/module/decode/p25/message/ldu/LDU1Message.java @@ -1,14 +1,15 @@ -package decode.p25.message.ldu; +package module.decode.p25.message.ldu; + +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Vendor; +import module.decode.p25.reference.VendorLinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Vendor; -import decode.p25.reference.VendorLinkControlOpcode; import edac.CRC; import edac.Hamming10; import edac.ReedSolomon_63_47_17; diff --git a/src/decode/p25/message/ldu/LDU2Message.java b/src/module/decode/p25/message/ldu/LDU2Message.java similarity index 98% rename from src/decode/p25/message/ldu/LDU2Message.java rename to src/module/decode/p25/message/ldu/LDU2Message.java index 946061fd..776a33c7 100644 --- a/src/decode/p25/message/ldu/LDU2Message.java +++ b/src/module/decode/p25/message/ldu/LDU2Message.java @@ -1,12 +1,13 @@ -package decode.p25.message.ldu; +package module.decode.p25.message.ldu; + +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Encryption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Encryption; import edac.CRC; import edac.Hamming10; import edac.ReedSolomon_63_47_17; diff --git a/src/decode/p25/message/ldu/LDUMessage.java b/src/module/decode/p25/message/ldu/LDUMessage.java similarity index 95% rename from src/decode/p25/message/ldu/LDUMessage.java rename to src/module/decode/p25/message/ldu/LDUMessage.java index cb8c432c..2d67e4f8 100644 --- a/src/decode/p25/message/ldu/LDUMessage.java +++ b/src/module/decode/p25/message/ldu/LDUMessage.java @@ -1,15 +1,16 @@ -package decode.p25.message.ldu; +package module.decode.p25.message.ldu; import java.util.ArrayList; import java.util.List; +import module.decode.p25.message.P25Message; +import module.decode.p25.reference.DataUnitID; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.P25Message; -import decode.p25.reference.DataUnitID; public abstract class LDUMessage extends P25Message { diff --git a/src/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java similarity index 90% rename from src/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java rename to src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java index 9e2d49f2..b123ee9d 100644 --- a/src/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcast.java @@ -1,14 +1,14 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Service; - public class AdjacentSiteStatusBroadcast extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java similarity index 93% rename from src/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java rename to src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java index 2b5d6628..16c3e056 100644 --- a/src/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/AdjacentSiteStatusBroadcastExplicit.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class AdjacentSiteStatusBroadcastExplicit extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/CallAlert.java b/src/module/decode/p25/message/ldu/lc/CallAlert.java similarity index 89% rename from src/decode/p25/message/ldu/lc/CallAlert.java rename to src/module/decode/p25/message/ldu/lc/CallAlert.java index b564d230..731fc4ba 100644 --- a/src/decode/p25/message/ldu/lc/CallAlert.java +++ b/src/module/decode/p25/message/ldu/lc/CallAlert.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class CallAlert extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/CallTermination.java b/src/module/decode/p25/message/ldu/lc/CallTermination.java similarity index 85% rename from src/decode/p25/message/ldu/lc/CallTermination.java rename to src/module/decode/p25/message/ldu/lc/CallTermination.java index 8eaeace1..2d69f791 100644 --- a/src/decode/p25/message/ldu/lc/CallTermination.java +++ b/src/module/decode/p25/message/ldu/lc/CallTermination.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class CallTermination extends LDU1Message { private final static Logger mLog = LoggerFactory.getLogger( CallTermination.class ); diff --git a/src/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java similarity index 91% rename from src/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java rename to src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java index 1adf1b34..8fa255ec 100644 --- a/src/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java +++ b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdate.java @@ -1,12 +1,12 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class ChannelIdentifierUpdate extends LDU1Message implements IBandIdentifier { diff --git a/src/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java similarity index 91% rename from src/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java rename to src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java index 18ebe8a6..c924e693 100644 --- a/src/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/ChannelIdentifierUpdateExplicit.java @@ -1,12 +1,12 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class ChannelIdentifierUpdateExplicit extends LDU1Message implements IBandIdentifier { diff --git a/src/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java b/src/module/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java similarity index 91% rename from src/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java rename to src/module/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java index cee555f0..28b925ea 100644 --- a/src/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java +++ b/src/module/decode/p25/message/ldu/lc/ExtendedFunctionCommand.java @@ -1,12 +1,12 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.ExtendedFunction; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.ExtendedFunction; -import decode.p25.reference.LinkControlOpcode; - public class ExtendedFunctionCommand extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/GroupAffiliationQuery.java b/src/module/decode/p25/message/ldu/lc/GroupAffiliationQuery.java similarity index 88% rename from src/decode/p25/message/ldu/lc/GroupAffiliationQuery.java rename to src/module/decode/p25/message/ldu/lc/GroupAffiliationQuery.java index 22100c83..5f0a8b4a 100644 --- a/src/decode/p25/message/ldu/lc/GroupAffiliationQuery.java +++ b/src/module/decode/p25/message/ldu/lc/GroupAffiliationQuery.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class GroupAffiliationQuery extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java similarity index 93% rename from src/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java rename to src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java index c4670f49..4091664b 100644 --- a/src/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java +++ b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdate.java @@ -1,13 +1,13 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class GroupVoiceChannelUpdate extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java similarity index 91% rename from src/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java rename to src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java index c9555ba3..cd946525 100644 --- a/src/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUpdateExplicit.java @@ -1,15 +1,15 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.P25Message.DuplexMode; +import module.decode.p25.message.P25Message.SessionMode; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.P25Message.DuplexMode; -import decode.p25.message.P25Message.SessionMode; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class GroupVoiceChannelUpdateExplicit extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java similarity index 93% rename from src/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java rename to src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java index bc5578ac..c950bcc8 100644 --- a/src/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java +++ b/src/module/decode/p25/message/ldu/lc/GroupVoiceChannelUser.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class GroupVoiceChannelUser extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/LDULCMessageFactory.java b/src/module/decode/p25/message/ldu/lc/LDULCMessageFactory.java similarity index 95% rename from src/decode/p25/message/ldu/lc/LDULCMessageFactory.java rename to src/module/decode/p25/message/ldu/lc/LDULCMessageFactory.java index e12871a8..f6e053a1 100644 --- a/src/decode/p25/message/ldu/lc/LDULCMessageFactory.java +++ b/src/module/decode/p25/message/ldu/lc/LDULCMessageFactory.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.Vendor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.Vendor; - public class LDULCMessageFactory { diff --git a/src/decode/p25/message/ldu/lc/MessageUpdate.java b/src/module/decode/p25/message/ldu/lc/MessageUpdate.java similarity index 91% rename from src/decode/p25/message/ldu/lc/MessageUpdate.java rename to src/module/decode/p25/message/ldu/lc/MessageUpdate.java index 1af4bc29..bfcc239d 100644 --- a/src/decode/p25/message/ldu/lc/MessageUpdate.java +++ b/src/module/decode/p25/message/ldu/lc/MessageUpdate.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class MessageUpdate extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java b/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java similarity index 89% rename from src/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java rename to src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java index 0e4ab8a8..ec22b3ff 100644 --- a/src/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcast.java @@ -1,14 +1,14 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Service; - public class NetworkStatusBroadcast extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java b/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java similarity index 92% rename from src/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java rename to src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java index cbc6f51a..682882ff 100644 --- a/src/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/NetworkStatusBroadcastExplicit.java @@ -1,13 +1,13 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class NetworkStatusBroadcastExplicit extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java b/src/module/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java similarity index 88% rename from src/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java rename to src/module/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java index fdae5e3a..2cf1c570 100644 --- a/src/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/ProtectionParameterBroadcast.java @@ -1,12 +1,12 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.Encryption; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.Encryption; -import decode.p25.reference.LinkControlOpcode; - public class ProtectionParameterBroadcast extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java b/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java similarity index 89% rename from src/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java rename to src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java index 501a2c86..07edbef1 100644 --- a/src/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcast.java @@ -1,14 +1,14 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class RFSSStatusBroadcast extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java b/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java similarity index 91% rename from src/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java rename to src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java index e49a363c..b3b7b7ec 100644 --- a/src/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/RFSSStatusBroadcastExplicit.java @@ -1,14 +1,14 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class RFSSStatusBroadcastExplicit extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java b/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java similarity index 91% rename from src/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java rename to src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java index 81342a55..e154f8d5 100644 --- a/src/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcast.java @@ -1,14 +1,14 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class SecondaryControlChannelBroadcast extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java b/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java similarity index 91% rename from src/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java rename to src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java index 60433ff1..299531b2 100644 --- a/src/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java +++ b/src/module/decode/p25/message/ldu/lc/SecondaryControlChannelBroadcastExplicit.java @@ -1,14 +1,14 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class SecondaryControlChannelBroadcastExplicit extends LDU1Message implements IdentifierReceiver { diff --git a/src/decode/p25/message/ldu/lc/StatusQuery.java b/src/module/decode/p25/message/ldu/lc/StatusQuery.java similarity index 88% rename from src/decode/p25/message/ldu/lc/StatusQuery.java rename to src/module/decode/p25/message/ldu/lc/StatusQuery.java index 41637685..c00c29e6 100644 --- a/src/decode/p25/message/ldu/lc/StatusQuery.java +++ b/src/module/decode/p25/message/ldu/lc/StatusQuery.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class StatusQuery extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/StatusUpdate.java b/src/module/decode/p25/message/ldu/lc/StatusUpdate.java similarity index 91% rename from src/decode/p25/message/ldu/lc/StatusUpdate.java rename to src/module/decode/p25/message/ldu/lc/StatusUpdate.java index 08c83342..ceced2df 100644 --- a/src/decode/p25/message/ldu/lc/StatusUpdate.java +++ b/src/module/decode/p25/message/ldu/lc/StatusUpdate.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class StatusUpdate extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/SystemServiceBroadcast.java b/src/module/decode/p25/message/ldu/lc/SystemServiceBroadcast.java similarity index 88% rename from src/decode/p25/message/ldu/lc/SystemServiceBroadcast.java rename to src/module/decode/p25/message/ldu/lc/SystemServiceBroadcast.java index d7679ecb..c030f00d 100644 --- a/src/decode/p25/message/ldu/lc/SystemServiceBroadcast.java +++ b/src/module/decode/p25/message/ldu/lc/SystemServiceBroadcast.java @@ -1,14 +1,14 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; import java.util.List; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Service; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Service; - public class SystemServiceBroadcast extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java b/src/module/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java similarity index 92% rename from src/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java rename to src/module/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java index c70c7796..c035c931 100644 --- a/src/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java +++ b/src/module/decode/p25/message/ldu/lc/TelephoneInterconnectAnswerRequest.java @@ -1,15 +1,15 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; import java.util.ArrayList; import java.util.List; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.Digit; +import module.decode.p25.reference.LinkControlOpcode; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.Digit; -import decode.p25.reference.LinkControlOpcode; - public class TelephoneInterconnectAnswerRequest extends LDU1Message { private final static Logger mLog = LoggerFactory.getLogger( diff --git a/src/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java b/src/module/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java similarity index 89% rename from src/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java rename to src/module/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java index 00e6a04a..af1fe0a7 100644 --- a/src/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java +++ b/src/module/decode/p25/message/ldu/lc/TelephoneInterconnectVoiceChannelUser.java @@ -1,13 +1,13 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.P25Message.DuplexMode; +import module.decode.p25.message.P25Message.SessionMode; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.P25Message.DuplexMode; -import decode.p25.message.P25Message.SessionMode; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class TelephoneInterconnectVoiceChannelUser extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java b/src/module/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java similarity index 91% rename from src/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java rename to src/module/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java index 2e0cf1e2..6b4843aa 100644 --- a/src/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java +++ b/src/module/decode/p25/message/ldu/lc/UnitAuthenticationCommand.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class UnitAuthenticationCommand extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/UnitRegistrationCommand.java b/src/module/decode/p25/message/ldu/lc/UnitRegistrationCommand.java similarity index 91% rename from src/decode/p25/message/ldu/lc/UnitRegistrationCommand.java rename to src/module/decode/p25/message/ldu/lc/UnitRegistrationCommand.java index 50c2b559..34c37c34 100644 --- a/src/decode/p25/message/ldu/lc/UnitRegistrationCommand.java +++ b/src/module/decode/p25/message/ldu/lc/UnitRegistrationCommand.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class UnitRegistrationCommand extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java b/src/module/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java similarity index 88% rename from src/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java rename to src/module/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java index 26a0fd70..9309578f 100644 --- a/src/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java +++ b/src/module/decode/p25/message/ldu/lc/UnitToUnitAnswerRequest.java @@ -1,13 +1,13 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.P25Message.DuplexMode; +import module.decode.p25.message.P25Message.SessionMode; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.P25Message.DuplexMode; -import decode.p25.message.P25Message.SessionMode; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class UnitToUnitAnswerRequest extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java b/src/module/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java similarity index 88% rename from src/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java rename to src/module/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java index 37b582c4..383cb9b7 100644 --- a/src/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java +++ b/src/module/decode/p25/message/ldu/lc/UnitToUnitVoiceChannelUser.java @@ -1,13 +1,13 @@ -package decode.p25.message.ldu.lc; +package module.decode.p25.message.ldu.lc; + +import module.decode.p25.message.P25Message.DuplexMode; +import module.decode.p25.message.P25Message.SessionMode; +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.P25Message.DuplexMode; -import decode.p25.message.P25Message.SessionMode; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class UnitToUnitVoiceChannelUser extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java b/src/module/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java similarity index 88% rename from src/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java rename to src/module/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java index f15cc939..f9e1a6fc 100644 --- a/src/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java +++ b/src/module/decode/p25/message/ldu/lc/motorola/MotorolaOpcode_0.java @@ -1,11 +1,11 @@ -package decode.p25.message.ldu.lc.motorola; +package module.decode.p25.message.ldu.lc.motorola; + +import module.decode.p25.message.ldu.LDU1Message; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.ldu.LDU1Message; -import decode.p25.reference.LinkControlOpcode; - public class MotorolaOpcode_0 extends LDU1Message { private final static Logger mLog = diff --git a/src/decode/p25/message/pdu/PDUMessage.java b/src/module/decode/p25/message/pdu/PDUMessage.java similarity index 91% rename from src/decode/p25/message/pdu/PDUMessage.java rename to src/module/decode/p25/message/pdu/PDUMessage.java index 18b17e8c..78c703ad 100644 --- a/src/decode/p25/message/pdu/PDUMessage.java +++ b/src/module/decode/p25/message/pdu/PDUMessage.java @@ -1,14 +1,14 @@ -package decode.p25.message.pdu; +package module.decode.p25.message.pdu; +import module.decode.p25.message.P25Message; +import module.decode.p25.message.tsbk.vendor.VendorOpcode; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.PDUFormat; +import module.decode.p25.reference.ServiceAccessPoint; +import module.decode.p25.reference.Vendor; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.P25Message; -import decode.p25.message.tsbk.vendor.VendorOpcode; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.PDUFormat; -import decode.p25.reference.ServiceAccessPoint; -import decode.p25.reference.Vendor; import edac.CRC; public class PDUMessage extends P25Message diff --git a/src/decode/p25/message/pdu/PDUMessageFactory.java b/src/module/decode/p25/message/pdu/PDUMessageFactory.java similarity index 66% rename from src/decode/p25/message/pdu/PDUMessageFactory.java rename to src/module/decode/p25/message/pdu/PDUMessageFactory.java index aa831079..bafef26b 100644 --- a/src/decode/p25/message/pdu/PDUMessageFactory.java +++ b/src/module/decode/p25/message/pdu/PDUMessageFactory.java @@ -1,31 +1,31 @@ -package decode.p25.message.pdu; +package module.decode.p25.message.pdu; +import module.decode.p25.message.pdu.osp.control.AdjacentStatusBroadcastExtended; +import module.decode.p25.message.pdu.osp.control.CallAlertExtended; +import module.decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended; +import module.decode.p25.message.pdu.osp.control.GroupAffiliationResponseExtended; +import module.decode.p25.message.pdu.osp.control.MessageUpdateExtended; +import module.decode.p25.message.pdu.osp.control.NetworkStatusBroadcastExtended; +import module.decode.p25.message.pdu.osp.control.ProtectionParameterBroadcast; +import module.decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended; +import module.decode.p25.message.pdu.osp.control.RoamingAddressUpdateExtended; +import module.decode.p25.message.pdu.osp.control.StatusQueryExtended; +import module.decode.p25.message.pdu.osp.control.StatusUpdateExtended; +import module.decode.p25.message.pdu.osp.control.UnitRegistrationResponseExtended; +import module.decode.p25.message.pdu.osp.data.GroupDataChannelGrantExtended; +import module.decode.p25.message.pdu.osp.data.IndividualDataChannelGrantExtended; +import module.decode.p25.message.pdu.osp.voice.GroupVoiceChannelGrantExplicit; +import module.decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantExplicit; +import module.decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantUpdateExplicit; +import module.decode.p25.message.pdu.osp.voice.UnitToUnitAnswerRequestExplicit; +import module.decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantExtended; +import module.decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantUpdateExtended; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.PDUFormat; +import module.decode.p25.reference.Vendor; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.osp.control.AdjacentStatusBroadcastExtended; -import decode.p25.message.pdu.osp.control.CallAlertExtended; -import decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended; -import decode.p25.message.pdu.osp.control.GroupAffiliationResponseExtended; -import decode.p25.message.pdu.osp.control.MessageUpdateExtended; -import decode.p25.message.pdu.osp.control.NetworkStatusBroadcastExtended; -import decode.p25.message.pdu.osp.control.ProtectionParameterBroadcast; -import decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended; -import decode.p25.message.pdu.osp.control.RoamingAddressUpdateExtended; -import decode.p25.message.pdu.osp.control.StatusQueryExtended; -import decode.p25.message.pdu.osp.control.StatusUpdateExtended; -import decode.p25.message.pdu.osp.control.UnitRegistrationResponseExtended; -import decode.p25.message.pdu.osp.data.GroupDataChannelGrantExtended; -import decode.p25.message.pdu.osp.data.IndividualDataChannelGrantExtended; -import decode.p25.message.pdu.osp.voice.GroupVoiceChannelGrantExplicit; -import decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantExplicit; -import decode.p25.message.pdu.osp.voice.TelephoneInterconnectChannelGrantUpdateExplicit; -import decode.p25.message.pdu.osp.voice.UnitToUnitAnswerRequestExplicit; -import decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantExtended; -import decode.p25.message.pdu.osp.voice.UnitToUnitVoiceChannelGrantUpdateExtended; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.PDUFormat; -import decode.p25.reference.Vendor; public class PDUMessageFactory { diff --git a/src/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java b/src/module/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java similarity index 95% rename from src/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java rename to src/module/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java index e5c0c03c..d54c1011 100644 --- a/src/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java +++ b/src/module/decode/p25/message/pdu/UnitToUnitChannelGrantExtended.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu; +package module.decode.p25.message.pdu; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public abstract class UnitToUnitChannelGrantExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java b/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java similarity index 95% rename from src/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java rename to src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java index 3ee3ad8c..174c9466 100644 --- a/src/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java +++ b/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessage.java @@ -1,14 +1,15 @@ -package decode.p25.message.pdu.confirmed; +package module.decode.p25.message.pdu.confirmed; + +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.PDUType; +import module.decode.p25.reference.Vendor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.PDUType; -import decode.p25.reference.Vendor; import edac.CRC; import edac.CRCP25; diff --git a/src/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java b/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java similarity index 97% rename from src/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java rename to src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java index 379f09de..b695dd79 100644 --- a/src/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java +++ b/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java @@ -1,4 +1,4 @@ -package decode.p25.message.pdu.confirmed; +package module.decode.p25.message.pdu.confirmed; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java b/src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java similarity index 93% rename from src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java rename to src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java index fa47e6c7..d6ddbd4b 100644 --- a/src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java +++ b/src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextAccept.java @@ -1,16 +1,17 @@ -package decode.p25.message.pdu.confirmed; +package module.decode.p25.message.pdu.confirmed; + +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.MDPConfigurationOption; +import module.decode.p25.reference.PDUType; +import module.decode.p25.reference.SNDCPActivationRejectReason; +import module.decode.p25.reference.Vendor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.MDPConfigurationOption; -import decode.p25.reference.PDUType; -import decode.p25.reference.SNDCPActivationRejectReason; -import decode.p25.reference.Vendor; import edac.CRC; import edac.CRCP25; diff --git a/src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java b/src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java similarity index 92% rename from src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java rename to src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java index f8d0ec8b..e9a56414 100644 --- a/src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java +++ b/src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextReject.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.confirmed; +package module.decode.p25.message.pdu.confirmed; + +import module.decode.p25.reference.SNDCPActivationRejectReason; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.SNDCPActivationRejectReason; - public class SNDCPActivateTDSContextReject extends PDUConfirmedMessage { public final static Logger mLog = diff --git a/src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java b/src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java similarity index 97% rename from src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java rename to src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java index 194087ce..ba7966dd 100644 --- a/src/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java +++ b/src/module/decode/p25/message/pdu/confirmed/SNDCPActivateTDSContextRequest.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.confirmed; +package module.decode.p25.message.pdu.confirmed; + +import module.decode.p25.reference.MDPConfigurationOption; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.MDPConfigurationOption; - public class SNDCPActivateTDSContextRequest extends PDUConfirmedMessage { public final static Logger mLog = diff --git a/src/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java b/src/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java similarity index 89% rename from src/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java rename to src/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java index c8d0b778..000b4f0a 100644 --- a/src/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java +++ b/src/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.confirmed; +package module.decode.p25.message.pdu.confirmed; + +import module.decode.p25.reference.PDUType; +import module.decode.p25.reference.SNDCPActivationRejectReason; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.PDUType; -import decode.p25.reference.SNDCPActivationRejectReason; - public class SNDCPDeactivateTDSContext extends PDUConfirmedMessage { public final static Logger mLog = diff --git a/src/decode/p25/message/pdu/confirmed/SNDCPUserData.java b/src/module/decode/p25/message/pdu/confirmed/SNDCPUserData.java similarity index 98% rename from src/decode/p25/message/pdu/confirmed/SNDCPUserData.java rename to src/module/decode/p25/message/pdu/confirmed/SNDCPUserData.java index bb242dda..072e5a24 100644 --- a/src/decode/p25/message/pdu/confirmed/SNDCPUserData.java +++ b/src/module/decode/p25/message/pdu/confirmed/SNDCPUserData.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.confirmed; +package module.decode.p25.message.pdu.confirmed; + +import module.decode.p25.reference.IPHeaderCompression; +import module.decode.p25.reference.IPProtocol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.IPHeaderCompression; -import decode.p25.reference.IPProtocol; - public class SNDCPUserData extends PDUConfirmedMessage { public final static Logger mLog = diff --git a/src/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java b/src/module/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java similarity index 92% rename from src/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java rename to src/module/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java index a685a213..e4d2a5a4 100644 --- a/src/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/AdjacentStatusBroadcastExtended.java @@ -1,13 +1,13 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class AdjacentStatusBroadcastExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/CallAlertExtended.java b/src/module/decode/p25/message/pdu/osp/control/CallAlertExtended.java similarity index 91% rename from src/decode/p25/message/pdu/osp/control/CallAlertExtended.java rename to src/module/decode/p25/message/pdu/osp/control/CallAlertExtended.java index eeb84130..ef23c505 100644 --- a/src/decode/p25/message/pdu/osp/control/CallAlertExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/CallAlertExtended.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class CallAlertExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java b/src/module/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java similarity index 92% rename from src/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java rename to src/module/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java index 0725a28a..0cacd7c2 100644 --- a/src/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/GroupAffiliationQueryExtended.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class GroupAffiliationQueryExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java b/src/module/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java similarity index 94% rename from src/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java rename to src/module/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java index 06d312a1..03daa837 100644 --- a/src/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/GroupAffiliationResponseExtended.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Response; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Response; import edac.CRCP25; public class GroupAffiliationResponseExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java b/src/module/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java similarity index 93% rename from src/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java rename to src/module/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java index f73213fa..cb3bd496 100644 --- a/src/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/MessageUpdateExtended.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class MessageUpdateExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java b/src/module/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java similarity index 90% rename from src/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java rename to src/module/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java index 730666de..f9f812a5 100644 --- a/src/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/NetworkStatusBroadcastExtended.java @@ -1,14 +1,14 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.P25NetworkCallsign; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.P25NetworkCallsign; import edac.CRCP25; public class NetworkStatusBroadcastExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java b/src/module/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java similarity index 93% rename from src/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java rename to src/module/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java index b99858a9..a983bbf0 100644 --- a/src/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java +++ b/src/module/decode/p25/message/pdu/osp/control/ProtectionParameterBroadcast.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Encryption; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Encryption; -import decode.p25.reference.Opcode; import edac.CRCP25; public class ProtectionParameterBroadcast extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java b/src/module/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java similarity index 92% rename from src/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java rename to src/module/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java index 00206dd1..a8b6a03f 100644 --- a/src/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/RFSSStatusBroadcastExtended.java @@ -1,13 +1,13 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class RFSSStatusBroadcastExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java b/src/module/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java similarity index 97% rename from src/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java rename to src/module/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java index 32337408..72353452 100644 --- a/src/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/RoamingAddressUpdateExtended.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.P25MessageFramer; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.P25MessageFramer; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class RoamingAddressUpdateExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/StatusQueryExtended.java b/src/module/decode/p25/message/pdu/osp/control/StatusQueryExtended.java similarity index 92% rename from src/decode/p25/message/pdu/osp/control/StatusQueryExtended.java rename to src/module/decode/p25/message/pdu/osp/control/StatusQueryExtended.java index 9ff27ed4..decb3a26 100644 --- a/src/decode/p25/message/pdu/osp/control/StatusQueryExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/StatusQueryExtended.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class StatusQueryExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java b/src/module/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java similarity index 93% rename from src/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java rename to src/module/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java index 9f831af0..04bc1f4f 100644 --- a/src/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/StatusUpdateExtended.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class StatusUpdateExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java b/src/module/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java similarity index 92% rename from src/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java rename to src/module/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java index faedb1e5..17670d49 100644 --- a/src/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java +++ b/src/module/decode/p25/message/pdu/osp/control/UnitRegistrationResponseExtended.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.osp.control; +package module.decode.p25.message.pdu.osp.control; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Response; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Response; import edac.CRCP25; public class UnitRegistrationResponseExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java b/src/module/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java similarity index 94% rename from src/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java rename to src/module/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java index f8dd957a..d6354d45 100644 --- a/src/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java +++ b/src/module/decode/p25/message/pdu/osp/data/GroupDataChannelGrantExtended.java @@ -1,14 +1,14 @@ -package decode.p25.message.pdu.osp.data; +package module.decode.p25.message.pdu.osp.data; import java.util.Date; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class GroupDataChannelGrantExtended extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java b/src/module/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java similarity index 65% rename from src/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java rename to src/module/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java index efeaf5f7..8b641093 100644 --- a/src/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java +++ b/src/module/decode/p25/message/pdu/osp/data/IndividualDataChannelGrantExtended.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.osp.data; +package module.decode.p25.message.pdu.osp.data; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.UnitToUnitChannelGrantExtended; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.UnitToUnitChannelGrantExtended; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class IndividualDataChannelGrantExtended extends UnitToUnitChannelGrantExtended diff --git a/src/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java b/src/module/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java similarity index 94% rename from src/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java rename to src/module/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java index 7ec29745..7b9105d7 100644 --- a/src/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java +++ b/src/module/decode/p25/message/pdu/osp/voice/GroupVoiceChannelGrantExplicit.java @@ -1,12 +1,12 @@ -package decode.p25.message.pdu.osp.voice; +package module.decode.p25.message.pdu.osp.voice; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class GroupVoiceChannelGrantExplicit extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java b/src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java similarity index 94% rename from src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java rename to src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java index 7bc32e65..48105b9a 100644 --- a/src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java +++ b/src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantExplicit.java @@ -1,14 +1,14 @@ -package decode.p25.message.pdu.osp.voice; +package module.decode.p25.message.pdu.osp.voice; import java.util.Date; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class TelephoneInterconnectChannelGrantExplicit extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java b/src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java similarity index 78% rename from src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java rename to src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java index 320dfe4f..67f92214 100644 --- a/src/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java +++ b/src/module/decode/p25/message/pdu/osp/voice/TelephoneInterconnectChannelGrantUpdateExplicit.java @@ -1,9 +1,9 @@ -package decode.p25.message.pdu.osp.voice; +package module.decode.p25.message.pdu.osp.voice; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class TelephoneInterconnectChannelGrantUpdateExplicit extends TelephoneInterconnectChannelGrantExplicit diff --git a/src/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java b/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java similarity index 94% rename from src/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java rename to src/module/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java index 39904e82..81ef77ca 100644 --- a/src/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java +++ b/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitAnswerRequestExplicit.java @@ -1,10 +1,10 @@ -package decode.p25.message.pdu.osp.voice; +package module.decode.p25.message.pdu.osp.voice; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class UnitToUnitAnswerRequestExplicit extends PDUMessage diff --git a/src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java b/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java similarity index 65% rename from src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java rename to src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java index b065f085..f9542589 100644 --- a/src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java +++ b/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantExtended.java @@ -1,11 +1,11 @@ -package decode.p25.message.pdu.osp.voice; +package module.decode.p25.message.pdu.osp.voice; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.UnitToUnitChannelGrantExtended; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.UnitToUnitChannelGrantExtended; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class UnitToUnitVoiceChannelGrantExtended extends UnitToUnitChannelGrantExtended diff --git a/src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java b/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java similarity index 95% rename from src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java rename to src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java index 4c3ac589..a6b3ff06 100644 --- a/src/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java +++ b/src/module/decode/p25/message/pdu/osp/voice/UnitToUnitVoiceChannelGrantUpdateExtended.java @@ -1,12 +1,12 @@ -package decode.p25.message.pdu.osp.voice; +package module.decode.p25.message.pdu.osp.voice; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.pdu.PDUMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.pdu.PDUMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; import edac.CRCP25; public class UnitToUnitVoiceChannelGrantUpdateExtended extends PDUMessage diff --git a/src/decode/p25/message/tdu/TDUMessage.java b/src/module/decode/p25/message/tdu/TDUMessage.java similarity index 76% rename from src/decode/p25/message/tdu/TDUMessage.java rename to src/module/decode/p25/message/tdu/TDUMessage.java index 441baee7..0b41292f 100644 --- a/src/decode/p25/message/tdu/TDUMessage.java +++ b/src/module/decode/p25/message/tdu/TDUMessage.java @@ -1,9 +1,9 @@ -package decode.p25.message.tdu; +package module.decode.p25.message.tdu; +import module.decode.p25.message.P25Message; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.P25Message; -import decode.p25.reference.DataUnitID; import edac.CRC; public class TDUMessage extends P25Message diff --git a/src/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java similarity index 90% rename from src/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java rename to src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java index ae47e968..2cc67bc7 100644 --- a/src/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcast.java @@ -1,14 +1,14 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Service; - public class AdjacentSiteStatusBroadcast extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java similarity index 93% rename from src/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java rename to src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java index 7e3ff089..d0863292 100644 --- a/src/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/AdjacentSiteStatusBroadcastExplicit.java @@ -1,11 +1,11 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class AdjacentSiteStatusBroadcastExplicit extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/CallAlert.java b/src/module/decode/p25/message/tdu/lc/CallAlert.java similarity index 92% rename from src/decode/p25/message/tdu/lc/CallAlert.java rename to src/module/decode/p25/message/tdu/lc/CallAlert.java index 4980933b..6f746519 100644 --- a/src/decode/p25/message/tdu/lc/CallAlert.java +++ b/src/module/decode/p25/message/tdu/lc/CallAlert.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class CallAlert extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/CallTermination.java b/src/module/decode/p25/message/tdu/lc/CallTermination.java similarity index 90% rename from src/decode/p25/message/tdu/lc/CallTermination.java rename to src/module/decode/p25/message/tdu/lc/CallTermination.java index 81e5d699..2a2baeeb 100644 --- a/src/decode/p25/message/tdu/lc/CallTermination.java +++ b/src/module/decode/p25/message/tdu/lc/CallTermination.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class CallTermination extends TDULinkControlMessage { private final static Logger mLog = LoggerFactory.getLogger( CallTermination.class ); diff --git a/src/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java similarity index 93% rename from src/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java rename to src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java index db607906..2730cec4 100644 --- a/src/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java +++ b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdate.java @@ -1,11 +1,11 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.reference.LinkControlOpcode; - public class ChannelIdentifierUpdate extends TDULinkControlMessage implements IBandIdentifier { diff --git a/src/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java similarity index 93% rename from src/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java rename to src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java index 3aa1a959..b70210d5 100644 --- a/src/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/ChannelIdentifierUpdateExplicit.java @@ -1,11 +1,11 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.reference.LinkControlOpcode; - public class ChannelIdentifierUpdateExplicit extends TDULinkControlMessage implements IBandIdentifier { diff --git a/src/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java b/src/module/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java similarity index 93% rename from src/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java rename to src/module/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java index 7e9e6061..55226ec6 100644 --- a/src/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java +++ b/src/module/decode/p25/message/tdu/lc/ExtendedFunctionCommand.java @@ -1,11 +1,11 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.ExtendedFunction; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.ExtendedFunction; -import decode.p25.reference.LinkControlOpcode; - public class ExtendedFunctionCommand extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/GroupAffiliationQuery.java b/src/module/decode/p25/message/tdu/lc/GroupAffiliationQuery.java similarity index 92% rename from src/decode/p25/message/tdu/lc/GroupAffiliationQuery.java rename to src/module/decode/p25/message/tdu/lc/GroupAffiliationQuery.java index ef8e56b5..9a28ea64 100644 --- a/src/decode/p25/message/tdu/lc/GroupAffiliationQuery.java +++ b/src/module/decode/p25/message/tdu/lc/GroupAffiliationQuery.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class GroupAffiliationQuery extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java similarity index 95% rename from src/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java rename to src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java index 579d0db5..8f9521ef 100644 --- a/src/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java +++ b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdate.java @@ -1,15 +1,15 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; import java.util.Set; import java.util.TreeSet; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.LinkControlOpcode; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.LinkControlOpcode; - public class GroupVoiceChannelUpdate extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java similarity index 92% rename from src/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java rename to src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java index a934e37f..16a0211b 100644 --- a/src/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUpdateExplicit.java @@ -1,14 +1,14 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.P25Message.DuplexMode; +import module.decode.p25.message.P25Message.SessionMode; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.P25Message.DuplexMode; -import decode.p25.message.P25Message.SessionMode; -import decode.p25.reference.LinkControlOpcode; - public class GroupVoiceChannelUpdateExplicit extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java similarity index 95% rename from src/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java rename to src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java index b4268dc8..3db30f4e 100644 --- a/src/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java +++ b/src/module/decode/p25/message/tdu/lc/GroupVoiceChannelUser.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class GroupVoiceChannelUser extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/MessageUpdate.java b/src/module/decode/p25/message/tdu/lc/MessageUpdate.java similarity index 93% rename from src/decode/p25/message/tdu/lc/MessageUpdate.java rename to src/module/decode/p25/message/tdu/lc/MessageUpdate.java index 6e719b2a..d0130219 100644 --- a/src/decode/p25/message/tdu/lc/MessageUpdate.java +++ b/src/module/decode/p25/message/tdu/lc/MessageUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class MessageUpdate extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java b/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java similarity index 90% rename from src/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java rename to src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java index a240a49b..b298fad6 100644 --- a/src/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcast.java @@ -1,13 +1,13 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Service; - public class NetworkStatusBroadcast extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java b/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java similarity index 92% rename from src/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java rename to src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java index db36edc5..ba136500 100644 --- a/src/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/NetworkStatusBroadcastExplicit.java @@ -1,13 +1,13 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Service; - public class NetworkStatusBroadcastExplicit extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java b/src/module/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java similarity index 91% rename from src/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java rename to src/module/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java index 3d5a98d6..61b58083 100644 --- a/src/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/ProtectionParameterBroadcast.java @@ -1,11 +1,11 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.Encryption; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.Encryption; -import decode.p25.reference.LinkControlOpcode; - public class ProtectionParameterBroadcast extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java b/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java similarity index 91% rename from src/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java rename to src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java index d67fbb7b..7864b3c8 100644 --- a/src/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcast.java @@ -1,13 +1,13 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class RFSSStatusBroadcast extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java b/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java similarity index 93% rename from src/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java rename to src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java index acfca7cf..452b3d51 100644 --- a/src/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/RFSSStatusBroadcastExplicit.java @@ -1,13 +1,13 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class RFSSStatusBroadcastExplicit extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java b/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java similarity index 93% rename from src/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java rename to src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java index d454c2e3..6a3587df 100644 --- a/src/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcast.java @@ -1,13 +1,13 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class SecondaryControlChannelBroadcast extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java b/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java similarity index 92% rename from src/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java rename to src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java index 6607c7e2..fb35f425 100644 --- a/src/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java +++ b/src/module/decode/p25/message/tdu/lc/SecondaryControlChannelBroadcastExplicit.java @@ -1,13 +1,13 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.osp.control.SystemService; +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.osp.control.SystemService; -import decode.p25.reference.LinkControlOpcode; - public class SecondaryControlChannelBroadcastExplicit extends TDULinkControlMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tdu/lc/StatusQuery.java b/src/module/decode/p25/message/tdu/lc/StatusQuery.java similarity index 92% rename from src/decode/p25/message/tdu/lc/StatusQuery.java rename to src/module/decode/p25/message/tdu/lc/StatusQuery.java index c0e4da8a..22ffeb3e 100644 --- a/src/decode/p25/message/tdu/lc/StatusQuery.java +++ b/src/module/decode/p25/message/tdu/lc/StatusQuery.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class StatusQuery extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/StatusUpdate.java b/src/module/decode/p25/message/tdu/lc/StatusUpdate.java similarity index 94% rename from src/decode/p25/message/tdu/lc/StatusUpdate.java rename to src/module/decode/p25/message/tdu/lc/StatusUpdate.java index 949687da..2f99d620 100644 --- a/src/decode/p25/message/tdu/lc/StatusUpdate.java +++ b/src/module/decode/p25/message/tdu/lc/StatusUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class StatusUpdate extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/SystemServiceBroadcast.java b/src/module/decode/p25/message/tdu/lc/SystemServiceBroadcast.java similarity index 91% rename from src/decode/p25/message/tdu/lc/SystemServiceBroadcast.java rename to src/module/decode/p25/message/tdu/lc/SystemServiceBroadcast.java index 4a13d385..70188f08 100644 --- a/src/decode/p25/message/tdu/lc/SystemServiceBroadcast.java +++ b/src/module/decode/p25/message/tdu/lc/SystemServiceBroadcast.java @@ -1,13 +1,13 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; import java.util.List; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Service; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Service; - public class SystemServiceBroadcast extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/TDULCMessageFactory.java b/src/module/decode/p25/message/tdu/lc/TDULCMessageFactory.java similarity index 97% rename from src/decode/p25/message/tdu/lc/TDULCMessageFactory.java rename to src/module/decode/p25/message/tdu/lc/TDULCMessageFactory.java index 230348c2..67801d93 100644 --- a/src/decode/p25/message/tdu/lc/TDULCMessageFactory.java +++ b/src/module/decode/p25/message/tdu/lc/TDULCMessageFactory.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.Vendor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.Vendor; - public class TDULCMessageFactory { diff --git a/src/decode/p25/message/tdu/lc/TDULinkControlMessage.java b/src/module/decode/p25/message/tdu/lc/TDULinkControlMessage.java similarity index 96% rename from src/decode/p25/message/tdu/lc/TDULinkControlMessage.java rename to src/module/decode/p25/message/tdu/lc/TDULinkControlMessage.java index a33424f6..35d8b979 100644 --- a/src/decode/p25/message/tdu/lc/TDULinkControlMessage.java +++ b/src/module/decode/p25/message/tdu/lc/TDULinkControlMessage.java @@ -1,16 +1,17 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.message.P25Message; +import module.decode.p25.message.tsbk.motorola.MotorolaOpcode; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.LinkControlOpcode; +import module.decode.p25.reference.Vendor; +import module.decode.p25.reference.VendorLinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.P25Message; -import decode.p25.message.tsbk.motorola.MotorolaOpcode; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.LinkControlOpcode; -import decode.p25.reference.Vendor; -import decode.p25.reference.VendorLinkControlOpcode; import edac.CRC; import edac.Golay24; import edac.ReedSolomon_63_47_17; diff --git a/src/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java b/src/module/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java similarity index 94% rename from src/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java rename to src/module/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java index 1efc2d83..fd8f4c9f 100644 --- a/src/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java +++ b/src/module/decode/p25/message/tdu/lc/TelephoneInterconnectAnswerRequest.java @@ -1,14 +1,14 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; import java.util.ArrayList; import java.util.List; +import module.decode.p25.reference.Digit; +import module.decode.p25.reference.LinkControlOpcode; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.Digit; -import decode.p25.reference.LinkControlOpcode; - public class TelephoneInterconnectAnswerRequest extends TDULinkControlMessage { private final static Logger mLog = LoggerFactory.getLogger( TelephoneInterconnectAnswerRequest.class ); diff --git a/src/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java b/src/module/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java similarity index 95% rename from src/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java rename to src/module/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java index 6589339c..d10b347e 100644 --- a/src/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java +++ b/src/module/decode/p25/message/tdu/lc/TelephoneInterconnectVoiceChannelUser.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class TelephoneInterconnectVoiceChannelUser extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java b/src/module/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java similarity index 94% rename from src/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java rename to src/module/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java index 96af11b2..7cdc9d59 100644 --- a/src/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java +++ b/src/module/decode/p25/message/tdu/lc/UnitAuthenticationCommand.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class UnitAuthenticationCommand extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/UnitRegistrationCommand.java b/src/module/decode/p25/message/tdu/lc/UnitRegistrationCommand.java similarity index 94% rename from src/decode/p25/message/tdu/lc/UnitRegistrationCommand.java rename to src/module/decode/p25/message/tdu/lc/UnitRegistrationCommand.java index 6b8e4e2c..d66360d6 100644 --- a/src/decode/p25/message/tdu/lc/UnitRegistrationCommand.java +++ b/src/module/decode/p25/message/tdu/lc/UnitRegistrationCommand.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class UnitRegistrationCommand extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java b/src/module/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java similarity index 95% rename from src/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java rename to src/module/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java index 880bccea..90bacc50 100644 --- a/src/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java +++ b/src/module/decode/p25/message/tdu/lc/UnitToUnitAnswerRequest.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class UnitToUnitAnswerRequest extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java b/src/module/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java similarity index 95% rename from src/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java rename to src/module/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java index b1430222..47157a58 100644 --- a/src/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java +++ b/src/module/decode/p25/message/tdu/lc/UnitToUnitVoiceChannelUser.java @@ -1,10 +1,10 @@ -package decode.p25.message.tdu.lc; +package module.decode.p25.message.tdu.lc; + +import module.decode.p25.reference.LinkControlOpcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import decode.p25.reference.LinkControlOpcode; - public class UnitToUnitVoiceChannelUser extends TDULinkControlMessage { private final static Logger mLog = diff --git a/src/decode/p25/message/tsbk/ChannelGrant.java b/src/module/decode/p25/message/tsbk/ChannelGrant.java similarity index 85% rename from src/decode/p25/message/tsbk/ChannelGrant.java rename to src/module/decode/p25/message/tsbk/ChannelGrant.java index f419a4cd..b0c81548 100644 --- a/src/decode/p25/message/tsbk/ChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/ChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; public abstract class ChannelGrant extends ServiceMessage implements IdentifierReceiver diff --git a/src/decode/p25/message/tsbk/GroupChannelGrant.java b/src/module/decode/p25/message/tsbk/GroupChannelGrant.java similarity index 92% rename from src/decode/p25/message/tsbk/GroupChannelGrant.java rename to src/module/decode/p25/message/tsbk/GroupChannelGrant.java index e224dea2..446f3631 100644 --- a/src/decode/p25/message/tsbk/GroupChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/GroupChannelGrant.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.reference.DataUnitID; import alias.Alias; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public abstract class GroupChannelGrant extends ChannelGrant { diff --git a/src/decode/p25/message/tsbk/GroupChannelGrantExplicit.java b/src/module/decode/p25/message/tsbk/GroupChannelGrantExplicit.java similarity index 92% rename from src/decode/p25/message/tsbk/GroupChannelGrantExplicit.java rename to src/module/decode/p25/message/tsbk/GroupChannelGrantExplicit.java index 7bf8d4a7..5233162f 100644 --- a/src/decode/p25/message/tsbk/GroupChannelGrantExplicit.java +++ b/src/module/decode/p25/message/tsbk/GroupChannelGrantExplicit.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.reference.DataUnitID; public abstract class GroupChannelGrantExplicit extends ChannelGrant { diff --git a/src/decode/p25/message/tsbk/GroupMultiChannelGrant.java b/src/module/decode/p25/message/tsbk/GroupMultiChannelGrant.java similarity index 91% rename from src/decode/p25/message/tsbk/GroupMultiChannelGrant.java rename to src/module/decode/p25/message/tsbk/GroupMultiChannelGrant.java index 41262669..38a64d8f 100644 --- a/src/decode/p25/message/tsbk/GroupMultiChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/GroupMultiChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; public abstract class GroupMultiChannelGrant extends ChannelGrant implements IdentifierReceiver diff --git a/src/decode/p25/message/tsbk/ServiceMessage.java b/src/module/decode/p25/message/tsbk/ServiceMessage.java similarity index 88% rename from src/decode/p25/message/tsbk/ServiceMessage.java rename to src/module/decode/p25/message/tsbk/ServiceMessage.java index 88548fca..78d200ca 100644 --- a/src/decode/p25/message/tsbk/ServiceMessage.java +++ b/src/module/decode/p25/message/tsbk/ServiceMessage.java @@ -1,8 +1,8 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public abstract class ServiceMessage extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/TSBKMessage.java b/src/module/decode/p25/message/tsbk/TSBKMessage.java similarity index 91% rename from src/decode/p25/message/tsbk/TSBKMessage.java rename to src/module/decode/p25/message/tsbk/TSBKMessage.java index 6aa79d87..cbbec09e 100644 --- a/src/decode/p25/message/tsbk/TSBKMessage.java +++ b/src/module/decode/p25/message/tsbk/TSBKMessage.java @@ -1,13 +1,13 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.message.P25Message; +import module.decode.p25.message.tsbk.motorola.MotorolaOpcode; +import module.decode.p25.message.tsbk.vendor.VendorOpcode; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Vendor; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.P25Message; -import decode.p25.message.tsbk.motorola.MotorolaOpcode; -import decode.p25.message.tsbk.vendor.VendorOpcode; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Vendor; import edac.CRC; public class TSBKMessage extends P25Message diff --git a/src/decode/p25/message/tsbk/TSBKMessageFactory.java b/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java similarity index 67% rename from src/decode/p25/message/tsbk/TSBKMessageFactory.java rename to src/module/decode/p25/message/tsbk/TSBKMessageFactory.java index dae0b96b..872e11ef 100644 --- a/src/decode/p25/message/tsbk/TSBKMessageFactory.java +++ b/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java @@ -1,62 +1,62 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.message.tsbk.motorola.ControlChannelBaseStationIdentification; +import module.decode.p25.message.tsbk.motorola.MotorolaOpcode; +import module.decode.p25.message.tsbk.motorola.MotorolaTSBKMessage; +import module.decode.p25.message.tsbk.motorola.PatchGroupAdd; +import module.decode.p25.message.tsbk.motorola.PatchGroupDelete; +import module.decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrant; +import module.decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrantUpdate; +import module.decode.p25.message.tsbk.motorola.PlannedControlChannnelShutdown; +import module.decode.p25.message.tsbk.motorola.SystemLoading; +import module.decode.p25.message.tsbk.motorola.TrafficChannelBaseStationIdentification; +import module.decode.p25.message.tsbk.osp.control.AcknowledgeResponse; +import module.decode.p25.message.tsbk.osp.control.AdjacentStatusBroadcast; +import module.decode.p25.message.tsbk.osp.control.AuthenticationCommand; +import module.decode.p25.message.tsbk.osp.control.CallAlert; +import module.decode.p25.message.tsbk.osp.control.DenyResponse; +import module.decode.p25.message.tsbk.osp.control.ExtendedFunctionCommand; +import module.decode.p25.message.tsbk.osp.control.GroupAffiliationQuery; +import module.decode.p25.message.tsbk.osp.control.GroupAffiliationResponse; +import module.decode.p25.message.tsbk.osp.control.IdentifierUpdateNonVUHF; +import module.decode.p25.message.tsbk.osp.control.IdentifierUpdateVUHF; +import module.decode.p25.message.tsbk.osp.control.LocationRegistrationResponse; +import module.decode.p25.message.tsbk.osp.control.MessageUpdate; +import module.decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast; +import module.decode.p25.message.tsbk.osp.control.ProtectionParameterUpdate; +import module.decode.p25.message.tsbk.osp.control.QueuedResponse; +import module.decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast; +import module.decode.p25.message.tsbk.osp.control.RadioUnitMonitorCommand; +import module.decode.p25.message.tsbk.osp.control.RoamingAddressCommand; +import module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast; +import module.decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcastExplicit; +import module.decode.p25.message.tsbk.osp.control.StatusQuery; +import module.decode.p25.message.tsbk.osp.control.StatusUpdate; +import module.decode.p25.message.tsbk.osp.control.SystemServiceBroadcast; +import module.decode.p25.message.tsbk.osp.control.TimeAndDateAnnouncement; +import module.decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge; +import module.decode.p25.message.tsbk.osp.control.UnitRegistrationCommand; +import module.decode.p25.message.tsbk.osp.control.UnitRegistrationResponse; +import module.decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncement; +import module.decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncementExplicit; +import module.decode.p25.message.tsbk.osp.data.GroupDataChannelGrant; +import module.decode.p25.message.tsbk.osp.data.IndividualDataChannelGrant; +import module.decode.p25.message.tsbk.osp.data.SNDCPDataChannelAnnouncementExplicit; +import module.decode.p25.message.tsbk.osp.data.SNDCPDataChannelGrant; +import module.decode.p25.message.tsbk.osp.data.SNDCPDataPageRequest; +import module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant; +import module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate; +import module.decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit; +import module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectAnswerRequest; +import module.decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrant; +import module.decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest; +import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrant; +import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Vendor; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.motorola.ControlChannelBaseStationIdentification; -import decode.p25.message.tsbk.motorola.MotorolaOpcode; -import decode.p25.message.tsbk.motorola.MotorolaTSBKMessage; -import decode.p25.message.tsbk.motorola.PatchGroupAdd; -import decode.p25.message.tsbk.motorola.PatchGroupDelete; -import decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrant; -import decode.p25.message.tsbk.motorola.PatchGroupVoiceChannelGrantUpdate; -import decode.p25.message.tsbk.motorola.PlannedControlChannnelShutdown; -import decode.p25.message.tsbk.motorola.SystemLoading; -import decode.p25.message.tsbk.motorola.TrafficChannelBaseStationIdentification; -import decode.p25.message.tsbk.osp.control.AcknowledgeResponse; -import decode.p25.message.tsbk.osp.control.AdjacentStatusBroadcast; -import decode.p25.message.tsbk.osp.control.AuthenticationCommand; -import decode.p25.message.tsbk.osp.control.CallAlert; -import decode.p25.message.tsbk.osp.control.DenyResponse; -import decode.p25.message.tsbk.osp.control.ExtendedFunctionCommand; -import decode.p25.message.tsbk.osp.control.GroupAffiliationQuery; -import decode.p25.message.tsbk.osp.control.GroupAffiliationResponse; -import decode.p25.message.tsbk.osp.control.IdentifierUpdateNonVUHF; -import decode.p25.message.tsbk.osp.control.IdentifierUpdateVUHF; -import decode.p25.message.tsbk.osp.control.LocationRegistrationResponse; -import decode.p25.message.tsbk.osp.control.MessageUpdate; -import decode.p25.message.tsbk.osp.control.NetworkStatusBroadcast; -import decode.p25.message.tsbk.osp.control.ProtectionParameterUpdate; -import decode.p25.message.tsbk.osp.control.QueuedResponse; -import decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast; -import decode.p25.message.tsbk.osp.control.RadioUnitMonitorCommand; -import decode.p25.message.tsbk.osp.control.RoamingAddressCommand; -import decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcast; -import decode.p25.message.tsbk.osp.control.SecondaryControlChannelBroadcastExplicit; -import decode.p25.message.tsbk.osp.control.StatusQuery; -import decode.p25.message.tsbk.osp.control.StatusUpdate; -import decode.p25.message.tsbk.osp.control.SystemServiceBroadcast; -import decode.p25.message.tsbk.osp.control.TimeAndDateAnnouncement; -import decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge; -import decode.p25.message.tsbk.osp.control.UnitRegistrationCommand; -import decode.p25.message.tsbk.osp.control.UnitRegistrationResponse; -import decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncement; -import decode.p25.message.tsbk.osp.data.GroupDataChannelAnnouncementExplicit; -import decode.p25.message.tsbk.osp.data.GroupDataChannelGrant; -import decode.p25.message.tsbk.osp.data.IndividualDataChannelGrant; -import decode.p25.message.tsbk.osp.data.SNDCPDataChannelAnnouncementExplicit; -import decode.p25.message.tsbk.osp.data.SNDCPDataChannelGrant; -import decode.p25.message.tsbk.osp.data.SNDCPDataPageRequest; -import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant; -import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate; -import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit; -import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectAnswerRequest; -import decode.p25.message.tsbk.osp.voice.TelephoneInterconnectVoiceChannelGrant; -import decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest; -import decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrant; -import decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Vendor; public class TSBKMessageFactory { diff --git a/src/decode/p25/message/tsbk/UnitChannelGrant.java b/src/module/decode/p25/message/tsbk/UnitChannelGrant.java similarity index 91% rename from src/decode/p25/message/tsbk/UnitChannelGrant.java rename to src/module/decode/p25/message/tsbk/UnitChannelGrant.java index 678fe0b3..5d49f4b7 100644 --- a/src/decode/p25/message/tsbk/UnitChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/UnitChannelGrant.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk; +package module.decode.p25.message.tsbk; +import module.decode.p25.reference.DataUnitID; import alias.Alias; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public abstract class UnitChannelGrant extends ChannelGrant { diff --git a/src/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java b/src/module/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java similarity index 90% rename from src/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java rename to src/module/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java index f441f87f..987d84ad 100644 --- a/src/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java +++ b/src/module/decode/p25/message/tsbk/motorola/ControlChannelBaseStationIdentification.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; public class ControlChannelBaseStationIdentification extends MotorolaTSBKMessage implements IdentifierReceiver diff --git a/src/decode/p25/message/tsbk/motorola/MotorolaOpcode.java b/src/module/decode/p25/message/tsbk/motorola/MotorolaOpcode.java similarity index 98% rename from src/decode/p25/message/tsbk/motorola/MotorolaOpcode.java rename to src/module/decode/p25/message/tsbk/motorola/MotorolaOpcode.java index 848578bb..d610874d 100644 --- a/src/decode/p25/message/tsbk/motorola/MotorolaOpcode.java +++ b/src/module/decode/p25/message/tsbk/motorola/MotorolaOpcode.java @@ -1,4 +1,4 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; public enum MotorolaOpcode { diff --git a/src/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java b/src/module/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java similarity index 84% rename from src/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java rename to src/module/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java index 9f5b3372..09cbaa4c 100644 --- a/src/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java +++ b/src/module/decode/p25/message/tsbk/motorola/MotorolaTSBKMessage.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; public class MotorolaTSBKMessage extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/motorola/PatchGroup.java b/src/module/decode/p25/message/tsbk/motorola/PatchGroup.java similarity index 94% rename from src/decode/p25/message/tsbk/motorola/PatchGroup.java rename to src/module/decode/p25/message/tsbk/motorola/PatchGroup.java index 594635f8..f5c5305a 100644 --- a/src/decode/p25/message/tsbk/motorola/PatchGroup.java +++ b/src/module/decode/p25/message/tsbk/motorola/PatchGroup.java @@ -1,8 +1,8 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public abstract class PatchGroup extends MotorolaTSBKMessage { diff --git a/src/decode/p25/message/tsbk/motorola/PatchGroupAdd.java b/src/module/decode/p25/message/tsbk/motorola/PatchGroupAdd.java similarity index 79% rename from src/decode/p25/message/tsbk/motorola/PatchGroupAdd.java rename to src/module/decode/p25/message/tsbk/motorola/PatchGroupAdd.java index 737b8677..53e8504c 100644 --- a/src/decode/p25/message/tsbk/motorola/PatchGroupAdd.java +++ b/src/module/decode/p25/message/tsbk/motorola/PatchGroupAdd.java @@ -1,8 +1,8 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public class PatchGroupAdd extends PatchGroup { diff --git a/src/decode/p25/message/tsbk/motorola/PatchGroupDelete.java b/src/module/decode/p25/message/tsbk/motorola/PatchGroupDelete.java similarity index 79% rename from src/decode/p25/message/tsbk/motorola/PatchGroupDelete.java rename to src/module/decode/p25/message/tsbk/motorola/PatchGroupDelete.java index 0d9029aa..a8496d55 100644 --- a/src/decode/p25/message/tsbk/motorola/PatchGroupDelete.java +++ b/src/module/decode/p25/message/tsbk/motorola/PatchGroupDelete.java @@ -1,8 +1,8 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public class PatchGroupDelete extends PatchGroup { diff --git a/src/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java b/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java similarity index 94% rename from src/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java rename to src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java index 4993cb8d..6d7f5e5a 100644 --- a/src/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; public class PatchGroupVoiceChannelGrant extends MotorolaTSBKMessage implements IdentifierReceiver diff --git a/src/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java b/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java similarity index 94% rename from src/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java rename to src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java index 612fa27e..9dfa50b2 100644 --- a/src/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java +++ b/src/module/decode/p25/message/tsbk/motorola/PatchGroupVoiceChannelGrantUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; public class PatchGroupVoiceChannelGrantUpdate extends MotorolaTSBKMessage implements IdentifierReceiver diff --git a/src/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java b/src/module/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java similarity index 83% rename from src/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java rename to src/module/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java index ee2236c6..19f57c30 100644 --- a/src/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java +++ b/src/module/decode/p25/message/tsbk/motorola/PlannedControlChannnelShutdown.java @@ -1,8 +1,8 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public class PlannedControlChannnelShutdown extends MotorolaTSBKMessage { diff --git a/src/decode/p25/message/tsbk/motorola/SystemLoading.java b/src/module/decode/p25/message/tsbk/motorola/SystemLoading.java similarity index 79% rename from src/decode/p25/message/tsbk/motorola/SystemLoading.java rename to src/module/decode/p25/message/tsbk/motorola/SystemLoading.java index fb0c9225..a77df53d 100644 --- a/src/decode/p25/message/tsbk/motorola/SystemLoading.java +++ b/src/module/decode/p25/message/tsbk/motorola/SystemLoading.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; public class SystemLoading extends MotorolaTSBKMessage { diff --git a/src/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java b/src/module/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java similarity index 92% rename from src/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java rename to src/module/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java index ba83eea6..d55ea401 100644 --- a/src/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java +++ b/src/module/decode/p25/message/tsbk/motorola/TrafficChannelBaseStationIdentification.java @@ -1,8 +1,8 @@ -package decode.p25.message.tsbk.motorola; +package module.decode.p25.message.tsbk.motorola; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public class TrafficChannelBaseStationIdentification extends MotorolaTSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java b/src/module/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java similarity index 90% rename from src/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java rename to src/module/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java index 7f399f4e..5e0bc56c 100644 --- a/src/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java +++ b/src/module/decode/p25/message/tsbk/osp/control/AcknowledgeResponse.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class AcknowledgeResponse extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java similarity index 90% rename from src/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java rename to src/module/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java index b51d91da..227bbcc3 100644 --- a/src/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/AdjacentStatusBroadcast.java @@ -1,12 +1,12 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class AdjacentStatusBroadcast extends TSBKMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java b/src/module/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java similarity index 86% rename from src/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java rename to src/module/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java index 62db6654..8fc0f6b2 100644 --- a/src/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java +++ b/src/module/decode/p25/message/tsbk/osp/control/AuthenticationCommand.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class AuthenticationCommand extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/CallAlert.java b/src/module/decode/p25/message/tsbk/osp/control/CallAlert.java similarity index 84% rename from src/decode/p25/message/tsbk/osp/control/CallAlert.java rename to src/module/decode/p25/message/tsbk/osp/control/CallAlert.java index 50a531e5..ce95f61f 100644 --- a/src/decode/p25/message/tsbk/osp/control/CallAlert.java +++ b/src/module/decode/p25/message/tsbk/osp/control/CallAlert.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class CallAlert extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/DenyResponse.java b/src/module/decode/p25/message/tsbk/osp/control/DenyResponse.java similarity index 87% rename from src/decode/p25/message/tsbk/osp/control/DenyResponse.java rename to src/module/decode/p25/message/tsbk/osp/control/DenyResponse.java index 1981488c..7e3e67fe 100644 --- a/src/decode/p25/message/tsbk/osp/control/DenyResponse.java +++ b/src/module/decode/p25/message/tsbk/osp/control/DenyResponse.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.DenyReason; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.DenyReason; -import decode.p25.reference.Opcode; public class DenyResponse extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java b/src/module/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java similarity index 86% rename from src/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java rename to src/module/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java index f63d2422..92cd17e8 100644 --- a/src/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java +++ b/src/module/decode/p25/message/tsbk/osp/control/ExtendedFunctionCommand.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.ExtendedFunction; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.ExtendedFunction; -import decode.p25.reference.Opcode; public class ExtendedFunctionCommand extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java b/src/module/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java similarity index 85% rename from src/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java rename to src/module/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java index 58c4bad1..683ab076 100644 --- a/src/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java +++ b/src/module/decode/p25/message/tsbk/osp/control/GroupAffiliationQuery.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class GroupAffiliationQuery extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java b/src/module/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java similarity index 87% rename from src/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java rename to src/module/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java index 3c59ef10..2c964a0c 100644 --- a/src/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java +++ b/src/module/decode/p25/message/tsbk/osp/control/GroupAffiliationResponse.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Response; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Response; public class GroupAffiliationResponse extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java similarity index 82% rename from src/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java rename to src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java index 6df9feb9..1f3e7e5e 100644 --- a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java +++ b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; public abstract class IdentifierUpdate extends TSBKMessage implements IBandIdentifier diff --git a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java similarity index 88% rename from src/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java rename to src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java index 60e62f00..e4719edb 100644 --- a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java +++ b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateNonVUHF.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class IdentifierUpdateNonVUHF extends IdentifierUpdate { diff --git a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java similarity index 89% rename from src/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java rename to src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java index d6dca040..3dffa8cd 100644 --- a/src/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java +++ b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateVUHF.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class IdentifierUpdateVUHF extends IdentifierUpdate { diff --git a/src/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java b/src/module/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java similarity index 86% rename from src/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java rename to src/module/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java index 2d8b3375..aa84caf6 100644 --- a/src/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java +++ b/src/module/decode/p25/message/tsbk/osp/control/LocationRegistrationResponse.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Response; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Response; public class LocationRegistrationResponse extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/MessageUpdate.java b/src/module/decode/p25/message/tsbk/osp/control/MessageUpdate.java similarity index 86% rename from src/decode/p25/message/tsbk/osp/control/MessageUpdate.java rename to src/module/decode/p25/message/tsbk/osp/control/MessageUpdate.java index 4fcbee3b..c3a17372 100644 --- a/src/decode/p25/message/tsbk/osp/control/MessageUpdate.java +++ b/src/module/decode/p25/message/tsbk/osp/control/MessageUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class MessageUpdate extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java similarity index 86% rename from src/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java rename to src/module/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java index fcce3e48..9acd5b99 100644 --- a/src/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/NetworkStatusBroadcast.java @@ -1,13 +1,13 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.P25NetworkCallsign; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.P25NetworkCallsign; public class NetworkStatusBroadcast extends TSBKMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java b/src/module/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java similarity index 84% rename from src/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java rename to src/module/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java index aa2d9e81..38134ae3 100644 --- a/src/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java +++ b/src/module/decode/p25/message/tsbk/osp/control/ProtectionParameterUpdate.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Encryption; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Encryption; -import decode.p25.reference.Opcode; public class ProtectionParameterUpdate extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/QueuedResponse.java b/src/module/decode/p25/message/tsbk/osp/control/QueuedResponse.java similarity index 88% rename from src/decode/p25/message/tsbk/osp/control/QueuedResponse.java rename to src/module/decode/p25/message/tsbk/osp/control/QueuedResponse.java index 8548d0e3..4a7de58b 100644 --- a/src/decode/p25/message/tsbk/osp/control/QueuedResponse.java +++ b/src/module/decode/p25/message/tsbk/osp/control/QueuedResponse.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.QueuedResponseReason; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.QueuedResponseReason; public class QueuedResponse extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java similarity index 88% rename from src/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java rename to src/module/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java index 1939bb93..b1f95c7a 100644 --- a/src/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/RFSSStatusBroadcast.java @@ -1,12 +1,12 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class RFSSStatusBroadcast extends TSBKMessage implements IdentifierReceiver { diff --git a/src/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java b/src/module/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java similarity index 86% rename from src/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java rename to src/module/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java index a7e8217d..e4df302e 100644 --- a/src/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java +++ b/src/module/decode/p25/message/tsbk/osp/control/RadioUnitMonitorCommand.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class RadioUnitMonitorCommand extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java b/src/module/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java similarity index 82% rename from src/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java rename to src/module/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java index 27967138..e5869719 100644 --- a/src/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java +++ b/src/module/decode/p25/message/tsbk/osp/control/RoamingAddressCommand.java @@ -1,12 +1,12 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Response; +import module.decode.p25.reference.StackOperation; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Response; -import decode.p25.reference.StackOperation; public class RoamingAddressCommand extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java similarity index 90% rename from src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java rename to src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java index ac4cb6b6..48b61992 100644 --- a/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcast.java @@ -1,15 +1,16 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; + +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class SecondaryControlChannelBroadcast extends TSBKMessage implements IdentifierReceiver, Comparable diff --git a/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java b/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java similarity index 91% rename from src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java rename to src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java index 9cdb0369..e918901f 100644 --- a/src/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java +++ b/src/module/decode/p25/message/tsbk/osp/control/SecondaryControlChannelBroadcastExplicit.java @@ -1,12 +1,13 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; + +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class SecondaryControlChannelBroadcastExplicit extends SecondaryControlChannelBroadcast diff --git a/src/decode/p25/message/tsbk/osp/control/StatusQuery.java b/src/module/decode/p25/message/tsbk/osp/control/StatusQuery.java similarity index 84% rename from src/decode/p25/message/tsbk/osp/control/StatusQuery.java rename to src/module/decode/p25/message/tsbk/osp/control/StatusQuery.java index 321bf613..8d856dbc 100644 --- a/src/decode/p25/message/tsbk/osp/control/StatusQuery.java +++ b/src/module/decode/p25/message/tsbk/osp/control/StatusQuery.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class StatusQuery extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/StatusUpdate.java b/src/module/decode/p25/message/tsbk/osp/control/StatusUpdate.java similarity index 87% rename from src/decode/p25/message/tsbk/osp/control/StatusUpdate.java rename to src/module/decode/p25/message/tsbk/osp/control/StatusUpdate.java index ac4da983..59290a61 100644 --- a/src/decode/p25/message/tsbk/osp/control/StatusUpdate.java +++ b/src/module/decode/p25/message/tsbk/osp/control/StatusUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class StatusUpdate extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/SystemService.java b/src/module/decode/p25/message/tsbk/osp/control/SystemService.java similarity index 93% rename from src/decode/p25/message/tsbk/osp/control/SystemService.java rename to src/module/decode/p25/message/tsbk/osp/control/SystemService.java index b87c0d7e..e296775a 100644 --- a/src/decode/p25/message/tsbk/osp/control/SystemService.java +++ b/src/module/decode/p25/message/tsbk/osp/control/SystemService.java @@ -1,4 +1,4 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; public class SystemService { diff --git a/src/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java similarity index 83% rename from src/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java rename to src/module/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java index ed3f298f..4ff2b25f 100644 --- a/src/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/SystemServiceBroadcast.java @@ -1,13 +1,13 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; import java.util.List; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Service; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Service; public class SystemServiceBroadcast extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java b/src/module/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java similarity index 94% rename from src/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java rename to src/module/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java index 08f36752..acd12f83 100644 --- a/src/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java +++ b/src/module/decode/p25/message/tsbk/osp/control/TimeAndDateAnnouncement.java @@ -1,4 +1,4 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -6,11 +6,11 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class TimeAndDateAnnouncement extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java b/src/module/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java similarity index 83% rename from src/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java rename to src/module/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java index 2bd147fc..483bdea5 100644 --- a/src/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java +++ b/src/module/decode/p25/message/tsbk/osp/control/UnitDeregistrationAcknowledge.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Response; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Response; public class UnitDeregistrationAcknowledge extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java b/src/module/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java similarity index 85% rename from src/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java rename to src/module/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java index 28f18beb..b5d4b72a 100644 --- a/src/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java +++ b/src/module/decode/p25/message/tsbk/osp/control/UnitRegistrationCommand.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class UnitRegistrationCommand extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java b/src/module/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java similarity index 85% rename from src/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java rename to src/module/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java index 9e4809dd..7c85fd9a 100644 --- a/src/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java +++ b/src/module/decode/p25/message/tsbk/osp/control/UnitRegistrationResponse.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.control; +package module.decode.p25.message.tsbk.osp.control; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; +import module.decode.p25.reference.Response; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; -import decode.p25.reference.Response; public class UnitRegistrationResponse extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java b/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java similarity index 68% rename from src/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java rename to src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java index bf4d0e97..27cbafe3 100644 --- a/src/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java +++ b/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncement.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.message.tsbk.GroupMultiChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.GroupMultiChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class GroupDataChannelAnnouncement extends GroupMultiChannelGrant { diff --git a/src/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java b/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java similarity index 69% rename from src/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java rename to src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java index 796a754f..fd4fe1bb 100644 --- a/src/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java +++ b/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelAnnouncementExplicit.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.message.tsbk.GroupChannelGrantExplicit; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.GroupChannelGrantExplicit; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class GroupDataChannelAnnouncementExplicit extends GroupChannelGrantExplicit { diff --git a/src/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java b/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java similarity index 67% rename from src/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java rename to src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java index 62eff3eb..0873f1e9 100644 --- a/src/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/osp/data/GroupDataChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.message.tsbk.GroupChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.GroupChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class GroupDataChannelGrant extends GroupChannelGrant diff --git a/src/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java b/src/module/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java similarity index 68% rename from src/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java rename to src/module/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java index ac42a1a0..905f9a5d 100644 --- a/src/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/osp/data/IndividualDataChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.message.tsbk.UnitChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.UnitChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class IndividualDataChannelGrant extends UnitChannelGrant { diff --git a/src/decode/p25/message/tsbk/osp/data/SNDCPData.java b/src/module/decode/p25/message/tsbk/osp/data/SNDCPData.java similarity index 82% rename from src/decode/p25/message/tsbk/osp/data/SNDCPData.java rename to src/module/decode/p25/message/tsbk/osp/data/SNDCPData.java index 577c8662..e5559def 100644 --- a/src/decode/p25/message/tsbk/osp/data/SNDCPData.java +++ b/src/module/decode/p25/message/tsbk/osp/data/SNDCPData.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.message.tsbk.ServiceMessage; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.ServiceMessage; -import decode.p25.reference.DataUnitID; public abstract class SNDCPData extends ServiceMessage { diff --git a/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java similarity index 95% rename from src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java rename to src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java index 31a99e01..e7a9f5c1 100644 --- a/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java +++ b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelAnnouncementExplicit.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; public class SNDCPDataChannelAnnouncementExplicit extends SNDCPData implements IdentifierReceiver diff --git a/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java similarity index 94% rename from src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java rename to src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java index 06c451c0..88589309 100644 --- a/src/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.message.IBandIdentifier; +import module.decode.p25.message.IdentifierReceiver; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.IBandIdentifier; -import decode.p25.message.IdentifierReceiver; -import decode.p25.reference.DataUnitID; public class SNDCPDataChannelGrant extends SNDCPData implements IdentifierReceiver { diff --git a/src/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java similarity index 92% rename from src/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java rename to src/module/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java index 0a53c5c4..3fc0ef04 100644 --- a/src/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java +++ b/src/module/decode/p25/message/tsbk/osp/data/SNDCPDataPageRequest.java @@ -1,8 +1,8 @@ -package decode.p25.message.tsbk.osp.data; +package module.decode.p25.message.tsbk.osp.data; +import module.decode.p25.reference.DataUnitID; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; public class SNDCPDataPageRequest extends SNDCPData { diff --git a/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java b/src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java similarity index 68% rename from src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java rename to src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java index 1232c291..982d9510 100644 --- a/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.message.tsbk.GroupChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.GroupChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class GroupVoiceChannelGrant extends GroupChannelGrant { diff --git a/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java b/src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java similarity index 68% rename from src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java rename to src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java index 30a9446d..a202ecd1 100644 --- a/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.message.tsbk.GroupMultiChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.GroupMultiChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class GroupVoiceChannelGrantUpdate extends GroupMultiChannelGrant { diff --git a/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java b/src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java similarity index 69% rename from src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java rename to src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java index 0a2b776d..46831755 100644 --- a/src/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/GroupVoiceChannelGrantUpdateExplicit.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.message.tsbk.GroupChannelGrantExplicit; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.GroupChannelGrantExplicit; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class GroupVoiceChannelGrantUpdateExplicit extends GroupChannelGrantExplicit { diff --git a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java b/src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java similarity index 88% rename from src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java rename to src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java index 5fe3ee75..c21efcd1 100644 --- a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectAnswerRequest.java @@ -1,14 +1,14 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; import java.util.ArrayList; import java.util.List; +import module.decode.p25.message.tsbk.TSBKMessage; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Digit; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.TSBKMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Digit; -import decode.p25.reference.Opcode; public class TelephoneInterconnectAnswerRequest extends TSBKMessage { diff --git a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java b/src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java similarity index 84% rename from src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java rename to src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java index 174232f4..61553d59 100644 --- a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrant.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.message.tsbk.UnitChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.UnitChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class TelephoneInterconnectVoiceChannelGrant extends UnitChannelGrant { diff --git a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java b/src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java similarity index 75% rename from src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java rename to src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java index 5459f361..02622b73 100644 --- a/src/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/TelephoneInterconnectVoiceChannelGrantUpdate.java @@ -1,9 +1,9 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class TelephoneInterconnectVoiceChannelGrantUpdate extends TelephoneInterconnectVoiceChannelGrant diff --git a/src/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java b/src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java similarity index 68% rename from src/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java rename to src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java index b1b8aabf..370ee3d6 100644 --- a/src/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitAnswerRequest.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.message.tsbk.UnitChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.UnitChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class UnitToUnitAnswerRequest extends UnitChannelGrant { diff --git a/src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java b/src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java similarity index 78% rename from src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java rename to src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java index 8c191d27..71274d20 100644 --- a/src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrant.java @@ -1,11 +1,11 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.message.tsbk.UnitChannelGrant; +import module.decode.p25.message.tsbk.osp.control.IdentifierUpdate; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.UnitChannelGrant; -import decode.p25.message.tsbk.osp.control.IdentifierUpdate; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class UnitToUnitVoiceChannelGrant extends UnitChannelGrant { diff --git a/src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java b/src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java similarity index 67% rename from src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java rename to src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java index 0f0e0014..9b88d265 100644 --- a/src/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java +++ b/src/module/decode/p25/message/tsbk/osp/voice/UnitToUnitVoiceChannelGrantUpdate.java @@ -1,10 +1,10 @@ -package decode.p25.message.tsbk.osp.voice; +package module.decode.p25.message.tsbk.osp.voice; +import module.decode.p25.message.tsbk.UnitChannelGrant; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Opcode; import alias.AliasList; import bits.BinaryMessage; -import decode.p25.message.tsbk.UnitChannelGrant; -import decode.p25.reference.DataUnitID; -import decode.p25.reference.Opcode; public class UnitToUnitVoiceChannelGrantUpdate extends UnitChannelGrant { diff --git a/src/decode/p25/message/tsbk/vendor/VendorOpcode.java b/src/module/decode/p25/message/tsbk/vendor/VendorOpcode.java similarity index 98% rename from src/decode/p25/message/tsbk/vendor/VendorOpcode.java rename to src/module/decode/p25/message/tsbk/vendor/VendorOpcode.java index daddb6b1..0067a098 100644 --- a/src/decode/p25/message/tsbk/vendor/VendorOpcode.java +++ b/src/module/decode/p25/message/tsbk/vendor/VendorOpcode.java @@ -1,4 +1,4 @@ -package decode.p25.message.tsbk.vendor; +package module.decode.p25.message.tsbk.vendor; public enum VendorOpcode { diff --git a/src/decode/p25/reference/DataUnitID.java b/src/module/decode/p25/reference/DataUnitID.java similarity index 98% rename from src/decode/p25/reference/DataUnitID.java rename to src/module/decode/p25/reference/DataUnitID.java index 31e8bcd2..64a2f728 100644 --- a/src/decode/p25/reference/DataUnitID.java +++ b/src/module/decode/p25/reference/DataUnitID.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; /** * The Data Unit ID (DUID) is part of the Network ID (NID) field and indicates diff --git a/src/decode/p25/reference/DenyReason.java b/src/module/decode/p25/reference/DenyReason.java similarity index 95% rename from src/decode/p25/reference/DenyReason.java rename to src/module/decode/p25/reference/DenyReason.java index be82407c..1668bf68 100644 --- a/src/decode/p25/reference/DenyReason.java +++ b/src/module/decode/p25/reference/DenyReason.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum DenyReason { diff --git a/src/decode/p25/reference/Digit.java b/src/module/decode/p25/reference/Digit.java similarity index 93% rename from src/decode/p25/reference/Digit.java rename to src/module/decode/p25/reference/Digit.java index 1906923b..88ea0448 100644 --- a/src/decode/p25/reference/Digit.java +++ b/src/module/decode/p25/reference/Digit.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; import java.util.List; diff --git a/src/decode/p25/reference/Encryption.java b/src/module/decode/p25/reference/Encryption.java similarity index 96% rename from src/decode/p25/reference/Encryption.java rename to src/module/decode/p25/reference/Encryption.java index 2606c597..1b9fb032 100644 --- a/src/decode/p25/reference/Encryption.java +++ b/src/module/decode/p25/reference/Encryption.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum Encryption { diff --git a/src/decode/p25/reference/ExtendedFunction.java b/src/module/decode/p25/reference/ExtendedFunction.java similarity index 97% rename from src/decode/p25/reference/ExtendedFunction.java rename to src/module/decode/p25/reference/ExtendedFunction.java index 3fe9548a..5a72848f 100644 --- a/src/decode/p25/reference/ExtendedFunction.java +++ b/src/module/decode/p25/reference/ExtendedFunction.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum ExtendedFunction { diff --git a/src/decode/p25/reference/IPHeaderCompression.java b/src/module/decode/p25/reference/IPHeaderCompression.java similarity index 94% rename from src/decode/p25/reference/IPHeaderCompression.java rename to src/module/decode/p25/reference/IPHeaderCompression.java index 9418f678..d275d466 100644 --- a/src/decode/p25/reference/IPHeaderCompression.java +++ b/src/module/decode/p25/reference/IPHeaderCompression.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum IPHeaderCompression { diff --git a/src/decode/p25/reference/IPProtocol.java b/src/module/decode/p25/reference/IPProtocol.java similarity index 99% rename from src/decode/p25/reference/IPProtocol.java rename to src/module/decode/p25/reference/IPProtocol.java index 8a384502..b1884eb8 100644 --- a/src/decode/p25/reference/IPProtocol.java +++ b/src/module/decode/p25/reference/IPProtocol.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum IPProtocol { diff --git a/src/decode/p25/reference/LinkControlOpcode.java b/src/module/decode/p25/reference/LinkControlOpcode.java similarity index 99% rename from src/decode/p25/reference/LinkControlOpcode.java rename to src/module/decode/p25/reference/LinkControlOpcode.java index 00262831..c81474b2 100644 --- a/src/decode/p25/reference/LinkControlOpcode.java +++ b/src/module/decode/p25/reference/LinkControlOpcode.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum LinkControlOpcode { diff --git a/src/decode/p25/reference/MDPConfigurationOption.java b/src/module/decode/p25/reference/MDPConfigurationOption.java similarity index 93% rename from src/decode/p25/reference/MDPConfigurationOption.java rename to src/module/decode/p25/reference/MDPConfigurationOption.java index d1356380..979f5b48 100644 --- a/src/decode/p25/reference/MDPConfigurationOption.java +++ b/src/module/decode/p25/reference/MDPConfigurationOption.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum MDPConfigurationOption { diff --git a/src/decode/p25/reference/Opcode.java b/src/module/decode/p25/reference/Opcode.java similarity index 99% rename from src/decode/p25/reference/Opcode.java rename to src/module/decode/p25/reference/Opcode.java index f76c8f21..cd3bc40c 100644 --- a/src/decode/p25/reference/Opcode.java +++ b/src/module/decode/p25/reference/Opcode.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum Opcode { diff --git a/src/decode/p25/reference/P25NetworkCallsign.java b/src/module/decode/p25/reference/P25NetworkCallsign.java similarity index 97% rename from src/decode/p25/reference/P25NetworkCallsign.java rename to src/module/decode/p25/reference/P25NetworkCallsign.java index 0f3b7028..c2e80860 100644 --- a/src/decode/p25/reference/P25NetworkCallsign.java +++ b/src/module/decode/p25/reference/P25NetworkCallsign.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public class P25NetworkCallsign { diff --git a/src/decode/p25/reference/PDUFormat.java b/src/module/decode/p25/reference/PDUFormat.java similarity index 96% rename from src/decode/p25/reference/PDUFormat.java rename to src/module/decode/p25/reference/PDUFormat.java index fdec7b7c..e2541b8d 100644 --- a/src/decode/p25/reference/PDUFormat.java +++ b/src/module/decode/p25/reference/PDUFormat.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum PDUFormat { diff --git a/src/decode/p25/reference/PDUType.java b/src/module/decode/p25/reference/PDUType.java similarity index 96% rename from src/decode/p25/reference/PDUType.java rename to src/module/decode/p25/reference/PDUType.java index bb200c55..23f5da1e 100644 --- a/src/decode/p25/reference/PDUType.java +++ b/src/module/decode/p25/reference/PDUType.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum PDUType { diff --git a/src/decode/p25/reference/QueuedResponseReason.java b/src/module/decode/p25/reference/QueuedResponseReason.java similarity index 94% rename from src/decode/p25/reference/QueuedResponseReason.java rename to src/module/decode/p25/reference/QueuedResponseReason.java index 9dec86d6..b5de14dd 100644 --- a/src/decode/p25/reference/QueuedResponseReason.java +++ b/src/module/decode/p25/reference/QueuedResponseReason.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum QueuedResponseReason { diff --git a/src/decode/p25/reference/Response.java b/src/module/decode/p25/reference/Response.java similarity index 85% rename from src/decode/p25/reference/Response.java rename to src/module/decode/p25/reference/Response.java index 4951300f..31c7e521 100644 --- a/src/decode/p25/reference/Response.java +++ b/src/module/decode/p25/reference/Response.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum Response { diff --git a/src/decode/p25/reference/SNDCPActivationRejectReason.java b/src/module/decode/p25/reference/SNDCPActivationRejectReason.java similarity index 92% rename from src/decode/p25/reference/SNDCPActivationRejectReason.java rename to src/module/decode/p25/reference/SNDCPActivationRejectReason.java index 904045cb..210c4030 100644 --- a/src/decode/p25/reference/SNDCPActivationRejectReason.java +++ b/src/module/decode/p25/reference/SNDCPActivationRejectReason.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum SNDCPActivationRejectReason { diff --git a/src/decode/p25/reference/Service.java b/src/module/decode/p25/reference/Service.java similarity index 97% rename from src/decode/p25/reference/Service.java rename to src/module/decode/p25/reference/Service.java index 8ca2da92..56995f20 100644 --- a/src/decode/p25/reference/Service.java +++ b/src/module/decode/p25/reference/Service.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; import java.util.ArrayList; import java.util.List; diff --git a/src/decode/p25/reference/ServiceAccessPoint.java b/src/module/decode/p25/reference/ServiceAccessPoint.java similarity index 98% rename from src/decode/p25/reference/ServiceAccessPoint.java rename to src/module/decode/p25/reference/ServiceAccessPoint.java index ac1efa2b..ea1c735d 100644 --- a/src/decode/p25/reference/ServiceAccessPoint.java +++ b/src/module/decode/p25/reference/ServiceAccessPoint.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum ServiceAccessPoint { diff --git a/src/decode/p25/reference/StackOperation.java b/src/module/decode/p25/reference/StackOperation.java similarity index 85% rename from src/decode/p25/reference/StackOperation.java rename to src/module/decode/p25/reference/StackOperation.java index 6d93356c..e6250f33 100644 --- a/src/decode/p25/reference/StackOperation.java +++ b/src/module/decode/p25/reference/StackOperation.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum StackOperation { diff --git a/src/decode/p25/reference/Status.java b/src/module/decode/p25/reference/Status.java similarity index 85% rename from src/decode/p25/reference/Status.java rename to src/module/decode/p25/reference/Status.java index de624579..581bb188 100644 --- a/src/decode/p25/reference/Status.java +++ b/src/module/decode/p25/reference/Status.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum Status { diff --git a/src/decode/p25/reference/Vendor.java b/src/module/decode/p25/reference/Vendor.java similarity index 99% rename from src/decode/p25/reference/Vendor.java rename to src/module/decode/p25/reference/Vendor.java index 07b504fa..ea521380 100644 --- a/src/decode/p25/reference/Vendor.java +++ b/src/module/decode/p25/reference/Vendor.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum Vendor { diff --git a/src/decode/p25/reference/VendorLinkControlOpcode.java b/src/module/decode/p25/reference/VendorLinkControlOpcode.java similarity index 98% rename from src/decode/p25/reference/VendorLinkControlOpcode.java rename to src/module/decode/p25/reference/VendorLinkControlOpcode.java index e32d2c06..8607d5c8 100644 --- a/src/decode/p25/reference/VendorLinkControlOpcode.java +++ b/src/module/decode/p25/reference/VendorLinkControlOpcode.java @@ -1,4 +1,4 @@ -package decode.p25.reference; +package module.decode.p25.reference; public enum VendorLinkControlOpcode { diff --git a/src/decode/config/DecodeConfigPassport.java b/src/module/decode/passport/DecodeConfigPassport.java similarity index 90% rename from src/decode/config/DecodeConfigPassport.java rename to src/module/decode/passport/DecodeConfigPassport.java index f6d8d9ae..4ec50db4 100644 --- a/src/decode/config/DecodeConfigPassport.java +++ b/src/module/decode/passport/DecodeConfigPassport.java @@ -15,9 +15,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.passport; -import decode.DecoderType; +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; public class DecodeConfigPassport extends DecodeConfiguration { diff --git a/src/decode/passport/PassportBand.java b/src/module/decode/passport/PassportBand.java similarity index 98% rename from src/decode/passport/PassportBand.java rename to src/module/decode/passport/PassportBand.java index adfa71a2..0a7613c7 100644 --- a/src/decode/passport/PassportBand.java +++ b/src/module/decode/passport/PassportBand.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.passport; +package module.decode.passport; public enum PassportBand diff --git a/src/decode/passport/PassportCallEvent.java b/src/module/decode/passport/PassportCallEvent.java similarity index 97% rename from src/decode/passport/PassportCallEvent.java rename to src/module/decode/passport/PassportCallEvent.java index cc9ce255..c0f5bd63 100644 --- a/src/decode/passport/PassportCallEvent.java +++ b/src/module/decode/passport/PassportCallEvent.java @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.passport; +package module.decode.passport; +import module.decode.DecoderType; +import module.decode.event.CallEvent; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; public class PassportCallEvent extends CallEvent { diff --git a/src/module/decode/passport/PassportDecoder.java b/src/module/decode/passport/PassportDecoder.java new file mode 100644 index 00000000..0f76acb1 --- /dev/null +++ b/src/module/decode/passport/PassportDecoder.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.passport; + +import instrument.Instrumentable; +import instrument.tap.Tap; + +import java.util.ArrayList; +import java.util.List; + +import module.decode.Decoder; +import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; +import alias.AliasList; +import bits.MessageFramer; +import bits.SyncPattern; +import dsp.fsk.LTRFSKDecoder; + +public class PassportDecoder extends Decoder + implements IRealBufferListener, Instrumentable +{ + public static final int PASSPORT_MESSAGE_LENGTH = 68; + public static final int PASSPORT_SYNC_LENGTH = 9; + + private LTRFSKDecoder mPassportFSKDecoder; + private MessageFramer mPassportMessageFramer; + private PassportMessageProcessor mPassportMessageProcessor; + + private List mAvailableTaps; + + public PassportDecoder( DecodeConfiguration config, + AliasList aliasList ) + { + super( new PassportDecoderState( aliasList ) ); + + mPassportFSKDecoder = new LTRFSKDecoder(); + + mPassportMessageFramer = + new MessageFramer( SyncPattern.PASSPORT.getPattern(), + PASSPORT_MESSAGE_LENGTH ); + + mPassportFSKDecoder.addListener( mPassportMessageFramer ); + + mPassportMessageProcessor = new PassportMessageProcessor( aliasList ); + mPassportMessageFramer.addMessageListener( mPassportMessageProcessor ); + mPassportMessageProcessor.setMessageListener( mMessageBroadcaster ); + } + + @Override + public Listener getRealBufferListener() + { + return mPassportFSKDecoder; + } + + @Override + public DecoderType getDecoderType() + { + return DecoderType.PASSPORT; + } + + @Override + public List getTaps() + { + if( mAvailableTaps == null ) + { + mAvailableTaps = new ArrayList(); + + mAvailableTaps.addAll( mPassportFSKDecoder.getTaps() ); + } + + return mAvailableTaps; + } + + @Override + public void addTap( Tap tap ) + { + mPassportFSKDecoder.addTap( tap ); + } + + @Override + public void removeTap( Tap tap ) + { + mPassportFSKDecoder.removeTap( tap ); + } +} diff --git a/src/decode/passport/PassportPanel.java b/src/module/decode/passport/PassportDecoderPanel.java similarity index 72% rename from src/decode/passport/PassportPanel.java rename to src/module/decode/passport/PassportDecoderPanel.java index e99bde91..a2b01ec1 100644 --- a/src/decode/passport/PassportPanel.java +++ b/src/module/decode/passport/PassportDecoderPanel.java @@ -15,28 +15,24 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.passport; +package module.decode.passport; import java.awt.EventQueue; import javax.swing.JLabel; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; import settings.ColorSetting; import settings.Setting; import settings.SettingsManager; import alias.Alias; import controller.channel.Channel; -import controller.state.ChannelState.ChangedAttribute; -import controller.state.ChannelStatePanel; -public class PassportPanel extends ChannelStatePanel +public class PassportDecoderPanel extends DecoderPanel { private static final long serialVersionUID = 1L; - private JLabel mStateLabel; - private JLabel mSourceLabel; - private JLabel mChannelLabel; - private JLabel mProtocol = new JLabel( "Passport" ); private JLabel mToTalkgroup = new JLabel(); private JLabel mToTalkgroupAlias = new JLabel(); @@ -45,13 +41,9 @@ public class PassportPanel extends ChannelStatePanel private JLabel mFromTalkgroup = new JLabel(); private JLabel mFromTalkgroupAlias = new JLabel(); - private SettingsManager mSettingsManager; - - public PassportPanel( SettingsManager settingsManager, Channel channel ) + public PassportDecoderPanel( SettingsManager settingsManager, PassportDecoder decoder ) { - super( channel ); - - mSettingsManager = settingsManager; + super( settingsManager, decoder ); init(); } @@ -59,24 +51,10 @@ public class PassportPanel extends ChannelStatePanel public void dispose() { super.dispose(); - mSettingsManager = null; } 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 ); @@ -95,10 +73,6 @@ public class PassportPanel extends ChannelStatePanel mFromTalkgroupAlias.setFont( mFontDecoder ); mFromTalkgroupAlias.setForeground( mColorLabelDecoder ); - add( mStateLabel ); - add( mSourceLabel ); - add( mChannelLabel, "wrap" ); - add( mProtocol ); add( mToTalkgroup ); add( mToTalkgroupAlias, "wrap" ); @@ -107,6 +81,11 @@ public class PassportPanel extends ChannelStatePanel add( mFromTalkgroup ); add( mFromTalkgroupAlias, "wrap" ); } + + private PassportDecoderState getDecoderState() + { + return (PassportDecoderState)getDecoder().getDecoderState(); + } @Override public void receive( final ChangedAttribute changedAttribute ) @@ -116,24 +95,10 @@ public class PassportPanel extends ChannelStatePanel @Override public void run() { - PassportChannelState state = (PassportChannelState)getChannel() - .getProcessingChain().getChannelState(); - + PassportDecoderState state = getDecoderState(); switch( changedAttribute ) { - case CHANNEL_STATE: - mStateLabel.setText( state.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; case TO_TALKGROUP: String tg = state.getTalkgroup(); @@ -222,10 +187,6 @@ public class PassportPanel extends ChannelStatePanel case CHANNEL_STATE_LABEL_DECODER: if( mColorLabelDecoder != null ) { - if( mStateLabel != null ) - { - mStateLabel.setForeground( mColorLabelDecoder ); - } if( mProtocol != null ) { mProtocol.setForeground( mColorLabelDecoder ); @@ -252,18 +213,7 @@ public class PassportPanel extends ChannelStatePanel } } break; - case CHANNEL_STATE_LABEL_DETAILS: - if( mColorLabelDetails != null ) - { - if( mSourceLabel != null ) - { - mSourceLabel.setForeground( mColorLabelDetails ); - } - if( mChannelLabel != null ) - { - mChannelLabel.setForeground( mColorLabelDetails ); - } - } + default: break; } } diff --git a/src/module/decode/passport/PassportDecoderState.java b/src/module/decode/passport/PassportDecoderState.java new file mode 100644 index 00000000..96d97725 --- /dev/null +++ b/src/module/decode/passport/PassportDecoderState.java @@ -0,0 +1,529 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.passport; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.TreeSet; + +import message.Message; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import alias.Alias; +import alias.AliasList; + +public class PassportDecoderState extends DecoderState +{ + private final static Logger mLog = + LoggerFactory.getLogger( PassportDecoderState.class ); + + private static final int sINT_NULL_VALUE = -1; + private DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000"); + + private HashSet mTalkgroupsFirstHeard = new HashSet(); + private TreeSet mTalkgroups = new TreeSet(); + private TreeSet mMobileIDs = new TreeSet(); + private HashMap mSiteLCNs = new HashMap(); + private HashMap mNeighborLCNs = new HashMap(); + + private String mTalkgroup; + private Alias mTalkgroupAlias; + private String mMobileID; + private Alias mMobileIDAlias; + private int mChannelNumber; + private int mSiteNumber; + private PassportBand mSiteBand; + private HashMap mActiveCalls = new HashMap(); + + public PassportDecoderState( AliasList aliasList ) + { + super( aliasList ); + } + + @Override + @SuppressWarnings( "incomplete-switch" ) + public void receive( Message message ) + { +// if( message instanceof PassportMessage ) +// { +// PassportMessage passport = ((PassportMessage)message); +// +// if( passport.isValid() ) +// { +// switch( passport.getMessageType() ) +// { +// case CA_STRT: +// mSiteLCNs.put( passport.getLCN(), +// passport.getLCNFrequencyFormatted() ); +// case CA_ENDD: +// String talkgroup = +// String.valueOf( passport.getTalkgroupID() ); +// +// if( mTalkgroupsFirstHeard.contains( talkgroup ) ) +// { +// mTalkgroups.add( talkgroup ); +// } +// else +// { +// mTalkgroupsFirstHeard.add( talkgroup ); +// } +// break; +// case ID_RDIO: +// String min = passport.getMobileID(); +// +// if( min != null ) +// { +// mMobileIDs.add( min ); +// } +// break; +// case SY_IDLE: +// if( passport.getFree() != 0 ) +// { +// mNeighborLCNs.put( passport.getFree(), +// passport.getFreeFrequencyFormatted() ); +// } +// break; +// default: +// break; +// } +// } +// } +// +// if( message instanceof PassportMessage ) +// { +// mActivitySummary.receive( message ); +// +// PassportMessage passport = (PassportMessage)message; +// +// if( passport.isValid() ) +// { +// switch( passport.getMessageType() ) +// { +// case CA_STRT: +// if( mChannelNumber == 0 ) +// { +// mChannelNumber = passport.getLCN(); +// } +// +// if( passport.getLCN() == mChannelNumber ) +// { +// setTalkgroup( String.valueOf( passport.getTalkgroupID() ) ); +// setTalkgroupAlias( passport.getTalkgroupIDAlias() ); +// +// if( mTalkgroupAlias != null ) +// { +// try +// { +// if( mTalkgroupAlias.hasAudioType() ) +// { +// mAudioTypeListener.setAudioType( +// mTalkgroupAlias.getAudioType() ); +// } +// } +// catch( Exception e ) +// { +// mLog.error( "Error setting audio for " +// + "passport call", e ); +// } +// } +// +// PassportCallEvent current = +// getCurrentPassportCallEvent(); +// +// /* If we're already in a call event, add the message +// * to the current call event ... if false, then we +// * have a different call ... cleanup the old one. */ +// if( current != null && !current.addMessage( passport ) ) +// { +// if( current.isValid() ) +// { +// final PassportCallEvent endEvent = +// getCurrentPassportCallEvent(); +// +// mCallEventModel.setEnd( endEvent ); +// } +// else +// { +// mCallEventModel.remove( current ); +// } +// +// current = null; +// setCurrentCallEvent( null ); +// } +// +// if( current == null ) +// { +// mCurrentCallEvent = new PassportCallEvent +// .Builder( CallEventType.CALL ) +// .aliasList( mAliasList ) +// .channel( String.valueOf( mChannelNumber ) ) +// .frequency( passport.getLCNFrequency() ) +// .to( String.valueOf( passport.getTalkgroupID() ) ) +// .build(); +// +// mCallEventModel.add( mCurrentCallEvent ); +// } +// +// State state = getState(); +// +// if( state == State.IDLE || state == State.CALL ) +// { +// setState( State.CALL ); +// } +// else if( state == State.DATA ) +// { +// setState( State.DATA ); +// } +// } +// else +// { +// //Call Detection +// int lcn = passport.getLCN(); +// String tg = String.valueOf( passport.getTalkgroupID() ); +// +// if( !mActiveCalls.containsKey( lcn ) || +// !mActiveCalls.get( lcn ).contentEquals( tg ) ) +// { +// mActiveCalls.put( lcn, tg ); +// +// PassportCallEvent event = new PassportCallEvent +// .Builder( CallEventType.CALL_DETECT ) +// .aliasList( mAliasList ) +// .channel( String.valueOf( lcn ) ) +// .details( "Site: " + passport.getSite() ) +// .frequency( passport.getLCNFrequency() ) +// .to( tg ) +// .build(); +// +// mCallEventModel.add( event ); +// } +// } +// break; +// case DA_STRT: +////TODO: add channel number checking and log a call event if on another site +// setTalkgroup( String.valueOf( passport.getTalkgroupID() ) ); +// setTalkgroupAlias( passport.getTalkgroupIDAlias() ); +// setState( State.DATA ); +// break; +// case CA_ENDD: +// setTalkgroup( String.valueOf( passport.getTalkgroupID() ) ); +// setTalkgroupAlias( passport.getTalkgroupIDAlias() ); +// fade( CallEventType.CALL_END ); +// break; +// case ID_RDIO: +// setMobileID( passport.getMobileID() ); +// setMobileIDAlias( passport.getMobileIDAlias() ); +// +// final String mid = passport.getMobileID(); +// +// final PassportCallEvent event = +// getCurrentPassportCallEvent(); +// +// if( event != null && mid != null ) +// { +// mCallEventModel.setFromID( event, mid ); +// } +// +// setState( State.CALL ); +// break; +// case RA_REGI: +// mCallEventModel.add( new PassportCallEvent +// .Builder( CallEventType.REGISTER ) +// .aliasList( mAliasList ) +// .channel( String.valueOf( passport.getLCN() ) ) +// .frequency( passport.getLCNFrequency() ) +// .to( passport.getToID() ) +// .build() ); +////TODO: add logic to recognize the register event and suppress the subsequent +////call messages, but capture the group assignments. +// setState( State.DATA ); +// break; +// case SY_IDLE: +// PassportBand band = passport.getSiteBand(); +// +// if( mSiteBand == null || mSiteBand != band ) +// { +// mSiteBand = band; +// +// mSiteNumber = passport.getSite(); +// +// long frequency = mProcessingChain.getChannel() +// .getTunerChannel().getFrequency(); +// +// mChannelNumber = mSiteBand.getChannel( frequency ); +// +// broadcast( ChangedAttribute.CHANNEL_NUMBER ); +// } +// default: +// break; +// } +// } +// } + } + + public static String formatTalkgroup( String talkgroup ) + { + StringBuilder sb = new StringBuilder(); + + if( talkgroup.length() == 6 ) + { + sb.append( talkgroup.substring( 0, 1 ) ); + sb.append( "-" ); + sb.append( talkgroup.substring( 1, 3 ) ); + sb.append( "-" ); + sb.append( talkgroup.substring( 3, 6 ) ); + + return sb.toString(); + } + else + { + return talkgroup; + } + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "Activity Summary\n" ); + sb.append( "Decoder:\tPassport\n\n" ); + sb.append( "Site Channels\n" ); + + if( mSiteLCNs.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + ArrayList channels = new ArrayList( mSiteLCNs.keySet() ); + Collections.sort( channels ); + + for( Integer channel: channels ) + { + sb.append( " " + channel ); + sb.append( "\t" + mSiteLCNs.get( channel ) ); + sb.append( "\n" ); + } + } + + sb.append( "\nNeighbor Channels\n" ); + + if( mNeighborLCNs.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + ArrayList channels = new ArrayList( mNeighborLCNs.keySet() ); + Collections.sort( channels ); + + for( Integer channel: channels ) + { + sb.append( " " + channel ); + sb.append( "\t" + mNeighborLCNs.get( channel ) ); + sb.append( "\n" ); + } + } + + sb.append( "\nTalkgroups\n" ); + + if( mTalkgroups.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mTalkgroups.iterator(); + + while( it.hasNext() ) + { + String tgid = it.next(); + + sb.append( " " ); + sb.append( tgid ); + sb.append( " " ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getTalkgroupAlias( tgid ); + + if( alias != null ) + { + sb.append( alias.getName() ); + } + } + + sb.append( "\n" ); + } + } + + sb.append( "\nMobile ID Numbers\n" ); + + if( mMobileIDs.isEmpty() ) + { + sb.append( " None\n" ); + } + else + { + Iterator it = mMobileIDs.iterator(); + + while( it.hasNext() ) + { + String min = it.next(); + + sb.append( " " ); + sb.append( min ); + sb.append( " " ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getMobileIDNumberAlias( min ); + + if( alias != null ) + { + sb.append( alias.getName() ); + } + } + + sb.append( "\n" ); + } + } + + return sb.toString(); + } + +// /** +// * Intercept the fade event so that we can generate a call end event +// */ +// @Override +// public void fade( final CallEventType type ) +// { +// /* +// * We can receive multiple call tear-down messages -- only post a call +// * end event for the one that can change the state to fade +// */ +// if( getState().canChangeTo( State.FADE ) ) +// { +// final PassportCallEvent current = getCurrentPassportCallEvent(); +// +// if( current != null ) +// { +// mCallEventModel.setEnd( current ); +// } +// } +// +// setCurrentCallEvent( null ); +// +// mActiveCalls.clear(); +// +// super.fade( type ); +// } + + public void reset() + { + //TODO: finish this + resetState(); + } + + private void resetState() + { + setTalkgroup( null ); + setTalkgroupAlias( null ); + + setMobileID( null ); + setMobileIDAlias( null ); + } + + public int getChannelNumber() + { + return mChannelNumber; + } + + public int getSiteNumber() + { + return mSiteNumber; + } + + public String getTalkgroup() + { + return mTalkgroup; + } + + public void setTalkgroup( String talkgroup ) + { + mTalkgroup = talkgroup; + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + + public Alias getTalkgroupAlias() + { + return mTalkgroupAlias; + } + + public void setTalkgroupAlias( Alias alias ) + { + mTalkgroupAlias = alias; + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + } + + public String getMobileID() + { + return mMobileID; + } + + public void setMobileID( String id ) + { + mMobileID = id; + broadcast( ChangedAttribute.FROM_TALKGROUP ); + } + + public Alias getMobileIDAlias() + { + return mMobileIDAlias; + } + + public void setMobileIDAlias( Alias alias ) + { + mMobileIDAlias = alias; + broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + } + + @Override + public void init() + { + // TODO Auto-generated method stub + + } + + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + // TODO Auto-generated method stub + + } +} diff --git a/src/decode/passport/PassportEditor.java b/src/module/decode/passport/PassportEditor.java similarity index 90% rename from src/decode/passport/PassportEditor.java rename to src/module/decode/passport/PassportEditor.java index 26cc814f..4b39e15d 100644 --- a/src/decode/passport/PassportEditor.java +++ b/src/module/decode/passport/PassportEditor.java @@ -15,10 +15,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.passport; +package module.decode.passport; -import decode.DecodeEditor; -import decode.config.DecodeConfiguration; +import module.decode.DecodeEditor; +import module.decode.config.DecodeConfiguration; public class PassportEditor extends DecodeEditor { diff --git a/src/decode/passport/PassportMessage.java b/src/module/decode/passport/PassportMessage.java similarity index 99% rename from src/decode/passport/PassportMessage.java rename to src/module/decode/passport/PassportMessage.java index 59d156e1..36b183a5 100644 --- a/src/decode/passport/PassportMessage.java +++ b/src/module/decode/passport/PassportMessage.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.passport; +package module.decode.passport; import java.text.DecimalFormat; @@ -26,10 +26,10 @@ import java.util.List; import map.Plottable; import message.Message; import message.MessageType; +import module.decode.DecoderType; import alias.Alias; import alias.AliasList; import bits.BinaryMessage; -import decode.DecoderType; import edac.CRC; import edac.CRCPassport; diff --git a/src/decode/passport/PassportMessageFilter.java b/src/module/decode/passport/PassportMessageFilter.java similarity index 98% rename from src/decode/passport/PassportMessageFilter.java rename to src/module/decode/passport/PassportMessageFilter.java index f4026101..6be05f84 100644 --- a/src/decode/passport/PassportMessageFilter.java +++ b/src/module/decode/passport/PassportMessageFilter.java @@ -1,4 +1,4 @@ -package decode.passport; +package module.decode.passport; import java.util.ArrayList; import java.util.HashMap; diff --git a/src/decode/passport/PassportMessageProcessor.java b/src/module/decode/passport/PassportMessageProcessor.java similarity index 77% rename from src/decode/passport/PassportMessageProcessor.java rename to src/module/decode/passport/PassportMessageProcessor.java index c3031390..553e5a35 100644 --- a/src/decode/passport/PassportMessageProcessor.java +++ b/src/module/decode/passport/PassportMessageProcessor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,20 +15,19 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.passport; +package module.decode.passport; import java.util.HashMap; import message.Message; import message.MessageType; -import sample.Broadcaster; import sample.Listener; import alias.AliasList; import bits.BinaryMessage; public class PassportMessageProcessor implements Listener { - private Broadcaster mBroadcaster = new Broadcaster(); + private Listener mMessageListener; private IdleMessageFinder mIdleFinder = new IdleMessageFinder(); private AliasList mAliasList; private PassportMessage mIdleMessage; @@ -41,29 +40,32 @@ public class PassportMessageProcessor implements Listener @Override public void receive( BinaryMessage buffer ) { - PassportMessage message; - - if( mIdleMessage != null ) + if( mMessageListener != null ) { - message = new PassportMessage( buffer, mIdleMessage, mAliasList ); - } - else - { - message = new PassportMessage( buffer, mAliasList ); - mIdleFinder.receive( message ); - } + PassportMessage message; + + if( mIdleMessage != null ) + { + message = new PassportMessage( buffer, mIdleMessage, mAliasList ); + } + else + { + message = new PassportMessage( buffer, mAliasList ); + mIdleFinder.receive( message ); + } - mBroadcaster.receive( message ); + mMessageListener.receive( message ); + } } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } public class IdleMessageFinder diff --git a/src/module/decode/state/AlwaysUnsquelchedDecoderState.java b/src/module/decode/state/AlwaysUnsquelchedDecoderState.java new file mode 100644 index 00000000..c147a1e8 --- /dev/null +++ b/src/module/decode/state/AlwaysUnsquelchedDecoderState.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.state; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import message.Message; +import module.ProcessingChain; +import module.decode.state.DecoderStateEvent.Event; + +/** + * Basic decoder channel state - provides the minimum channel state functionality + * to support an always un-squelched audio decoder. + */ +public class AlwaysUnsquelchedDecoderState extends DecoderState +{ + private final static Logger mLog = + LoggerFactory.getLogger( AlwaysUnsquelchedDecoderState.class ); + + private String mProtocol; + + public AlwaysUnsquelchedDecoderState( String protocol ) + { + super( null ); + + mProtocol = protocol; + } + + @Override + public void init() + { + mLog.debug( "init() - broadcasting unsquelch event" ); + + broadcast( new DecoderStateEvent( this, Event.ALWAYS_UNSQUELCH, State.IDLE ) ); + } + + @Override + public void reset() + { + /* No reset actions needed */ + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "Activity Summary\n" ); + sb.append( "\tDecoder:\t" ); + sb.append( mProtocol ); + sb.append( "\n\n" ); + + return sb.toString(); + } + + @Override + public void receive( Message t ) + { + /* Not implemented */ + } + + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + /* Not implemented */ + } +} diff --git a/src/module/decode/state/ChangedAttribute.java b/src/module/decode/state/ChangedAttribute.java new file mode 100644 index 00000000..34a2881b --- /dev/null +++ b/src/module/decode/state/ChangedAttribute.java @@ -0,0 +1,29 @@ +package module.decode.state; + +public enum ChangedAttribute +{ + AUDIO_TYPE, + CHANNEL_NUMBER, + CHANNEL_NAME, + CHANNEL_SITE_NUMBER, + CHANNEL_SITE_NUMBER_ALIAS, + CHANNEL_STATE, + DESCRIPTION, + FROM_TALKGROUP, + FROM_TALKGROUP_ALIAS, + FROM_TALKGROUP_TYPE, + MESSAGE, + MESSAGE_TYPE, + NAC, + SITE, + SITE_ALIAS, + SITE_NAME, + SOURCE, + SYSTEM, + SYSTEM_NAME, + SQUELCH, + TO_TALKGROUP, + TO_TALKGROUP_ALIAS, + TO_TALKGROUP_TYPE, + WACN; +} \ No newline at end of file diff --git a/src/module/decode/state/ChannelCollectionPanel.java b/src/module/decode/state/ChannelCollectionPanel.java new file mode 100644 index 00000000..e7d74e0b --- /dev/null +++ b/src/module/decode/state/ChannelCollectionPanel.java @@ -0,0 +1,150 @@ +package module.decode.state; + +import java.awt.Component; +import java.awt.Point; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JMenu; +import javax.swing.JPanel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.miginfocom.swing.MigLayout; +import playlist.PlaylistManager; +import settings.SettingsManager; +import controller.channel.Channel; + +/** + * Channel Collection assembles a primary channel panel and any related traffic + * channel panels, provides contextual access to the primary channel and + * supports channel selection for the primary or any traffic channels. + */ +public class ChannelCollectionPanel extends JPanel +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = LoggerFactory.getLogger( ChannelCollectionPanel.class ); + + private PlaylistManager mPlaylistManager; + private SettingsManager mSettingsManager; + + private List mChannelPanels = new ArrayList(); + + public ChannelCollectionPanel( PlaylistManager playlistManager, + SettingsManager settingsManager, + Channel channel ) + { + mPlaylistManager = playlistManager; + mSettingsManager = settingsManager; + + init( channel ); + } + + protected void init( Channel channel ) + { + setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]0[]") ); + + setOpaque( false ); + + ChannelStatePanel csp = new ChannelStatePanel( mPlaylistManager, + mSettingsManager, channel ); + + mChannelPanels.add( csp ); + + add( csp, "span,grow" ); + + } + + public Channel getChannel() + { + if( mChannelPanels.size() >= 1 ) + { + return mChannelPanels.get( 0 ).getChannel(); + } + + return null; + } + + public void dispose() + { + for( ChannelStatePanel panel: mChannelPanels ) + { + panel.dispose(); + } + + mChannelPanels.clear(); + + mPlaylistManager = null; + mSettingsManager = null; + } + + /** + * Toggles the selection state of the channel if it matches the channel + * argument. Otherwise, sets the selection state to false. + */ + public void setSelectedChannel( Channel channel ) + { + for( ChannelStatePanel panel: mChannelPanels ) + { + if( panel.getChannel() == channel ) + { + /* Toggle the selection state */ + panel.getChannel().setSelected( !panel.getChannel().isSelected() ); + panel.repaint(); + } + else + { + if( panel.getChannel().isSelected() ) + { + panel.getChannel().setSelected( false ); + panel.repaint(); + } + } + } + } + + /** + * Returns the channel represented by the channel panel at the gui point + * location, to selecting the channel + * + * @param point - current location of the mouse + * + * @return - channel or null + */ + public Channel getChannelAt( Point point ) + { + Component c = this.getComponentAt( point ); + + if( c instanceof ChannelCollectionPanel ) + { + return ((ChannelCollectionPanel)c).getChannel(); + } + else + { + return getChannel(); + } + } + + protected SettingsManager getSettingsManager() + { + return mSettingsManager; + } + + + public JMenu getContextMenu() + { + if( !mChannelPanels.isEmpty() ) + { + return mChannelPanels.get( 0 ).getContextMenu(); + } + + return null; + } + + public void addTrafficChannel( Channel channel ) + { + //TODO: add logic to handle traffic channels. + } +} diff --git a/src/controller/state/ChannelStateList.java b/src/module/decode/state/ChannelList.java similarity index 56% rename from src/controller/state/ChannelStateList.java rename to src/module/decode/state/ChannelList.java index bcd1aa72..920fe1e0 100644 --- a/src/controller/state/ChannelStateList.java +++ b/src/module/decode/state/ChannelList.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.state; +package module.decode.state; import java.awt.Color; import java.awt.Component; @@ -30,47 +30,38 @@ import javax.swing.JPopupMenu; import javax.swing.JSeparator; import javax.swing.SwingUtilities; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import net.miginfocom.swing.MigLayout; +import playlist.PlaylistManager; import settings.ColorSetting.ColorSettingName; import settings.ColorSettingMenuItem; import settings.ColorSettingResetMenuItem; import settings.SettingsManager; 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.lj1200.LJ1200ChannelState; -import decode.lj1200.LJ1200Panel; -import decode.ltrnet.LTRNetChannelState; -import decode.ltrnet.LTRNetPanel; -import decode.ltrstandard.LTRChannelState; -import decode.ltrstandard.LTRPanel; -import decode.mdc1200.MDCChannelState; -import decode.mdc1200.MDCPanel; -import decode.mpt1327.MPT1327ChannelState; -import decode.mpt1327.MPT1327Panel; -import decode.nbfm.NBFMChannelState; -import decode.nbfm.NBFMPanel; -import decode.p25.P25ChannelState; -import decode.p25.P25Panel; -import decode.passport.PassportChannelState; -import decode.passport.PassportPanel; -public class ChannelStateList extends JPanel implements ChannelEventListener +/** + * Gui wrapper for the list of currently processing primary channels + */ +public class ChannelList extends JPanel implements ChannelEventListener { private static final long serialVersionUID = 1L; - - private HashMap mDisplayedPanels = - new HashMap(); - + + private final static Logger mLog = LoggerFactory.getLogger( ChannelList.class ); + + private HashMap mDisplayedPanels = + new HashMap(); + + private PlaylistManager mPlaylistManager; private SettingsManager mSettingsManager; - public ChannelStateList( SettingsManager settingsManager ) + public ChannelList( PlaylistManager playlistManager, + SettingsManager settingsManager ) { + mPlaylistManager = playlistManager; mSettingsManager = settingsManager; init(); @@ -87,205 +78,60 @@ public class ChannelStateList extends JPanel implements ChannelEventListener @Override public void channelChanged( ChannelEvent event ) { - ChannelType type = event.getChannel().getChannelType(); - switch( event.getEvent() ) { - case PROCESSING_STARTED: - if( type == ChannelType.STANDARD ) - { - channelStarted( event.getChannel() ); - } - else if( type == ChannelType.TRAFFIC ) - { - for( Channel parent: mDisplayedPanels.keySet() ) - { - if( parent.hasTrafficChannel( event.getChannel() ) ) - { - mDisplayedPanels.get( parent ) - .trafficChannelAdded( event.getChannel() ); - validate(); - } - } - } + case CHANNEL_PROCESSING_STARTED: + addChannelPanel( event.getChannel() ); break; - case PROCESSING_STOPPED: - if( type == ChannelType.STANDARD ) - { - channelStopped( event.getChannel() ); - } - /* Since we don't have a reference to the parent channel that - * owns the traffic channel, iterate all channels to find the - * parent */ - else if( type == ChannelType.TRAFFIC ) - { - for( Channel control: mDisplayedPanels.keySet() ) - { - if( control.hasTrafficChannel( event.getChannel() ) ) - { - control.removeTrafficChannel( event.getChannel() ); - - mDisplayedPanels.get( control ) - .trafficChannelDeleted( event.getChannel() ); - - validate(); - } - } - } + case CHANNEL_PROCESSING_STOPPED: + removeChannelPanel( event.getChannel() ); break; default: break; } } - public void channelStarted( Channel channel ) + private void addChannelPanel( Channel channel ) { if( !mDisplayedPanels.containsKey( channel ) ) { - ChannelState state = channel.getProcessingChain().getChannelState(); - - ChannelStatePanel panel = null; - - if( state instanceof AMChannelState ) - { - panel = new AMPanel( channel ); - } - else if( state instanceof NBFMChannelState ) - { - NBFMChannelState convState = - (NBFMChannelState)state; - - panel = new NBFMPanel( channel ); - - for( AuxChannelState aux: convState.getAuxChannelStates() ) - { - if( aux instanceof FleetsyncChannelState ) - { - FleetsyncPanel fsPanel = - new FleetsyncPanel( mSettingsManager, - (FleetsyncChannelState)aux ); - - panel.addPanel( fsPanel ); - } - else if( aux instanceof MDCChannelState ) - { - MDCPanel mdsPanel = new MDCPanel( mSettingsManager, - (MDCChannelState)aux ); - - panel.addPanel( mdsPanel ); - } - else if( aux instanceof LJ1200ChannelState ) - { - LJ1200Panel ljPanel = new LJ1200Panel( mSettingsManager, - (LJ1200ChannelState)aux ); - - panel.addPanel( ljPanel ); - } - } - } - else if( state instanceof LTRChannelState ) - { - LTRChannelState ltrState = (LTRChannelState)state; - - panel = new LTRPanel( mSettingsManager, channel ); - - for( AuxChannelState aux: ltrState.getAuxChannelStates() ) - { - if( aux instanceof FleetsyncChannelState ) - { - FleetsyncPanel fsPanel = new FleetsyncPanel( mSettingsManager, - (FleetsyncChannelState)aux ); - - panel.addPanel( fsPanel ); - } - } - } - else if( state instanceof LTRNetChannelState ) - { - LTRNetChannelState ltrState = (LTRNetChannelState)state; - - panel = new LTRNetPanel( channel ); - - for( AuxChannelState aux: ltrState.getAuxChannelStates() ) - { - if( aux instanceof FleetsyncChannelState ) - { - FleetsyncPanel fsPanel = new FleetsyncPanel( mSettingsManager, - (FleetsyncChannelState)aux ); - - panel.addPanel( fsPanel ); - } - } - } - else if( state instanceof MPT1327ChannelState ) - { - MPT1327ChannelState mptState = (MPT1327ChannelState)state; - - panel = new MPT1327Panel( channel ); - } - else if( state instanceof PassportChannelState ) - { - PassportChannelState passportState = (PassportChannelState)state; - - panel = new PassportPanel( mSettingsManager, channel ); - - for( AuxChannelState aux: passportState.getAuxChannelStates() ) - { - if( aux instanceof FleetsyncChannelState ) - { - FleetsyncPanel fsPanel = new FleetsyncPanel( mSettingsManager, - (FleetsyncChannelState)aux ); - - panel.addPanel( fsPanel ); - } - } - } - else if( state instanceof P25ChannelState ) - { - panel = new P25Panel( channel ); - } + ChannelCollectionPanel panel = new ChannelCollectionPanel( + mPlaylistManager, mSettingsManager, channel ); - if( panel != null ) - { - add( panel, "wrap" ); - - mDisplayedPanels.put( channel, panel ); - - validate(); - } + add( panel, "wrap" ); + + mDisplayedPanels.put( channel, panel ); + + revalidate(); + repaint(); } } - public void channelStopped( Channel channel ) + public void removeChannelPanel( Channel channel ) { if( mDisplayedPanels.containsKey( channel ) ) { - ChannelStatePanel panel = mDisplayedPanels.get( channel ); + ChannelCollectionPanel panel = mDisplayedPanels.remove( channel ); if( panel != null ) { - remove( panel ); + remove( panel ); - validate(); + panel.dispose(); } - mDisplayedPanels.remove( channel ); + revalidate(); + repaint(); } } - public void setSelectedChannel( ChannelStatePanel selectedPanel ) + public void setSelectedChannel( Channel channel ) { - //De-select any other selected panel - for( ChannelStatePanel panel: mDisplayedPanels.values() ) + /* Send channel selection to each channel panel */ + for( ChannelCollectionPanel panel: mDisplayedPanels.values() ) { - if( panel != selectedPanel && panel.isSelected() ) - { - panel.setSelected( false ); - } + panel.setSelectedChannel( channel ); } - - //Toggle selection state - selectedPanel.setSelected( !selectedPanel.isSelected() ); } public class ListSelectionListener implements MouseListener @@ -295,38 +141,29 @@ public class ChannelStateList extends JPanel implements ChannelEventListener { Component component = getComponentAt( e.getPoint() ); - if( component instanceof ChannelStatePanel ) + if( component instanceof ChannelCollectionPanel ) { - ChannelStatePanel panel = (ChannelStatePanel)component; + ChannelCollectionPanel panel = (ChannelCollectionPanel)component; - /* If the panel has child traffic channels, attempt to get - * one of them for the mouse event */ - if( panel.getChannel().getTrafficChannels().size() > 0 ) - { - for( ChannelStatePanel trafficPanel: panel.getTrafficPanels().values() ) - { - Point translatedPoint = SwingUtilities - .convertPoint( e.getComponent(), e.getPoint(), - trafficPanel ); - - if( trafficPanel.contains( translatedPoint ) ) - { - panel = trafficPanel; - break; - } - } - } + Point translatedPoint = SwingUtilities + .convertPoint( e.getComponent(), e.getPoint(), panel ); + Channel channel = panel.getChannelAt( translatedPoint ); + if( e.getButton() == MouseEvent.BUTTON1 ) { - setSelectedChannel( panel ); + setSelectedChannel( channel ); } else if( e.getButton() == MouseEvent.BUTTON3 ) { JPopupMenu popupMenu = new JPopupMenu(); - JMenu menu = panel.getChannel().getContextMenu(); - - popupMenu.add( menu ); + + JMenu menu = panel.getContextMenu(); + + if( menu != null ) + { + popupMenu.add( menu ); + } JMenu colorMenu = new JMenu( "Color" ); popupMenu.add( colorMenu ); @@ -454,7 +291,7 @@ public class ChannelStateList extends JPanel implements ChannelEventListener selectedMenu.add( new ColorSettingResetMenuItem( mSettingsManager, ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL ) ); - popupMenu.show( ChannelStateList.this, e.getX(), e.getY() ); + popupMenu.show( ChannelList.this, e.getX(), e.getY() ); } } } diff --git a/src/module/decode/state/ChannelState.java b/src/module/decode/state/ChannelState.java new file mode 100644 index 00000000..546d2e60 --- /dev/null +++ b/src/module/decode/state/ChannelState.java @@ -0,0 +1,587 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.state; + +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import module.Module; +import module.decode.config.DecodeConfiguration; +import module.decode.event.CallEvent; +import module.decode.event.ICallEventProvider; +import module.decode.state.DecoderStateEvent.Event; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import alias.Metadata; +import alias.MetadataReset; +import alias.MetadataType; +import audio.metadata.IMetadataProvider; +import audio.squelch.ISquelchStateProvider; +import audio.squelch.SquelchState; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; +import controller.channel.Channel.ChannelType; + +/** + * Channel state tracks the overall state of all processing modules and decoders + * configured for the channel and provides squelch control events. + * + * Primary channel state provides a state machine for tracking decode events + * during a voice or data call through it's life cycle of CALL/DATA, FADE, END, + * and then back to an IDLE state. This class also provides squelch control + * to support any audio processing. + * + * State Descriptions: + * IDLE No voice or data call activity + * CALL/DATA A voice or data call has started, or is continuing + * CONTROL Control channel + * FADE The phase after a voice or data call when either an explicit + * call end has been received, or when no new signalling updates + * have been received, and the fade timer has expired. This phase + * allows for gui updates to signal to the user that the call is + * ended, while continuing to display the call details for the user + * END The phase after the voice or data call FADE, when the call is + * now over, and the gui should be reset to IDLE. + * + * When a call is started, invoke mTimerService.startFadeTimer() to start the + * call fade monitoring timer process that will automatically signal a call end + * once no more call signalling has been detected after the fade timeout has + * occurred, by invoking the fade() method. + * + * When a call ends, invoke the mTimerService.stopFadeTimer() and then invoke + * the mTimerService.startResetTimer(). Once the reset timeout occurs, + * the reset() method will be called, allowing the channel state to perform + * any reset actions, like clearing status and id labels on any view panels. + */ +public class ChannelState extends Module implements ICallEventProvider, + IChangedAttributeProvider, IDecoderStateEventListener, + IDecoderStateEventProvider, IMetadataProvider, ISquelchStateProvider +{ + private final static Logger mLog = + LoggerFactory.getLogger( ChannelState.class ); + + public static final long FADE_TIMEOUT_DELAY = 1000; + public static final long RESET_TIMEOUT_DELAY = 2000; + + private State mState = State.IDLE; + private long mTrafficChannelTimeout = + DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS; + + private Listener mCallEventListener; + private Listener mChangedAttributeListener; + private Listener mDecoderStateListener; + private Listener mMetadataListener; + private Listener mSquelchStateListener; + private DecoderStateEventReceiver mDecoderStateEventReceiver = + new DecoderStateEventReceiver(); + + private boolean mSquelchLocked = false; + private boolean mSelected = false; + + private long mFadeTimeout; + private long mResetTimeout; + + private StateMonitor mStateMonitor = new StateMonitor(); + private AtomicBoolean mMonitoring = new AtomicBoolean(); + private ScheduledFuture mRunningMonitor; + + private ThreadPoolManager mThreadPoolManager; + private ChannelType mChannelType; + + private TrafficChannelStatusListener mTrafficChannelStatusListener; + private CallEvent mTrafficChannelCallEvent; + + public ChannelState( ThreadPoolManager manager, ChannelType channelType ) + { + mThreadPoolManager = manager; + mChannelType = channelType; + } + + @Override + public void init() + { + + } + + public void dispose() + { + stopMonitor(); + + mCallEventListener = null; + mChangedAttributeListener = null; + mDecoderStateListener = null; + mSquelchStateListener = null; + mThreadPoolManager = null; + mStateMonitor = null; + } + + public void setTrafficChannelTimeout( long milliseconds ) + { + mTrafficChannelTimeout = milliseconds; + } + + public void setSelected( boolean selected ) + { + mSelected = selected; + } + + public boolean isSelected() + { + return mSelected; + } + + public State getState() + { + return mState; + } + + private boolean isEndingState() + { + return mState == State.IDLE || + mState == State.FADE || + mState == State.END; + } + + + /** + * Starts the state monitor running when we're not in IDLE state. Calls to + * start an already started monitor are ignored. + */ + private void startMonitor() + { + if( mMonitoring.compareAndSet( false, true ) ) + { + if( mRunningMonitor == null ) + { + mRunningMonitor = mThreadPoolManager.scheduleFixedRate( + ThreadType.DECODER, mStateMonitor, 20, TimeUnit.MILLISECONDS ); + } + else + { + throw new RuntimeException( "Channel state monitor's scheduled " + + "future pointer was not null" ); + } + } + } + + /** + * Stops the state monitor when we're in an IDLE state. Calls to stop an + * already stopped monitor are ignored. + */ + private void stopMonitor() + { + if( mMonitoring.compareAndSet( true, false ) ) + { + if( mRunningMonitor != null ) + { + mThreadPoolManager.cancel( mRunningMonitor ); + } + else + { + throw new RuntimeException( "Channel state monitor's scheduled " + + "future pointer was null when trying to stop the monitor" ); + } + + mRunningMonitor = null; + } + } + + /** + * Updates the fade timeout threshold to the current time plus delay + */ + private void updateFadeTimeout() + { + /* Standard channel uses the FADE_TIMEOUT_DELAY and traffic channels + * use the TRAFFIC_FADE_TIMEOUT_DELAY. Some traffic channels (e.g. MPT) + * don't have signaling on the traffic channel that can be used to + * update the fade timeout, so we set the timeout value larger to ensure + * the channel doesn't fade before the end of the call. */ + long timeout = System.currentTimeMillis() + + ( mChannelType == ChannelType.STANDARD ? FADE_TIMEOUT_DELAY : + mTrafficChannelTimeout ); + + if( timeout > mFadeTimeout ) + { + mFadeTimeout = timeout; + } + } + + /** + * Updates the reset timeout threshold to the current time plus delay + */ + private void updateResetTimeout() + { + mResetTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY; + } + + /** + * Broadcasts the squelch state to the registered listener + */ + protected void broadcast( SquelchState state ) + { + if( mSquelchStateListener != null && !mSquelchLocked ) + { + mSquelchStateListener.receive( state ); + } + } + + /** + * Sets the squelch state listener + */ + @Override + public void setSquelchStateListener( Listener listener ) + { + mSquelchStateListener = listener; + } + + /** + * Removes the squelch state listener + */ + @Override + public void removeSquelchStateListener() + { + mSquelchStateListener = null; + } + + /** + * Sets the channel state to the specified state, or updates the timeout + * values so that the state monitor will not change state. Broadcasts a + * squelch event when the state changes and the audio squelch state should + * change. Also broadcasts changed attribute and decoder state events so + * that external processes can maintain sync with this channel state. + */ + protected void setState( State state ) + { + if( mState == state ) + { + switch( state ) + { + case CALL: + case CONTROL: + case DATA: + case ENCRYPTED: + updateFadeTimeout(); + break; + default: + break; + } + } + else if( mState.canChangeTo( state ) ) + { + switch( state ) + { + case CONTROL: + case DATA: + case ENCRYPTED: + broadcast( SquelchState.SQUELCH ); + updateFadeTimeout(); + startMonitor(); + break; + case CALL: + broadcast( SquelchState.UNSQUELCH ); + updateFadeTimeout(); + startMonitor(); + break; + case FADE: + broadcast( SquelchState.SQUELCH ); + updateResetTimeout(); + startMonitor(); + break; + case END: + broadcast( SquelchState.SQUELCH ); + broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) ); + broadcast( new MetadataReset() ); + break; + case IDLE: + broadcast( SquelchState.SQUELCH ); + stopMonitor(); + + if( mChannelType == ChannelType.TRAFFIC ) + { + if( mTrafficChannelStatusListener != null ) + { + mTrafficChannelStatusListener.callEnd(); + mTrafficChannelStatusListener = null; + } + + if( mTrafficChannelCallEvent != null ) + { + mTrafficChannelCallEvent.end(); + + broadcast( mTrafficChannelCallEvent ); + mTrafficChannelCallEvent = null; + } + } + break; + default: + break; + } + + mState = state; + + broadcast( ChangedAttribute.CHANNEL_STATE ); + } + } + + public class StateMonitor implements Runnable + { + @Override + public void run() + { + if( getState() == State.FADE ) + { + if( mResetTimeout <= System.currentTimeMillis() ) + { + setState( State.END ); + } + } + else if( getState() == State.END ) + { + setState( State.IDLE ); + } + else + { + if( mFadeTimeout <= System.currentTimeMillis() ) + { + setState( State.FADE ); + } + } + } + } + + /** + * Broadcasts the call event to the registered listener + */ + protected void broadcast( CallEvent event ) + { + if( mCallEventListener != null ) + { + mCallEventListener.receive( event ); + } + } + + @Override + public void setCallEventListener( Listener listener ) + { + mCallEventListener = listener; + } + + @Override + public void removeCallEventListener() + { + mCallEventListener = null; + } + + /** + * Broadcasts the channel state attribute change event to all registered + * listeners + */ + protected void broadcast( ChangedAttribute attribute ) + { + if( mChangedAttributeListener != null ) + { + mChangedAttributeListener.receive( attribute ); + } + } + + /** + * Adds the listener to receive channel state attribute change events + */ + @Override + public void setChangedAttributeListener( Listener listener ) + { + mChangedAttributeListener = listener; + } + + /** + * Removes the listener to receive channel state attribute change events + */ + @Override + public void removeChangedAttributeListener() + { + mChangedAttributeListener = null; + } + + /** + * Broadcasts a channel state event to any registered listeners + */ + protected void broadcast( DecoderStateEvent event ) + { + if( mDecoderStateListener != null ) + { + mDecoderStateListener.receive( event ); + } + } + + /** + * Adds a decoder state event listener + */ + @Override + public void setDecoderStateListener( Listener listener ) + { + mDecoderStateListener = listener; + } + + /** + * Removes the decoder state event listener + */ + @Override + public void removeDecoderStateListener() + { + mDecoderStateListener = null; + } + + @Override + public Listener getDecoderStateListener() + { + return mDecoderStateEventReceiver; + } + + /** + * Broadcasts metadata to the registered listener + */ + protected void broadcast( Metadata metadata ) + { + if( mMetadataListener != null ) + { + mMetadataListener.receive( metadata ); + } + } + + /** + * Adds a metadata listener + */ + @Override + public void setMetadataListener( Listener listener ) + { + mMetadataListener = listener; + } + + /** + * Removes the decoder state event listener + */ + @Override + public void removeMetadataListener() + { + mMetadataListener = null; + } + + /** + * Registers a listener to be notified when a traffic channel call event is + * completed, so that the listener can perform call tear-down + */ + public void configureAsTrafficChannel( TrafficChannelStatusListener listener, + TrafficChannelAllocationEvent allocationEvent ) + { + mTrafficChannelStatusListener = listener; + mTrafficChannelCallEvent = allocationEvent.getCallEvent(); + + /* Broadcast the call event details as metadata for the audio manager */ + String channel = mTrafficChannelCallEvent.getChannel(); + + if( channel != null ) + { + broadcast( new Metadata( MetadataType.CHANNEL, channel, true ) ); + } + + broadcast( new Metadata( MetadataType.PROTOCOL, + mTrafficChannelCallEvent.getDecoderType().getDisplayString(), true ) ); + + String details = mTrafficChannelCallEvent.getDetails(); + + if( details != null ) + { + broadcast( new Metadata( MetadataType.DETAILS, details, true ) ); + } + + String from = mTrafficChannelCallEvent.getFromID(); + + if( from != null ) + { + broadcast( new Metadata( mTrafficChannelCallEvent.getFromIDType(), + from, mTrafficChannelCallEvent.getFromIDAlias(), true ) ); + } + + String to = mTrafficChannelCallEvent.getToID(); + + if( to != null ) + { + broadcast( new Metadata( mTrafficChannelCallEvent.getToIDType(), to, + mTrafficChannelCallEvent.getToIDAlias(), true ) ); + } + + long frequency = mTrafficChannelCallEvent.getFrequency(); + + if( frequency > 0 ) + { + broadcast( new Metadata( MetadataType.FREQUENCY, + String.valueOf( frequency ), true ) ); + } + + /* Rebroadcast the allocation event so that the internal decoder states + * can self-configure with the call event details */ + broadcast( allocationEvent ); + } + + /** + * DecoderStateEvent receiver wrapper + */ + public class DecoderStateEventReceiver implements Listener + { + @Override + public void receive( DecoderStateEvent event ) + { + if( event.getSource() != this ) + { + switch( event.getEvent() ) + { + case ALWAYS_UNSQUELCH: + broadcast( SquelchState.UNSQUELCH ); + mSquelchLocked = true; + break; + case CONTINUATION: + case DECODE: + if( isEndingState() ) + { + setState( event.getState() ); + } + else + { + updateFadeTimeout(); + } + break; + case END: + setState( State.FADE ); + break; + case RESET: + /* Channel State does not respond to reset events */ + break; + case START: + setState( event.getState() ); + break; + default: + break; + } + } + } + } +} diff --git a/src/module/decode/state/ChannelStatePanel.java b/src/module/decode/state/ChannelStatePanel.java new file mode 100644 index 00000000..34240743 --- /dev/null +++ b/src/module/decode/state/ChannelStatePanel.java @@ -0,0 +1,457 @@ +package module.decode.state; + +import java.awt.Color; +import java.awt.EventQueue; +import java.awt.Font; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JSeparator; + +import module.decode.Decoder; +import module.decode.DecoderFactory; +import module.decode.event.ActivitySummaryFrame; +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import playlist.PlaylistManager; +import sample.Listener; +import settings.ColorSetting; +import settings.ColorSetting.ColorSettingName; +import settings.Setting; +import settings.SettingChangeListener; +import settings.SettingsManager; +import controller.channel.Channel; + +public class ChannelStatePanel extends JPanel + implements Listener, SettingChangeListener +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = LoggerFactory.getLogger( ChannelStatePanel.class ); + + private static final boolean ENABLED = true; + private static final boolean DISABLED = false; + private static final boolean BROADCAST_CHANGE = true; + + private Font mFontDetails = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); + private Font mFontDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); + + protected Color mColorChannelBackground; + protected Color mColorChannelSelected; + protected Color mColorTopCall; + protected Color mColorMiddleCall; + protected Color mColorTopControl; + protected Color mColorMiddleControl; + protected Color mColorTopData; + protected Color mColorMiddleData; + protected Color mColorTopFade; + protected Color mColorMiddleFade; + protected Color mColorTopIdle; + protected Color mColorMiddleIdle; + protected Color mColorTopNoTuner; + protected Color mColorMiddleNoTuner; + protected Color mColorLabelDetails; + protected Color mColorLabelDecoder; + protected Color mColorLabelAuxDecoder; + + private JLabel mStateLabel; + private JLabel mSourceLabel; + private JLabel mChannelLabel; + + private PlaylistManager mPlaylistManager; + protected SettingsManager mSettingsManager; + + private Channel mChannel; + private List mDecoderPanels = new ArrayList<>(); + + /** + * Gui component to a channel state. Provides System, Site and channel + * name, source and channel state visualization. + * + * Updates color background according to current channel state. + */ + public ChannelStatePanel( PlaylistManager playlistManager, + SettingsManager settingsManager, + Channel channel ) + { + mPlaylistManager = playlistManager; + + mSettingsManager = settingsManager; + mSettingsManager.addListener( this ); + + mChannel = channel; + mChannel.getChannelState().setChangedAttributeListener( this ); + + init(); + + for( Decoder decoder: getChannel().getProcessingChain().getDecoders() ) + { + DecoderPanel panel = DecoderFactory.getDecoderPanel( mSettingsManager, decoder ); + mDecoderPanels.add( panel ); + add( panel, "grow,span" ); + } + } + + private void init() + { + getColors(); + + setLayout( new MigLayout( "insets 3 2 2 2", "[grow,fill]", "[]0[]0[]") ); + + mStateLabel = new JLabel( mChannel.getChannelState().getState() + .getDisplayValue() ); + mStateLabel.setFont( mFontDecoder ); + mStateLabel.setForeground( mColorLabelDecoder ); + add( mStateLabel ); + + mSourceLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() ); + mSourceLabel.setFont( mFontDetails ); + mSourceLabel.setForeground( mColorLabelDetails ); + add( mSourceLabel ); + + mChannelLabel = new JLabel( mChannel.getChannelDisplayName() ); + mChannelLabel.setFont( mFontDetails ); + mChannelLabel.setForeground( mColorLabelDetails ); + add( mChannelLabel, "wrap" ); + } + + public Channel getChannel() + { + return mChannel; + } + + public void dispose() + { + mPlaylistManager = null; + + mSettingsManager.removeListener( this ); + mSettingsManager = null; + + mChannel.getChannelState().removeChangedAttributeListener(); + mChannel = null; + + for( DecoderPanel panel: mDecoderPanels ) + { + panel.dispose(); + remove( panel ); + } + + mDecoderPanels.clear(); + } + + @Override + public void receive( final ChangedAttribute changedAttribute ) + { + final ChannelState state = mChannel.getChannelState(); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + switch( changedAttribute ) + { + case CHANNEL_STATE: + mStateLabel.setText( state.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; + default: + break; + } + + repaint(); + } + }); + } + + @Override + protected void paintComponent(Graphics g) + { + super.paintComponent( g ); + + setBackground( mColorChannelBackground ); + + Graphics2D g2 = (Graphics2D)g.create(); + + Paint p = null; + + switch( mChannel.getChannelState().getState() ) + { + case CALL: + p = getGradient( mColorTopCall, mColorMiddleCall ); + break; + case CONTROL: + p = getGradient( mColorTopControl, mColorMiddleControl ); + break; + case DATA: + p = getGradient( mColorTopData, mColorMiddleData ); + break; + case FADE: + p = getGradient( mColorTopFade, mColorMiddleFade ); + break; + case END: + case IDLE: + default: + p = getGradient( mColorTopIdle, mColorMiddleIdle ); + break; + } + + g2.setPaint( p ); + g2.fillRect( 0, 0, getWidth(), getHeight() ); + + /* Draw bottom separator line */ + g2.setColor( Color.LIGHT_GRAY ); + g2.drawLine( 0, getHeight() - 1, getWidth(), getHeight() - 1 ); + + /* Draw channel selected box */ + if( getChannel().isSelected() ) + { + g2.setColor( mColorChannelSelected ); + g2.drawRect( 1, 1, getWidth() - 2, getHeight() - 2 ); + } + + g2.dispose(); + } + + + private void getColors() + { + mColorChannelBackground = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_BACKGROUND ).getColor(); + mColorChannelSelected = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_SELECTED_CHANNEL ).getColor(); + mColorTopCall = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CALL ).getColor(); + mColorMiddleCall = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CALL ).getColor(); + mColorTopControl = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_CONTROL ).getColor(); + mColorMiddleControl = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL ).getColor(); + mColorTopData = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_DATA ).getColor(); + mColorMiddleData = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_DATA ).getColor(); + mColorTopFade = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_FADE ).getColor(); + mColorMiddleFade = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_FADE ).getColor(); + mColorTopIdle = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE ).getColor(); + mColorMiddleIdle = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_IDLE ).getColor(); + mColorTopNoTuner = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ).getColor(); + mColorMiddleNoTuner = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ).getColor(); + + mColorLabelDetails = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ).getColor(); + mColorLabelDecoder = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_LABEL_DECODER ).getColor(); + mColorLabelAuxDecoder = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ).getColor(); + } + + private GradientPaint getGradient( Color top, Color middle ) + { + return new GradientPaint( 0f, -50.0f, top, + 0f, (float)getHeight() / 2.2f, middle, true ); + } + + @Override + public void settingChanged( Setting setting ) + { + if( setting instanceof ColorSetting ) + { + ColorSetting colorSetting = (ColorSetting)setting; + + switch( colorSetting.getColorSettingName() ) + { + case CHANNEL_STATE_BACKGROUND: + mColorChannelBackground = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_MIDDLE_CALL: + mColorMiddleCall = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_TOP_CALL: + mColorTopCall = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_MIDDLE_CONTROL: + mColorMiddleControl = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_TOP_CONTROL: + mColorTopControl = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_MIDDLE_DATA: + mColorMiddleData = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_TOP_DATA: + mColorTopData = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_MIDDLE_FADE: + mColorMiddleFade = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_TOP_FADE: + mColorTopFade = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_MIDDLE_IDLE: + mColorMiddleIdle = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_TOP_IDLE: + mColorTopIdle = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER: + mColorMiddleNoTuner = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_GRADIENT_TOP_NO_TUNER: + mColorTopNoTuner = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_LABEL_AUX_DECODER: + mColorLabelAuxDecoder = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_LABEL_DECODER: + mColorLabelDecoder = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_LABEL_DETAILS: + mColorLabelDetails = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_SELECTED_CHANNEL: + mColorChannelSelected = colorSetting.getColor(); + repaint(); + break; + default: + break; + } + } + } + + @Override + public void settingDeleted( Setting setting ) {} + + public JMenu getContextMenu() + { + JMenu menu = new JMenu( "Channel: " + mChannel.getName() ); + + if( mChannel.getEnabled() ) + { + JMenuItem disable = new JMenuItem( "Disable" ); + disable.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + mChannel.setEnabled( DISABLED, BROADCAST_CHANGE ); + + if( mPlaylistManager != null ) + { + mPlaylistManager.save(); + } + } + } ); + + menu.add( disable ); + + menu.add( new JSeparator() ); + + JMenuItem actySummaryItem = + new JMenuItem( "Activity Summary" ); + + actySummaryItem.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + StringBuilder sb = new StringBuilder(); + + for( Decoder decoder: mChannel.getProcessingChain().getDecoders() ) + { + sb.append( decoder.getDecoderState().getActivitySummary() ); + } + + new ActivitySummaryFrame( sb.toString(), ChannelStatePanel.this ); + } + } ); + + menu.add( actySummaryItem ); + } + else + { + JMenuItem enable = new JMenuItem( "Enable" ); + enable.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + mChannel.setEnabled( ENABLED, BROADCAST_CHANGE ); + + if( mPlaylistManager != null ) + { + mPlaylistManager.save(); + } + } + } ); + + menu.add( enable ); + } + + menu.add( new JSeparator() ); + + JMenuItem deleteItem = new JMenuItem( "Delete" ); + deleteItem.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + dispose(); + + if( mPlaylistManager != null ) + { + mPlaylistManager.save(); + } + } + } ); + + menu.add( deleteItem ); + + return menu; + } + +} diff --git a/src/controller/state/ChannelStateSelectionListener.java b/src/module/decode/state/ChannelStateSelectionListener.java similarity index 97% rename from src/controller/state/ChannelStateSelectionListener.java rename to src/module/decode/state/ChannelStateSelectionListener.java index 2e4c611a..c0496acf 100644 --- a/src/controller/state/ChannelStateSelectionListener.java +++ b/src/module/decode/state/ChannelStateSelectionListener.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.state; +package module.decode.state; public interface ChannelStateSelectionListener diff --git a/src/controller/state/AuxStatePanel.java b/src/module/decode/state/DecoderPanel.java similarity index 50% rename from src/controller/state/AuxStatePanel.java rename to src/module/decode/state/DecoderPanel.java index 8d3bdba9..bd629754 100644 --- a/src/controller/state/AuxStatePanel.java +++ b/src/module/decode/state/DecoderPanel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,108 +15,79 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.state; +package module.decode.state; import java.awt.Color; import java.awt.Font; -import javax.swing.ImageIcon; -import javax.swing.JLabel; import javax.swing.JPanel; -import net.miginfocom.swing.MigLayout; +import module.decode.Decoder; import sample.Listener; import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingChangeListener; import settings.SettingsManager; -import alias.Alias; -import controller.state.ChannelState.ChangedAttribute; -public abstract class AuxStatePanel extends JPanel - implements Listener, - SettingChangeListener +public abstract class DecoderPanel extends JPanel + implements Listener, SettingChangeListener { - private static final long serialVersionUID = 1L; - + private static final long serialVersionUID = 1L; + protected Font mFontDetails = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); + protected Font mFontDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); protected Font mFontAuxDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 10 ); - + protected Color mColorLabelDetails; + protected Color mColorLabelDecoder; protected Color mColorLabelAuxDecoder; - - protected SettingsManager mSettingsManager; - protected AuxChannelState mAuxChannelState; - - public AuxStatePanel( SettingsManager settingsManager, - AuxChannelState auxChannelState ) - { - mSettingsManager = settingsManager; - mAuxChannelState = auxChannelState; - - /* Register to receive event updates */ - mAuxChannelState.addListener( this ); - - /* Register to receive settings updates */ - mSettingsManager.addListener( this ); - - getColors(); - - setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") ); - } - - public void dispose() - { - /* Deregister from changed settings events */ - mSettingsManager.removeListener( this ); - mSettingsManager = null; - - mAuxChannelState = null; - } - - protected void addLabel( JLabel label, boolean wrap ) + + protected SettingsManager mSettingsManager; + protected Decoder mDecoder; + + public DecoderPanel( SettingsManager settingsManager, Decoder decoder ) { - label.setFont( mFontAuxDecoder ); - label.setForeground( mColorLabelAuxDecoder ); + mSettingsManager = settingsManager; + mSettingsManager.addListener( this ); - if( wrap ) - { - add( label, "wrap" ); - } - else - { - add( label ); - } + mDecoder = decoder; + + mDecoder.getDecoderState().setChangedAttributeListener( this ); } - - protected void updateAliasLabel( JLabel label, Alias alias ) + + public void dispose() { - if( alias != null ) - { - label.setText( alias.getName() ); - - String iconName = alias.getIconName(); - - ImageIcon icon = mSettingsManager.getImageIcon( iconName, 12 ); - - label.setIcon( icon ); - } - else - { - label.setText( null ); - label.setIcon( null ); - } + mSettingsManager.removeListener( this ); + mSettingsManager = null; + + mDecoder.getDecoderState().removeChangedAttributeListener(); + mDecoder = null; + } + + public Decoder getDecoder() + { + return mDecoder; + } + + protected void init() + { + getColors(); + + setOpaque( false ); //Use the parent panel's background color } private void getColors() { mColorLabelDetails = mSettingsManager.getColorSetting( ColorSettingName.CHANNEL_STATE_LABEL_DETAILS ).getColor(); + mColorLabelDecoder = mSettingsManager.getColorSetting( + ColorSettingName.CHANNEL_STATE_LABEL_DECODER ).getColor(); mColorLabelAuxDecoder = mSettingsManager.getColorSetting( ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER ).getColor(); } - + + @Override public void settingChanged( Setting setting ) { @@ -126,13 +97,19 @@ public abstract class AuxStatePanel extends JPanel switch( colorSetting.getColorSettingName() ) { - case CHANNEL_STATE_LABEL_AUX_DECODER: - mColorLabelAuxDecoder = colorSetting.getColor(); - repaint(); - break; - case CHANNEL_STATE_LABEL_DETAILS: - mColorLabelDetails = colorSetting.getColor(); - repaint(); + case CHANNEL_STATE_LABEL_AUX_DECODER: + mColorLabelAuxDecoder = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_LABEL_DECODER: + mColorLabelDecoder = colorSetting.getColor(); + repaint(); + break; + case CHANNEL_STATE_LABEL_DETAILS: + mColorLabelDetails = colorSetting.getColor(); + repaint(); + break; + default: break; } } diff --git a/src/module/decode/state/DecoderState.java b/src/module/decode/state/DecoderState.java new file mode 100644 index 00000000..7540950d --- /dev/null +++ b/src/module/decode/state/DecoderState.java @@ -0,0 +1,186 @@ +package module.decode.state; + +import message.Message; +import module.decode.event.ActivitySummaryProvider; +import module.decode.event.CallEvent; +import module.decode.event.ICallEventProvider; +import sample.Listener; +import alias.AliasList; + +/** + * Channel state monitors the stream of decoded messages produced by the + * decoder and broadcasts call events as they occur within the decoded message activity. + * + * Provides access to a textual activity summary of events observed. + */ +public abstract class DecoderState implements ActivitySummaryProvider, + Listener, + ICallEventProvider, + IChangedAttributeProvider, + IDecoderStateEventListener, + IDecoderStateEventProvider +{ + private Listener mCallEventListener; + private Listener mChangedAttributeListener; + private Listener mDecoderStateListener; + + private DecoderStateEventListener mDecoderStateEventListener = + new DecoderStateEventListener(); + + protected CallEvent mCurrentCallEvent; + + private AliasList mAliasList; + + public DecoderState( AliasList aliasList ) + { + mAliasList = aliasList; + } + + public AliasList getAliasList() + { + return mAliasList; + } + + public boolean hasAliasList() + { + return mAliasList != null; + } + + /** + * Reset the decoder state to prepare for processing a different sample + * source + */ + public abstract void reset(); + + /** + * Allow the decoder to perform any setup actions + */ + public abstract void init(); + + /** + * Implements the IDecoderStateEventListener interface to receive state + * reset events. + */ + public abstract void receiveDecoderStateEvent( DecoderStateEvent event ); + + /** + * Disposes any resources or pointers held by this instance to prepare for + * garbage collection + */ + public void dispose() + { + mCallEventListener = null; + mChangedAttributeListener = null; + mDecoderStateListener = null; + + mAliasList = null; + } + + /** + * Activity Summary - textual summary of activity observed by the channel state. + */ + public abstract String getActivitySummary(); + + /** + * Broadcasts a call event to any registered listeners + */ + protected void broadcast( CallEvent event ) + { + if( mCallEventListener != null ) + { + mCallEventListener.receive( event ); + } + } + + /** + * Adds a call event listener + */ + @Override + public void setCallEventListener( Listener listener ) + { + mCallEventListener = listener; + } + + /** + * Removes the call event listener + */ + @Override + public void removeCallEventListener() + { + mCallEventListener = null; + } + + @Override + public Listener getDecoderStateListener() + { + return mDecoderStateEventListener; + } + + private class DecoderStateEventListener implements Listener + { + @Override + public void receive( DecoderStateEvent event ) + { + receiveDecoderStateEvent( event ); + } + } + + /** + * Broadcasts the channel state attribute change event to all registered + * listeners + */ + protected void broadcast( ChangedAttribute attribute ) + { + if( mChangedAttributeListener != null ) + { + mChangedAttributeListener.receive( attribute ); + } + } + + /** + * Adds the listener to receive channel state attribute change events + */ + @Override + public void setChangedAttributeListener( Listener listener ) + { + mChangedAttributeListener = listener; + } + + /** + * Removes the listener to receive channel state attribute change events + */ + @Override + public void removeChangedAttributeListener() + { + mChangedAttributeListener = null; + } + + /** + * Broadcasts a channel state event to any registered listeners + */ + protected void broadcast( DecoderStateEvent event ) + { + if( mDecoderStateListener != null ) + { + mDecoderStateListener.receive( event ); + } + } + + /** + * Adds a decoder state event listener + */ + @Override + public void setDecoderStateListener( Listener listener ) + { + mDecoderStateListener = listener; + } + + /** + * Removes the decoder state event listener + */ + @Override + public void removeDecoderStateListener() + { + mDecoderStateListener = null; + } +} diff --git a/src/module/decode/state/DecoderStateEvent.java b/src/module/decode/state/DecoderStateEvent.java new file mode 100644 index 00000000..ae1a485e --- /dev/null +++ b/src/module/decode/state/DecoderStateEvent.java @@ -0,0 +1,55 @@ +package module.decode.state; + +public class DecoderStateEvent +{ + private Object mSource; + private Event mEvent; + private State mState; + private long mFrequency; + + public DecoderStateEvent( Object source, Event event, State state ) + { + mSource = source; + mEvent = event; + mState = state; + } + + public DecoderStateEvent( Object source, Event event, State state, long frequency ) + { + this( source, event, state ); + + mFrequency = frequency; + } + + public Object getSource() + { + return mSource; + } + + public Event getEvent() + { + return mEvent; + } + + public State getState() + { + return mState; + } + + public long getFrequency() + { + return mFrequency; + } + + public enum Event + { + ALWAYS_UNSQUELCH, + CONTINUATION, + DECODE, + END, + RESET, + SOURCE_FREQUENCY, + START, + TRAFFIC_CHANNEL_ALLOCATION; + } +} diff --git a/src/module/decode/state/IChangedAttributeListener.java b/src/module/decode/state/IChangedAttributeListener.java new file mode 100644 index 00000000..04d3ce66 --- /dev/null +++ b/src/module/decode/state/IChangedAttributeListener.java @@ -0,0 +1,8 @@ +package module.decode.state; + +import sample.Listener; + +public interface IChangedAttributeListener +{ + public Listener getChangedAttributeListener(); +} diff --git a/src/module/decode/state/IChangedAttributeProvider.java b/src/module/decode/state/IChangedAttributeProvider.java new file mode 100644 index 00000000..283c3e6b --- /dev/null +++ b/src/module/decode/state/IChangedAttributeProvider.java @@ -0,0 +1,9 @@ +package module.decode.state; + +import sample.Listener; + +public interface IChangedAttributeProvider +{ + public void setChangedAttributeListener( Listener listener ); + public void removeChangedAttributeListener(); +} diff --git a/src/module/decode/state/IDecoderStateEventListener.java b/src/module/decode/state/IDecoderStateEventListener.java new file mode 100644 index 00000000..72b9eb1c --- /dev/null +++ b/src/module/decode/state/IDecoderStateEventListener.java @@ -0,0 +1,8 @@ +package module.decode.state; + +import sample.Listener; + +public interface IDecoderStateEventListener +{ + public Listener getDecoderStateListener(); +} diff --git a/src/module/decode/state/IDecoderStateEventProvider.java b/src/module/decode/state/IDecoderStateEventProvider.java new file mode 100644 index 00000000..afd8c2d0 --- /dev/null +++ b/src/module/decode/state/IDecoderStateEventProvider.java @@ -0,0 +1,9 @@ +package module.decode.state; + +import sample.Listener; + +public interface IDecoderStateEventProvider +{ + public void setDecoderStateListener( Listener listener ); + public void removeDecoderStateListener(); +} diff --git a/src/module/decode/state/State.java b/src/module/decode/state/State.java new file mode 100644 index 00000000..2820429d --- /dev/null +++ b/src/module/decode/state/State.java @@ -0,0 +1,83 @@ +package module.decode.state; + +public enum State +{ + IDLE( "IDLE" ) + { + @Override + public boolean canChangeTo( State state ) + { + return true; + } + }, + CALL( "CALL" ) + { + @Override + public boolean canChangeTo( State state ) + { + return state == CALL || + state == CONTROL || + state == DATA || + state == ENCRYPTED || + state == FADE; + } + }, + DATA( "DATA" ) + { + @Override + public boolean canChangeTo( State state ) + { + return state == CALL || + state == CONTROL || + state == DATA || + state == State.ENCRYPTED || + state == FADE; + } + }, + ENCRYPTED( "ENCRYPTED" ) + { + @Override + public boolean canChangeTo( State state ) + { + return state == FADE; + } + }, + CONTROL( "CONTROL" ) + { + @Override + public boolean canChangeTo( State state ) + { + return true; + } + }, + FADE( "FADE" ) + { + @Override + public boolean canChangeTo( State state ) + { + return state != FADE; //All states except fade allowed + } + }, + END( "END" ) + { + @Override + public boolean canChangeTo( State state ) + { + return state != FADE; //Fade is only disallowed state + } + }; + + private String mDisplayValue; + + private State( String displayValue ) + { + mDisplayValue = displayValue; + } + + public abstract boolean canChangeTo( State state ); + + public String getDisplayValue() + { + return mDisplayValue; + } +} diff --git a/src/module/decode/state/TrafficChannelAllocationEvent.java b/src/module/decode/state/TrafficChannelAllocationEvent.java new file mode 100644 index 00000000..14441fde --- /dev/null +++ b/src/module/decode/state/TrafficChannelAllocationEvent.java @@ -0,0 +1,25 @@ +package module.decode.state; + +import module.decode.event.CallEvent; + +/** + * Traffic channel allocation event wraps a call event indicating the channel + * and frequency for a traffic channel allocation event, so that the traffic + * channel manager can respond and allocate a decoder channel and source if + * available, or change the wrapped call event to a call detect event and log it. + */ +public class TrafficChannelAllocationEvent extends DecoderStateEvent +{ + private CallEvent mCallEvent; + + public TrafficChannelAllocationEvent( Object source, CallEvent callEvent ) + { + super( source, Event.TRAFFIC_CHANNEL_ALLOCATION, State.CALL ); + mCallEvent = callEvent; + } + + public CallEvent getCallEvent() + { + return mCallEvent; + } +} diff --git a/src/module/decode/state/TrafficChannelManager.java b/src/module/decode/state/TrafficChannelManager.java new file mode 100644 index 00000000..cf3021cf --- /dev/null +++ b/src/module/decode/state/TrafficChannelManager.java @@ -0,0 +1,255 @@ +package module.decode.state; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import module.Module; +import module.decode.config.DecodeConfiguration; +import module.decode.event.CallEvent; +import module.decode.event.CallEvent.CallEventType; +import module.decode.event.ICallEventProvider; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import source.config.SourceConfigTuner; +import source.tuner.TunerChannel; +import source.tuner.TunerChannel.Type; +import controller.ResourceManager; +import controller.channel.Channel; +import controller.channel.Channel.ChannelType; +import controller.site.Site; +import controller.system.System; + +public class TrafficChannelManager extends Module + implements ICallEventProvider, IDecoderStateEventListener +{ + private final static Logger mLog = LoggerFactory.getLogger( TrafficChannelManager.class ); + + private int mTrafficChannelPoolMaximumSize = + DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT; + private List mTrafficChannelPool = new ArrayList(); + private Map mTrafficChannelsInUse = new ConcurrentHashMap(); + + private DecoderStateEventListener mEventListener = new DecoderStateEventListener(); + private Listener mCallEventListener; + + private DecodeConfiguration mDecodeConfiguration; + private ResourceManager mResourceManager; + private long mTrafficChannelTimeout; + + /** + * Monitors call events and allocates traffic decoder channels in response + * to traffic channel allocation call events. Manages a pool of reusable + * traffic channel allocations. + * + * @param resourceManager - resource manager + * + * @param decodeConfiguration - decoder configuration to use for each + * traffic channel allocation. + * + * @param trafficChannelTimeout - millisecond call timer limit used when an + * end of call signalling event is not decoded. + * + * @param trafficChannelPoolSize - maximum number of allocated traffic channels + * in the pool + */ + public TrafficChannelManager( ResourceManager resourceManager, + DecodeConfiguration decodeConfiguration, + long trafficChannelTimeout, + int trafficChannelPoolSize ) + { + mResourceManager = resourceManager; + mDecodeConfiguration = decodeConfiguration; + mTrafficChannelTimeout = trafficChannelTimeout; + mTrafficChannelPoolMaximumSize = trafficChannelPoolSize; + } + + @Override + public void dispose() + { + mCallEventListener = null; + mResourceManager = null; + mDecodeConfiguration = null; + } + + private Channel getChannel( String channelNumber, TunerChannel tunerChannel ) + { + Channel channel = null; + + if( !mTrafficChannelsInUse.containsKey( channelNumber ) ) + { + for( Channel configuredChannel: mTrafficChannelPool ) + { + if( !configuredChannel.isProcessing() ) + { + channel = configuredChannel; + break; + } + } + + if( channel == null && mTrafficChannelPool.size() < mTrafficChannelPoolMaximumSize ) + { + channel = new Channel( "Traffic", ChannelType.TRAFFIC ); + + channel.setDecodeConfiguration( mDecodeConfiguration ); + + channel.setResourceManager( mResourceManager ); + + channel.setTrafficChannelTimeout( mTrafficChannelTimeout ); + + mTrafficChannelPool.add( channel ); + } + + /* If we have a configured channel, start it and track it */ + if( channel != null ) + { + channel.setSourceConfiguration( new SourceConfigTuner( tunerChannel ) ); + + channel.setSystem( new System( "Traffic" ), false ); + channel.setSite( new Site( "Channel" ), false ); + channel.setName( channelNumber ); + + channel.setEnabled( true ); + + /* Check the channel obtained a source and is currently processing */ + if( channel.isProcessing() ) + { + mTrafficChannelsInUse.put( channelNumber, channel ); + } + else + { + channel.setEnabled( false ); + + channel.setSystem( null, false ); + channel.setSite( null, false ); + channel.setName( "Traffic" ); + } + } + } + + return channel; + } + + private void process( TrafficChannelAllocationEvent event ) + { + if( mCallEventListener != null ) + { + CallEvent callEvent = event.getCallEvent(); + + /* Check for duplicate events and suppress */ + if( mTrafficChannelsInUse.containsKey( callEvent.getChannel() ) ) + { + return; + } + + long frequency = callEvent.getFrequency(); + + if( frequency > 0 ) + { + Channel channel = getChannel( callEvent.getChannel(), + new TunerChannel( Type.TRAFFIC, frequency, + mDecodeConfiguration.getDecoderType() + .getChannelBandwidth() ) ); + + if( channel != null ) + { + if( channel.isProcessing() ) + { + ChannelState state = channel.getChannelState(); + + /* Register as a listener to be notified when the call + * is completed */ + state.configureAsTrafficChannel( + new TrafficChannelStatusListener( this, + callEvent.getChannel() ), event ); + + /* Set state to call so that audio squelch state is correct */ + state.setState( State.CALL ); + } + else + { + callEvent.setCallEventType( CallEventType.CALL_DETECT ); + } + } + else + { + callEvent.setCallEventType( CallEventType.CALL_DETECT ); + } + } + else + { + callEvent.setCallEventType( CallEventType.CALL_DETECT ); + } + + mCallEventListener.receive( callEvent ); + } + } + + @Override + public Listener getDecoderStateListener() + { + return mEventListener; + } + + @Override + public void setCallEventListener( Listener listener ) + { + mCallEventListener = listener; + } + + @Override + public void removeCallEventListener() + { + mCallEventListener = null; + } + + @Override + public void init() + { + + } + + /** + * Callback used by the TrafficChannelStatusListener class to signal the + * end of a an allocated traffic channel call event + * + * @param channelNumber - channel number from the call event that signaled + * the start of a traffic channel allocation + */ + public void callEnd( String channelNumber ) + { + if( channelNumber != null && mTrafficChannelsInUse.containsKey( channelNumber ) ) + { + Channel channel = mTrafficChannelsInUse.get( channelNumber ); + + mTrafficChannelsInUse.remove( channelNumber ); + + /* Disable the channel and broadcast a notification */ + channel.setEnabled( false, true ); + } + } + + /** + * Wrapper class for the decoder state event listener interface to catch + * traffic channel allocation requests + */ + public class DecoderStateEventListener implements Listener + { + @Override + public void receive( DecoderStateEvent event ) + { + switch( event.getEvent() ) + { + case TRAFFIC_CHANNEL_ALLOCATION: + process( (TrafficChannelAllocationEvent)event ); + break; + default: + break; + } + } + } +} diff --git a/src/module/decode/state/TrafficChannelStatusListener.java b/src/module/decode/state/TrafficChannelStatusListener.java new file mode 100644 index 00000000..e1b31a58 --- /dev/null +++ b/src/module/decode/state/TrafficChannelStatusListener.java @@ -0,0 +1,21 @@ +package module.decode.state; + +public class TrafficChannelStatusListener +{ + private TrafficChannelManager mTrafficChannelManager; + private String mChannelNumber; + + public TrafficChannelStatusListener( TrafficChannelManager manager, String channelNumber ) + { + mTrafficChannelManager = manager; + mChannelNumber = channelNumber; + } + + public void callEnd() + { + if( mTrafficChannelManager != null ) + { + mTrafficChannelManager.callEnd( mChannelNumber ); + } + } +} diff --git a/src/decode/tait/Tait1200ANIMessage.java b/src/module/decode/tait/Tait1200ANIMessage.java similarity index 99% rename from src/decode/tait/Tait1200ANIMessage.java rename to src/module/decode/tait/Tait1200ANIMessage.java index 968ef2ad..08eeb6d5 100644 --- a/src/decode/tait/Tait1200ANIMessage.java +++ b/src/module/decode/tait/Tait1200ANIMessage.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package module.decode.tait; import java.text.SimpleDateFormat; import java.util.ArrayList; diff --git a/src/decode/tait/Tait1200ANIMessageProcessor.java b/src/module/decode/tait/Tait1200ANIMessageProcessor.java similarity index 78% rename from src/decode/tait/Tait1200ANIMessageProcessor.java rename to src/module/decode/tait/Tait1200ANIMessageProcessor.java index e091b620..34a1a9df 100644 --- a/src/decode/tait/Tait1200ANIMessageProcessor.java +++ b/src/module/decode/tait/Tait1200ANIMessageProcessor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,14 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package module.decode.tait; import message.Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Broadcaster; import sample.Listener; import alias.AliasList; import bits.BinaryMessage; @@ -32,7 +31,7 @@ public class Tait1200ANIMessageProcessor implements Listener private final static Logger mLog = LoggerFactory.getLogger( Tait1200ANIMessageProcessor.class ); - private Broadcaster mBroadcaster = new Broadcaster(); + private Listener mMessageListener; private AliasList mAliasList; @@ -43,22 +42,26 @@ public class Tait1200ANIMessageProcessor implements Listener public void dispose() { - mBroadcaster.dispose(); + mMessageListener = null; + mAliasList = null; } @Override public void receive( BinaryMessage buffer ) { - mBroadcaster.receive( new Tait1200ANIMessage( buffer, mAliasList ) ); + if( mMessageListener != null ) + { + mMessageListener.receive( new Tait1200ANIMessage( buffer, mAliasList ) ); + } } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } public void removeMessageListener( Listener listener ) { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } } diff --git a/src/decode/tait/Tait1200CallEvent.java b/src/module/decode/tait/Tait1200CallEvent.java similarity index 95% rename from src/decode/tait/Tait1200CallEvent.java rename to src/module/decode/tait/Tait1200CallEvent.java index 73aa69ff..90bd6c53 100644 --- a/src/decode/tait/Tait1200CallEvent.java +++ b/src/module/decode/tait/Tait1200CallEvent.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,12 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package module.decode.tait; +import module.decode.DecoderType; +import module.decode.event.CallEvent; import alias.Alias; import alias.AliasList; -import controller.activity.CallEvent; -import decode.DecoderType; public class Tait1200CallEvent extends CallEvent { diff --git a/src/decode/tait/Tait1200Decoder.java b/src/module/decode/tait/Tait1200Decoder.java similarity index 69% rename from src/decode/tait/Tait1200Decoder.java rename to src/module/decode/tait/Tait1200Decoder.java index 455ba341..0a328bda 100644 --- a/src/decode/tait/Tait1200Decoder.java +++ b/src/module/decode/tait/Tait1200Decoder.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,35 +15,37 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package module.decode.tait; import instrument.Instrumentable; import instrument.tap.Tap; import instrument.tap.stream.BinaryTap; +import instrument.tap.stream.FloatBufferTap; import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; +import module.decode.Decoder; +import module.decode.DecoderType; import sample.Broadcaster; -import sample.real.RealSampleListener; -import source.Source.SampleType; +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; import alias.AliasList; -import audio.IAudioOutput; import bits.MessageFramer; import bits.SyncPattern; -import decode.Decoder; -import decode.DecoderType; import dsp.filter.Filters; -import dsp.filter.FloatFIRFilter; -import dsp.filter.FloatHalfBandFilter; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; +import dsp.filter.halfband.real.HalfBandFilter_RB_RB; import dsp.fsk.FSK2Decoder; import dsp.fsk.FSK2Decoder.Output; /** * TAIT 1200 - 1200 baud 2FSK decoder */ -public class Tait1200Decoder extends Decoder implements Instrumentable +public class Tait1200Decoder extends Decoder implements IRealBufferListener, + Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ private static final int DECIMATED_SAMPLE_RATE = 24000; @@ -56,8 +58,6 @@ public class Tait1200Decoder extends Decoder implements Instrumentable /* Instrumentation Taps */ private ArrayList mAvailableTaps; - private static final String INSTRUMENT_INPUT = - "Tap Point: Float Input"; private static final String INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD = "Tap Point: Bandpass Filter > < FSK2 Decoder"; private static final String INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER = @@ -65,24 +65,23 @@ public class Tait1200Decoder extends Decoder implements Instrumentable private FSK2Decoder mFSKDecoder; - private FloatHalfBandFilter mDecimationFilter; - private FloatFIRFilter mBandPassFilter; + private HalfBandFilter_RB_RB mDecimationFilter; + private RealFIRFilter_RB_RB mBandPassFilter; private MessageFramer mMessageFramerGPS; - private MessageFramer mMessageFramerSELCALL; + private MessageFramer mMessageFramerANI; private Broadcaster mFSKBroadcaster = new Broadcaster(); private Tait1200GPSMessageProcessor mMessageAProcessor; private Tait1200ANIMessageProcessor mMessageBProcessor; public Tait1200Decoder( AliasList aliasList ) { - super( SampleType.REAL ); + super( new Tait1200DecoderState( aliasList ) ); - mDecimationFilter = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); - addRealSampleListener( mDecimationFilter ); + mDecimationFilter = new HalfBandFilter_RB_RB( + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0002f, true ); - mBandPassFilter = new FloatFIRFilter( - Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02 ); + mBandPassFilter = new RealFIRFilter_RB_RB( + Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02f ); mDecimationFilter.setListener( mBandPassFilter ); mFSKDecoder = new FSK2Decoder( DECIMATED_SAMPLE_RATE, @@ -94,20 +93,20 @@ public class Tait1200Decoder extends Decoder implements Instrumentable mMessageFramerGPS = new MessageFramer( SyncPattern.TAIT_CCDI_GPS_MESSAGE.getPattern(), MESSAGE_LENGTH ); - mMessageFramerSELCALL = new MessageFramer( + mMessageFramerANI = new MessageFramer( SyncPattern.TAIT_SELCAL_MESSAGE.getPattern(), MESSAGE_LENGTH ); mFSKBroadcaster.addListener( mMessageFramerGPS ); - mFSKBroadcaster.addListener( mMessageFramerSELCALL ); + mFSKBroadcaster.addListener( mMessageFramerANI ); mMessageAProcessor = new Tait1200GPSMessageProcessor( aliasList ); mMessageBProcessor = new Tait1200ANIMessageProcessor( aliasList ); mMessageFramerGPS.addMessageListener( mMessageAProcessor ); - mMessageFramerSELCALL.addMessageListener( mMessageBProcessor ); + mMessageFramerANI.addMessageListener( mMessageBProcessor ); - mMessageAProcessor.addMessageListener( this ); - mMessageBProcessor.addMessageListener( this ); + mMessageAProcessor.setMessageListener( mMessageBroadcaster ); + mMessageBProcessor.setMessageListener( mMessageBroadcaster ); } public void dispose() @@ -118,26 +117,11 @@ public class Tait1200Decoder extends Decoder implements Instrumentable mBandPassFilter.dispose(); mFSKDecoder.dispose(); mMessageFramerGPS.dispose(); - mMessageFramerSELCALL.dispose(); + mMessageFramerANI.dispose(); mMessageAProcessor.dispose(); mMessageBProcessor.dispose(); } - @Override - public DecoderType getType() - { - return DecoderType.TAIT_1200; - } - - /** - * Returns a float listener interface for connecting this decoder to a - * float stream provider - */ - public RealSampleListener getRealReceiver() - { - return (RealSampleListener)mDecimationFilter; - } - @Override public List getTaps() { @@ -145,7 +129,6 @@ public class Tait1200Decoder extends Decoder implements Instrumentable { mAvailableTaps = new ArrayList(); - mAvailableTaps.add( new FloatTap( INSTRUMENT_INPUT, 0, 1.0f ) ); mAvailableTaps.add( new FloatTap( INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD, 0, 0.5f ) ); mAvailableTaps.addAll( mFSKDecoder.getTaps() ); @@ -163,12 +146,8 @@ public class Tait1200Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - addRealSampleListener( inputTap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: - FloatTap bpTap = (FloatTap)tap; + FloatBufferTap bpTap = (FloatBufferTap)tap; mBandPassFilter.setListener( bpTap ); bpTap.setListener( mFSKDecoder ); break; @@ -186,10 +165,6 @@ public class Tait1200Decoder extends Decoder implements Instrumentable switch( tap.getName() ) { - case INSTRUMENT_INPUT: - FloatTap inputTap = (FloatTap)tap; - removeRealListener( inputTap ); - break; case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD: mBandPassFilter.setListener( mFSKDecoder ); break; @@ -200,15 +175,14 @@ public class Tait1200Decoder extends Decoder implements Instrumentable } @Override - public void addUnfilteredRealSampleListener( RealSampleListener listener ) - { - //Not implemented - } + public Listener getRealBufferListener() + { + return mDecimationFilter; + } @Override - public IAudioOutput getAudioOutput() + public DecoderType getDecoderType() { - // Not implemented - return null; + return DecoderType.TAIT_1200; } } diff --git a/src/module/decode/tait/Tait1200DecoderState.java b/src/module/decode/tait/Tait1200DecoderState.java new file mode 100644 index 00000000..7f0a8e71 --- /dev/null +++ b/src/module/decode/tait/Tait1200DecoderState.java @@ -0,0 +1,236 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.decode.tait; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.TreeSet; + +import org.jdesktop.swingx.mapviewer.GeoPosition; + +import message.Message; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderState; +import module.decode.state.DecoderStateEvent; +import alias.Alias; +import alias.AliasList; + +public class Tait1200DecoderState extends DecoderState +{ + private TreeSet mIdents = new TreeSet(); + + private String mFrom; + private Alias mFromAlias; + + private String mTo; + private Alias mToAlias; + + private String mMessage; + private String mMessageType; + + public Tait1200DecoderState( AliasList aliasList ) + { + super( aliasList ); + } + + @Override + public void reset() + { + mIdents.clear(); + + resetState(); + } + + private void resetState() + { + setFromID( null ); + setFromIDAlias( null ); + setToID( null ); + setToIDAlias( null ); + setMessage( null ); + setMessageType( null ); + } + + @Override + public void receive( Message message ) + { + if( message instanceof Tait1200GPSMessage ) + { + Tait1200GPSMessage gps = (Tait1200GPSMessage)message; + + setFromID( gps.getFromID() ); + setFromIDAlias( gps.getFromIDAlias() ); + mIdents.add( gps.getFromID() ); + + setToID( gps.getToID() ); + setToIDAlias( gps.getToIDAlias() ); + mIdents.add( gps.getToID()); + + GeoPosition position = gps.getGPSLocation(); + + if( position != null ) + { + setMessage( gps.getGPSLocation().toString().replace( "[", "" ) + .replace( "]", "" ) ); + } + setMessageType( "GPS" ); + } + else if( message instanceof Tait1200ANIMessage ) + { + Tait1200ANIMessage ani = (Tait1200ANIMessage)message; + + setFromID( ani.getFromID() ); + setFromIDAlias( ani.getFromIDAlias() ); + mIdents.add( ani.getFromID() ); + + setToID( ani.getToID() ); + setToIDAlias( ani.getToIDAlias() ); + mIdents.add( ani.getToID()); + + setMessage( null ); + setMessageType( "ANI" ); + } + } + + @Override + public String getActivitySummary() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "=============================\n" ); + sb.append( "Decoder:\tTait-1200I\n\n" ); + + if( !mIdents.isEmpty() ) + { + sb.append( "Radio Identifiers:\n" ); + + List idents = new ArrayList( mIdents ); + + Collections.sort( idents ); + + for( String ident: idents ) + { + sb.append( "\t" ); + sb.append( ident ); + + if( hasAliasList() ) + { + Alias alias = getAliasList().getTalkgroupAlias( ident ); + + if( alias != null ) + { + sb.append( "\t" ); + sb.append( alias.getName() ); + } + } + sb.append( "\n" ); + } + } + + return sb.toString(); + } + + @Override + public void init() + { + /* No initialization required */ + } + + /** + * Responds to reset events issued by the channel state + */ + @Override + public void receiveDecoderStateEvent( DecoderStateEvent event ) + { + switch( event.getEvent() ) + { + case RESET: + resetState(); + break; + default: + break; + } + } + + public String getFromID() + { + return mFrom; + } + + public void setFromID( String from ) + { + mFrom = from; + broadcast( ChangedAttribute.FROM_TALKGROUP ); + } + + public Alias getFromIDAlias() + { + return mFromAlias; + } + + public void setFromIDAlias( Alias alias ) + { + mFromAlias = alias; + broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); + } + + public String getToID() + { + return mTo; + } + + public void setToID( String to ) + { + mTo = to; + broadcast( ChangedAttribute.TO_TALKGROUP ); + } + + public Alias getToIDAlias() + { + return mToAlias; + } + + public void setToIDAlias( Alias alias ) + { + mToAlias = alias; + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + } + + public String getMessage() + { + return mMessage; + } + + public void setMessage( String message ) + { + mMessage = message; + broadcast( ChangedAttribute.MESSAGE ); + } + + public String getMessageType() + { + return mMessageType; + } + + public void setMessageType( String type ) + { + mMessageType = type; + broadcast( ChangedAttribute.MESSAGE_TYPE ); + } +} diff --git a/src/decode/tait/Tait1200GPSMessage.java b/src/module/decode/tait/Tait1200GPSMessage.java similarity index 99% rename from src/decode/tait/Tait1200GPSMessage.java rename to src/module/decode/tait/Tait1200GPSMessage.java index f68e8207..990341cc 100644 --- a/src/decode/tait/Tait1200GPSMessage.java +++ b/src/module/decode/tait/Tait1200GPSMessage.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package module.decode.tait; import java.text.SimpleDateFormat; import java.util.ArrayList; diff --git a/src/decode/tait/Tait1200GPSMessageProcessor.java b/src/module/decode/tait/Tait1200GPSMessageProcessor.java similarity index 75% rename from src/decode/tait/Tait1200GPSMessageProcessor.java rename to src/module/decode/tait/Tait1200GPSMessageProcessor.java index 6e1887b0..2d8d732a 100644 --- a/src/decode/tait/Tait1200GPSMessageProcessor.java +++ b/src/module/decode/tait/Tait1200GPSMessageProcessor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,14 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.tait; +package module.decode.tait; import message.Message; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Broadcaster; import sample.Listener; import alias.AliasList; import bits.BinaryMessage; @@ -32,7 +31,7 @@ public class Tait1200GPSMessageProcessor implements Listener private final static Logger mLog = LoggerFactory.getLogger( Tait1200GPSMessageProcessor.class ); - private Broadcaster mBroadcaster = new Broadcaster(); + private Listener mMessageListener; private AliasList mAliasList; @@ -43,22 +42,26 @@ public class Tait1200GPSMessageProcessor implements Listener public void dispose() { - mBroadcaster.dispose(); + mMessageListener = null; + mAliasList = null; } @Override public void receive( BinaryMessage buffer ) { - mBroadcaster.receive( new Tait1200GPSMessage( buffer, mAliasList ) ); + if( mMessageListener != null ) + { + mMessageListener.receive( new Tait1200GPSMessage( buffer, mAliasList ) ); + } } - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mBroadcaster.addListener( listener ); + mMessageListener = listener; } - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mBroadcaster.removeListener( listener ); + mMessageListener = null; } } diff --git a/src/decode/config/DecodeConfigP25Phase1.java b/src/module/decode/tait/Tait1200MessageFilter.java similarity index 56% rename from src/decode/config/DecodeConfigP25Phase1.java rename to src/module/decode/tait/Tait1200MessageFilter.java index 7de83b52..70d9e4cd 100644 --- a/src/decode/config/DecodeConfigP25Phase1.java +++ b/src/module/decode/tait/Tait1200MessageFilter.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,38 +15,38 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.config; +package module.decode.tait; -import javax.xml.bind.annotation.XmlAttribute; +import java.util.Collections; +import java.util.List; -import decode.DecoderType; -import decode.p25.P25Decoder; -import decode.p25.P25Decoder.Modulation; +import message.Message; +import filter.Filter; +import filter.FilterElement; -public class DecodeConfigP25Phase1 extends DecodeConfiguration +public class Tait1200MessageFilter extends Filter { - private P25Decoder.Modulation mModulation = Modulation.C4FM; - public DecodeConfigP25Phase1() - { - super( DecoderType.P25_PHASE1 ); - - setAFC( false ); - } - - public boolean supportsAFC() - { - return false; - } - - @XmlAttribute( name = "modulation" ) - public P25Decoder.Modulation getModulation() + public Tait1200MessageFilter() { - return mModulation; + super( "Tait-1200 Message Filter" ); } - public void setModulation( P25Decoder.Modulation modulation ) - { - mModulation = modulation; - } + @Override + public boolean passes( Message message ) + { + return mEnabled && canProcess( message ); + } + + @Override + public boolean canProcess( Message message ) + { + return message instanceof Tait1200GPSMessage; + } + + @Override + public List> getFilterElements() + { + return Collections.EMPTY_LIST; + } } diff --git a/src/decode/fleetsync2/FleetsyncPanel.java b/src/module/decode/tait/Tait1200Panel.java similarity index 73% rename from src/decode/fleetsync2/FleetsyncPanel.java rename to src/module/decode/tait/Tait1200Panel.java index a601baab..88e86c57 100644 --- a/src/decode/fleetsync2/FleetsyncPanel.java +++ b/src/module/decode/tait/Tait1200Panel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,24 +15,25 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package decode.fleetsync2; +package module.decode.tait; import java.awt.EventQueue; import javax.swing.JLabel; +import alias.Alias; +import module.decode.state.ChangedAttribute; +import module.decode.state.DecoderPanel; import net.miginfocom.swing.MigLayout; import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingsManager; -import controller.state.AuxStatePanel; -import controller.state.ChannelState.ChangedAttribute; -public class FleetsyncPanel extends AuxStatePanel +public class Tait1200Panel extends DecoderPanel { private static final long serialVersionUID = 1L; - private static final String PROTOCOL = "FSync II"; + private static final String PROTOCOL = "TAIT-1200"; private JLabel mFromLabel = new JLabel( " " ); private JLabel mFrom = new JLabel(); @@ -46,10 +47,9 @@ public class FleetsyncPanel extends AuxStatePanel private JLabel mMessage = new JLabel(); private JLabel mMessageType = new JLabel(); - public FleetsyncPanel( SettingsManager settingsManager, - FleetsyncChannelState state ) + public Tait1200Panel( SettingsManager settingsManager, Tait1200Decoder decoder ) { - super( settingsManager, state ); + super( settingsManager, decoder ); init(); } @@ -59,28 +59,30 @@ public class FleetsyncPanel extends AuxStatePanel super.dispose(); } - public FleetsyncChannelState getState() + public Tait1200DecoderState getState() { - return (FleetsyncChannelState)mAuxChannelState; + return (Tait1200DecoderState)getDecoder().getDecoderState(); } - private void init() + protected void init() { - setOpaque( false ); //Use the parent panel's background color + /* Calling super init will get and broadcast color settings to properly + * setup the jlabel colors */ + super.init(); setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") ); - addLabel( mFromLabel, false ); - addLabel( mFrom, false ); - addLabel( mFromAlias, true ); + add( mFromLabel ); + add( mFrom ); + add( mFromAlias, "wrap" ); - addLabel( mToLabel, false ); - addLabel( mTo, false ); - addLabel( mToAlias, true ); + add( mToLabel ); + add( mTo ); + add( mToAlias, "wrap" ); - addLabel( mProtocol, false ); - addLabel( mMessageType, false ); - addLabel( mMessage, true ); + add( mProtocol ); + add( mMessageType ); + add( mMessage, "wrap" ); } @Override @@ -94,7 +96,7 @@ public class FleetsyncPanel extends AuxStatePanel switch( changedAttribute ) { case FROM_TALKGROUP: - String from = getState().getFleetIDFrom(); + String from = getState().getFromID(); if( from != null ) { @@ -107,11 +109,15 @@ public class FleetsyncPanel extends AuxStatePanel mFrom.setText( from ); break; case FROM_TALKGROUP_ALIAS: - updateAliasLabel( mFromAlias, - getState().getFleetIDFromAlias() ); + Alias fromAlias = getState().getFromIDAlias(); + + if( fromAlias != null ) + { + mFromAlias.setText( fromAlias.getName() ); + } break; case TO_TALKGROUP: - String to = getState().getFleetIDTo(); + String to = getState().getToID(); if( to != null ) { @@ -124,8 +130,12 @@ public class FleetsyncPanel extends AuxStatePanel mTo.setText( to ); break; case TO_TALKGROUP_ALIAS: - updateAliasLabel( mToAlias, - getState().getFleetIDToAlias() ); + Alias toAlias = getState().getToIDAlias(); + + if( toAlias != null ) + { + mToAlias.setText( toAlias.getName() ); + } break; case MESSAGE: mMessage.setText( getState().getMessage() ); @@ -133,6 +143,8 @@ public class FleetsyncPanel extends AuxStatePanel case MESSAGE_TYPE: mMessageType.setText( getState().getMessageType() ); break; + default: + break; } repaint(); diff --git a/src/module/demodulate/am/AMDemodulatorModule.java b/src/module/demodulate/am/AMDemodulatorModule.java new file mode 100644 index 00000000..5476529c --- /dev/null +++ b/src/module/demodulate/am/AMDemodulatorModule.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.demodulate.am; + +import module.Module; +import sample.Listener; +import sample.complex.ComplexBuffer; +import sample.complex.IComplexBufferListener; +import sample.real.IRealBufferProvider; +import sample.real.RealBuffer; +import dsp.am.AMDemodulator_CB; +import dsp.filter.FilterFactory; +import dsp.filter.Window.WindowType; +import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; +import dsp.gain.AutomaticGainControl_RB; + +public class AMDemodulatorModule extends Module + implements IComplexBufferListener, IRealBufferProvider +{ + private static final int SAMPLE_RATE = 48000; + + private ComplexFIRFilter_CB_CB mIQFilter; + private AMDemodulator_CB mDemodulator; + private RealFIRFilter_RB_RB mLowPassFilter; + private AutomaticGainControl_RB mAGC; + + /** + * FM Demodulator with integrated DC removal and automatic frequency + * correction control. + * + * @param pass - pass frequency for IQ filtering prior to demodulation. This + * frequency should be half of the signal bandwidth since the filter will + * be applied against each of the inphase and quadrature signals and the + * combined pass bandwidth will be twice this value. + * + * @param stop - stop frequency for IQ filtering prior to demodulation. + * + * @param maxFrequencyCorrection - defines the maximum frequency +/- that + * the correction controller can adjust from the center tuned frequency. + * Set this value to 0 for no frequency correction control. + */ + public AMDemodulatorModule() + { + mIQFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass( + SAMPLE_RATE, 6500, 73, WindowType.HAMMING ), 1.0f ); + + mDemodulator = new AMDemodulator_CB( 500.0f ); + mIQFilter.setListener( mDemodulator ); + + mLowPassFilter = new RealFIRFilter_RB_RB( + FilterFactory.getLowPass( 48000, 3000, 31, WindowType.COSINE ), 1.0f ); + mDemodulator.setListener( mLowPassFilter ); + + mAGC = new AutomaticGainControl_RB(); + mLowPassFilter.setListener( mAGC ); + } + + @Override + public Listener getComplexBufferListener() + { + return mIQFilter; + } + + @Override + public void dispose() + { + mIQFilter.dispose(); + mIQFilter = null; + + mDemodulator.dispose(); + mDemodulator = null; + + mLowPassFilter.dispose(); + mLowPassFilter = null; + + mAGC.dispose(); + mAGC = null; + } + + @Override + public void init() + { + } + + @Override + public void setRealBufferListener( Listener listener ) + { + mAGC.setListener( listener ); + } + + @Override + public void removeRealBufferListener() + { + mAGC.removeListener(); + } +} diff --git a/src/module/demodulate/fm/FMDemodulatorModule.java b/src/module/demodulate/fm/FMDemodulatorModule.java new file mode 100644 index 00000000..299b523b --- /dev/null +++ b/src/module/demodulate/fm/FMDemodulatorModule.java @@ -0,0 +1,196 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package module.demodulate.fm; + +import module.Module; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Broadcaster; +import sample.Listener; +import sample.complex.ComplexBuffer; +import sample.complex.IComplexBufferListener; +import sample.real.IRealBufferProvider; +import sample.real.RealBuffer; +import source.tuner.frequency.AutomaticFrequencyControl_RB; +import source.tuner.frequency.FrequencyCorrectionControl; +import source.tuner.frequency.IFrequencyCorrectionController; +import dsp.filter.FilterFactory; +import dsp.filter.Window.WindowType; +import dsp.filter.dc.AveragingDCRemovalFilter_RB; +import dsp.filter.dc.DCRemovalFilter_RB; +import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; +import dsp.fm.FMDemodulator_CB; + +public class FMDemodulatorModule extends Module implements IComplexBufferListener, + IFrequencyCorrectionController, IRealBufferProvider +{ + private final static Logger mLog = LoggerFactory.getLogger( FMDemodulatorModule.class ); + + /* Determines responsiveness of DC filter to frequency changes */ + private static final float DC_REMOVAL_RATIO = 0.000003f; + + private static final int SAMPLE_RATE = 48000; + + private ComplexFIRFilter_CB_CB mIQFilter; + private FMDemodulator_CB mDemodulator; + private Broadcaster mUnfilteredBufferBroadcaster = new Broadcaster(); + private DCRemovalFilter_RB mDCRemovalFilter; + private AutomaticFrequencyControl_RB mAutomaticFrequencyControl; + private boolean mRemoveDC = true; + + private Listener mListener; + + /** + * FM Demodulator with integrated DC removal and automatic frequency + * correction control. + * + * @param pass - pass frequency for IQ filtering prior to demodulation. This + * frequency should be half of the signal bandwidth since the filter will + * be applied against each of the inphase and quadrature signals and the + * combined pass bandwidth will be twice this value. + * + * @param stop - stop frequency for IQ filtering prior to demodulation. + * + * @param maxFrequencyCorrection - defines the maximum frequency +/- that + * the correction controller can adjust from the center tuned frequency. + * Set this value to 0 for no frequency correction control. + */ + public FMDemodulatorModule( int pass, int stop, int maxFrequencyCorrection ) + { + this( pass, stop, maxFrequencyCorrection, true ); + } + + public FMDemodulatorModule( int pass, int stop, int maxFrequencyCorrection, boolean removeDC ) + { + assert( stop > pass ); + + mRemoveDC = removeDC; + + mIQFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass( + SAMPLE_RATE, pass, stop, 60, WindowType.HAMMING, true ), 1.0f ); + + mDemodulator = new FMDemodulator_CB( 1.0f ); + mIQFilter.setListener( mDemodulator ); + + mUnfilteredBufferBroadcaster = new Broadcaster(); + mDemodulator.setListener( mUnfilteredBufferBroadcaster ); + + if( maxFrequencyCorrection > 0 ) + { + /* Frequency correction requires unfiltered samples in order to detect + * the DC component present in a mistuned FM demodulated sample stream */ + mAutomaticFrequencyControl = new AutomaticFrequencyControl_RB( maxFrequencyCorrection ); + mUnfilteredBufferBroadcaster.addListener( mAutomaticFrequencyControl ); + } + + if( mRemoveDC ) + { + mDCRemovalFilter = new AveragingDCRemovalFilter_RB( DC_REMOVAL_RATIO ); + mUnfilteredBufferBroadcaster.addListener( mDCRemovalFilter ); + } + } + + @Override + public boolean hasFrequencyCorrectionControl() + { + return mAutomaticFrequencyControl != null; + } + + @Override + public FrequencyCorrectionControl getFrequencyCorrectionControl() + { + return mAutomaticFrequencyControl; + } + + @Override + public Listener getComplexBufferListener() + { + return mIQFilter; + } + + @Override + public void dispose() + { + mIQFilter.dispose(); + mIQFilter = null; + + mDemodulator.dispose(); + mDemodulator = null; + + mDCRemovalFilter.dispose(); + mDCRemovalFilter = null; + + if( mAutomaticFrequencyControl != null ) + { + mAutomaticFrequencyControl.dispose(); + mAutomaticFrequencyControl = null; + } + + if( mUnfilteredBufferBroadcaster != null ) + { + mUnfilteredBufferBroadcaster.dispose(); + mUnfilteredBufferBroadcaster = null; + } + } + + @Override + public void init() + { + mDemodulator.reset(); + + if( mDCRemovalFilter != null ) + { + mDCRemovalFilter.reset(); + } + + if( mAutomaticFrequencyControl != null ) + { + mAutomaticFrequencyControl.reset(); + } + } + + @Override + public void setRealBufferListener( Listener listener ) + { + mListener = listener; + + if( mRemoveDC ) + { + mDCRemovalFilter.setListener( listener ); + } + else + { + mUnfilteredBufferBroadcaster.addListener( listener ); + } + } + + @Override + public void removeRealBufferListener() + { + if( mRemoveDC ) + { + mDCRemovalFilter.removeListener(); + } + else + { + mUnfilteredBufferBroadcaster.removeListener( mListener ); + } + } +} diff --git a/src/module/record/ComplexBufferRecorderModule.java b/src/module/record/ComplexBufferRecorderModule.java new file mode 100644 index 00000000..2e896095 --- /dev/null +++ b/src/module/record/ComplexBufferRecorderModule.java @@ -0,0 +1,64 @@ +package module.record; + +import java.io.IOException; + +import module.Module; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import record.wave.ComplexWaveRecorder; +import sample.Listener; +import sample.complex.ComplexBuffer; +import sample.complex.IComplexBufferListener; + +public class ComplexBufferRecorderModule extends Module + implements IComplexBufferListener +{ + private final static Logger mLog = + LoggerFactory.getLogger( ComplexBufferRecorderModule.class ); + + private ComplexWaveRecorder mRecorder; + private String mFilename; + + public ComplexBufferRecorderModule( String filename ) + { + mFilename = filename; + mRecorder = new ComplexWaveRecorder( 48000, mFilename ); + } + + @Override + public Listener getComplexBufferListener() + { + return mRecorder; + } + + @Override + public void dispose() + { + try + { + mRecorder.stop(); + } + catch ( IOException e ) + { + mLog.error( "Error stopping the recorder", e ); + } + } + + @Override + public void init() + { + mLog.debug( "Starting the recorder [" + mFilename + "]" ); + + try + { + mRecorder.start(); + } + catch ( IOException e ) + { + mLog.error( "Error starting the recorder", e ); + } + } + +} diff --git a/src/module/record/RealBufferRecorderModule.java b/src/module/record/RealBufferRecorderModule.java new file mode 100644 index 00000000..a31bb981 --- /dev/null +++ b/src/module/record/RealBufferRecorderModule.java @@ -0,0 +1,64 @@ +package module.record; + +import java.io.IOException; + +import module.Module; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import record.wave.FloatWaveRecorder; +import sample.Listener; +import sample.real.IRealBufferListener; +import sample.real.RealBuffer; + +public class RealBufferRecorderModule extends Module + implements IRealBufferListener +{ + private final static Logger mLog = + LoggerFactory.getLogger( RealBufferRecorderModule.class ); + + private FloatWaveRecorder mRecorder; + private String mFilename; + + public RealBufferRecorderModule( String filename ) + { + mFilename = filename; + mRecorder = new FloatWaveRecorder( 48000, mFilename ); + } + + @Override + public Listener getRealBufferListener() + { + return mRecorder; + } + + @Override + public void dispose() + { + try + { + mRecorder.stop(); + } + catch ( IOException e ) + { + mLog.error( "Error stopping the recorder", e ); + } + } + + @Override + public void init() + { + mLog.debug( "Starting the recorder [" + mFilename + "]" ); + + try + { + mRecorder.start(); + } + catch ( IOException e ) + { + mLog.error( "Error starting the recorder", e ); + } + } + +} diff --git a/src/org/jdesktop/swingx/mapviewer/AbstractTileFactory.java b/src/org/jdesktop/swingx/mapviewer/AbstractTileFactory.java index eed918b2..cb67d693 100644 --- a/src/org/jdesktop/swingx/mapviewer/AbstractTileFactory.java +++ b/src/org/jdesktop/swingx/mapviewer/AbstractTileFactory.java @@ -210,7 +210,7 @@ public abstract class AbstractTileFactory extends TileFactory @Override public Thread newThread(Runnable r) { - Thread t = new Thread(r, "tile-pool-" + count++); + Thread t = new Thread(r, "map tile fetcher-pool-" + count++); t.setPriority(Thread.MIN_PRIORITY); t.setDaemon(true); return t; diff --git a/src/playlist/Playlist.java b/src/playlist/Playlist.java index c44214d7..859f3968 100644 --- a/src/playlist/Playlist.java +++ b/src/playlist/Playlist.java @@ -22,7 +22,7 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; import alias.AliasDirectory; -import controller.channel.ChannelMapList; +import controller.channel.map.ChannelMapList; import controller.system.SystemList; @XmlSeeAlso( { AliasDirectory.class, diff --git a/src/playlist/PlaylistNode.java b/src/playlist/PlaylistNode.java index 1f740a57..93fdc2a8 100644 --- a/src/playlist/PlaylistNode.java +++ b/src/playlist/PlaylistNode.java @@ -23,8 +23,8 @@ import alias.AliasDirectory; import alias.AliasDirectoryNode; import controller.BaseNode; import controller.EmptyEditor; -import controller.channel.ChannelMapList; -import controller.channel.ChannelMapListNode; +import controller.channel.map.ChannelMapList; +import controller.channel.map.ChannelMapListNode; import controller.system.SystemList; import controller.system.SystemListNode; diff --git a/src/record/RecorderManager.java b/src/record/RecorderManager.java index 388fd5b7..0ce16c73 100644 --- a/src/record/RecorderManager.java +++ b/src/record/RecorderManager.java @@ -21,12 +21,12 @@ import java.io.File; import java.util.ArrayList; import java.util.List; +import module.ProcessingChainOld; import properties.SystemProperties; import record.config.RecordConfiguration; import record.wave.ComplexWaveRecorder; import record.wave.FloatWaveRecorder; import util.TimeStamp; -import controller.channel.ProcessingChain; public class RecorderManager { @@ -36,7 +36,7 @@ public class RecorderManager { } - public List getRecorder( ProcessingChain channel ) + public List getRecorder( ProcessingChainOld channel ) { /* Note: the file suffix (ie .wav) gets added by the recorder */ diff --git a/src/record/wave/ComplexWaveRecorder.java b/src/record/wave/ComplexWaveRecorder.java index 1c0336ef..efe94c21 100644 --- a/src/record/wave/ComplexWaveRecorder.java +++ b/src/record/wave/ComplexWaveRecorder.java @@ -19,6 +19,7 @@ package record.wave; import java.io.IOException; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.concurrent.LinkedBlockingQueue; import javax.sound.sampled.AudioFormat; @@ -29,15 +30,14 @@ import org.slf4j.LoggerFactory; import record.Recorder; import record.RecorderType; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.ComplexBuffer; import util.waveaudio.WaveWriter; -import buffer.ComplexSampleBufferAssembler; /** * Threaded WAVE audio recorder for recording complex (I&Q) samples to a wave * file */ -public class ComplexWaveRecorder extends Recorder implements Listener +public class ComplexWaveRecorder extends Recorder implements Listener { private final static Logger mLog = LoggerFactory.getLogger( ComplexWaveRecorder.class ); @@ -48,11 +48,12 @@ public class ComplexWaveRecorder extends Recorder implements Listener mListener; //Sample buffer queue ... constrained to 512 sample buffers (arbitrary size) private LinkedBlockingQueue mToWriteBufferQueue = new LinkedBlockingQueue( 512 ); - private ComplexSampleBufferAssembler mCurrentBuffer; public ComplexWaveRecorder( int sampleRate, String filename ) { @@ -64,11 +65,28 @@ public class ComplexWaveRecorder extends Recorder implements Listener listener ) + { + mListener = listener; + } public class BufferProcessor extends Thread { @@ -175,5 +196,4 @@ public class ComplexWaveRecorder extends Recorder implements Listener { private final static Logger mLog = LoggerFactory.getLogger( FloatWaveRecorder.class ); @@ -52,6 +57,8 @@ public class FloatWaveRecorder extends Recorder implements RealSampleListener new LinkedBlockingQueue( 512 ); private FloatSampleBufferAssembler mCurrentBuffer; + private Listener mListener; + public FloatWaveRecorder( int sampleRate, String filename ) { super( RecorderType.AUDIO ); @@ -67,6 +74,11 @@ public class FloatWaveRecorder extends Recorder implements RealSampleListener mFileName = filename; } + public void setListener( Listener listener ) + { + mListener = listener; + } + @Override public String getFileName() { @@ -127,7 +139,42 @@ public class FloatWaveRecorder extends Recorder implements RealSampleListener } } } - + + @Override + public void receive( RealBuffer buffer ) + { + //Dispatch the buffer to be written to disk + if( !mToWriteBufferQueue.offer( convert( buffer ) ) ) + { + //We had an overflow ... log it + mLog.error( "buffer overflow - throwing away a buffer of sample data" ); + } + + if( mListener != null ) + { + mListener.receive( buffer ); + } + } + + /** + * Converts the float samples in a complex buffer to a little endian 16-bit + * buffer + */ + public static ByteBuffer convert( RealBuffer buffer ) + { + float[] samples = buffer.getSamples(); + + ByteBuffer converted = ByteBuffer.allocate( samples.length * 2 ); + converted.order( ByteOrder.LITTLE_ENDIAN ); + + for( float sample: samples ) + { + converted.putShort( (short)( sample * Short.MAX_VALUE ) ); + } + + return converted; + } + @Override public void receive( float sample ) { @@ -173,5 +220,4 @@ public class FloatWaveRecorder extends Recorder implements RealSampleListener } } } - } diff --git a/src/sample/SampleType.java b/src/sample/SampleType.java new file mode 100644 index 00000000..09e9196b --- /dev/null +++ b/src/sample/SampleType.java @@ -0,0 +1,8 @@ +package sample; + +public enum SampleType +{ + COMPLEX, //Baseband + REAL, //Demodulated + COMPLEX_AND_REAL; //Baseband and Demodulated +} diff --git a/src/sample/adapter/ComplexToFloatSampleConverter.java b/src/sample/adapter/ComplexToFloatSampleConverter.java index 8416fc88..2d90c878 100644 --- a/src/sample/adapter/ComplexToFloatSampleConverter.java +++ b/src/sample/adapter/ComplexToFloatSampleConverter.java @@ -20,14 +20,14 @@ package sample.adapter; import java.util.List; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; import source.mixer.MixerChannel; /** * Receives complex samples and send either the left channel or the right * channel to the short sample listener */ -public class ComplexToFloatSampleConverter implements Listener> +public class ComplexToFloatSampleConverter implements Listener> { private MixerChannel mChannel; private Listener mListener; @@ -40,9 +40,9 @@ public class ComplexToFloatSampleConverter implements Listener samples ) + public void receive( List samples ) { - for( ComplexSample sample: samples ) + for( Complex sample: samples ) { mListener.receive( mChannel == MixerChannel.LEFT ? sample.left() : diff --git a/src/sample/adapter/FloatArrayToComplexAdapter.java b/src/sample/adapter/FloatArrayToComplexAdapter.java index 36e520f5..9a6abb41 100644 --- a/src/sample/adapter/FloatArrayToComplexAdapter.java +++ b/src/sample/adapter/FloatArrayToComplexAdapter.java @@ -18,11 +18,11 @@ package sample.adapter; import sample.Listener; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class FloatArrayToComplexAdapter implements Listener { - private Listener mListener; + private Listener mListener; @Override public void receive( Float[] samples ) @@ -35,7 +35,7 @@ public class FloatArrayToComplexAdapter implements Listener samples[ x ] != null && samples[ x + 1 ] != null ) { - mListener.receive( new ComplexSample( samples[ x ], + mListener.receive( new Complex( samples[ x ], samples[ x + 1 ] ) ); } } @@ -44,12 +44,12 @@ public class FloatArrayToComplexAdapter implements Listener } } - public void setListener( Listener listener ) + public void setListener( Listener listener ) { mListener = listener; } - public void removeListener( Listener listener ) + public void removeListener( Listener listener ) { mListener = null; } diff --git a/src/sample/complex/ComplexSample.java b/src/sample/complex/Complex.java similarity index 58% rename from src/sample/complex/ComplexSample.java rename to src/sample/complex/Complex.java index 7b4bd6a9..0a061ac1 100644 --- a/src/sample/complex/ComplexSample.java +++ b/src/sample/complex/Complex.java @@ -23,33 +23,48 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * ComplexSample for handling left/right channel audio, or inphase/quadrature - * samples, etc. + * Complex sample and related utility methods */ -public class ComplexSample implements Serializable +public class Complex implements Serializable { private final static Logger mLog = - LoggerFactory.getLogger( ComplexSample.class ); + LoggerFactory.getLogger( Complex.class ); private static final long serialVersionUID = 1L; private float mLeft; private float mRight; - public ComplexSample( float left, float right ) + public Complex( float left, float right ) { mLeft = left; mRight = right; } - public ComplexSample() + public Complex() { this( 0.0f, 0.0f ); } - - public ComplexSample copy() + + public void setInphase( float inphase ) { - return new ComplexSample( mLeft, mRight ); + mLeft = inphase; + } + + public void setQuadrature( float quadrature ) + { + mRight = quadrature; + } + + public void setValues( float inphase, float quadrature ) + { + mLeft = inphase; + mRight = quadrature; + } + + public Complex copy() + { + return new Complex( mLeft, mRight ); } public String toString() @@ -67,9 +82,9 @@ public class ComplexSample implements Serializable /** * Returns a new sample representing the conjugate of this one */ - public ComplexSample conjugate() + public Complex conjugate() { - return new ComplexSample( mLeft, -mRight ); + return new Complex( mLeft, -mRight ); } /** @@ -81,22 +96,37 @@ public class ComplexSample implements Serializable mRight *= scalor; } - public static ComplexSample multiply( ComplexSample sample, float scalor ) + public static Complex multiply( Complex sample, float scalor ) { - return new ComplexSample( sample.left() * scalor, + return new Complex( sample.left() * scalor, sample.right() * scalor ); } + /** + * Calculates the inphase component of multiplying two complex numbers, A and B. + */ + public static float multiplyInphase( float inphaseA, float quadratureA, float inphaseB, float quadratureB ) + { + return ( inphaseA * inphaseB ) - ( quadratureA * quadratureB ); + } + + /** + * Calculates the quadrature component of multiplying two complex numbers, A and B. + */ + public static float multiplyQuadrature( float inphaseA, float quadratureA, float inphaseB, float quadratureB ) + { + return ( quadratureA * inphaseB ) + ( inphaseA * quadratureB ); + } + /** * Multiplies this sample by the multiplier sample */ - public void multiply( ComplexSample multiplier ) + public void multiply( Complex multiplier ) { - float inphase = ( inphase() * multiplier.inphase() ) - - ( quadrature() * multiplier.quadrature() ); - - float quadrature = ( quadrature() * multiplier.inphase() ) + - ( inphase() * multiplier.quadrature() ); + float inphase = multiplyInphase( inphase(), quadrature(), + multiplier.inphase(), multiplier.quadrature() ); + float quadrature = multiplyQuadrature( inphase(), quadrature(), + multiplier.inphase(), multiplier.quadrature() ); mLeft = inphase; mRight = quadrature; @@ -105,31 +135,47 @@ public class ComplexSample implements Serializable /** * Multiplies both samples returning a new sample with the results */ - public static ComplexSample multiply( ComplexSample sample1, - ComplexSample sample2 ) + public static Complex multiply( Complex sample1, Complex sample2 ) { - float inphase = ( sample1.inphase() * sample2.inphase() ) - - ( sample1.quadrature() * sample2.quadrature() ); + float inphase = multiplyInphase( sample1.inphase(), sample1.quadrature(), + sample2.inphase(), sample2.quadrature() ); + float quadrature = multiplyQuadrature( sample1.inphase(), sample1.quadrature(), + sample2.inphase(), sample2.quadrature() ); - float quadrature = ( sample1.quadrature() * sample2.inphase() ) + - ( sample1.inphase() * sample2.quadrature() ); - - return new ComplexSample( inphase, quadrature ); + return new Complex( inphase, quadrature ); } - public static ComplexSample multiply( ComplexSample sample, + public static Complex multiply( Complex sample, Float inphase, Float quadrature ) { - float i = ( sample.inphase() * inphase ) - ( sample.quadrature() * quadrature ); - float q = ( sample.quadrature() * inphase ) + ( sample.inphase() * quadrature ); + float i = multiplyInphase( sample.inphase(), sample.quadrature(), + inphase, quadrature ); + float q = multiplyQuadrature( sample.inphase(), sample.quadrature(), + inphase, quadrature ); - return new ComplexSample( i, q ); + return new Complex( i, q ); + } + + public static Complex multiply( Float inphase, Float quadrature, Complex sample ) + { + float i = multiplyInphase( inphase, quadrature, sample.inphase(), sample.quadrature() ); + float q = multiplyQuadrature( inphase, quadrature, sample.inphase(), sample.quadrature() ); + + return new Complex( i, q ); } + public static Complex multiply( float inphaseA, float quadratureA, float inphaseB, float quadratureB ) + { + float i = multiplyInphase( inphaseA, quadratureA, inphaseB, quadratureB ); + float q = multiplyQuadrature( inphaseA, quadratureA, inphaseB, quadratureB ); + + return new Complex( i, q ); + } + /** * Adds the adder sample value to this sample */ - public void add( ComplexSample adder ) + public void add( Complex adder ) { mLeft += adder.left(); mRight += adder.right(); @@ -138,18 +184,18 @@ public class ComplexSample implements Serializable /** * Adds the two complex samples returning a new complex sample with the result */ - public static ComplexSample add( ComplexSample first, ComplexSample second ) + public static Complex add( Complex first, Complex second ) { - return new ComplexSample( first.left() + second.left(), + return new Complex( first.left() + second.left(), first.right() + second.right() ); } /** * Adds the two complex samples returning a new complex sample with the result */ - public static ComplexSample subtract( ComplexSample first, ComplexSample second ) + public static Complex subtract( Complex first, Complex second ) { - return new ComplexSample( first.left() - second.left(), + return new Complex( first.left() - second.left(), first.right() - second.right() ); } @@ -160,6 +206,11 @@ public class ComplexSample implements Serializable { return (float)Math.sqrt( magnitudeSquared() ); } + + public static float magnitude( float inphase, float quadrature ) + { + return (float)Math.sqrt( ( inphase * inphase ) + ( quadrature * quadrature ) ); + } /** * Magnitude squared of this sample @@ -293,9 +344,9 @@ public class ComplexSample implements Serializable * @param angle in radians * @return */ - public static ComplexSample fromAngle( float angle ) + public static Complex fromAngle( float angle ) { - return new ComplexSample( (float)Math.cos( angle ), + return new Complex( (float)Math.cos( angle ), (float)Math.sin( angle ) ); } @@ -346,16 +397,21 @@ public class ComplexSample implements Serializable */ public float envelope() { - float realAbsolute = realAbsolute(); - float imagAbsolute = imaginaryAbsolute(); + return envelope( mLeft, mRight ); + } + + public static float envelope( float inphase, float quadrature ) + { + float inphaseAbsolute = Math.abs( inphase ); + float quadratureAbsolute = Math.abs( quadrature ); - if( realAbsolute > imagAbsolute ) + if( inphaseAbsolute > quadratureAbsolute ) { - return realAbsolute + ( 0.4f * imagAbsolute ); + return inphaseAbsolute + ( 0.4f * quadratureAbsolute ); } else { - return imagAbsolute + ( 0.4f * realAbsolute ); + return quadratureAbsolute + ( 0.4f * inphaseAbsolute ); } } @@ -363,11 +419,11 @@ public class ComplexSample implements Serializable { double angle = Math.PI; //180 degrees - ComplexSample s1 = new ComplexSample( (float)Math.sin( angle ), (float)Math.cos( angle ) ); + Complex s1 = new Complex( (float)Math.sin( angle ), (float)Math.cos( angle ) ); - ComplexSample tap = new ComplexSample( 1.0f, -0.1f ); + Complex tap = new Complex( 1.0f, -0.1f ); - ComplexSample convolved = ComplexSample.multiply( s1, tap ); + Complex convolved = Complex.multiply( s1, tap ); mLog.debug( "s: " + s1.toString() + " t: " + tap.toString() + diff --git a/src/sample/complex/ComplexBuffer.java b/src/sample/complex/ComplexBuffer.java index 6aa3b37d..9a3a4b66 100644 --- a/src/sample/complex/ComplexBuffer.java +++ b/src/sample/complex/ComplexBuffer.java @@ -1,6 +1,8 @@ package sample.complex; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; public class ComplexBuffer { @@ -32,6 +34,21 @@ public class ComplexBuffer return mSamples; } + /** + * Converts this sample buffer into a list of complex samples + */ + public List getComplexSamples() + { + List samples = new ArrayList(); + + for( int x = 0; x < mSamples.length; x += 2 ) + { + samples.add( new Complex( mSamples[ x ], mSamples[ x + 1 ] ) ); + } + + return samples; + } + /** * Cleanup method to nullify all data and references */ diff --git a/src/sample/complex/ComplexBufferToStreamConverter.java b/src/sample/complex/ComplexBufferToStreamConverter.java new file mode 100644 index 00000000..a4809c2b --- /dev/null +++ b/src/sample/complex/ComplexBufferToStreamConverter.java @@ -0,0 +1,30 @@ +package sample.complex; + +import sample.Listener; + +/** + * Real sample buffer to stream converter + */ +public class ComplexBufferToStreamConverter implements Listener +{ + private ComplexSampleListener mListener; + + @Override + public void receive( ComplexBuffer buffer ) + { + if( mListener != null ) + { + float[] samples = buffer.getSamples(); + + for( int x = 0; x < samples.length; x += 2 ) + { + mListener.receive( samples[ x ], samples[ x + 1 ] ); + } + } + } + + public void setListener( ComplexSampleListener listener ) + { + mListener = listener; + } +} diff --git a/src/sample/complex/ComplexSampleAssembler.java b/src/sample/complex/ComplexSampleAssembler.java index ab30b868..f7872160 100644 --- a/src/sample/complex/ComplexSampleAssembler.java +++ b/src/sample/complex/ComplexSampleAssembler.java @@ -23,17 +23,17 @@ import java.util.List; import sample.Listener; import sample.Provider; -public class ComplexSampleAssembler implements Listener, - Provider> +public class ComplexSampleAssembler implements Listener, + Provider> { private ArrayList mBuffer; private int mBufferSize; - private Listener> mListener; + private Listener> mListener; public ComplexSampleAssembler( int bufferSize ) { mBufferSize = bufferSize; - mBuffer = new ArrayList(); + mBuffer = new ArrayList(); } public void dispose() @@ -43,7 +43,7 @@ public class ComplexSampleAssembler implements Listener, } @Override - public void receive( ComplexSample sample ) + public void receive( Complex sample ) { mBuffer.add( sample ); @@ -59,13 +59,13 @@ public class ComplexSampleAssembler implements Listener, } @Override - public void setListener( Listener> listener ) + public void setListener( Listener> listener ) { mListener = listener; } @Override - public void removeListener( Listener> listener ) + public void removeListener( Listener> listener ) { mListener = null; } diff --git a/src/sample/complex/ComplexSampleListListener.java b/src/sample/complex/ComplexSampleListListener.java index ed2cc1ea..564ff798 100644 --- a/src/sample/complex/ComplexSampleListListener.java +++ b/src/sample/complex/ComplexSampleListListener.java @@ -21,5 +21,5 @@ import java.util.List; public interface ComplexSampleListListener { - public void receive( List samples ); + public void receive( List samples ); } diff --git a/src/sample/complex/ComplexSampleUtils.java b/src/sample/complex/ComplexSampleUtils.java index ba95a39b..a2bd93eb 100644 --- a/src/sample/complex/ComplexSampleUtils.java +++ b/src/sample/complex/ComplexSampleUtils.java @@ -21,26 +21,26 @@ package sample.complex; public class ComplexSampleUtils { - public static ComplexSample multiply( ComplexSample a, - ComplexSample b ) + public static Complex multiply( Complex a, + Complex b ) { - return new ComplexSample( + return new Complex( ( a.inphase() * b.inphase() ) - ( a.quadrature() * b.quadrature() ), ( ( a.quadrature() * b.inphase() ) + ( a.inphase() * b.quadrature() ) ) ); } - public static ComplexSample minus( ComplexSample a, ComplexSample b ) + public static Complex minus( Complex a, Complex b ) { - return new ComplexSample( a.inphase() - b.inphase(), + return new Complex( a.inphase() - b.inphase(), a.quadrature() - b.quadrature() ); } - public static double magnitude( ComplexSample sample ) + public static double magnitude( Complex sample ) { return Math.sqrt( magnitudeSquared( sample ) ); } - public static int magnitudeSquared( ComplexSample sample ) + public static int magnitudeSquared( Complex sample ) { return (int)( ( sample.inphase() * sample.inphase() ) + ( sample.quadrature() * sample.quadrature() ) ); diff --git a/src/sample/complex/ComplexToComplexBufferAssembler.java b/src/sample/complex/ComplexToComplexBufferAssembler.java new file mode 100644 index 00000000..28c032f6 --- /dev/null +++ b/src/sample/complex/ComplexToComplexBufferAssembler.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package sample.complex; + +import sample.Listener; + +/** + * Assembles complex float samples into a ComplexBuffer containing an array of floats + */ +public class ComplexToComplexBufferAssembler implements ComplexSampleListener +{ + private int mBufferSize; + private float[] mBuffer; + private int mBufferPointer; + + private Listener mListener; + + public ComplexToComplexBufferAssembler( int bufferSize ) + { + assert( bufferSize % 2 == 0 ); + + mBufferSize = bufferSize; + mBuffer = new float[ mBufferSize ]; + } + + public void dispose() + { + mListener = null; + mBuffer = null; + } + + public void reset() + { + mBuffer = new float[ mBufferSize ]; + mBufferPointer = 0; + } + + @Override + public void receive( float i, float q ) + { + mBuffer[ mBufferPointer ] = i; + + mBufferPointer++; + + mBuffer[ mBufferPointer ] = q; + + mBufferPointer++; + + if( mBufferPointer >= mBufferSize ) + { + if( mListener != null ) + { + mListener.receive( new ComplexBuffer( mBuffer ) ); + } + + mBuffer = new float[ mBufferSize ]; + mBufferPointer = 0; + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } +} diff --git a/src/sample/complex/IComplexBufferListener.java b/src/sample/complex/IComplexBufferListener.java new file mode 100644 index 00000000..5ec64dbb --- /dev/null +++ b/src/sample/complex/IComplexBufferListener.java @@ -0,0 +1,8 @@ +package sample.complex; + +import sample.Listener; + +public interface IComplexBufferListener +{ + public Listener getComplexBufferListener(); +} diff --git a/src/sample/complex/RateCounter_CB.java b/src/sample/complex/RateCounter_CB.java new file mode 100644 index 00000000..abdc35e5 --- /dev/null +++ b/src/sample/complex/RateCounter_CB.java @@ -0,0 +1,56 @@ +package sample.complex; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; + +public class RateCounter_CB implements Listener +{ + private final static Logger mLog = + LoggerFactory.getLogger( RateCounter_CB.class ); + + private Listener mListener; + private long mCount; + private long mStartTime = -1; + + private String mName; + + public RateCounter_CB() + { + mName = "UNNAMED"; + } + + public RateCounter_CB( String name ) + { + mName = name; + } + + @Override + public void receive( ComplexBuffer buffer ) + { + if( mStartTime == -1 ) + { + mStartTime = System.currentTimeMillis(); + } + + mCount += ( buffer.getSamples().length / 2 ); + + long elapsed = System.currentTimeMillis() - mStartTime; + + if( elapsed != 0 ) + { + mLog.debug( "[" + mName + "] Sample Rate: " + ( mCount / elapsed ) + " kHz" ); + } + + if( mListener != null ) + { + mListener.receive( buffer ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } +} diff --git a/src/sample/complex/ThreadKiller.java b/src/sample/complex/ThreadKiller.java index 3d695332..a6928865 100644 --- a/src/sample/complex/ThreadKiller.java +++ b/src/sample/complex/ThreadKiller.java @@ -22,7 +22,7 @@ package sample.complex; * ComplexSample objects, send this class to the thread as a sentinal value * to kill the processing thread. */ -public class ThreadKiller extends ComplexSample +public class ThreadKiller extends Complex { private static final long serialVersionUID = 1L; diff --git a/src/sample/real/IRealBufferListener.java b/src/sample/real/IRealBufferListener.java new file mode 100644 index 00000000..6ef33052 --- /dev/null +++ b/src/sample/real/IRealBufferListener.java @@ -0,0 +1,8 @@ +package sample.real; + +import sample.Listener; + +public interface IRealBufferListener +{ + public Listener getRealBufferListener(); +} diff --git a/src/sample/real/IRealBufferProvider.java b/src/sample/real/IRealBufferProvider.java new file mode 100644 index 00000000..0b11debd --- /dev/null +++ b/src/sample/real/IRealBufferProvider.java @@ -0,0 +1,9 @@ +package sample.real; + +import sample.Listener; + +public interface IRealBufferProvider +{ + public void setRealBufferListener( Listener listener ); + public void removeRealBufferListener(); +} diff --git a/src/sample/real/RateCounter_RB.java b/src/sample/real/RateCounter_RB.java new file mode 100644 index 00000000..de3ca3d7 --- /dev/null +++ b/src/sample/real/RateCounter_RB.java @@ -0,0 +1,56 @@ +package sample.real; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; + +public class RateCounter_RB implements Listener +{ + private final static Logger mLog = + LoggerFactory.getLogger( RateCounter_RB.class ); + + private Listener mListener; + private long mCount; + private long mStartTime = -1; + + private String mName; + + public RateCounter_RB() + { + mName = "UNNAMED"; + } + + public RateCounter_RB( String name ) + { + mName = name; + } + + @Override + public void receive( RealBuffer buffer ) + { + if( mStartTime == -1 ) + { + mStartTime = System.currentTimeMillis(); + } + + mCount += buffer.getSamples().length; + + long elapsed = System.currentTimeMillis() - mStartTime; + + if( elapsed != 0 ) + { + mLog.debug( "[" + mName + "] Sample Rate: " + ( mCount / elapsed ) + " kHz" ); + } + + if( mListener != null ) + { + mListener.receive( buffer ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } +} diff --git a/src/sample/real/RealBufferToStreamConverter.java b/src/sample/real/RealBufferToStreamConverter.java new file mode 100644 index 00000000..0758fb20 --- /dev/null +++ b/src/sample/real/RealBufferToStreamConverter.java @@ -0,0 +1,28 @@ +package sample.real; + +import sample.Listener; + +/** + * Real sample buffer to stream converter + */ +public class RealBufferToStreamConverter implements Listener +{ + private RealSampleListener mListener; + + @Override + public void receive( RealBuffer buffer ) + { + if( mListener != null ) + { + for( float sample: buffer.getSamples() ) + { + mListener.receive( sample ); + } + } + } + + public void setListener( RealSampleListener listener ) + { + mListener = listener; + } +} diff --git a/src/sample/real/RealToRealBufferAssembler.java b/src/sample/real/RealToRealBufferAssembler.java new file mode 100644 index 00000000..de12e4de --- /dev/null +++ b/src/sample/real/RealToRealBufferAssembler.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ +package sample.real; + +import sample.Listener; + +/** + * Assembles single float samples into a RealBuffer containing an array of floats + */ +public class RealToRealBufferAssembler implements RealSampleListener +{ + private int mBufferSize; + private float[] mBuffer; + private int mBufferPointer; + + private Listener mListener; + + public RealToRealBufferAssembler( int bufferSize ) + { + mBufferSize = bufferSize; + mBuffer = new float[ mBufferSize ]; + } + + public void dispose() + { + mListener = null; + mBuffer = null; + } + + public void reset() + { + mBuffer = new float[ mBufferSize ]; + mBufferPointer = 0; + } + + @Override + public void receive( float sample ) + { + mBuffer[ mBufferPointer ] = sample; + + mBufferPointer++; + + if( mBufferPointer >= mBufferSize ) + { + if( mListener != null ) + { + mListener.receive( new RealBuffer( mBuffer ) ); + } + + mBuffer = new float[ mBufferSize ]; + mBufferPointer = 0; + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } +} diff --git a/src/source/ComplexSource.java b/src/source/ComplexSource.java index c1b697b7..eeb4084e 100644 --- a/src/source/ComplexSource.java +++ b/src/source/ComplexSource.java @@ -1,12 +1,10 @@ package source; -import java.util.List; - import sample.Provider; -import sample.complex.ComplexSample; +import sample.SampleType; +import sample.complex.ComplexBuffer; -public abstract class ComplexSource extends Source - implements Provider> +public abstract class ComplexSource extends Source implements Provider { public ComplexSource( String name ) { diff --git a/src/source/FloatSource.java b/src/source/FloatSource.java index a4d297a9..c8161a9c 100644 --- a/src/source/FloatSource.java +++ b/src/source/FloatSource.java @@ -1,6 +1,7 @@ package source; import sample.Provider; +import sample.SampleType; public abstract class FloatSource extends Source implements Provider { diff --git a/src/source/RealSource.java b/src/source/RealSource.java index 352ff26f..6b09ccd5 100644 --- a/src/source/RealSource.java +++ b/src/source/RealSource.java @@ -1,6 +1,7 @@ package source; import sample.Provider; +import sample.SampleType; import sample.real.RealBuffer; diff --git a/src/source/SampleProvider.java b/src/source/SampleProvider.java index d9af4796..3cc84749 100644 --- a/src/source/SampleProvider.java +++ b/src/source/SampleProvider.java @@ -17,7 +17,7 @@ ******************************************************************************/ package source; -import source.Source.SampleType; +import sample.SampleType; public interface SampleProvider { diff --git a/src/source/Source.java b/src/source/Source.java index 506955d0..064cc6ce 100644 --- a/src/source/Source.java +++ b/src/source/Source.java @@ -17,6 +17,8 @@ ******************************************************************************/ package source; +import sample.SampleType; + /** * Abstract class to define the minimum functionality of a sample data provider. */ @@ -25,8 +27,6 @@ public abstract class Source protected String mName; protected SampleType mSampleType; - public enum SampleType { COMPLEX, REAL } - public Source( String name, SampleType sampleType ) { mName = name; diff --git a/src/source/SourceComponentEditor.java b/src/source/SourceComponentEditor.java index 19d60a9c..0cf4f186 100644 --- a/src/source/SourceComponentEditor.java +++ b/src/source/SourceComponentEditor.java @@ -23,13 +23,13 @@ import java.awt.event.ActionListener; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; +import javax.swing.JLabel; +import net.miginfocom.swing.MigLayout; import source.config.SourceConfigFactory; import source.config.SourceConfiguration; -import controller.Editor; import controller.channel.AbstractChannelEditor; import controller.channel.ChannelNode; -import controller.channel.ChannelValidationException; public class SourceComponentEditor extends AbstractChannelEditor { @@ -42,7 +42,9 @@ public class SourceComponentEditor extends AbstractChannelEditor { super( channelNode ); - mComboSources = new JComboBox(); + setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][grow]" ) ); + + mComboSources = new JComboBox(); mComboSources.setModel( new DefaultComboBoxModel( SourceType.values() ) ); mComboSources.addActionListener( new ActionListener() { @@ -86,6 +88,7 @@ public class SourceComponentEditor extends AbstractChannelEditor } }); + add( new JLabel( "Source:" ) ); add( mComboSources, "wrap" ); reset(); diff --git a/src/source/SourceManager.java b/src/source/SourceManager.java index 7febb994..8c783ab9 100644 --- a/src/source/SourceManager.java +++ b/src/source/SourceManager.java @@ -17,9 +17,9 @@ ******************************************************************************/ package source; +import source.config.SourceConfiguration; import source.mixer.MixerManager; import controller.ResourceManager; -import controller.channel.ProcessingChain; public class SourceManager { /** @@ -40,28 +40,25 @@ public class SourceManager { mResourceManager = resourceManager; } - public Source getSource( ProcessingChain channel ) throws SourceException + public Source getSource( SourceConfiguration config, int bandwidth ) + throws SourceException { Source retVal = null; - if( channel != null ) + switch( config.getSourceType() ) { - switch( channel.getChannel() - .getSourceConfiguration().getSourceType() ) - { - case MIXER: - retVal = MixerManager.getInstance().getSource( channel ); - break; - case TUNER: - retVal = mResourceManager.getTunerManager().getSource( channel ); - break; - case RECORDING: - retVal = mResourceManager.getRecordingSourceManager() - .getSource( channel ); - case NONE: - default: - break; - } + case MIXER: + retVal = MixerManager.getInstance().getSource( config ); + break; + case TUNER: + retVal = mResourceManager.getTunerManager().getSource( config, bandwidth ); + break; + case RECORDING: + retVal = mResourceManager.getRecordingSourceManager() + .getSource( config, bandwidth ); + case NONE: + default: + break; } return retVal; diff --git a/src/source/config/SourceConfigRecording.java b/src/source/config/SourceConfigRecording.java index 43204a2d..801fb211 100644 --- a/src/source/config/SourceConfigRecording.java +++ b/src/source/config/SourceConfigRecording.java @@ -20,6 +20,8 @@ package source.config; import javax.xml.bind.annotation.XmlAttribute; import source.SourceType; +import source.tuner.TunerChannel; +import source.tuner.TunerChannel.Type; public class SourceConfigRecording extends SourceConfiguration { @@ -58,4 +60,11 @@ public class SourceConfigRecording extends SourceConfiguration { return getRecordingAlias(); } + + public TunerChannel getTunerChannel() + { + /* Create a tuner channel with a default bandwidth of 15 kHz */ + return new TunerChannel( Type.LOCKED, mFrequency, 15000 ); + } + } diff --git a/src/source/config/SourceConfigTuner.java b/src/source/config/SourceConfigTuner.java index ee4c5f54..61637d12 100644 --- a/src/source/config/SourceConfigTuner.java +++ b/src/source/config/SourceConfigTuner.java @@ -22,18 +22,29 @@ import java.text.DecimalFormat; import javax.xml.bind.annotation.XmlAttribute; import source.SourceType; +import source.tuner.TunerChannel; +import source.tuner.TunerChannel.Type; public class SourceConfigTuner extends SourceConfiguration { private static DecimalFormat sFORMAT = new DecimalFormat( "0.00000" ); private long mFrequency = 0; + private int mBandwidth = 15000; public SourceConfigTuner() { super( SourceType.TUNER ); } + public SourceConfigTuner( TunerChannel tunerChannel ) + { + this(); + + mFrequency = tunerChannel.getFrequency(); + mBandwidth = tunerChannel.getBandwidth(); + } + @XmlAttribute( name = "frequency" ) public long getFrequency() { @@ -50,4 +61,9 @@ public class SourceConfigTuner extends SourceConfiguration { return sFORMAT.format( (double)mFrequency / 1000000.0d ) + " MHz"; } + + public TunerChannel getTunerChannel() + { + return new TunerChannel( Type.LOCKED, mFrequency, mBandwidth ); + } } diff --git a/src/source/mixer/ComplexMixer.java b/src/source/mixer/ComplexMixer.java index 5a92d8ae..cb7a68ea 100644 --- a/src/source/mixer/ComplexMixer.java +++ b/src/source/mixer/ComplexMixer.java @@ -32,7 +32,7 @@ import org.slf4j.LoggerFactory; import sample.Listener; import sample.adapter.SampleAdapter; import sample.complex.ComplexBuffer; -import sample.complex.ComplexSample; +import sample.complex.Complex; import source.SourceException; public class ComplexMixer @@ -102,15 +102,15 @@ public class ComplexMixer mBufferReader.stop(); } - private List convert( ComplexBuffer sampleBuffer ) + private List convert( ComplexBuffer sampleBuffer ) { float[] samples = sampleBuffer.getSamples(); - ArrayList converted = new ArrayList(); + ArrayList converted = new ArrayList(); for( int x = 0; x < samples.length; x += 2 ) { - converted.add( new ComplexSample( samples[ x ], samples[ x + 1 ] ) ); + converted.add( new Complex( samples[ x ], samples[ x + 1 ] ) ); } sampleBuffer.dispose(); diff --git a/src/source/mixer/MixerManager.java b/src/source/mixer/MixerManager.java index d233776d..b3156568 100644 --- a/src/source/mixer/MixerManager.java +++ b/src/source/mixer/MixerManager.java @@ -31,6 +31,8 @@ import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.Mixer; import javax.sound.sampled.TargetDataLine; +import module.ProcessingChainOld; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,7 +42,6 @@ import source.config.SourceConfigMixer; import source.config.SourceConfiguration; import source.tuner.MixerTunerDataLine; import source.tuner.MixerTunerType; -import controller.channel.ProcessingChain; public class MixerManager { @@ -82,13 +83,10 @@ public class MixerManager return sInstance; } - public RealMixerSource getSource( ProcessingChain processingChain ) + public RealMixerSource getSource( SourceConfiguration config ) { RealMixerSource retVal = null; - SourceConfiguration config = - processingChain.getChannel().getSourceConfiguration(); - if( config instanceof SourceConfigMixer ) { SourceConfigMixer mixerConfig = (SourceConfigMixer)config; diff --git a/src/source/mixer/RealMixerSource.java b/src/source/mixer/RealMixerSource.java index 2e9ad4bd..2de626eb 100644 --- a/src/source/mixer/RealMixerSource.java +++ b/src/source/mixer/RealMixerSource.java @@ -29,7 +29,7 @@ import javax.sound.sampled.TargetDataLine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import dsp.gain.RealAutomaticGainControl; +import dsp.gain.AutomaticGainControl; import sample.Listener; import sample.adapter.SampleAdapter; import sample.real.RealBuffer; @@ -50,7 +50,7 @@ public class RealMixerSource extends RealSource private String mDisplayName; private int mBytesPerFrame = 0; private SampleAdapter mSampleAdapter; - private RealAutomaticGainControl mAGC = new RealAutomaticGainControl(); + private AutomaticGainControl mAGC = new AutomaticGainControl(); CopyOnWriteArrayList> mSampleListeners = new CopyOnWriteArrayList>(); diff --git a/src/source/recording/RecordingSourceManager.java b/src/source/recording/RecordingSourceManager.java index c0de2b69..63c03d8a 100644 --- a/src/source/recording/RecordingSourceManager.java +++ b/src/source/recording/RecordingSourceManager.java @@ -17,8 +17,6 @@ ******************************************************************************/ package source.recording; -import gui.SDRTrunk; - import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -30,10 +28,10 @@ import settings.SettingsManager; import source.Source; import source.SourceException; import source.config.SourceConfigRecording; +import source.config.SourceConfiguration; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import controller.ResourceManager; -import controller.channel.ProcessingChain; public class RecordingSourceManager { @@ -55,21 +53,21 @@ public class RecordingSourceManager * Iterates current recordings to get a tuner channel source for the frequency * specified in the channel config's source config object */ - public Source getSource( ProcessingChain processingChain ) + public Source getSource( SourceConfiguration config, int bandwidth ) throws SourceException { TunerChannelSource retVal = null; - if( processingChain.getChannel().getSourceConfiguration() - instanceof SourceConfigRecording ) + if( config instanceof SourceConfigRecording ) { - TunerChannel tunerChannel = - processingChain.getChannel().getTunerChannel(); + SourceConfigRecording configRecording = (SourceConfigRecording)config; + + TunerChannel tunerChannel = configRecording.getTunerChannel(); + + tunerChannel.setBandwidth( bandwidth ); - SourceConfigRecording config = (SourceConfigRecording)processingChain - .getChannel().getSourceConfiguration(); - - Recording recording = getRecordingFromAlias( config.getRecordingAlias() ); + Recording recording = getRecordingFromAlias( + configRecording.getRecordingAlias() ); if( recording != null ) { @@ -80,7 +78,7 @@ public class RecordingSourceManager else { throw new SourceException( "Recording with alias name [" + - config.getRecordingAlias() + "] is not currently available" ); + configRecording.getRecordingAlias() + "] is not currently available" ); } } diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index f2c1a95f..775c6c43 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -23,25 +23,25 @@ import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; +import sample.complex.Complex; import sample.complex.ComplexBuffer; -import sample.complex.ComplexSample; -import sample.complex.ComplexSampleAssembler; import source.ComplexSource; import source.SourceException; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; import source.tuner.frequency.FrequencyChangeEvent.Attribute; +import source.tuner.frequency.FrequencyChangeListener; import util.Oscillator; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; -import dsp.filter.ComplexPrimeCICDecimate; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; +import dsp.filter.cic.ComplexPrimeCICDecimate; public class TunerChannelSource extends ComplexSource implements FrequencyChangeListener, @@ -57,17 +57,18 @@ public class TunerChannelSource extends ComplexSource new LinkedTransferQueue(); private Tuner mTuner; private TunerChannel mTunerChannel; - private Oscillator mSineWaveGenerator; + private Oscillator mMixer; private ComplexPrimeCICDecimate mDecimationFilter; - - private ComplexSampleAssembler mBufferAssembler = - new ComplexSampleAssembler( 3000 ); - private long mTunerFrequency = 0; - private int mTunerFrequencyError = 0; - private int mTunerSampleRate; + private Listener mListener; private FrequencyChangeListener mFrequencyChangeListener; private ThreadPoolManager mThreadPoolManager; private ScheduledFuture mTaskHandle; + + private long mTunerFrequency = 0; + private int mTunerFrequencyError = 0; + private int mTunerSampleRate; + + private DecimationProcessor mDecimationProcessor = new DecimationProcessor(); public TunerChannelSource( ThreadPoolManager threadPoolManager, Tuner tuner, @@ -77,7 +78,6 @@ public class TunerChannelSource extends ComplexSource super( "Tuner Channel Source" ); mThreadPoolManager = threadPoolManager; - mTuner = tuner; mTuner.addListener( (FrequencyChangeListener)this ); @@ -89,7 +89,7 @@ public class TunerChannelSource extends ComplexSource /* Setup the frequency translator to the current source frequency */ long frequencyOffset = mTunerFrequency - mTunerChannel.getFrequency(); - mSineWaveGenerator = new Oscillator( frequencyOffset, mTuner.getSampleRate() ); + mMixer = new Oscillator( frequencyOffset, mTuner.getSampleRate() ); /* Fire a sample rate change event to get the decimation chain setup */ frequencyChanged( new FrequencyChangeEvent( @@ -97,7 +97,7 @@ public class TunerChannelSource extends ComplexSource /* Schedule the decimation task to run 50 times a second */ mTaskHandle = mThreadPoolManager.scheduleFixedRate( ThreadType.DECIMATION, - new DecimationProcessor(), 20, TimeUnit.MILLISECONDS ); + mDecimationProcessor, 20, TimeUnit.MILLISECONDS ); /* Finally, register to receive samples from the tuner */ mTuner.addListener( (Listener)this ); @@ -106,31 +106,44 @@ public class TunerChannelSource extends ComplexSource @Override public void dispose() { - mFrequencyChangeListener = null; - - //Tell the tuner to release our resources + /* Tell the tuner to release/unregister our resources */ mTuner.removeListener( (FrequencyChangeListener)this ); mTuner.releaseChannel( this ); + mTuner = null; + mTunerChannel = null; + /* Flag the decimation processor to shutdown */ + if( mDecimationProcessor != null ) + { + mDecimationProcessor.dispose(); + } + + } + + /** + * Cleanup method to allow the decimation processor to perform final cleanup + * once it gracefully shuts itself down + */ + private void cleanup() + { if( mTaskHandle != null ) { mThreadPoolManager.cancel( mTaskHandle ); + mThreadPoolManager = null; + mTaskHandle = null; } - + + mBuffer.clear(); mBuffer = null; -// mDecimationFilter.dispose(); + mFrequencyChangeListener = null; + mListener = null; + + mMixer = null; + + mDecimationFilter.dispose(); mDecimationFilter = null; - - mBufferAssembler.dispose(); - - mTuner = null; - - mTunerChannel = null; - - mTunerChannel = null; - mTuner = null; } public Tuner getTuner() @@ -144,22 +157,31 @@ public class TunerChannelSource extends ComplexSource } @Override - public void receive( ComplexBuffer sampleArray ) + public void receive( ComplexBuffer buffer ) { - mBuffer.add( sampleArray ); + mBuffer.add( buffer ); + } + + public void setFrequencyChangeListener( FrequencyChangeListener listener ) + { + mFrequencyChangeListener = listener; } @Override - public void setListener( Listener> listener ) - { - mBufferAssembler.setListener( listener ); - } - + public void setListener( Listener listener ) + { + /* Get a pointer to the listener so that if we have to change the + * decimation filter, we can re-add the listener */ + mListener = listener; + + mDecimationFilter.setListener( listener ); + } + @Override - public void removeListener( Listener> listener ) - { - mBufferAssembler.removeListener( listener ); - } + public void removeListener( Listener listener ) + { + mDecimationFilter.removeListener(); + } @Override public void frequencyChanged( FrequencyChangeEvent event ) @@ -180,7 +202,7 @@ public class TunerChannelSource extends ComplexSource if( mTunerSampleRate != sampleRate ) { /* Set the oscillator to the new frequency */ - mSineWaveGenerator.setSampleRate( sampleRate ); + mMixer.setSampleRate( sampleRate ); /* Get new decimation filter */ mDecimationFilter = FilterFactory @@ -192,7 +214,7 @@ public class TunerChannelSource extends ComplexSource WindowType.HAMMING ); /* re-add the original output listener */ - mDecimationFilter.setListener( mBufferAssembler ); + mDecimationFilter.setListener( mListener ); mTunerSampleRate = sampleRate; } @@ -204,7 +226,7 @@ public class TunerChannelSource extends ComplexSource mTunerChannel.getFrequency() - mTunerFrequencyError; - mSineWaveGenerator.setFrequency( frequencyErrorOffset ); + mMixer.setFrequency( frequencyErrorOffset ); break; case FREQUENCY: long frequency = event.getValue().longValue(); @@ -215,7 +237,7 @@ public class TunerChannelSource extends ComplexSource long frequencyOffset = frequency - mTunerChannel.getFrequency(); - mSineWaveGenerator.setFrequency( frequencyOffset ); + mMixer.setFrequency( frequencyOffset ); mTunerFrequency = frequency; break; @@ -226,16 +248,27 @@ public class TunerChannelSource extends ComplexSource public int getSampleRate() throws SourceException { - return mTuner.getSampleRate(); + return CHANNEL_RATE; } public long getFrequency() throws SourceException { - return mTuner.getFrequency(); + return mTunerChannel.getFrequency(); } + /** + * Decimates an inbound buffer of I/Q samples from the source down to the + * standard 48000 channel sample rate + */ public class DecimationProcessor implements Runnable { + private AtomicBoolean mDispose = new AtomicBoolean( false ); + + public void dispose() + { + mDispose.set( true ); + } + @Override public void run() { @@ -244,38 +277,59 @@ public class TunerChannelSource extends ComplexSource * run the program out of memory */ try { - List sampleBuffers = - new ArrayList(); - - if( mBuffer != null ) + /* Check to see if we've been shutdown */ + if( mDispose.get() ) { - /* Limit to 4, 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( sampleBuffers, 4 ); - - for( ComplexBuffer buffer: sampleBuffers ) + cleanup(); + return; + } + else + { + List sampleBuffers = + new ArrayList(); + + if( mBuffer != null ) { - float[] samples = buffer.getSamples(); - float[] translated = new float[ samples.length ]; - - /* Perform frequency translation */ - for( int x = 0; x < samples.length; x += 2 ) + mBuffer.drainTo( sampleBuffers, 4 ); + + for( ComplexBuffer buffer: sampleBuffers ) { - ComplexSample multiplier = - mSineWaveGenerator.nextComplex(); - - translated[ x ] = ( samples[ x ] * multiplier.inphase() ) - - ( samples[ x + 1 ] * multiplier.quadrature() ); - - translated[ x + 1 ] = ( samples[ x + 1 ] * multiplier.inphase() ) + - ( samples[ x ] * multiplier.quadrature() ); + /* Check to see if we've been shutdown */ + if( mDispose.get() ) + { + cleanup(); + return; + } + else + { + float[] samples = buffer.getSamples(); + + /* We make a copy of the buffer so that we don't affect + * anyone else that is using the same buffer, like other + * channels or the spectral display */ + float[] translated = new float[ samples.length ]; + + /* Perform frequency translation */ + for( int x = 0; x < samples.length; x += 2 ) + { + mMixer.rotate(); + + translated[ x ] = Complex.multiplyInphase( + samples[ x ], samples[ x + 1 ], mMixer.inphase(), mMixer.quadrature() ); + + translated[ x + 1 ] = Complex.multiplyQuadrature( + samples[ x ], samples[ x + 1 ], mMixer.inphase(), mMixer.quadrature() ); + } + + if( mDecimationFilter != null ) + { + mDecimationFilter.receive( new ComplexBuffer( translated ) ); + } + } } - mDecimationFilter.receive( translated ); + sampleBuffers.clear(); } - - sampleBuffers.clear(); } } catch( Exception e ) @@ -284,9 +338,4 @@ public class TunerChannelSource extends ComplexSource } } } - - public void addListener( FrequencyChangeListener listener ) - { - mFrequencyChangeListener = listener; - } } diff --git a/src/source/tuner/TunerController.java b/src/source/tuner/TunerController.java index c5cf2064..eb978ef5 100644 --- a/src/source/tuner/TunerController.java +++ b/src/source/tuner/TunerController.java @@ -156,9 +156,8 @@ public abstract class TunerController implements Tunable } public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager, - Tuner tuner, TunerChannel tunerChannel ) - throws RejectedExecutionException, - SourceException + Tuner tuner, TunerChannel tunerChannel ) + throws RejectedExecutionException, SourceException { TunerChannelSource source = null; diff --git a/src/source/tuner/TunerManager.java b/src/source/tuner/TunerManager.java index c069ccd3..f11fedc4 100644 --- a/src/source/tuner/TunerManager.java +++ b/src/source/tuner/TunerManager.java @@ -33,6 +33,7 @@ import org.usb4java.LibUsb; import source.Source; import source.SourceException; import source.config.SourceConfigTuner; +import source.config.SourceConfiguration; import source.mixer.MixerManager; import source.tuner.fcd.FCDTuner; import source.tuner.fcd.proV1.FCD1TunerController; @@ -44,7 +45,6 @@ import source.tuner.rtl.RTL2832TunerController; import source.tuner.rtl.e4k.E4KTunerController; import source.tuner.rtl.r820t.R820TTunerController; import controller.ResourceManager; -import controller.channel.ProcessingChain; public class TunerManager { @@ -76,15 +76,17 @@ public class TunerManager * 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 processingChain ) + public Source getSource( SourceConfiguration config, int bandwidth ) { TunerChannelSource retVal = null; - - if( processingChain.getChannel().getSourceConfiguration() - instanceof SourceConfigTuner ) + if( config instanceof SourceConfigTuner ) { - TunerChannel tunerChannel = processingChain.getChannel().getTunerChannel(); + SourceConfigTuner tunerConfig = (SourceConfigTuner)config; + + TunerChannel tunerChannel = tunerConfig.getTunerChannel(); + + tunerChannel.setBandwidth( bandwidth ); Iterator it = mTuners.iterator(); @@ -358,7 +360,8 @@ public class TunerManager try { HackRFTunerController hackRFController = - new HackRFTunerController( device, descriptor ); + new HackRFTunerController( device, descriptor, + mResourceManager.getThreadPoolManager() ); hackRFController.init(); @@ -409,7 +412,8 @@ public class TunerManager try { E4KTunerController controller = - new E4KTunerController( device, deviceDescriptor ); + new E4KTunerController( device, deviceDescriptor, + mResourceManager.getThreadPoolManager() ); controller.init(); @@ -432,14 +436,12 @@ public class TunerManager + "controller - " + se.getLocalizedMessage() ); } case RAFAELMICRO_R820T: - mLog.debug( "attempting to construct R820T " - + "tuner controller" ); try { R820TTunerController controller = - new R820TTunerController( device, deviceDescriptor ); + new R820TTunerController( device, deviceDescriptor, + mResourceManager.getThreadPoolManager() ); - mLog.debug( "initializing R820T tuner controller" ); controller.init(); RTL2832Tuner rtlTuner = @@ -450,14 +452,8 @@ public class TunerManager if( config != null ) { - mLog.debug( "applying tuner config to R820T tuner" ); rtlTuner.apply( config ); } - else - { - mLog.debug( "R820T tuner config was " - + "null - not applied" ); - } return new TunerInitStatus( rtlTuner, "LOADED" ); } diff --git a/src/source/tuner/frequency/AutomaticFrequencyControl.java b/src/source/tuner/frequency/AutomaticFrequencyControl_R.java similarity index 95% rename from src/source/tuner/frequency/AutomaticFrequencyControl.java rename to src/source/tuner/frequency/AutomaticFrequencyControl_R.java index b655a1d5..4c52f7ef 100644 --- a/src/source/tuner/frequency/AutomaticFrequencyControl.java +++ b/src/source/tuner/frequency/AutomaticFrequencyControl_R.java @@ -25,11 +25,11 @@ import sample.real.RealSampleListener; import source.tuner.frequency.FrequencyChangeEvent.Attribute; import buffer.FloatAveragingBuffer; -public class AutomaticFrequencyControl extends FrequencyCorrectionControl +public class AutomaticFrequencyControl_R extends FrequencyCorrectionControl implements RealSampleListener { private final static Logger mLog = - LoggerFactory.getLogger( AutomaticFrequencyControl.class ); + LoggerFactory.getLogger( AutomaticFrequencyControl_R.class ); private static float LARGE_ERROR = 0.300f; private static float MEDIUM_ERROR = 0.150f; @@ -64,7 +64,7 @@ public class AutomaticFrequencyControl extends FrequencyCorrectionControl * * @param maximum - maximum allowable (+/-) frequency correction value. */ - public AutomaticFrequencyControl( int maximum ) + public AutomaticFrequencyControl_R( int maximum ) { super( maximum ); } diff --git a/src/source/tuner/frequency/AutomaticFrequencyControl_RB.java b/src/source/tuner/frequency/AutomaticFrequencyControl_RB.java new file mode 100644 index 00000000..e51225d3 --- /dev/null +++ b/src/source/tuner/frequency/AutomaticFrequencyControl_RB.java @@ -0,0 +1,197 @@ +package source.tuner.frequency; + +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2015 Dennis Sheirer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + ******************************************************************************/ + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import sample.real.RealBuffer; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; +import buffer.FloatAveragingBuffer; + +public class AutomaticFrequencyControl_RB extends FrequencyCorrectionControl + implements Listener +{ + private final static Logger mLog = + LoggerFactory.getLogger( AutomaticFrequencyControl_RB.class ); + + private static float LARGE_ERROR = 0.300f; + private static float MEDIUM_ERROR = 0.150f; + private static float SMALL_ERROR = 0.030f; + private static float FINE_ERROR = 0.015f; + + private static int LARGE_FREQUENCY_CORRECTION = 1500; //Hertz + private static int MEDIUM_FREQUENCY_CORRECTION = 500; + private static int SMALL_FREQUENCY_CORRECTION = 100; + private static int FINE_FREQUENCY_CORRECTION = 50; + + private static int SAMPLE_THRESHOLD = 15000; + private Mode mMode = Mode.FAST; + + private FloatAveragingBuffer mBuffer = + new FloatAveragingBuffer( SAMPLE_THRESHOLD ); + + private int mSampleCounter = 0; + private int mSkipCounter = 0; + private int mSkipThreshold = 4; + private int mErrorCorrection = 0; + + public enum Mode + { + NORMAL,FAST; + } + + /** + * Automatic Frequency Control. Monitors DC bias present in an FM + * demodulated audio stream and issues automatic frequency corrections to + * maintain the DC bias close to zero. + * + * @param maximum - maximum allowable (+/-) frequency correction value. + */ + public AutomaticFrequencyControl_RB( int maximum ) + { + super( maximum ); + } + + public void dispose() + { + super.dispose(); + + mBuffer = null; + } + + /** + * Resets the error correction value to zero and broadcasts a change event + */ + public void reset() + { + super.reset(); + + mMode = Mode.FAST; + dispatch(); + } + + /** + * Broadcasts a frequency correction change event to the registered listener + */ + private void dispatch() + { + if( mListener != null ) + { + mListener.frequencyChanged( + new FrequencyChangeEvent( Attribute.FREQUENCY_ERROR, + mErrorCorrection ) ); + } + } + + @Override + public void receive( RealBuffer t ) + { + float average = 0.0f; + + for( float sample: t.getSamples() ) + { + average = mBuffer.get( sample ); + } + + //TODO: fix this + +// update( average ); + } + + public void receive( float sample ) + { + if( mMode == Mode.FAST ) + { + float average = mBuffer.get( sample ); + + mSampleCounter++; + + if( mSampleCounter >= SAMPLE_THRESHOLD ) + { + mSampleCounter = 0; + + update( average ); + } + } + else + { + mSkipCounter++; + + if( mSkipCounter >= mSkipThreshold ) + { + mSkipCounter = 0; + + float average = mBuffer.get( sample ); + + mSampleCounter++; + + if( mSampleCounter >= SAMPLE_THRESHOLD ) + { + mSampleCounter = 0; + + update( average ); + } + } + } + } + + private void update( float average ) + { + mMode = Mode.NORMAL; + + if( average > LARGE_ERROR ) + { + adjust( -LARGE_FREQUENCY_CORRECTION ); + mMode = Mode.FAST; + } + else if( average > MEDIUM_ERROR ) + { + adjust( -MEDIUM_FREQUENCY_CORRECTION ); + mMode = Mode.FAST; + } + else if( average > SMALL_ERROR ) + { + adjust( -SMALL_FREQUENCY_CORRECTION ); + } + else if( average > FINE_ERROR ) + { + adjust( -FINE_FREQUENCY_CORRECTION ); + } + else if( average < -LARGE_ERROR ) + { + adjust( LARGE_FREQUENCY_CORRECTION ); + mMode = Mode.FAST; + } + else if( average < -MEDIUM_ERROR ) + { + adjust( MEDIUM_FREQUENCY_CORRECTION ); + mMode = Mode.FAST; + } + else if( average < -SMALL_ERROR ) + { + adjust( SMALL_FREQUENCY_CORRECTION ); + } + else if( average < -FINE_ERROR ) + { + adjust( FINE_FREQUENCY_CORRECTION ); + } + } +} diff --git a/src/source/tuner/frequency/FrequencyCorrectionControl.java b/src/source/tuner/frequency/FrequencyCorrectionControl.java index a2853c67..bf3f6a7c 100644 --- a/src/source/tuner/frequency/FrequencyCorrectionControl.java +++ b/src/source/tuner/frequency/FrequencyCorrectionControl.java @@ -59,7 +59,7 @@ public class FrequencyCorrectionControl implements FrequencyChangeListener /** * Registers a listener to receive frequency correction events */ - public void setListener( FrequencyChangeListener listener ) + public void setFrequencyChangeListener( FrequencyChangeListener listener ) { mListener = listener; } @@ -92,7 +92,7 @@ public class FrequencyCorrectionControl implements FrequencyChangeListener * Resets frequency correction value to zero and notifies any reset * listeners */ - protected void reset() + public void reset() { setErrorCorrection( 0, false ); diff --git a/src/source/tuner/frequency/IFrequencyCorrectionController.java b/src/source/tuner/frequency/IFrequencyCorrectionController.java new file mode 100644 index 00000000..e937bd4a --- /dev/null +++ b/src/source/tuner/frequency/IFrequencyCorrectionController.java @@ -0,0 +1,8 @@ +package source.tuner.frequency; + + +public interface IFrequencyCorrectionController +{ + public boolean hasFrequencyCorrectionControl(); + public FrequencyCorrectionControl getFrequencyCorrectionControl(); +} diff --git a/src/source/tuner/hackrf/HackRFTunerController.java b/src/source/tuner/hackrf/HackRFTunerController.java index 66461a43..27acd03e 100644 --- a/src/source/tuner/hackrf/HackRFTunerController.java +++ b/src/source/tuner/hackrf/HackRFTunerController.java @@ -58,9 +58,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Executors; import java.util.concurrent.LinkedTransferQueue; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -78,12 +76,15 @@ import org.usb4java.LibUsbException; import org.usb4java.Transfer; import org.usb4java.TransferCallback; +import sample.Broadcaster; import sample.Listener; import sample.adapter.ByteSampleAdapter; import sample.complex.ComplexBuffer; import source.SourceException; import source.tuner.TunerConfiguration; import source.tuner.TunerController; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; public class HackRFTunerController extends TunerController @@ -114,8 +115,7 @@ public class HackRFTunerController extends TunerController private LinkedTransferQueue mFilledBuffers = new LinkedTransferQueue(); - private CopyOnWriteArrayList> mSampleListeners = - new CopyOnWriteArrayList>(); + private Broadcaster mComplexBufferBroadcaster = new Broadcaster<>(); private ByteSampleAdapter mSampleAdapter = new ByteSampleAdapter(); private BufferProcessor mBufferProcessor = new BufferProcessor(); @@ -127,14 +127,16 @@ public class HackRFTunerController extends TunerController private Device mDevice; private DeviceDescriptor mDeviceDescriptor; private DeviceHandle mDeviceHandle; + private ThreadPoolManager mThreadPoolManager; - public HackRFTunerController( Device device, DeviceDescriptor descriptor ) - throws SourceException + public HackRFTunerController( Device device, DeviceDescriptor descriptor, + ThreadPoolManager threadPoolManager ) throws SourceException { super( MIN_FREQUENCY, MAX_FREQUENCY ); mDevice = device; mDeviceDescriptor = descriptor; + mThreadPoolManager = threadPoolManager; } public void init() throws SourceException @@ -870,7 +872,7 @@ public class HackRFTunerController extends TunerController */ public void addListener( Listener listener ) { - mSampleListeners.add( listener ); + mComplexBufferBroadcaster.addListener( listener ); if( mBufferProcessor == null || !mBufferProcessor.isRunning() ) { @@ -890,51 +892,20 @@ public class HackRFTunerController extends TunerController */ public void removeListener( Listener listener ) { - mSampleListeners.remove( listener ); - - if( mSampleListeners.isEmpty() ) + mComplexBufferBroadcaster.removeListener( listener ); + + if( !mComplexBufferBroadcaster.hasListeners() ) { mBufferProcessor.stop(); } } - /** - * Dispatches the sample array to each registered listener. If there is more - * than one listener, they receive a copy of the samples. If there is only - * one listener, or the last listener, they receive the original sample array. - * - * This is to facilitate garbage collection of the array when the listener - * is done processing the samples. - */ - public void broadcast( ComplexBuffer samples ) - { - Iterator> it = mSampleListeners.iterator(); - - while( it.hasNext() ) - { - Listener next = it.next(); - - /* if this is the last (or only) listener, send the original - * buffer, otherwise send a copy of the buffer */ - if( it.hasNext() ) - { - next.receive( samples.copyOf() ); - } - else - { - next.receive( samples ); - } - } - } - /** * Buffer processing thread. Fetches samples from the HackRF Tuner and * dispatches them to all registered listeners */ public class BufferProcessor implements Runnable, TransferCallback { - private ScheduledExecutorService mExecutor = - Executors.newScheduledThreadPool( 1 ); private ScheduledFuture mSampleDispatcherTask; private CopyOnWriteArrayList mTransfers; private AtomicBoolean mRunning = new AtomicBoolean(); @@ -960,9 +931,9 @@ public class HackRFTunerController extends TunerController } } - mSampleDispatcherTask = mExecutor - .scheduleAtFixedRate( new BufferDispatcher(), - 0, 20, TimeUnit.MILLISECONDS ); + mSampleDispatcherTask = mThreadPoolManager.scheduleFixedRate( + ThreadType.SOURCE_SAMPLE_PROCESSING, new BufferDispatcher(), + 20, TimeUnit.MILLISECONDS ); while( mRunning.get() ) { @@ -1125,7 +1096,7 @@ public class HackRFTunerController extends TunerController { float[] samples = mSampleAdapter.convert( buffer ); - broadcast( new ComplexBuffer( samples ) ); + mComplexBufferBroadcaster.broadcast( new ComplexBuffer( samples ) ); } } catch( Exception e ) diff --git a/src/source/tuner/rtl/RTL2832TunerController.java b/src/source/tuner/rtl/RTL2832TunerController.java index acffcde3..0266ee55 100644 --- a/src/source/tuner/rtl/RTL2832TunerController.java +++ b/src/source/tuner/rtl/RTL2832TunerController.java @@ -29,9 +29,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.Executors; import java.util.concurrent.LinkedTransferQueue; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -51,16 +49,20 @@ import org.usb4java.LibUsbException; import org.usb4java.Transfer; import org.usb4java.TransferCallback; +import sample.Broadcaster; import sample.Listener; import sample.adapter.ByteSampleAdapter; import sample.complex.ComplexBuffer; +import sample.complex.Complex; import source.SourceException; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeEvent.Attribute; import source.tuner.TunerController; import source.tuner.TunerType; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; import buffer.FloatAveragingBuffer; import controller.ResourceManager; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; public abstract class RTL2832TunerController extends TunerController { @@ -104,12 +106,11 @@ public abstract class RTL2832TunerController extends TunerController private SampleRate mSampleRate = DEFAULT_SAMPLE_RATE; - private BufferProcessor mBufferProcessor = new BufferProcessor(); + private BufferProcessor mBufferProcessor; private ByteSampleAdapter mSampleAdapter = new ByteSampleAdapter(); - private CopyOnWriteArrayList> mSampleListeners = - new CopyOnWriteArrayList>(); + private Broadcaster mComplexBufferBroadcaster = new Broadcaster<>(); private LinkedTransferQueue mFilledBuffers = new LinkedTransferQueue(); @@ -127,6 +128,7 @@ public abstract class RTL2832TunerController extends TunerController public int mBufferSize = 131072; protected Descriptor mDescriptor; + private ThreadPoolManager mThreadPoolManager; /** * Abstract tuner controller device. Use the static getTunerClass() method @@ -135,11 +137,14 @@ public abstract class RTL2832TunerController extends TunerController */ public RTL2832TunerController( Device device, DeviceDescriptor deviceDescriptor, + ThreadPoolManager threadPoolManager, long minTunableFrequency, long maxTunableFrequency ) throws SourceException { super( minTunableFrequency, maxTunableFrequency ); + mThreadPoolManager = threadPoolManager; + mDevice = device; mDeviceDescriptor = deviceDescriptor; } @@ -1345,12 +1350,15 @@ public abstract class RTL2832TunerController extends TunerController */ public void addListener( Listener listener ) { - mSampleListeners.add( listener ); + mComplexBufferBroadcaster.addListener( listener ); - if( mBufferProcessor == null || !mBufferProcessor.isRunning() ) + if( mBufferProcessor == null ) + { + mBufferProcessor = new BufferProcessor( mThreadPoolManager ); + } + + if( !mBufferProcessor.isRunning() ) { - mBufferProcessor = new BufferProcessor(); - Thread thread = new Thread( mBufferProcessor ); thread.setDaemon( true ); thread.setName( "RTL2832 Sample Processor" ); @@ -1365,59 +1373,35 @@ public abstract class RTL2832TunerController extends TunerController */ public void removeListener( Listener listener ) { - mSampleListeners.remove( listener ); - - if( mSampleListeners.isEmpty() ) + mComplexBufferBroadcaster.removeListener( listener ); + + if( !mComplexBufferBroadcaster.hasListeners() ) { mBufferProcessor.stop(); } } - /** - * Dispatches float sample buffers to all registered listeners - */ - public void broadcast( ComplexBuffer buffer ) - { - Iterator> it = mSampleListeners.iterator(); - - while( it.hasNext() ) - { - Listener 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( buffer.copyOf() ); - } - else - { - next.receive( buffer ); - } - } - } - /** * Buffer processing thread. Fetches samples from the RTL2832 Tuner and * dispatches them to all registered listeners */ public class BufferProcessor implements Runnable, TransferCallback { - private ScheduledExecutorService mExecutor = - Executors.newScheduledThreadPool( 2 ); private ScheduledFuture mSampleDispatcherTask; - private ScheduledFuture mSampleRateCounterTask; private CopyOnWriteArrayList mTransfers; private AtomicBoolean mRunning = new AtomicBoolean(); + private ThreadPoolManager mThreadPoolManager; + public BufferProcessor( ThreadPoolManager manager ) + { + mThreadPoolManager = manager; + } + @Override public void run() { if( mRunning.compareAndSet( false, true ) ) { - mLog.debug( "rtl2832 [" + getUniqueID() + - "] - starting sample fetch thread" ); - try { setSampleRate( mSampleRate ); @@ -1445,16 +1429,17 @@ public abstract class RTL2832TunerController extends TunerController } } - mSampleDispatcherTask = mExecutor - .scheduleAtFixedRate( new BufferDispatcher(), - 0, 20, TimeUnit.MILLISECONDS ); - - mSampleRateMonitor = - new SampleRateMonitor( mSampleRate.getRate() ); - - mSampleRateCounterTask = mExecutor - .scheduleAtFixedRate( mSampleRateMonitor, - 10, 10, TimeUnit.SECONDS ); + try + { + mSampleDispatcherTask = mThreadPoolManager.scheduleFixedRate( + ThreadType.SOURCE_SAMPLE_PROCESSING, new BufferDispatcher(), + 20, TimeUnit.MILLISECONDS ); + } + catch( NullPointerException npe ) + { + mLog.error( "NPE! = tpm null: " + ( mThreadPoolManager == null ), npe ); + } + while( mRunning.get() ) { @@ -1478,15 +1463,12 @@ public abstract class RTL2832TunerController extends TunerController { if( mRunning.compareAndSet( true, false ) ) { - mLog.debug( "rtl2832 [" + getUniqueID() + - "] - stopping sample fetch thread" ); - cancel(); if( mSampleDispatcherTask != null ) { - mSampleDispatcherTask.cancel( true ); - mSampleRateCounterTask.cancel( true ); + mThreadPoolManager.cancel( mSampleDispatcherTask ); + mSampleDispatcherTask = null; mFilledBuffers.clear(); } } @@ -1621,7 +1603,7 @@ public abstract class RTL2832TunerController extends TunerController { float[] samples = mSampleAdapter.convert( buffer ); - broadcast( new ComplexBuffer( samples ) ); + mComplexBufferBroadcaster.broadcast( new ComplexBuffer( samples ) ); } } catch( Exception e ) diff --git a/src/source/tuner/rtl/e4k/E4KTunerController.java b/src/source/tuner/rtl/e4k/E4KTunerController.java index 09577e01..b9689a85 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerController.java +++ b/src/source/tuner/rtl/e4k/E4KTunerController.java @@ -34,8 +34,8 @@ import source.SourceException; import source.tuner.TunerConfiguration; import source.tuner.TunerType; import source.tuner.rtl.RTL2832TunerController; -import source.tuner.rtl.RTL2832TunerController.Descriptor; import controller.ResourceManager; +import controller.ThreadPoolManager; public class E4KTunerController extends RTL2832TunerController { @@ -111,10 +111,12 @@ public class E4KTunerController extends RTL2832TunerController private E4KTunerEditorPanel mEditor; public E4KTunerController( Device device, - DeviceDescriptor deviceDescriptor ) + DeviceDescriptor deviceDescriptor, + ThreadPoolManager threadPoolManager ) throws SourceException { - super( device, deviceDescriptor, MIN_FREQUENCY, MAX_FREQUENCY ); + super( device, deviceDescriptor, threadPoolManager, + MIN_FREQUENCY, MAX_FREQUENCY ); } @Override diff --git a/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java b/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java index c80af5ab..fe08d0c3 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java +++ b/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java @@ -270,64 +270,64 @@ public class R820TTunerConfigurationPanel extends JPanel add( new JLabel( "Sample Rate:" ) ); add( mComboSampleRate, "growx,push" ); - /** - * Sample Rate Correction - */ - SpinnerModel sampleRateCorrectionModel = - new SpinnerNumberModel( 0, //initial value - -32768, //min - 32767, //max - 1 ); //step - - mSampleRateCorrection = new JSpinner( sampleRateCorrectionModel ); - - JSpinner.NumberEditor sampleRateCorrectionEditor = - (JSpinner.NumberEditor)mSampleRateCorrection.getEditor(); - - DecimalFormat sampleRateCorrectionFormat = - sampleRateCorrectionEditor.getFormat(); - - sampleRateCorrectionFormat.setMinimumFractionDigits( 0 ); - - sampleRateCorrectionEditor.getTextField() - .setHorizontalAlignment( SwingConstants.CENTER ); - - mSampleRateCorrection.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - final int value = ((SpinnerNumberModel)mSampleRateCorrection - .getModel()).getNumber().intValue(); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - mController.setSampleRateFrequencyCorrection( value ); - } - catch ( SourceException | LibUsbException e1 ) - { - JOptionPane.showMessageDialog( - R820TTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't apply " - + "sample rate correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply " - + "sample rate correction value: " + value + - e1.getLocalizedMessage() ); - } - } - } ); - } - } ); - - add( mSampleRateCorrection ); - add( new JLabel( "Sample Rate Correction (ppm)" ), "grow,push" ); +// /** +// * Sample Rate Correction +// */ +// SpinnerModel sampleRateCorrectionModel = +// new SpinnerNumberModel( 0, //initial value +// -32768, //min +// 32767, //max +// 1 ); //step +// +// mSampleRateCorrection = new JSpinner( sampleRateCorrectionModel ); +// +// JSpinner.NumberEditor sampleRateCorrectionEditor = +// (JSpinner.NumberEditor)mSampleRateCorrection.getEditor(); +// +// DecimalFormat sampleRateCorrectionFormat = +// sampleRateCorrectionEditor.getFormat(); +// +// sampleRateCorrectionFormat.setMinimumFractionDigits( 0 ); +// +// sampleRateCorrectionEditor.getTextField() +// .setHorizontalAlignment( SwingConstants.CENTER ); +// +// mSampleRateCorrection.addChangeListener( new ChangeListener() +// { +// @Override +// public void stateChanged( ChangeEvent e ) +// { +// final int value = ((SpinnerNumberModel)mSampleRateCorrection +// .getModel()).getNumber().intValue(); +// +// EventQueue.invokeLater( new Runnable() +// { +// @Override +// public void run() +// { +// try +// { +// mController.setSampleRateFrequencyCorrection( value ); +// } +// catch ( SourceException | LibUsbException e1 ) +// { +// JOptionPane.showMessageDialog( +// R820TTunerConfigurationPanel.this, +// "E4K Tuner Controller - couldn't apply " +// + "sample rate correction value: " + value + +// e1.getLocalizedMessage() ); +// +// mLog.error( "E4K Tuner Controller - couldn't apply " +// + "sample rate correction value: " + value + +// e1.getLocalizedMessage() ); +// } +// } +// } ); +// } +// } ); +// +// add( mSampleRateCorrection ); +// add( new JLabel( "Sample Rate Correction (ppm)" ), "grow,push" ); /** * Gain Controls diff --git a/src/source/tuner/rtl/r820t/R820TTunerController.java b/src/source/tuner/rtl/r820t/R820TTunerController.java index 375a79b2..4d8ec9ce 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerController.java +++ b/src/source/tuner/rtl/r820t/R820TTunerController.java @@ -38,6 +38,7 @@ import source.tuner.TunerConfiguration; import source.tuner.TunerType; import source.tuner.rtl.RTL2832TunerController; import controller.ResourceManager; +import controller.ThreadPoolManager; public class R820TTunerController extends RTL2832TunerController { @@ -73,10 +74,12 @@ public class R820TTunerController extends RTL2832TunerController private JPanel mEditor; public R820TTunerController( Device device, - DeviceDescriptor deviceDescriptor ) - throws SourceException + DeviceDescriptor deviceDescriptor, + ThreadPoolManager threadPoolManager ) + throws SourceException { - super( device, deviceDescriptor, MIN_FREQUENCY, MAX_FREQUENCY ); + super( device, deviceDescriptor, threadPoolManager, + MIN_FREQUENCY, MAX_FREQUENCY ); } @Override @@ -250,21 +253,15 @@ public class R820TTunerController extends RTL2832TunerController try { - mLog.debug( "initializing RTL2832 tuner baseband" ); initBaseband( mDeviceHandle ); - mLog.debug( "enabling I2C repeater" ); enableI2CRepeater( mDeviceHandle, true ); boolean i2CRepeaterControl = false; - mLog.debug( "initializing R820T tuner" ); initTuner( i2CRepeaterControl ); - mLog.debug( "disabling I2C repeater" ); enableI2CRepeater( mDeviceHandle, false ); - - mLog.debug( "initializing RTL2832 tuner controller super class" ); } catch( UsbException e ) { diff --git a/src/source/wave/ComplexWaveSource.java b/src/source/wave/ComplexWaveSource.java index c3e3729e..8dfd8895 100644 --- a/src/source/wave/ComplexWaveSource.java +++ b/src/source/wave/ComplexWaveSource.java @@ -35,7 +35,8 @@ import org.slf4j.LoggerFactory; import sample.Broadcaster; import sample.Listener; -import sample.complex.ComplexSample; +import sample.SampleType; +import sample.complex.Complex; public class ComplexWaveSource extends WaveSource { @@ -56,7 +57,7 @@ public class ComplexWaveSource extends WaveSource private static int BUFFER_SAMPLE_SIZE = 2000; - private Broadcaster mBroadcaster = new Broadcaster(); + private Broadcaster mBroadcaster = new Broadcaster(); /** * Single channel (mono) wave file playback source with optional looping @@ -227,7 +228,7 @@ public class ComplexWaveSource extends WaveSource if( broadcast ) { - send( new ComplexSample( (float)i / 32767.0f, (float)q / 32767.0f ) ); + send( new Complex( (float)i / 32767.0f, (float)q / 32767.0f ) ); } success = true; @@ -278,7 +279,7 @@ public class ComplexWaveSource extends WaveSource /** * Broadcasts a sample to the registered listeners */ - private void send( ComplexSample sample ) + private void send( Complex sample ) { mBroadcaster.broadcast( sample ); } @@ -286,12 +287,12 @@ public class ComplexWaveSource extends WaveSource /** * Adds a new listener to receive samples as they are read from the wave file */ - public void addListener( Listener listener ) + public void addListener( Listener listener ) { mBroadcaster.addListener( listener ); } - public void removeListener( Listener listener ) + public void removeListener( Listener listener ) { mBroadcaster.removeListener( listener ); } diff --git a/src/source/wave/FloatWaveSource.java b/src/source/wave/FloatWaveSource.java index fbec02fb..a660679d 100644 --- a/src/source/wave/FloatWaveSource.java +++ b/src/source/wave/FloatWaveSource.java @@ -29,6 +29,7 @@ import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.UnsupportedAudioFileException; +import sample.SampleType; import sample.adapter.ShortToFloatMap; import sample.real.RealSampleListener; diff --git a/src/source/wave/WaveSource.java b/src/source/wave/WaveSource.java index 55956520..f3470d1c 100644 --- a/src/source/wave/WaveSource.java +++ b/src/source/wave/WaveSource.java @@ -20,6 +20,7 @@ package source.wave; import java.io.File; import java.io.IOException; +import sample.SampleType; import source.Source; public abstract class WaveSource extends Source diff --git a/src/spectrum/ChannelSpectrumPanel.java b/src/spectrum/ChannelSpectrumPanel.java index e2f1cc32..d57870fe 100644 --- a/src/spectrum/ChannelSpectrumPanel.java +++ b/src/spectrum/ChannelSpectrumPanel.java @@ -17,14 +17,15 @@ import javax.swing.SwingUtilities; import net.miginfocom.swing.MigLayout; import sample.Listener; +import sample.SampleType; import sample.complex.ComplexBuffer; +import sample.real.RealBuffer; import sample.real.RealSampleListener; import settings.ColorSetting.ColorSettingName; import settings.ColorSettingMenuItem; import settings.Setting; import settings.SettingChangeListener; import settings.SettingsManager; -import source.Source.SampleType; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeEvent.Attribute; import spectrum.converter.DFTResultsConverter; @@ -44,7 +45,7 @@ import dsp.filter.Window.WindowType; public class ChannelSpectrumPanel extends JPanel implements ChannelEventListener, - RealSampleListener, + Listener, SettingChangeListener, SpectralDisplayAdjuster { @@ -179,7 +180,7 @@ public class ChannelSpectrumPanel extends JPanel start(); } break; - case CHANNEL_DELETED: + case CHANNEL_DISABLED: if( event.getChannel() == mCurrentChannel ) { if( mEnabled.get() ) @@ -190,7 +191,7 @@ public class ChannelSpectrumPanel extends JPanel mCurrentChannel = null; } break; - case CHANGE_ENABLED: + case CHANNEL_ENABLED: if( event.getChannel() == mCurrentChannel && !event.getChannel().isProcessing() ) { @@ -205,7 +206,7 @@ public class ChannelSpectrumPanel extends JPanel { if( mEnabled.get() && mCurrentChannel != null && mCurrentChannel.isProcessing() ) { - mCurrentChannel.getProcessingChain().addRealListener( (RealSampleListener)this ); + mCurrentChannel.getProcessingChain().addRealBufferListener( this ); mDFTProcessor.start(); } } @@ -215,7 +216,7 @@ public class ChannelSpectrumPanel extends JPanel if( mCurrentChannel != null && mCurrentChannel.isProcessing() ) { - mCurrentChannel.getProcessingChain().removeRealListener( (RealSampleListener)this ); + mCurrentChannel.getProcessingChain().removeRealBufferListener( this ); } mDFTProcessor.stop(); @@ -251,9 +252,12 @@ public class ChannelSpectrumPanel extends JPanel } @Override - public void receive( float sample ) + public void receive( RealBuffer buffer ) { - mDecimatingSampleAssembler.receive( sample ); + for( float sample: buffer.getSamples() ) + { + mDecimatingSampleAssembler.receive( sample ); + } } /** @@ -454,7 +458,7 @@ public class ChannelSpectrumPanel extends JPanel public class DecimatingSampleAssembler { private FloatHalfBandFilter mDecimationFilter = new FloatHalfBandFilter( - Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 ); + Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002f ); private SampleAssembler mSampleAssembler; diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java index 6df2e493..4172d1e5 100644 --- a/src/spectrum/DFTProcessor.java +++ b/src/spectrum/DFTProcessor.java @@ -30,10 +30,10 @@ import org.jtransforms.fft.FloatFFT_1D; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import controller.NamingThreadFactory; import sample.Listener; +import sample.SampleType; import sample.complex.ComplexBuffer; -import source.Source; -import source.Source.SampleType; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeListener; import spectrum.converter.DFTResultsConverter; @@ -56,8 +56,8 @@ public class DFTProcessor implements Listener, private ArrayBlockingQueue mQueue = new ArrayBlockingQueue( 200 ); - private ScheduledExecutorService mScheduler = - Executors.newScheduledThreadPool(1); + private ScheduledExecutorService mScheduler = Executors + .newScheduledThreadPool( 1, new NamingThreadFactory( "spectrum dft" ) ); private FFTWidth mFFTWidth = FFTWidth.FFT04096; private FFTWidth mNewFFTWidth = FFTWidth.FFT04096; @@ -129,7 +129,7 @@ public class DFTProcessor implements Listener, setWindowType( mWindowType ); } - public Source.SampleType getSampleType() + public SampleType getSampleType() { return mSampleType; } @@ -177,7 +177,7 @@ public class DFTProcessor implements Listener, /** * Reset the scheduler */ - mScheduler = Executors.newScheduledThreadPool(1); + mScheduler = Executors.newScheduledThreadPool( 1, new NamingThreadFactory( "spectrum dft" ) ); /** * Schedule the DFT to run calculations at a fixed rate @@ -293,16 +293,25 @@ public class DFTProcessor implements Listener, getNextBuffer(); } - int samplesAvailable = mCurrentBuffer.length - mCurrentBufferPointer; - - while( remaining > 0 && samplesAvailable > 0 ) + /* If we don't have new samples to use, send the current frame with + * the remaining values as zero */ + if( mCurrentBuffer == null ) { - currentFrame[ currentFramePointer++ ] = - (float)mCurrentBuffer[ mCurrentBufferPointer++ ]; - - samplesAvailable--; - remaining--; - newFloatsToConsumeThisFrame--; + remaining = 0; + } + else + { + int samplesAvailable = mCurrentBuffer.length - mCurrentBufferPointer; + + while( remaining > 0 && samplesAvailable > 0 ) + { + currentFrame[ currentFramePointer++ ] = + (float)mCurrentBuffer[ mCurrentBufferPointer++ ]; + + samplesAvailable--; + remaining--; + newFloatsToConsumeThisFrame--; + } } } @@ -355,18 +364,25 @@ public class DFTProcessor implements Listener, { getNextBuffer(); } - - int samplesAvailable = mCurrentBuffer.length - mCurrentBufferPointer; - - if( samplesAvailable >= samplesToPurge ) + + if( mCurrentBuffer != null ) { - mCurrentBufferPointer += samplesToPurge; - samplesToPurge = 0; + int samplesAvailable = mCurrentBuffer.length - mCurrentBufferPointer; + + if( samplesAvailable >= samplesToPurge ) + { + mCurrentBufferPointer += samplesToPurge; + samplesToPurge = 0; + } + else + { + samplesToPurge -= samplesAvailable; + mCurrentBufferPointer = mCurrentBuffer.length; + } } else { - samplesToPurge -= samplesAvailable; - mCurrentBufferPointer = mCurrentBuffer.length; + samplesToPurge = 0; } } } diff --git a/src/spectrum/OverlayPanel.java b/src/spectrum/OverlayPanel.java index 1ffd2835..0d91979b 100644 --- a/src/spectrum/OverlayPanel.java +++ b/src/spectrum/OverlayPanel.java @@ -466,16 +466,20 @@ public class OverlayPanel extends JPanel double yAxis = 0; //Draw the system label and adjust the y-axis position + String system = channel.hasSystem() ? channel.getSystem().getName() : " "; + yAxis += drawLabel( graphics, - channel.getSystem().getName(), + system, this.getFont(), xAxis, yAxis, width ); //Draw the site label and adjust the y-axis position + String site = channel.hasSite() ? channel.getSite().getName() : " "; + yAxis += drawLabel( graphics, - channel.getSite().getName(), + site, this.getFont(), xAxis, yAxis, @@ -615,6 +619,7 @@ public class OverlayPanel extends JPanel switch( event.getEvent() ) { case CHANNEL_ADDED: + case CHANNEL_ENABLED: if( !mChannels.contains( channel ) ) { mChannels.add( channel ); @@ -630,7 +635,7 @@ public class OverlayPanel extends JPanel mChannels.remove( channel ); mVisibleChannels.remove( channel ); break; - case PROCESSING_STOPPED: + case CHANNEL_PROCESSING_STOPPED: if( channel.getChannelType() == ChannelType.TRAFFIC ) { mChannels.remove( channel ); diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index f31d621e..bc3da83b 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -41,12 +41,14 @@ import javax.swing.JSeparator; import javax.swing.JSplitPane; import javax.swing.SwingUtilities; +import module.decode.DecoderType; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; +import sample.SampleType; import sample.complex.ComplexBuffer; import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; @@ -54,13 +56,12 @@ import settings.ColorSettingMenuItem; import settings.Setting; import settings.SettingChangeListener; import settings.SettingsManager; -import source.Source.SampleType; import source.SourceException; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; -import source.tuner.frequency.FrequencyChangeEvent.Attribute; import source.tuner.Tuner; import source.tuner.TunerSelectionListener; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeEvent.Attribute; +import source.tuner.frequency.FrequencyChangeListener; import spectrum.OverlayPanel.ChannelDisplay; import spectrum.converter.ComplexDecibelConverter; import spectrum.converter.DFTResultsConverter; @@ -76,7 +77,6 @@ import controller.ResourceManager; import controller.channel.Channel; import controller.channel.ChannelEvent; import controller.channel.ChannelEventListener; -import decode.DecoderType; import dsp.filter.Window.WindowType; /** @@ -515,15 +515,15 @@ public class SpectralDisplayPanel extends JPanel ArrayList channels = mOverlayPanel.getChannelsAtFrequency( frequency ); - for( Channel channel: channels ) - { - JMenu menu = channel.getContextMenu(); - - if( menu != null ) - { - contextMenu.add( menu ); - } - } +// for( Channel channel: channels ) +// { +// JMenu menu = channel.getContextMenu(); +// +// if( menu != null ) +// { +// contextMenu.add( menu ); +// } +// } if( !channels.isEmpty() ) { @@ -537,7 +537,7 @@ public class SpectralDisplayPanel extends JPanel JMenu decoderMenu = new JMenu( "Add Decoder" ); - for( DecoderType type: DecoderType.getAvailableDecoders() ) + for( DecoderType type: DecoderType.getPrimaryDecoders() ) { decoderMenu.add( new DecoderItem( mResourceManager, frequency, diff --git a/src/util/ArcTangent.java b/src/util/ArcTangent.java index 528717c0..234621b5 100644 --- a/src/util/ArcTangent.java +++ b/src/util/ArcTangent.java @@ -40,7 +40,7 @@ */ package util; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class ArcTangent { @@ -124,7 +124,7 @@ public class ArcTangent * @param sample representing polar coordinates * @return - angle in radians */ - public static double getAngle( ComplexSample sample ) + public static double getAngle( Complex sample ) { return getAngle( ( (double) sample.inphase() ), ( (double) sample.quadrature() ) ); diff --git a/src/util/Oscillator.java b/src/util/Oscillator.java index e320f685..f003c4be 100644 --- a/src/util/Oscillator.java +++ b/src/util/Oscillator.java @@ -17,15 +17,15 @@ ******************************************************************************/ package util; -import sample.complex.ComplexSample; +import sample.complex.Complex; public class Oscillator { private double mFrequency; private double mSampleRate; - private ComplexSample mAnglePerSample; - private ComplexSample mCurrentAngle = new ComplexSample( 0.0f, -1.0f ); + private Complex mAnglePerSample; + private Complex mCurrentAngle = new Complex( 0.0f, -1.0f ); /** * Oscillator produces complex or float samples corresponding to a sine wave @@ -42,17 +42,15 @@ public class Oscillator update(); } - public ComplexSample getCurrentAngle() - { - return mCurrentAngle; - } - + /** + * Updates the internal values after a frequency or sample rate change + */ private void update() { float anglePerSample = (float)( 2.0d * Math.PI * mFrequency / mSampleRate ); - mAnglePerSample = ComplexSample.fromAngle( anglePerSample ); + mAnglePerSample = Complex.fromAngle( anglePerSample ); } /** @@ -76,37 +74,35 @@ public class Oscillator /** * Steps the current angle by the angle per sample amount */ - private void rotate() + public void rotate() { mCurrentAngle.multiply( mAnglePerSample ); mCurrentAngle.fastNormalize(); } + public float inphase() + { + return mCurrentAngle.inphase(); + } + + public float quadrature() + { + return mCurrentAngle.quadrature(); + } + /** * Get next complex sample */ - public ComplexSample nextComplex() + public Complex getComplex() { - rotate(); return mCurrentAngle.copy(); } /** * Get the next float sample */ - public float nextFloat() + public float getFloat() { - rotate(); return mCurrentAngle.real(); } - - public static void main( String[] args ) - { - Oscillator o = new Oscillator( 1200, 2400 ); - - for( int x = 0; x < 20; x++ ) - { - System.out.println( o.nextComplex().toString() ); - } - } }