diff --git a/.gitignore b/.gitignore
index 7be9efe9..7599d847 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
/*.class
/classes*
-/product*
-/bin/
+/product*
\ No newline at end of file
diff --git a/README.md b/README.md
index 22bcc018..8c1e9dc4 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,19 @@
-# sdrtrunk #
-A cross-platform java application for decoding, monitoring and recording trunked
-mobile and related radio protocols using Software Defined Radios (SDR).
+# sdrtrunk
+A cross-platform java application for decoding, monitoring, recording and streaming trunked mobile and related radio protocols using Software Defined Radios (SDR).
-# Documentation #
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted)
-* [User's Manual (Version 0.2.0)](https://github.com/DSheirer/sdrtrunk/wiki/UserManual)
-* [Wiki](https://github.com/DSheirer/sdrtrunk/wiki)
-
-# Support Group and Error Reporting #
-You can get help with setting up the sdrtrunk application or report any errors
-that you encounter while running the application in the sdrtrunk Google Group at:
-
+* [User's Manual](https://github.com/DSheirer/sdrtrunk/wiki/UserManual)
+* [Download](https://drive.google.com/folderview?id=0B7BHsssXUq8eOHBTNndCczZTd0k&usp=sharing)
* [Support](https://groups.google.com/forum/#!forum/sdrtrunk)
-# Updates #
-Subscribe to this group to receive updates about new releases and the ongoing
-application development effort.
-
-* [Subscribe for Updates](https://groups.google.com/forum/#!forum/sdrtrunk)
-

+
diff --git a/build/build.xml b/build/build.xml
index 4cdb3eaa..3667c7ff 100644
--- a/build/build.xml
+++ b/build/build.xml
@@ -7,115 +7,113 @@
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
-
+
+
+
-
+
-
+
-
-
-
+
-
+
-
-
-
+
+
+
-
+
-
-
+
+
-
-
-
+
+
+
-
-
-
-
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
\ No newline at end of file
diff --git a/scripts/run_sdrtrunk_windows.bat b/scripts/run_sdrtrunk_windows.bat
index 27c0b763..1206e039 100644
--- a/scripts/run_sdrtrunk_windows.bat
+++ b/scripts/run_sdrtrunk_windows.bat
@@ -1 +1 @@
-java -XX:+UseG1GC -cp "sdrtrunk.jar;*;libs/*;config/*;images/*" gui.SDRTrunk
\ No newline at end of file
+java -XX:+UseG1GC -cp "SDRTrunk.jar;*;libs/*;config/*;images/*" gui.SDRTrunk
\ No newline at end of file
diff --git a/src/audio/AudioChannelPanel.java b/src/audio/AudioChannelPanel.java
index d23c2024..298e6b18 100644
--- a/src/audio/AudioChannelPanel.java
+++ b/src/audio/AudioChannelPanel.java
@@ -48,11 +48,8 @@ public class AudioChannelPanel extends JPanel
private JLabel mFromLabel = new JLabel( "FROM:" );
private JLabel mFrom = new JLabel( "" );
private JLabel mFromAlias = new JLabel( "" );
-
- private enum AudioStatus { STARTED,STOPPED };
- private AudioStatus mAudioStatus = AudioStatus.STOPPED;
- private AudioMetadata mAudioMetadata;
+ private boolean mConfigured = false;
public AudioChannelPanel( SettingsManager settingsManager, AudioOutput audioOutput )
{
@@ -137,19 +134,15 @@ public class AudioChannelPanel extends JPanel
{
switch( audioEvent.getType() )
{
- case AUDIO_STARTED:
- synchronized( mAudioStatus )
- {
- mAudioStatus = AudioStatus.STARTED;
- updateLabels();
- }
- break;
case AUDIO_STOPPED:
- synchronized( mAudioStatus )
+ EventQueue.invokeLater( new Runnable()
{
- mAudioStatus = AudioStatus.STOPPED;
- resetLabels();
- }
+ @Override
+ public void run()
+ {
+ resetLabels();
+ }
+ } );
break;
case AUDIO_MUTED:
case AUDIO_UNMUTED:
@@ -173,55 +166,13 @@ public class AudioChannelPanel extends JPanel
*/
private void resetLabels()
{
- EventQueue.invokeLater( new Runnable()
- {
- @Override
- public void run()
- {
- mFrom.setText( "" );
- updateAlias( mFromAlias, null );
-
- mTo.setText( "" );
- updateAlias( mToAlias, null );
- }
- } );
- }
-
- private void updateLabels()
- {
- if( mAudioMetadata != null )
- {
- final Metadata from = mAudioMetadata.getMetadata( MetadataType.FROM );
-
- final Metadata to = mAudioMetadata.getMetadata( MetadataType.TO );
-
- EventQueue.invokeLater( new Runnable()
- {
- @Override
- public void run()
- {
- if( from != null )
- {
- mFrom.setText( from.getValue() );
- updateAlias( mFromAlias, from.getAlias() );
- }
- else
- {
- mFrom.setText( "-----" );
- }
-
- if( to != null )
- {
- mTo.setText( to.getValue() );
- updateAlias( mToAlias, to.getAlias() );
- }
- else
- {
- mTo.setText( "-----" );
- }
- }
- } );
- }
+ mFrom.setText( "" );
+ updateAlias( mFromAlias, null );
+
+ mTo.setText( "" );
+ updateAlias( mToAlias, null );
+
+ mConfigured = false;
}
/**
@@ -263,17 +214,32 @@ public class AudioChannelPanel extends JPanel
@Override
public void receive( AudioMetadata audioMetadata )
{
- mAudioMetadata = audioMetadata;
-
- if( audioMetadata.isUpdated() )
+ if( !mConfigured || audioMetadata.isUpdated() )
{
- synchronized( mAudioStatus )
+ final Metadata from = audioMetadata.getMetadata( MetadataType.FROM );
+
+ final Metadata to = audioMetadata.getMetadata( MetadataType.TO );
+
+ EventQueue.invokeLater( new Runnable()
{
- if( mAudioStatus == AudioStatus.STARTED )
+ @Override
+ public void run()
{
- updateLabels();
+ if( from != null )
+ {
+ mFrom.setText( from.getValue() );
+ updateAlias( mFromAlias, from.getAlias() );
+ }
+
+ if( to != null )
+ {
+ mTo.setText( to.getValue() );
+ updateAlias( mToAlias, to.getAlias() );
+ }
}
- }
+ } );
+
+ mConfigured = true;
}
}
}
diff --git a/src/audio/AudioManager.java b/src/audio/AudioManager.java
index b8e1e183..6fa1a9f7 100644
--- a/src/audio/AudioManager.java
+++ b/src/audio/AudioManager.java
@@ -2,7 +2,7 @@ package audio;
/*******************************************************************************
* SDR Trunk
- * Copyright (C) 2014-2016 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
@@ -17,12 +17,16 @@ package audio;
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
******************************************************************************/
+
import java.util.ArrayList;
+
import java.util.Collections;
import java.util.Comparator;
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;
@@ -33,12 +37,10 @@ import javax.sound.sampled.Mixer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import properties.SystemProperties;
import sample.Broadcaster;
import sample.Listener;
import source.mixer.MixerChannel;
import source.mixer.MixerChannelConfiguration;
-import source.mixer.MixerManager;
import audio.AudioEvent.Type;
import audio.output.AudioOutput;
import audio.output.MonoAudioOutput;
@@ -51,9 +53,7 @@ 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 String AUDIO_CHANNELS_PROPERTY = "audio.manager.channels";
- public static final String AUDIO_MIXER_PROPERTY = "audio.manager.mixer";
+ public static final int NO_SOURCE = 0;
public static final AudioEvent CONFIGURATION_CHANGE_STARTED =
new AudioEvent( Type.AUDIO_CONFIGURATION_CHANGE_STARTED, null );
@@ -62,90 +62,49 @@ public class AudioManager implements Listener, IAudioController
new AudioEvent( Type.AUDIO_CONFIGURATION_CHANGE_COMPLETE, null );
private LinkedTransferQueue mAudioPacketQueue = new LinkedTransferQueue<>();
- private Map mChannelConnectionMap = new HashMap<>();
- private List mAudioOutputConnections = new ArrayList<>();
- private AudioOutputConnection mLowestPriorityConnection;
- private int mAvailableConnectionCount;
+ private Map mChannelAssignments = new HashMap<>();
+ private int mLowestPrioritySource;
+ private List mAvailableAudioOutputs = new ArrayList<>();
private Map mAudioOutputMap = new HashMap<>();
+ private List mChannelNames = new ArrayList<>();
private Broadcaster mControllerBroadcaster = new Broadcaster<>();
private ThreadPoolManager mThreadPoolManager;
private ScheduledFuture> mProcessingTask;
- private MixerManager mMixerManager;
- private MixerChannelConfiguration mMixerChannelConfiguration;
+ private MixerChannelConfiguration mMixerChannel;
/**
* Processes all audio produced by the decoding channels and routes audio
* packets to any combination of outputs based on any alias audio routing
* options specified by the user.
*/
- public AudioManager( ThreadPoolManager manager, MixerManager mixerManager )
+ public AudioManager( ThreadPoolManager manager )
{
mThreadPoolManager = manager;
- mMixerManager = mixerManager;
- loadSettings();
- }
-
- private void loadSettings()
- {
- MixerChannelConfiguration configuration = null;
+ //TODO: save audio system preferences to the settings manager
- SystemProperties properties = SystemProperties.getInstance();
+ /* Use the system default mixer and mono channel as default startup */
+ Mixer mixer = AudioSystem.getMixer( null );
- Mixer defaultMixer = AudioSystem.getMixer( null );
-
- String mixer = properties.get( AUDIO_MIXER_PROPERTY, defaultMixer.getMixerInfo().getName() );
-
- String channels = properties.get( AUDIO_CHANNELS_PROPERTY, MixerChannel.MONO.name() );
-
- MixerChannelConfiguration[] mixerConfigurations = mMixerManager.getOutputMixers();
-
- for( MixerChannelConfiguration mixerConfig: mixerConfigurations )
+ if( mixer != null )
{
- if( mixerConfig.matches( mixer, channels ) )
- {
- configuration = mixerConfig;
- }
- }
-
- if( configuration == null )
- {
- configuration = getDefaultConfiguration();
- }
-
- try
- {
- setMixerChannelConfiguration( configuration );
- }
- catch( Exception e )
- {
- mLog.error( "Couldn't set stored audio mixer/channel configuration"
- + " - using default", e );
+ mMixerChannel = new MixerChannelConfiguration( mixer, MixerChannel.MONO );
try
{
- setMixerChannelConfiguration( getDefaultConfiguration() );
- }
- catch( Exception e2 )
+ setMixerChannelConfiguration( mMixerChannel );
+ }
+ catch ( AudioException e )
{
- mLog.error( "Couldn't set default audio mixer/channel "
- + "configuration - no audio will be available", e2 );
+ mLog.error( "Couldn't create an audio output device", e );
}
}
}
- private MixerChannelConfiguration getDefaultConfiguration()
- {
- /* Use the system default mixer and mono channel as default startup */
- Mixer defaultMixer = AudioSystem.getMixer( null );
-
- return new MixerChannelConfiguration( defaultMixer, MixerChannel.MONO );
- }
-
public void dispose()
{
if( mThreadPoolManager != null && mProcessingTask != null )
@@ -157,15 +116,6 @@ public class AudioManager implements Listener, IAudioController
mThreadPoolManager = null;
mProcessingTask = null;
-
- mChannelConnectionMap.clear();
-
- for( AudioOutputConnection connection: mAudioOutputConnections )
- {
- connection.dispose();
- }
-
- mAudioOutputConnections.clear();
}
@Override
@@ -174,44 +124,207 @@ public class AudioManager implements Listener, IAudioController
mAudioPacketQueue.add( packet );
}
- /**
- * Checks each audio channel assignment and disconnects any inactive connections
- */
- private void disconnectInactiveChannelAssignments()
+ public class AudioPacketProcessor implements Runnable
{
- boolean changed = false;
-
- for( AudioOutputConnection connection: mAudioOutputConnections )
+ @Override
+ public void run()
{
- if( connection.isInactive() &&
- mChannelConnectionMap.containsKey( connection.getSource() ) )
+ removeCompletedChannelAssignments();
+
+ if( mAudioPacketQueue != null )
{
- mChannelConnectionMap.remove( connection.getSource() );
- connection.disconnect();
- changed = true;
+ List packets = new ArrayList();
+
+ mAudioPacketQueue.drainTo( packets );
+
+ for( AudioPacket packet: packets )
+ {
+ /* Don't process any packet's marked as do not monitor */
+ if( !packet.getAudioMetadata().isDoNotMonitor() )
+ {
+ int source = packet.getAudioMetadata().getSource();
+
+ if( packet.getType() != AudioPacket.Type.END )
+ {
+ /* 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.getAudioMetadata().getSource(),
+ new AudioChannelAssignment( channel, packet.getAudioMetadata().getPriority() ) );
+
+ updateLowestPriorityAssignment();
+
+ channel.receive( packet );
+ }
+ /* Override the lowest priority assignment */
+ else
+ {
+ if( mLowestPrioritySource > -1 )
+ {
+ int priority = packet.getAudioMetadata().getPriority();
+
+ if( packet.getAudioMetadata().isSelected() ||
+ priority < mChannelAssignments
+ .get( mLowestPrioritySource ).getPriority() )
+ {
+ AudioChannelAssignment assignment =
+ mChannelAssignments.remove( mLowestPrioritySource );
+
+ if( assignment != null )
+ {
+ assignment.setPriority( priority );
+
+ int overrideSource = packet.getAudioMetadata().getSource();
+
+ mChannelAssignments.put( overrideSource, assignment );
+
+ mLowestPrioritySource = overrideSource;
+
+ assignment.getAudioOutput().receive( packet );
+ }
+ else
+ {
+ mLowestPrioritySource = -1;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
}
-
- if( changed )
+ }
+
+ /**
+ * Checks each audio channel assignment and removes any completed channels.
+ */
+ private void removeCompletedChannelAssignments()
+ {
+ if( !mChannelAssignments.isEmpty() )
{
- updateLowestPriorityAssignment();
+ 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 connection
+ * Identifies the lowest priority channel assignment ... the one that can
+ * be overridden with a higher priority call
*/
private void updateLowestPriorityAssignment()
{
- mLowestPriorityConnection = null;
+ int lowestPriority = -1;
- for( AudioOutputConnection connection: mAudioOutputConnections )
+ mLowestPrioritySource = -1;
+
+ for( int source: mChannelAssignments.keySet() )
{
- if( connection.isConnected() &&
- ( mLowestPriorityConnection == null ||
- mLowestPriorityConnection.getPriority() > connection.getPriority() ) )
+ AudioChannelAssignment assignment = mChannelAssignments.get( source );
+
+ if( assignment.getPriority() > lowestPriority )
{
- mLowestPriorityConnection = connection;
+ lowestPriority = assignment.getPriority();
+
+ mLowestPrioritySource = source;
+ }
+ }
+ }
+
+ public void endAllAudioAssignments()
+ {
+ for( AudioOutput output: mAudioOutputMap.values() )
+ {
+ output.receive( new AudioPacket( AudioPacket.Type.END, null ) );
+ }
+ }
+
+ public class AudioChannelAssignment implements Listener
+ {
+ private static final long CALL_END_DELAY = 2000; //2 seconds
+
+ private AudioOutput mAudioOutput;
+ private int mPriority;
+ private long mAudioStoppedTime = System.currentTimeMillis();
+
+ 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.reset();
+ 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:
+ case AUDIO_STOPPED:
+ case AUDIO_CONTINUATION:
+ mAudioStoppedTime = System.currentTimeMillis();
+ break;
+ default:
+ break;
}
}
}
@@ -229,29 +342,41 @@ public class AudioManager implements Listener, IAudioController
{
mThreadPoolManager.cancel( mProcessingTask );
}
+
+ endAllAudioAssignments();
- disposeCurrentConfiguration();
+ for( Entry e: mChannelAssignments.entrySet() )
+ {
+ e.getValue().getAudioOutput().dispose();
+ }
+ mChannelAssignments.clear();
+ for( AudioOutput output: mAvailableAudioOutputs )
+ {
+ output.dispose();
+ }
+ mAvailableAudioOutputs.clear();
+
+ mChannelNames.clear();
+ mAudioOutputMap.clear();
+
switch( entry.getMixerChannel() )
{
case MONO:
- AudioOutput mono = new MonoAudioOutput( mThreadPoolManager,
- entry.getMixer() );
- mAudioOutputConnections.add( new AudioOutputConnection( mono ) );
- mAvailableConnectionCount++;
+ AudioOutput mono = new MonoAudioOutput( entry.getMixer() );
+ mAvailableAudioOutputs.add( mono );
+ mChannelNames.add( mono.getChannelName() );
mAudioOutputMap.put( mono.getChannelName(), mono );
break;
case STEREO:
- AudioOutput left = new StereoAudioOutput( mThreadPoolManager,
- entry.getMixer(), MixerChannel.LEFT );
- mAudioOutputConnections.add( new AudioOutputConnection( left ) );
- mAvailableConnectionCount++;
+ AudioOutput left = new StereoAudioOutput( entry.getMixer(), MixerChannel.LEFT );
+ mAvailableAudioOutputs.add( left );
+ mChannelNames.add( left.getChannelName() );
mAudioOutputMap.put( left.getChannelName(), left );
- AudioOutput right = new StereoAudioOutput( mThreadPoolManager,
- entry.getMixer(), MixerChannel.RIGHT );
- mAudioOutputConnections.add( new AudioOutputConnection( right ) );
- mAvailableConnectionCount++;
+ AudioOutput right = new StereoAudioOutput( entry.getMixer(), MixerChannel.RIGHT );
+ mAvailableAudioOutputs.add( right );
+ mChannelNames.add( right.getChannelName() );
mAudioOutputMap.put( right.getChannelName(), right );
break;
default:
@@ -264,12 +389,6 @@ public class AudioManager implements Listener, IAudioController
TimeUnit.MILLISECONDS );
mControllerBroadcaster.broadcast( CONFIGURATION_CHANGE_COMPLETE );
-
- SystemProperties properties = SystemProperties.getInstance();
-
- properties.set( AUDIO_MIXER_PROPERTY, entry.getMixer().getMixerInfo().getName() );
- properties.set( AUDIO_CHANNELS_PROPERTY, entry.getMixerChannel().name() );
- properties.save();
}
}
@@ -296,33 +415,10 @@ public class AudioManager implements Listener, IAudioController
mLowestPriorityConnection = null;
}
- /**
- * Clears all channel assignments and terminates all audio outputs in
- * preparation for complete shutdown or change to another mixer/channel
- * configuration
- */
- private void disposeCurrentConfiguration()
- {
- mChannelConnectionMap.clear();
-
- for( AudioOutputConnection connection : mAudioOutputConnections )
- {
- connection.dispose();
- }
-
- mAvailableConnectionCount = 0;
-
- mAudioOutputConnections.clear();
-
- mAudioOutputMap.clear();
-
- mLowestPriorityConnection = null;
- }
-
@Override
public MixerChannelConfiguration getMixerChannelConfiguration() throws AudioException
{
- return mMixerChannelConfiguration;
+ return mMixerChannel;
}
@Override
@@ -353,244 +449,4 @@ public class AudioManager implements Listener, IAudioController
{
mControllerBroadcaster.removeListener( listener );
}
-
- /**
- * Returns an audio output connection for the packet if one is
- * available, or overrides an existing lower priority connection.
- * Returns null if no connection is available for the audio packet.
- *
- * @param packet from a source
- * @return an audio output connection or null
- */
- private AudioOutputConnection getConnection( AudioPacket packet )
- {
- int source = packet.getAudioMetadata().getSource();
-
- //Use an existing connection
- if( mChannelConnectionMap.containsKey( source ) )
- {
- return mChannelConnectionMap.get( source );
- }
-
- //Connect to and use an available connection
- if( mAvailableConnectionCount > 0 )
- {
- for( AudioOutputConnection connection: mAudioOutputConnections )
- {
- if( connection.isDisconnected() )
- {
- connection.connect( source );
- mChannelConnectionMap.put( source, connection );
- return connection;
- }
- }
- }
- //Preempt an existing connection and connect it to this higher priority source
- else //Check for a channel priority override
- {
- int priority = packet.getAudioMetadata().getPriority();
-
- if( mLowestPriorityConnection != null &&
- priority < mLowestPriorityConnection.getPriority() )
- {
- AudioOutputConnection connection = mLowestPriorityConnection;
-
- mChannelConnectionMap.remove( connection.getSource() );
-
- connection.connect( source );
-
- mChannelConnectionMap.put( source, connection );
-
- updateLowestPriorityAssignment();
-
- return connection;
- }
- }
-
- return null;
- }
-
- public class AudioPacketProcessor implements Runnable
- {
- @Override
- public void run()
- {
- try
- {
- disconnectInactiveChannelAssignments();
-
- if( mAudioPacketQueue != null )
- {
- List packets = new ArrayList();
-
- mAudioPacketQueue.drainTo( packets );
-
- for( AudioPacket packet: packets )
- {
- /* Don't process any packet's marked as do not monitor */
- if( !packet.getAudioMetadata().isDoNotMonitor() &&
- packet.getType() == AudioPacket.Type.AUDIO )
- {
- AudioOutputConnection connection = getConnection( packet );
-
- if( connection != null )
- {
- connection.receive( packet );
- }
- }
- }
- }
- }
- catch( Exception e )
- {
- mLog.error( "Encountered error while processing audio packets", e );
- }
- }
- }
-
- /**
- * Audio output connection manages a connection between a source and an audio
- * output and maintains current state information about the audio activity
- * received form the source.
- */
- public class AudioOutputConnection implements Listener
- {
- private static final long CALL_END_DELAY = 2000; //2 seconds
- private static final int DISCONNECTED = -1;
-
- private AudioOutput mAudioOutput;
- private int mPriority = 0;
- private long mLastActivity = System.currentTimeMillis();
- private int mSource = DISCONNECTED;
-
- public AudioOutputConnection( AudioOutput audioOutput )
- {
- mAudioOutput = audioOutput;
- mAudioOutput.addAudioEventListener( this );
- }
-
- public void receive( AudioPacket packet )
- {
- if( packet.hasAudioMetadata() &&
- packet.getAudioMetadata().getSource() == mSource )
- {
- int priority = packet.getAudioMetadata().getPriority();
-
- if( mPriority != priority )
- {
- mPriority = priority;
- updateLowestPriorityAssignment();
- }
-
- updateTimestamp();
-
- if( mAudioOutput != null )
- {
- mAudioOutput.receive( packet );
- }
- }
- else
- {
- if( packet.hasAudioMetadata() )
- {
- mLog.error( "Received audio packet from channel source [" +
- packet.getAudioMetadata().getSource() +
- "] however this assignment is currently connected "
- + "to channel source [" + mSource + "]" );
- }
- else
- {
- mLog.error( "Received audio packet with no metadata - "
- + "cannot route audio packet" );
- }
- }
- }
-
- private void updateTimestamp()
- {
- mLastActivity = System.currentTimeMillis();
- }
-
- /**
- * Terminates the audio output and prepares this connection for disposal
- */
- public void dispose()
- {
- mAudioOutput.dispose();
- mAudioOutput = null;
- }
-
- /**
- * Indicates if this assignment is currently disconnected from a channel source
- */
- public boolean isDisconnected()
- {
- return mSource == DISCONNECTED;
- }
-
- /**
- * Indicates if this assignment is currently connected to a channel source
- */
- public boolean isConnected()
- {
- return !isDisconnected();
- }
-
- /**
- * Connects this assignment to the indicated source so that audio
- * packets from this source can be sent to the audio output
- */
- public void connect( int source )
- {
- mSource = source;
- updateTimestamp();
- }
-
- /**
- * Currently connected source or -1 if disconnected
- */
- public int getSource()
- {
- return mSource;
- }
-
- /**
- * Disconnects this assignment from the source and prevents any audio
- * from being routed to the audio output until another source is assigned
- */
- public void disconnect()
- {
- mSource = DISCONNECTED;
- }
-
- /**
- * Indicates if audio output is current inactive, meaning that the
- * audio output hasn't recently processed any audio packets.
- */
- public boolean isInactive()
- {
- return mLastActivity != Long.MAX_VALUE &&
- mLastActivity + CALL_END_DELAY < System.currentTimeMillis();
- }
-
- public int getPriority()
- {
- return mPriority;
- }
-
- @Override
- public void receive( AudioEvent event )
- {
- switch( event.getType() )
- {
- case AUDIO_STARTED:
- case AUDIO_STOPPED:
- case AUDIO_CONTINUATION:
- updateTimestamp();
- break;
- default:
- break;
- }
- }
- }
}
diff --git a/src/audio/output/AudioOutput.java b/src/audio/output/AudioOutput.java
index 74b2e822..d9506472 100644
--- a/src/audio/output/AudioOutput.java
+++ b/src/audio/output/AudioOutput.java
@@ -1,9 +1,10 @@
package audio.output;
import java.util.concurrent.LinkedTransferQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.sound.sampled.FloatControl;
-import javax.sound.sampled.LineListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -11,26 +12,23 @@ import org.slf4j.LoggerFactory;
import sample.Broadcaster;
import sample.Listener;
import audio.AudioEvent;
+import audio.AudioEvent.Type;
import audio.AudioPacket;
import audio.metadata.AudioMetadata;
-import controller.ThreadPoolManager;
-public abstract class AudioOutput implements Listener, LineListener
+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<>();
protected LinkedTransferQueue mBuffer = new LinkedTransferQueue<>();
- protected ThreadPoolManager mThreadPoolManager;
+ protected ScheduledExecutorService mExecutorService;
protected boolean mCanProcessAudio;
protected long mLastActivity;
- public AudioOutput( ThreadPoolManager threadPoolManager )
- {
- mThreadPoolManager = threadPoolManager;
- }
-
public void reset()
{
broadcast( new AudioEvent( AudioEvent.Type.AUDIO_STOPPED,
@@ -43,11 +41,14 @@ public abstract class AudioOutput implements Listener, LineListener
mBuffer.clear();
+ if( mExecutorService != null )
+ {
+ mExecutorService.shutdown();
+ }
+
mAudioEventBroadcaster.dispose();
mAudioEventBroadcaster = null;
mAudioMetadataListener = null;
-
- mThreadPoolManager = null;
}
public abstract String getChannelName();
@@ -71,6 +72,19 @@ public abstract class AudioOutput implements Listener, LineListener
mAudioEventBroadcaster.broadcast( audioEvent );
}
+ public void setSquelched( boolean squelched )
+ {
+ mSquelched.set( squelched );
+
+ broadcast( new AudioEvent( squelched ? Type.AUDIO_SQUELCHED :
+ Type.AUDIO_UNSQUELCHED, getChannelName() ) );
+ }
+
+ public boolean isSquelched()
+ {
+ return mSquelched.get();
+ }
+
/**
* Registers a single listener to receive audio start and audio stop events
*/
diff --git a/src/audio/output/MonoAudioOutput.java b/src/audio/output/MonoAudioOutput.java
index ec1f7816..76bf2324 100644
--- a/src/audio/output/MonoAudioOutput.java
+++ b/src/audio/output/MonoAudioOutput.java
@@ -5,14 +5,13 @@ import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.sound.sampled.BooleanControl;
import javax.sound.sampled.Control;
import javax.sound.sampled.FloatControl;
-import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;
@@ -25,8 +24,8 @@ import audio.AudioEvent;
import audio.AudioFormats;
import audio.AudioPacket;
import audio.AudioPacket.Type;
-import controller.ThreadPoolManager;
-import controller.ThreadPoolManager.ThreadType;
+import audio.output.StereoAudioOutput.QueueProcessor;
+import controller.NamingThreadFactory;
/**
* Mono Audio output with automatic flow control based on the availability of
@@ -51,14 +50,14 @@ public class MonoAudioOutput extends AudioOutput
private SourceDataLine mOutput;
private FloatControl mGainControl;
private BooleanControl mMuteControl;
+
private AudioEvent mAudioStartEvent;
private AudioEvent mAudioStopEvent;
private AudioEvent mAudioContinuationEvent;
- private ScheduledFuture> mProcessorTask;
- public MonoAudioOutput( ThreadPoolManager threadPoolManager, Mixer mixer )
+ public MonoAudioOutput( Mixer mixer )
{
- super( threadPoolManager );
+ super();
mAudioStartEvent = new AudioEvent( AudioEvent.Type.AUDIO_STARTED,
getChannelName() );
@@ -75,8 +74,15 @@ public class MonoAudioOutput extends AudioOutput
if( mOutput != null )
{
mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO, BUFFER_SIZE );
- mOutput.addLineListener( this );
+
mCanProcessAudio = true;
+
+ mExecutorService = Executors.newSingleThreadScheduledExecutor(
+ new NamingThreadFactory( "audio (mono) output" ) );
+
+ /* Run the queue processor task every 40 milliseconds */
+ mExecutorService.scheduleAtFixedRate( new QueueProcessor(),
+ 0, 40, TimeUnit.MILLISECONDS );
}
}
catch ( LineUnavailableException e )
@@ -109,10 +115,12 @@ public class MonoAudioOutput extends AudioOutput
mixer.getMixerInfo().getName() + "]" );
}
+ mExecutorService = Executors.newSingleThreadScheduledExecutor(
+ new NamingThreadFactory( "audio (mono) output" ) );
+
/* Run the queue processor task every 40 milliseconds */
- mProcessorTask = mThreadPoolManager.scheduleFixedRate(
- ThreadType.AUDIO_PROCESSING, new QueueProcessor(),
- 40, TimeUnit.MILLISECONDS );
+ mExecutorService.scheduleAtFixedRate( new QueueProcessor(),
+ 0, 40, TimeUnit.MILLISECONDS );
}
}
@@ -124,108 +132,102 @@ public class MonoAudioOutput extends AudioOutput
public void dispose()
{
- if( mProcessorTask != null && mThreadPoolManager != null )
- {
- mThreadPoolManager.cancel( mProcessorTask );
- }
-
- mProcessorTask = null;
-
super.dispose();
if( mOutput != null )
{
mOutput.close();
}
-
- mOutput = null;
- mGainControl = null;
- mMuteControl = null;
}
public class QueueProcessor implements Runnable
{
private AtomicBoolean mProcessing = new AtomicBoolean();
+ public QueueProcessor()
+ {
+ }
+
@Override
public void run()
{
- try
+ /* The mProcessing flag ensures that only one instance of the
+ * processor can run at any given time */
+ if( mProcessing.compareAndSet( false, true ) )
{
- /* The mProcessing flag ensures that only one instance of the
- * processor can run at any given time */
- if( mProcessing.compareAndSet( false, true ) )
+ List packets = new ArrayList();
+
+ mBuffer.drainTo( packets );
+
+ if( packets.size() > 0 )
{
- List packets = new ArrayList();
+ broadcast( mAudioContinuationEvent );
- mBuffer.drainTo( packets );
-
- if( packets.size() > 0 )
+ for( AudioPacket packet: packets )
{
- for( AudioPacket packet: packets )
+ if( mCanProcessAudio )
{
- broadcast( mAudioContinuationEvent );
-
- if( mCanProcessAudio )
+ if( isSquelched() )
{
- if( packet.getType() == Type.AUDIO )
+ checkStop();
+ }
+ else if( packet.getType() == Type.AUDIO )
+ {
+ float[] samples = packet.getAudioBuffer().getSamples();
+
+ /* Little-endian byte buffer */
+ ByteBuffer buffer =
+ ByteBuffer.allocate( samples.length * 2 )
+ .order( ByteOrder.LITTLE_ENDIAN );
+
+ ShortBuffer shortBuffer = buffer.asShortBuffer();
+
+ for( float sample: samples )
{
- float[] samples = packet.getAudioBuffer().getSamples();
-
- /* 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 ) );
- }
-
- //This is a blocking write
- mOutput.write( buffer.array(), 0, buffer.array().length );
-
- checkStart();
-
- broadcast( packet.getAudioMetadata() );
-
- mLastActivity = System.currentTimeMillis();
+ shortBuffer.put( (short)( sample * Short.MAX_VALUE ) );
}
+
+ mOutput.write( buffer.array(), 0,
+ buffer.array().length );
+
+ checkStart();
+
+ broadcast( packet.getAudioMetadata() );
+
+ mLastActivity = System.currentTimeMillis();
}
}
}
- else
- {
- checkStop();
- }
-
- mProcessing.set( false );
}
- }
- catch( Exception e )
- {
- mLog.error( "Error while processing audio buffers", e );
+ else
+ {
+ checkStop();
+ }
+
+ mProcessing.set( false );
}
}
private void checkStart()
{
- if( mCanProcessAudio &&
+ 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 )
+ ( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) )
{
mOutput.drain();
mOutput.stop();
+
+ mAudioEventBroadcaster.broadcast( mAudioStopEvent );
}
}
}
@@ -264,19 +266,4 @@ public class MonoAudioOutput extends AudioOutput
{
return mGainControl != null;
}
-
- @Override
- public void update( LineEvent event )
- {
- LineEvent.Type type = event.getType();
-
- if( type == LineEvent.Type.START )
- {
- mAudioEventBroadcaster.broadcast( mAudioStartEvent );
- }
- else if( type == LineEvent.Type.STOP )
- {
- mAudioEventBroadcaster.broadcast( mAudioStopEvent );
- }
- }
}
diff --git a/src/audio/output/StereoAudioOutput.java b/src/audio/output/StereoAudioOutput.java
index 1c3d1683..81892ca5 100644
--- a/src/audio/output/StereoAudioOutput.java
+++ b/src/audio/output/StereoAudioOutput.java
@@ -5,14 +5,13 @@ import java.nio.ByteOrder;
import java.nio.ShortBuffer;
import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.sound.sampled.BooleanControl;
import javax.sound.sampled.Control;
import javax.sound.sampled.FloatControl;
-import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;
@@ -25,8 +24,7 @@ import audio.AudioEvent;
import audio.AudioFormats;
import audio.AudioPacket;
import audio.AudioPacket.Type;
-import controller.ThreadPoolManager;
-import controller.ThreadPoolManager.ThreadType;
+import controller.NamingThreadFactory;
/**
* Mono Audio output in stereo format with automatic flow control based on the
@@ -41,9 +39,9 @@ public class StereoAudioOutput extends AudioOutput
private final static int BUFFER_SIZE = 96000;
/* The queue processor will run every 20 milliseconds checking for inbound
- * audio and automatically start or stop audio playback. We set the
+ * audio and automatically starting or stopping audio playback. We set the
* data line available space threshold to start when we're 20 ms away from
- * being full and the stop threshold when 20 ms away from being empty */
+ * being full, and the stop threshold when 20 ms away from being empty */
private final static int SAMPLE_SIZE_40_MS = BUFFER_SIZE / 25;
private final static int START_THRESHOLD = SAMPLE_SIZE_40_MS;
private final static int STOP_THRESHOLD = BUFFER_SIZE - SAMPLE_SIZE_40_MS;
@@ -51,16 +49,16 @@ public class StereoAudioOutput extends AudioOutput
private SourceDataLine mOutput;
private FloatControl mGainControl;
private BooleanControl mMuteControl;
+
private AudioEvent mAudioStartEvent;
private AudioEvent mAudioStopEvent;
private AudioEvent mAudioContinuationEvent;
- private MixerChannel mMixerChannel;
- private ScheduledFuture> mProcessorTask;
- public StereoAudioOutput( ThreadPoolManager threadPoolManager,
- Mixer mixer, MixerChannel channel )
+ private MixerChannel mMixerChannel;
+
+ public StereoAudioOutput( Mixer mixer, MixerChannel channel )
{
- super( threadPoolManager );
+ super();
assert( channel == MixerChannel.LEFT || channel == MixerChannel.RIGHT );
@@ -81,7 +79,6 @@ public class StereoAudioOutput extends AudioOutput
if( mOutput != null )
{
mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO, BUFFER_SIZE );
- mOutput.addLineListener( this );
mCanProcessAudio = true;
}
}
@@ -116,10 +113,12 @@ public class StereoAudioOutput extends AudioOutput
mixer.getMixerInfo().getName() + " | " + channel.name() + "]" );
}
+ mExecutorService = Executors.newSingleThreadScheduledExecutor(
+ new NamingThreadFactory( "audio (stereo) output" ) );
+
/* Run the queue processor task every 40 milliseconds */
- mProcessorTask = mThreadPoolManager.scheduleFixedRate(
- ThreadType.AUDIO_PROCESSING, new QueueProcessor(),
- 40, TimeUnit.MILLISECONDS );
+ mExecutorService.scheduleAtFixedRate( new QueueProcessor(),
+ 0, 40, TimeUnit.MILLISECONDS );
}
}
@@ -131,30 +130,20 @@ public class StereoAudioOutput extends AudioOutput
public void dispose()
{
- if( mProcessorTask != null && mThreadPoolManager != null )
- {
- mThreadPoolManager.cancel( mProcessorTask );
- }
-
- mProcessorTask = null;
-
super.dispose();
if( mOutput != null )
{
mOutput.close();
}
-
- mOutput = null;
- mGainControl = null;
- mMuteControl = null;
}
private ByteBuffer convert( float[] samples )
{
/* Little-endian byte buffer */
- ByteBuffer buffer = ByteBuffer.allocate( samples.length * 4 )
- .order( ByteOrder.LITTLE_ENDIAN );
+ ByteBuffer buffer =
+ ByteBuffer.allocate( samples.length * 4 )
+ .order( ByteOrder.LITTLE_ENDIAN );
ShortBuffer shortBuffer = buffer.asShortBuffer();
@@ -189,72 +178,72 @@ public class StereoAudioOutput extends AudioOutput
@Override
public void run()
{
- try
+ /* The mProcessing flag ensures that only one instance of the
+ * processor can run at any given time */
+ if( mProcessing.compareAndSet( false, true ) )
{
- /* The mProcessing flag ensures that only one instance of the
- * processor can run at any given time */
- if( mProcessing.compareAndSet( false, true ) )
+ List packets = new ArrayList();
+
+ mBuffer.drainTo( packets );
+
+ if( packets.size() > 0 )
{
- List packets = new ArrayList();
+ broadcast( mAudioContinuationEvent );
- mBuffer.drainTo( packets );
-
- if( packets.size() > 0 )
+ for( AudioPacket packet: packets )
{
-
- for( AudioPacket packet: packets )
+ if( mCanProcessAudio )
{
- broadcast( mAudioContinuationEvent );
-
- if( mCanProcessAudio )
+ if( isSquelched() )
{
- if( packet.getType() == Type.AUDIO )
- {
- ByteBuffer buffer = convert(
- packet.getAudioBuffer().getSamples() );
-
- mOutput.write( buffer.array(), 0,
- buffer.array().length );
-
- checkStart();
-
- broadcast( packet.getAudioMetadata() );
-
- mLastActivity = System.currentTimeMillis();
- }
+ checkStop();
+ }
+ else if( packet.getType() == Type.AUDIO )
+ {
+ ByteBuffer buffer = convert(
+ packet.getAudioBuffer().getSamples() );
+
+ mOutput.write( buffer.array(), 0,
+ buffer.array().length );
+
+ checkStart();
+
+ broadcast( packet.getAudioMetadata() );
+
+ mLastActivity = System.currentTimeMillis();
}
}
}
- else
- {
- checkStop();
- }
-
- mProcessing.set( false );
}
- }
- catch( Exception e )
- {
- mLog.error( "Error while processing audio buffers", e );
+ else
+ {
+ checkStop();
+ }
+
+ mProcessing.set( false );
}
}
private void checkStart()
{
- if( mCanProcessAudio &&
+ 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 )
+ ( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) )
{
mOutput.drain();
mOutput.stop();
+
+ mAudioEventBroadcaster.broadcast( mAudioStopEvent );
}
}
}
@@ -293,20 +282,4 @@ public class StereoAudioOutput extends AudioOutput
{
return mGainControl != null;
}
-
-
- @Override
- public void update( LineEvent event )
- {
- LineEvent.Type type = event.getType();
-
- if( type == LineEvent.Type.START )
- {
- mAudioEventBroadcaster.broadcast( mAudioStartEvent );
- }
- else if( type == LineEvent.Type.STOP )
- {
- mAudioEventBroadcaster.broadcast( mAudioStopEvent );
- }
- }
}
diff --git a/src/bits/MessageFramer.java b/src/bits/MessageFramer.java
index cd41737c..99d07888 100644
--- a/src/bits/MessageFramer.java
+++ b/src/bits/MessageFramer.java
@@ -58,16 +58,6 @@ public class MessageFramer implements Listener,
mMessageLength = messageLength;
}
- public void reset()
- {
- for( MessageAssembler assembler: mMessageAssemblers )
- {
- assembler.dispose();
- }
-
- mMessageAssemblers.clear();
- }
-
public void dispose()
{
mBroadcaster.dispose();
diff --git a/src/controller/ThreadPoolManager.java b/src/controller/ThreadPoolManager.java
index 0ca9a00b..c5527970 100644
--- a/src/controller/ThreadPoolManager.java
+++ b/src/controller/ThreadPoolManager.java
@@ -36,20 +36,24 @@ public class ThreadPoolManager
private HashMap,ThreadType> mTasks =
new HashMap,ThreadType>();
- private ScheduledExecutorService mExecutor =
- Executors.newScheduledThreadPool( THREAD_POOL_SIZE,
- new NamingThreadFactory( "sdrtrunk" ) );
+ private ScheduledExecutorService mExecutor;
public ThreadPoolManager()
{
}
-
+
public ScheduledFuture> scheduleFixedRate( ThreadType type,
Runnable command,
long period,
TimeUnit unit )
throws RejectedExecutionException
{
+ if( mExecutor == null )
+ {
+ mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE,
+ new NamingThreadFactory( "sdrtrunk" ) );
+ }
+
ScheduledFuture> task =
mExecutor.scheduleAtFixedRate( command, 0, period, unit );
@@ -61,16 +65,20 @@ public class ThreadPoolManager
public void scheduleOnce( Runnable command, long delay, TimeUnit unit )
throws RejectedExecutionException
{
+ if( mExecutor == null )
+ {
+ mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE,
+ new NamingThreadFactory( "sdrtrunk" ) );
+ }
+
mExecutor.schedule( command, delay, unit );
}
- public boolean cancel( ScheduledFuture> task )
+ public void cancel( ScheduledFuture> task )
{
- boolean success = task.cancel( true );
+ task.cancel( true );
mTasks.remove( task );
-
- return success;
}
public int getTaskCount( ThreadType type )
diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java
index a81ebe6e..db22429c 100644
--- a/src/gui/SDRTrunk.java
+++ b/src/gui/SDRTrunk.java
@@ -140,7 +140,7 @@ public class SDRTrunk
AliasActionManager aliasActionManager = new AliasActionManager( threadPoolManager );
channelProcessingManager.addMessageListener( aliasActionManager );
- AudioManager audioManager = new AudioManager( threadPoolManager, sourceManager.getMixerManager() );
+ AudioManager audioManager = new AudioManager( threadPoolManager );
channelProcessingManager.addAudioPacketListener( audioManager );
MapService mapService = new MapService( mSettingsManager );
@@ -471,7 +471,7 @@ public class SDRTrunk
if( version != null )
{
- sb.append( " v" );
+ sb.append( " V" );
sb.append( version );
}
}
diff --git a/src/message/Message.java b/src/message/Message.java
index 9b6b7a17..ac1ab775 100644
--- a/src/message/Message.java
+++ b/src/message/Message.java
@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
- * Copyright (C) 2014-2016 Dennis Sheirer
+ * 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
@@ -17,94 +17,52 @@
******************************************************************************/
package message;
+
import java.util.Collections;
+import java.util.Date;
import java.util.List;
import map.Plottable;
+import module.decode.event.MessageDetailsProvider;
import alias.Alias;
-public abstract class Message
+public abstract class Message implements MessageDetailsProvider
{
protected long mTimeReceived;
+ protected MessageType mType;
public Message()
{
- mTimeReceived = System.currentTimeMillis();
+ this( MessageType.UN_KNWN );
}
+ public Message( MessageType type )
+ {
+ mTimeReceived = System.currentTimeMillis();
+ mType = type;
+ }
+
+ public abstract Plottable getPlottable();
+
+ public abstract String toString();
+
+ public abstract boolean isValid();
+
public long getTimeReceived()
{
return mTimeReceived;
}
-
- /**
- * Map plottable
- */
- public Plottable getPlottable()
+
+ public Date getDateReceived()
{
-//TODO: move this to an IPlottable interface that only gets implemented as needed
- return null;
+ return new Date( mTimeReceived );
+ }
+
+ public MessageType getType()
+ {
+ return mType;
}
- /**
- * Decoded textual representation of the message
- */
- public abstract String toString();
-
- /**
- * Indicates if the message is valid and has passed crc/integrity checks
- */
- public abstract boolean isValid();
-
- /**
- * Status of the CRC check of the message
- */
- public abstract String getErrorStatus();
-
- /**
- * Parsed Message
- * @return
- */
- public abstract String getMessage();
-
- /**
- * Raw ( 0 & 1 ) message bits
- */
- public abstract String getBinaryMessage();
-
-
- /**
- * Decoded protocol
- */
- public abstract String getProtocol();
-
- /**
- * Event - call, data, idle, etc.
- */
- public abstract String getEventType();
-
- /**
- * Formatted from identifier
- */
- public abstract String getFromID();
-
- /**
- * From identifier alias (from AliasManager)
- */
- public abstract Alias getFromIDAlias();
-
- /**
- * Formatted to identifier
- */
- public abstract String getToID();
-
- /**
- * To identifier alias (from AliasManager)
- * @return
- */
- public abstract Alias getToIDAlias();
-
-
/**
* Provides a listing of aliases contained in the message.
*/
diff --git a/src/module/decode/event/CallEvent.java b/src/module/decode/event/CallEvent.java
index 28990ad9..000cb27c 100644
--- a/src/module/decode/event/CallEvent.java
+++ b/src/module/decode/event/CallEvent.java
@@ -310,11 +310,6 @@ public abstract class CallEvent implements Comparable
@Override
public int compareTo( CallEvent other )
{
- if( other != null && other == this )
- {
- return 0;
- }
-
return Long.compare( mEventStart, other.getEventStartTime() );
}
}
diff --git a/src/module/decode/event/MessageActivityModel.java b/src/module/decode/event/MessageActivityModel.java
index 390c4c59..65850196 100644
--- a/src/module/decode/event/MessageActivityModel.java
+++ b/src/module/decode/event/MessageActivityModel.java
@@ -19,7 +19,6 @@ package module.decode.event;
import java.awt.EventQueue;
import java.text.SimpleDateFormat;
-import java.util.Date;
import java.util.LinkedList;
import javax.swing.table.AbstractTableModel;
@@ -175,7 +174,7 @@ public class MessageActivityModel extends AbstractTableModel
switch( columnIndex )
{
case sTIME:
- return mSDFTime.format( new Date( message.getTimeReceived() ) );
+ return mSDFTime.format( message.getDateReceived() );
case sPROTOCOL:
return message.getProtocol();
case sERROR_STATUS:
diff --git a/src/module/decode/event/MessageDetailsProvider.java b/src/module/decode/event/MessageDetailsProvider.java
new file mode 100644
index 00000000..bfce825e
--- /dev/null
+++ b/src/module/decode/event/MessageDetailsProvider.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * 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.event;
+
+import alias.Alias;
+
+/**
+ * Getter methods to support the Message Activity Table/Model
+ */
+public interface MessageDetailsProvider
+{
+ /**
+ * Indicates if the message is valid and has passed crc/integrity checks
+ */
+ public abstract boolean isValid();
+
+ /**
+ * Status of the CRC check of the message
+ */
+ public abstract String getErrorStatus();
+
+ /**
+ * Parsed Message
+ * @return
+ */
+ public abstract String getMessage();
+
+ /**
+ * Raw ( 0 & 1 ) message bits
+ */
+ public abstract String getBinaryMessage();
+
+
+ /**
+ * Decoded protocol
+ */
+ public abstract String getProtocol();
+
+ /**
+ * Event - call, data, idle, etc.
+ */
+ public abstract String getEventType();
+
+ /**
+ * Formatted from identifier
+ */
+ public abstract String getFromID();
+
+ /**
+ * From identifier alias (from AliasManager)
+ */
+ public abstract Alias getFromIDAlias();
+
+ /**
+ * Formatted to identifier
+ */
+ public abstract String getToID();
+
+ /**
+ * To identifier alias (from AliasManager)
+ * @return
+ */
+ public abstract Alias getToIDAlias();
+
+}
diff --git a/src/module/decode/ltrnet/LTRCallEvent.java b/src/module/decode/ltrnet/LTRCallEvent.java
index 3bb553ca..21a46afb 100644
--- a/src/module/decode/ltrnet/LTRCallEvent.java
+++ b/src/module/decode/ltrnet/LTRCallEvent.java
@@ -51,16 +51,17 @@ public class LTRCallEvent extends CallEvent
*/
public boolean isMatchingTalkgroup( LTRNetMessage message )
{
- return isMatchingTalkgroup( message.getTalkgroupID() );
+ String talkgroup = message.getTalkgroupID();
+
+ if( talkgroup != null && getToID() != null &&
+ talkgroup.contentEquals( getToID() ) )
+ {
+ return true;
+ }
+
+ return false;
}
- public boolean isMatchingTalkgroup( String talkgroup )
- {
- return talkgroup != null &&
- getToID() != null &&
- talkgroup.contentEquals( getToID() );
- }
-
private Alias getAlias( String talkgroup )
{
if( hasAliasList() )
diff --git a/src/module/decode/ltrnet/LTRNetDecoder.java b/src/module/decode/ltrnet/LTRNetDecoder.java
index f5c1d3ca..ff12edca 100644
--- a/src/module/decode/ltrnet/LTRNetDecoder.java
+++ b/src/module/decode/ltrnet/LTRNetDecoder.java
@@ -117,7 +117,6 @@ public class LTRNetDecoder extends Decoder
@Override
public void reset()
{
- mLTRMessageFramer.reset();
}
@Override
diff --git a/src/module/decode/ltrstandard/LTRStandardDecoder.java b/src/module/decode/ltrstandard/LTRStandardDecoder.java
index 228d68f5..365bd74c 100644
--- a/src/module/decode/ltrstandard/LTRStandardDecoder.java
+++ b/src/module/decode/ltrstandard/LTRStandardDecoder.java
@@ -80,7 +80,6 @@ public class LTRStandardDecoder extends Decoder implements IUnFilteredRealBuffer
@Override
public void reset()
{
- mLTRMessageFramer.reset();
}
@Override
diff --git a/src/module/decode/ltrstandard/LTRStandardDecoderState.java b/src/module/decode/ltrstandard/LTRStandardDecoderState.java
index 6dacaf17..94cd32b9 100644
--- a/src/module/decode/ltrstandard/LTRStandardDecoderState.java
+++ b/src/module/decode/ltrstandard/LTRStandardDecoderState.java
@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
- * Copyright (C) 2014-2016 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
@@ -17,31 +17,21 @@
******************************************************************************/
package module.decode.ltrstandard;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.Iterator;
import java.util.TreeSet;
import message.Message;
import module.decode.DecoderType;
import module.decode.event.CallEvent.CallEventType;
import module.decode.ltrnet.LTRCallEvent;
-import module.decode.ltrstandard.message.CallEndMessage;
-import module.decode.ltrstandard.message.CallMessage;
-import module.decode.ltrstandard.message.IdleMessage;
-import module.decode.ltrstandard.message.LTRStandardMessage;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderState;
import module.decode.state.DecoderStateEvent;
import module.decode.state.DecoderStateEvent.Event;
import module.decode.state.State;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import alias.Alias;
import alias.AliasList;
import audio.metadata.Metadata;
@@ -49,17 +39,15 @@ import audio.metadata.MetadataType;
public class LTRStandardDecoderState extends DecoderState
{
- private final static Logger mLog =
- LoggerFactory.getLogger( LTRStandardDecoderState.class );
-
- private Map mActiveCalls = new HashMap<>();
- private Set mTalkgroupsFirstHeard = new HashSet<>();
- private Set mTalkgroups = new TreeSet<>();
+ private HashMap mActiveCalls = new HashMap();
+ private HashSet mTalkgroupsFirstHeard = new HashSet();
+ private TreeSet mTalkgroups = new TreeSet();
+ private TreeSet mActiveLCNs = new TreeSet();
private String mTalkgroup;
private Alias mTalkgroupAlias;
+ private int mChannelNumber;
private long mFrequency;
- private LCNTracker mLCNTracker = new LCNTracker();
public LTRStandardDecoderState( AliasList aliasList )
{
@@ -82,128 +70,171 @@ public class LTRStandardDecoderState extends DecoderState
{
}
+ private LTRCallEvent getCurrentCallEvent()
+ {
+ return (LTRCallEvent)mCurrentCallEvent;
+ }
+
@Override
public void receive( Message message )
{
- if( message.isValid() && message instanceof LTRStandardMessage )
+ if( message.isValid() )
{
- switch( ((LTRStandardMessage)message).getMessageType() )
+ if( message instanceof LTRStandardOSWMessage )
{
- case CA_STRT:
- CallMessage start = (CallMessage)message;
-
- int channel = start.getChannel();
-
- mLCNTracker.processCallChannel( channel );
-
- mLCNTracker.processFreeChannel( start.getFree() );
+ 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 );
+ }
- //Only process calls on this LCN, or call detects for
- //talkgroups that are homed on this LCN
- if( mLCNTracker.isValidChannel( channel ) &&
- ( mLCNTracker.isCurrentChannel( channel ) ||
- mLCNTracker.isCurrentChannel( start.getHomeRepeater() ) ) )
- {
- LTRCallEvent event = mActiveCalls.get( channel );
-
- if( event == null ||
- !event.isMatchingTalkgroup( start.getToID() ) )
- {
- //Check for different talkgroup
- if( event != null )
- {
- event.end();
- mActiveCalls.remove( channel );
- }
+ /* 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();
- boolean current = mLCNTracker.isCurrentChannel( channel );
-
- event = new LTRCallEvent.Builder(
- DecoderType.LTR_STANDARD,
- current ? CallEventType.CALL : CallEventType.CALL_DETECT )
- .to( start.getToID() )
- .aliasList( getAliasList() )
- .channel( start.getChannelFormatted() )
- .frequency( current ? mFrequency : 0 )
- .build();
-
- mActiveCalls.put( channel, event );
-
- broadcast( event );
-
- if( mLCNTracker.isCurrentChannel( channel ) )
- {
- mTalkgroup = start.getToID();
- broadcast( ChangedAttribute.TO_TALKGROUP );
-
- processTalkgroup( mTalkgroup );
-
- mTalkgroupAlias = start.getToIDAlias();
- broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
+ /* Detect when we haven't reset from the previous
+ * call and now have a new call coming in with
+ * a different talkgroup */
+ if( current != null && isDifferentTalkgroup( mTalkgroup ) )
+ {
+ mCurrentCallEvent.end();
+ broadcast( mCurrentCallEvent );
+ mCurrentCallEvent = null;
+ }
+
+ if( current == null )
+ {
+ mCurrentCallEvent =
+ new LTRCallEvent.Builder(
+ DecoderType.LTR_STANDARD, CallEventType.CALL )
+ .to( ltr.getTalkgroupID() )
+ .aliasList( getAliasList() )
+ .channel( String.valueOf( ltr.getChannel() ) )
+ .frequency( mFrequency )
+ .build();
- broadcast( new Metadata( MetadataType.TO,
- mTalkgroup, mTalkgroupAlias, true ) );
- }
- }
-
- broadcast( new DecoderStateEvent( this,
- Event.CONTINUATION, State.CALL ) );
- }
- break;
- case CA_ENDD:
- CallEndMessage end = (CallEndMessage)message;
+ broadcast( mCurrentCallEvent );
+
+ broadcast( new Metadata( MetadataType.TO,
+ mTalkgroup, mTalkgroupAlias, true ) );
- //Home channel is 31 for call end -- use the free channel
- //as the call end channel
- int repeater = end.getFree();
-
- mLCNTracker.processCallChannel( repeater );
-
- if( mLCNTracker.isCurrentChannel( repeater ) )
- {
- mTalkgroup = end.getToID();
- broadcast( ChangedAttribute.TO_TALKGROUP );
-
- processTalkgroup( mTalkgroup );
-
- mTalkgroupAlias = end.getToIDAlias();
- broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
+ 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.TO_TALKGROUP );
- LTRCallEvent event = mActiveCalls.remove( repeater );
-
- if( event != null )
- {
- event.end();
- broadcast( event );
- broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) );
- }
- }
- break;
- case SY_IDLE:
- IdleMessage idle = (IdleMessage)message;
+ mTalkgroupAlias = ltr.getTalkgroupIDAlias();
+ broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
- int lcn = idle.getChannel();
+ if( mCurrentCallEvent != null )
+ {
+ mCurrentCallEvent.end();
+ 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 );
+ }
+ }
- mLCNTracker.processCallChannel( lcn );
-
- break;
- case UN_KNWN:
- default:
- break;
+ int channel = ltr.getChannel();
+
+ if( 0 < channel && channel <= 20 )
+ {
+ if( channel == home )
+ {
+ if( mChannelNumber != channel )
+ {
+ mChannelNumber = 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( mChannelNumber != idleChannel )
+ {
+ mChannelNumber = idleChannel;
+ broadcast( ChangedAttribute.CHANNEL_NUMBER );
+ }
+
+ mActiveLCNs.add( idleChannel );
+ }
+ broadcast( new DecoderStateEvent( this,
+ Event.CONTINUATION, State.IDLE ) );
+ break;
+ case UN_KNWN:
+ default:
+ break;
+ }
}
}
}
-
- private void processTalkgroup( String talkgroup )
+
+ /**
+ * Indicates if the talkgroup is different than the talkgroup specified in
+ * the current call event
+ */
+ private boolean isDifferentTalkgroup( String talkgroup )
{
- if( mTalkgroupsFirstHeard.contains( talkgroup ) )
- {
- mTalkgroups.add( talkgroup );
- }
- else
- {
- mTalkgroupsFirstHeard.add( talkgroup );
- }
+ return talkgroup != null &&
+ mCurrentCallEvent != null &&
+ mCurrentCallEvent.getToID() != null &&
+ !mCurrentCallEvent.getToID().contentEquals( talkgroup );
}
/**
@@ -214,7 +245,8 @@ public class LTRStandardDecoderState extends DecoderState
mActiveCalls.clear();
mTalkgroupsFirstHeard.clear();
mTalkgroups.clear();
- mLCNTracker.reset();
+ mActiveLCNs.clear();
+ mChannelNumber = 0;
resetState();
}
@@ -224,20 +256,13 @@ public class LTRStandardDecoderState extends DecoderState
*/
private void resetState()
{
- for( Integer key: mActiveCalls.keySet() )
+ if( mCurrentCallEvent != null )
{
- LTRCallEvent event = mActiveCalls.get( key );
-
- if( event != null )
- {
- event.end();
-
- broadcast( event );
- }
+ mCurrentCallEvent.setEnd( System.currentTimeMillis() );
+ broadcast( mCurrentCallEvent );
+ mCurrentCallEvent = null;
}
-
- mActiveCalls.clear();
-
+
mTalkgroup = null;
broadcast( ChangedAttribute.TO_TALKGROUP );
@@ -257,12 +282,12 @@ public class LTRStandardDecoderState extends DecoderState
public boolean hasChannelNumber()
{
- return mLCNTracker.getCurrentChannel() != 0;
+ return mChannelNumber != 0;
}
public int getChannelNumber()
{
- return mLCNTracker.getCurrentChannel();
+ return mChannelNumber;
}
@Override
@@ -297,28 +322,28 @@ public class LTRStandardDecoderState extends DecoderState
sb.append( "Monitored LCN: " );
- if( hasChannelNumber() )
+ if( mChannelNumber > 0 )
{
- sb.append( getChannelNumber() );
+ sb.append( mChannelNumber );
}
else
{
- sb.append( "*Insufficient Data*" );
+ sb.append( "unknown" );
}
sb.append( "\n" );
- sb.append( "Active LCNs:\t" );
-
- List lcns = mLCNTracker.getActiveLCNs();
+ Integer[] lcns = mActiveLCNs.toArray( new Integer[0] );
- if( lcns.size() > 0 )
+ sb.append( "Active LCNs:\t" );
+
+ if( lcns.length > 0 )
{
- sb.append( mLCNTracker.getActiveLCNs() );
+ sb.append( Arrays.toString( lcns ) );
}
else
{
- sb.append( "*Insufficient Data*" );
+ sb.append( "none" );
}
sb.append( "\n\n" );
@@ -330,15 +355,19 @@ public class LTRStandardDecoderState extends DecoderState
}
else
{
- for( String talkgroup: mTalkgroups )
+ Iterator it = mTalkgroups.iterator();
+
+ while( it.hasNext() )
{
+ String tgid = it.next();
+
sb.append( " " );
- sb.append( talkgroup );
+ sb.append( formatTalkgroup( tgid ) );
sb.append( " " );
if( hasAliasList() )
{
- Alias alias = getAliasList().getTalkgroupAlias( talkgroup );
+ Alias alias = getAliasList().getTalkgroupAlias( tgid );
if( alias != null )
{
@@ -347,147 +376,30 @@ public class LTRStandardDecoderState extends DecoderState
}
sb.append( "\n" );
-
}
}
return sb.toString();
}
- /**
- * Tracks the set of call and free channels for a system in order to
- * dynamically determine the current LCN and minimize false triggers from
- * bogus decoded LTR messages. Tracks the number of occurances of each
- * LCN and uses a dynamic threshold to determine LCN validity.
- */
- public class LCNTracker
+ public static String formatTalkgroup( String talkgroup )
{
- private static final int DEFAULT_COUNT = 10;
- private int[] mCallLCNCounts;
- private int[] mFreeLCNCounts;
- private int mCallHighestCount;
- private int mFreeHighestCount;
- private int mCurrentLCN;
+ StringBuilder sb = new StringBuilder();
- public LCNTracker()
+ if( talkgroup.length() == 6 )
{
- reset();
- }
-
- public void logStatistics()
- {
- for( int x = 1; x <= 20; x++ )
- {
- mLog.debug( "Call " + x + ": " + mCallLCNCounts[ x ] );
- }
- mLog.debug( "Call Highest Count: " + mCallHighestCount );
- for( int x = 1; x <= 20; x++ )
- {
- mLog.debug( "Free " + x + ": " + mFreeLCNCounts[ x ] );
- }
- mLog.debug( "Free Highest Count: " + mFreeHighestCount );
+ sb.append( talkgroup.substring( 0, 1 ) );
+ sb.append( "-" );
+ sb.append( talkgroup.substring( 1, 3 ) );
+ sb.append( "-" );
+ sb.append( talkgroup.substring( 3, 6 ) );
- mLog.debug( "Current LCN: " + mCurrentLCN );
+ return sb.toString();
}
-
- public void reset()
+ else
{
- //Dim channel arrays to 21 -- ignore the 0 index
- mCallLCNCounts = new int[ 21 ];
- mFreeLCNCounts = new int[ 21 ];
- mCallHighestCount = 3;
- mFreeHighestCount = DEFAULT_COUNT;
- mCurrentLCN = 0;
- }
-
- public boolean isCurrentChannel( int channel )
- {
- if( mCurrentLCN == 0 )
- {
- return true;
- }
- else
- {
- return channel == mCurrentLCN;
- }
- }
-
- public int getCurrentChannel()
- {
- return mCurrentLCN;
- }
-
- /**
- * Indicates if the channel is valid based on the observed current and
- * free channels reported by the system. Tracks the count of each
- * reported free channel the highest free channel count is used as a
- * threshold where a channel is valid when its count exceeds 20% of the
- * highest free channel count. A channel is also valid when it is
- * the currently monitored channel.
- */
- public boolean isValidChannel( int channel )
- {
- if( isCurrentChannel( channel ) )
- {
- return true;
- }
-
- int count = mFreeLCNCounts[ channel ];
-
- int threshold = (int)( (double)mFreeHighestCount * 0.2 );
-
- return count >= threshold;
- }
-
- public void processCallChannel( int channel )
- {
- if( 1 <= channel && channel <= 20 )
- {
- mCallLCNCounts[ channel ]++;
-
- if( mCallLCNCounts[ channel ] > mCallHighestCount )
- {
- mCallHighestCount = mCallLCNCounts[ channel ];
- mCurrentLCN = channel;
-
- broadcast( ChangedAttribute.CHANNEL_NUMBER );
- }
- }
- }
-
- public void processFreeChannel( int channel )
- {
- if( 1 <= channel && channel <= 20 )
- {
- mFreeLCNCounts[ channel ]++;
-
- if( mFreeLCNCounts[ channel ] > mFreeHighestCount )
- {
- mFreeHighestCount = mFreeLCNCounts[ channel ];
- }
- }
- }
-
- public List getActiveLCNs()
- {
- List active = new ArrayList<>();
-
- if( mFreeHighestCount > DEFAULT_COUNT )
- {
- for( int x = 1; x <= 20; x++ )
- {
- if( mCurrentLCN != 0 && isCurrentChannel( x ) )
- {
- active.add( x );
- }
- else if( isValidChannel( x ) )
- {
- active.add( x );
- }
- }
- }
-
- return active;
+ return talkgroup;
}
}
+
}
diff --git a/src/module/decode/ltrstandard/LTRStandardISWMessage.java b/src/module/decode/ltrstandard/LTRStandardISWMessage.java
new file mode 100644
index 00000000..8f16ac1f
--- /dev/null
+++ b/src/module/decode/ltrstandard/LTRStandardISWMessage.java
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * 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.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import map.Plottable;
+import message.MessageDirection;
+import message.MessageType;
+import alias.Alias;
+import alias.AliasList;
+import bits.BinaryMessage;
+
+public class LTRStandardISWMessage extends LTRStandardMessage
+{
+ private static final int sCALL_END = 31;
+ private static final int sIDLE = 255;
+
+ public LTRStandardISWMessage( BinaryMessage message, AliasList list )
+ {
+ super( message, MessageDirection.OSW, list );
+ mMessageType = getType();
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append( mDatestampFormatter.format(
+ new Date( System.currentTimeMillis() ) ) );
+ sb.append( " LTR OSW [" );
+ sb.append( mCRC.getAbbreviation() );
+ sb.append( "] " );
+ sb.append( getMessage() );
+ pad( sb, 100 );
+ sb.append( mMessage.toString() );
+
+ return sb.toString();
+ }
+
+ @Override
+ public String getMessage()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ switch( mMessageType )
+ {
+ case SY_IDLE:
+ sb.append( "IDLE AREA:" );
+ sb.append( getArea() );
+ sb.append( " LCN:" );
+ sb.append( format( getChannel(), 2 ) );
+ sb.append( " HOME:" );
+ sb.append( format( getHomeRepeater(), 2 ) );
+ sb.append( " GRP:" );
+ sb.append( format( getGroup(), 3 ) );
+ sb.append( " FREE:" );
+ sb.append( format( getFree(), 2 ) );
+ break;
+ case CA_STRT:
+ sb.append( "CALL LCN:" );
+ sb.append( format( getChannel(), 2 ) );
+ sb.append( " TG [ " );
+ sb.append( getTalkgroupID() );
+ sb.append( "/" );
+ sb.append( getTalkgroupIDAlias() );
+ sb.append( " ] FREE:" );
+ sb.append( format( getFree(), 2 ) );
+ break;
+ case CA_ENDD:
+ sb.append( "END* AREA:" );
+ sb.append( getArea() );
+ sb.append( " LCN:" );
+ sb.append( format( getChannel(), 2 ) );
+ sb.append( " TG [" );
+ sb.append( getTalkgroupID() );
+ sb.append( "/" );
+ sb.append( getTalkgroupIDAlias() );
+ sb.append( "] FREE:" );
+ sb.append( format( getFree(), 2 ) );
+ break;
+ default:
+ sb.append( "UNKNOWN A:" );
+ sb.append( getArea() );
+ sb.append( " C:" );
+ sb.append( format( getChannel(), 2 ) );
+ sb.append( " H:" );
+ sb.append( format( getHomeRepeater(), 2 ) );
+ sb.append( " G:" );
+ sb.append( format( getGroup(), 3 ) );
+ sb.append( " F:" );
+ sb.append( format( getFree(), 2 ) );
+ }
+
+ return sb.toString();
+ }
+
+ public String getTalkgroupID()
+ {
+ return getTalkgroupID( true );
+ }
+
+ public String getTalkgroupID( boolean format )
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( getArea() );
+
+ if( format )
+ {
+ sb.append( "-" );
+ }
+
+ sb.append( format( getHomeRepeater(), 2 ) );
+
+ if( format )
+ {
+ sb.append( "-" );
+ }
+
+ sb.append( format( getGroup(), 3 ) );
+
+ return sb.toString();
+ }
+
+ public Alias getTalkgroupIDAlias()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append( getArea() );
+ sb.append( format( getHomeRepeater(), 2 ) );
+ sb.append( format( getGroup(), 3 ) );
+
+ return mAliasList.getTalkgroupAlias( sb.toString() );
+ }
+
+ public MessageType getType()
+ {
+ MessageType retVal = MessageType.UN_KNWN;
+
+ int group = getGroup();
+
+ if( group == sIDLE )
+ {
+ retVal = MessageType.SY_IDLE;
+ }
+ else
+ {
+ int channel = getChannel();
+
+ if( channel == sCALL_END )
+ {
+ retVal = MessageType.CA_ENDD;
+ }
+ else
+ {
+ retVal = MessageType.CA_STRT;
+ }
+ }
+
+ return retVal;
+ }
+
+ @Override
+ public String getFromID()
+ {
+ if( mMessageType == MessageType.CA_STRT ||
+ mMessageType == MessageType.CA_ENDD )
+ {
+ return getTalkgroupID();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public Alias getFromIDAlias()
+ {
+ if( mMessageType == MessageType.CA_STRT ||
+ mMessageType == MessageType.CA_ENDD )
+ {
+ return getTalkgroupIDAlias();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public Plottable getPlottable()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * Provides a listing of aliases contained in the message.
+ */
+ public List getAliases()
+ {
+ List aliases = new ArrayList();
+
+ Alias from = getFromIDAlias();
+
+ if( from != null )
+ {
+ aliases.add( from );
+ }
+
+ Alias to = getToIDAlias();
+
+ if( to != null )
+ {
+ aliases.add( to );
+ }
+
+ return aliases;
+ }
+}
diff --git a/src/module/decode/ltrstandard/LTRStandardMessage.java b/src/module/decode/ltrstandard/LTRStandardMessage.java
new file mode 100644
index 00000000..408c7bd9
--- /dev/null
+++ b/src/module/decode/ltrstandard/LTRStandardMessage.java
@@ -0,0 +1,254 @@
+/*******************************************************************************
+ * 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.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 edac.CRC;
+import edac.CRCLTR;
+
+public abstract class LTRStandardMessage extends Message
+{
+ protected static final String sUNKNOWN = "**UNKNOWN**";
+ protected static final int sINT_NULL_VALUE = -1;
+ protected static final double sDOUBLE_NULL_VALUE = -1.0D;
+ protected SimpleDateFormat mDatestampFormatter =
+ new SimpleDateFormat( "yyyyMMdd HHmmss" );
+ protected DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000");
+
+ protected static final int[] sSYNC = { 8,7,6,5,4,3,2,1,0 };
+ protected static final int[] sAREA = { 9 };
+ protected static final int[] sCHANNEL = { 14,13,12,11,10 };
+ protected static final int[] sHOME_REPEATER = { 19,18,17,16,15 };
+ protected static final int[] sGROUP = { 27,26,25,24,23,22,21,20 };
+ protected static final int[] sFREE = { 32,31,30,29,28 };
+ protected static final int[] sCRC = { 39,38,37,36,35,34,33 };
+
+ protected BinaryMessage mMessage;
+ protected MessageType mMessageType;
+ protected CRC mCRC;
+ protected AliasList mAliasList;
+
+ public LTRStandardMessage( BinaryMessage message,
+ MessageDirection direction,
+ AliasList list )
+ {
+ mMessage = message;
+ mCRC = CRCLTR.check( message, direction );
+ mAliasList = list;
+ }
+
+ public MessageType getMessageType()
+ {
+ return mMessageType;
+ }
+
+ public boolean isValid()
+ {
+ return mCRC != CRC.FAILED_CRC && mCRC != CRC.FAILED_PARITY;
+ }
+
+ /**
+ * Appends spaces to the end of the stringbuilder to make it length long
+ */
+ protected void pad( StringBuilder sb, int length )
+ {
+ while( sb.length() < length )
+ {
+ sb.append( " " );
+ }
+ }
+
+ @Override
+ public String getBinaryMessage()
+ {
+ return mMessage.toString();
+ }
+
+ /**
+ * Returns a value formatted in hex notation.
+ *
+ * @param value - integer value
+ * @return - hex digits returned
+ */
+ protected static String getHex( int value, int digits )
+ {
+ return String.format( "%0" + digits + "X", value );
+ }
+
+ protected int getInt( int[] bits )
+ {
+ int retVal = 0;
+
+ for( int x = 0; x < bits.length; x++ )
+ {
+ if( mMessage.get( bits[ x ] ) )
+ {
+ retVal += 1<
{
@@ -36,12 +29,6 @@ public class LTRStandardMessageProcessor implements Listener
private AliasList mAliasList;
private Listener mMessageListener;
- /**
- * Processes raw binary messages and converts them to the correct message class
- *
- * @param direction - inbound (ISW) or outbound (OSW)
- * @param list - alias list
- */
public LTRStandardMessageProcessor( MessageDirection direction, AliasList list )
{
mDirection = direction;
@@ -49,66 +36,27 @@ public class LTRStandardMessageProcessor implements Listener
}
@Override
- public void receive( BinaryMessage binaryMessage )
+ public void receive( BinaryMessage buffer )
{
if( mMessageListener != null )
{
- //Inbound Status Word (ISW) is a bit-flipped version of the Outbound
- //(OSW), so flip the bits and process it as an OSW
- if( mDirection == MessageDirection.ISW )
+ LTRStandardMessage message;
+
+ if( mDirection == MessageDirection.OSW )
{
- binaryMessage.flip( 0, 40 );
+ message = new LTRStandardOSWMessage( buffer, mAliasList );
+ }
+ else
+ {
+ buffer.flip( 0, 40 );
+
+ message = new LTRStandardISWMessage( buffer, mAliasList );
}
- CRC crc = CRCLTR.check( binaryMessage, mDirection );
-
- if( crc != CRC.FAILED_CRC &&
- crc != CRC.FAILED_PARITY )
- {
- LTRStandardMessage message;
-
- int channel = binaryMessage.getInt( LTRStandardMessage.CHANNEL );
- int home = binaryMessage.getInt( LTRStandardMessage.HOME_REPEATER );
- int free = binaryMessage.getInt( LTRStandardMessage.FREE );
- int group = binaryMessage.getInt( LTRStandardMessage.GROUP );
-
- if( isValidChannel( channel ) &&
- isValidChannel( home ) &&
- isValidChannel( free ) )
- {
- if( channel == free && group == 255 )
- {
- message = new IdleMessage( binaryMessage, mDirection, mAliasList, crc );
- }
- else
- {
- message = new CallMessage( binaryMessage, mDirection, mAliasList, crc );
- }
- }
- else if( channel == 31 &&
- isValidChannel( home ) &&
- isValidChannel( free ) )
- {
- message = new CallEndMessage( binaryMessage, mDirection, mAliasList, crc );
- }
- else
- {
- message = new UnknownMessage( binaryMessage, mDirection, mAliasList, crc );
- }
-
- mMessageListener.receive( message );
- }
+ mMessageListener.receive( message );
}
}
- /**
- * Checks the channel (LCN) number to ensure it is in the range 1 -20
- */
- private boolean isValidChannel( int channel )
- {
- return ( 1 <= channel && channel <= 20 );
- }
-
public void setMessageListener( Listener listener )
{
mMessageListener = listener;
diff --git a/src/module/decode/ltrstandard/LTRStandardOSWMessage.java b/src/module/decode/ltrstandard/LTRStandardOSWMessage.java
new file mode 100644
index 00000000..df3bde82
--- /dev/null
+++ b/src/module/decode/ltrstandard/LTRStandardOSWMessage.java
@@ -0,0 +1,248 @@
+/*******************************************************************************
+ * 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.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import map.Plottable;
+import message.MessageDirection;
+import message.MessageType;
+import alias.Alias;
+import alias.AliasList;
+import bits.BinaryMessage;
+
+public class LTRStandardOSWMessage extends LTRStandardMessage
+{
+ private static final int sCALL_END = 31;
+ private static final int sIDLE = 255;
+
+ public LTRStandardOSWMessage( BinaryMessage message, AliasList list )
+ {
+ super( message, MessageDirection.OSW, list );
+ mMessageType = getType();
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append( mDatestampFormatter.format(
+ new Date( System.currentTimeMillis() ) ) );
+ sb.append( " LTR OSW [" );
+ sb.append( mCRC.getAbbreviation() );
+ sb.append( "] " );
+ sb.append( getMessage() );
+ pad( sb, 100 );
+ sb.append( mMessage.toString() );
+
+ return sb.toString();
+ }
+
+ @Override
+ public String getMessage()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ switch( mMessageType )
+ {
+ case SY_IDLE:
+ sb.append( "IDLE AREA:" );
+ sb.append( getArea() );
+ sb.append( " LCN:" );
+ sb.append( format( getChannel(), 2 ) );
+ sb.append( " HOME:" );
+ sb.append( format( getHomeRepeater(), 2 ) );
+ sb.append( " GRP:" );
+ sb.append( format( getGroup(), 3 ) );
+ sb.append( " FREE:" );
+ sb.append( format( getFree(), 2 ) );
+ break;
+ case CA_STRT:
+ sb.append( "CALL LCN:" );
+ sb.append( format( getChannel(), 2 ) );
+ sb.append( " TG [ " );
+ sb.append( getTalkgroupID() );
+ sb.append( "/" );
+ Alias alias = getTalkgroupIDAlias();
+ if( alias != null )
+ {
+ sb.append( getTalkgroupIDAlias().getName() );
+ }
+ else
+ {
+ sb.append( "UNKNOWN" );
+ }
+ sb.append( " ] FREE:" );
+ sb.append( format( getFree(), 2 ) );
+ break;
+ case CA_ENDD:
+ sb.append( "END* AREA:" );
+ sb.append( getArea() );
+ sb.append( " LCN:" );
+ sb.append( format( getChannel(), 2 ) );
+ sb.append( " TG [" );
+ sb.append( getTalkgroupID() );
+ sb.append( "/" );
+ Alias endAlias = getTalkgroupIDAlias();
+ if( endAlias != null )
+ {
+ sb.append( getTalkgroupIDAlias().getName() );
+ }
+ else
+ {
+ sb.append( "UNKNOWN" );
+ }
+ sb.append( "] FREE:" );
+ sb.append( format( getFree(), 2 ) );
+ break;
+ default:
+ sb.append( "UNKNOWN A:" );
+ sb.append( getArea() );
+ sb.append( " C:" );
+ sb.append( format( getChannel(), 2 ) );
+ sb.append( " H:" );
+ sb.append( format( getHomeRepeater(), 2 ) );
+ sb.append( " G:" );
+ sb.append( format( getGroup(), 3 ) );
+ sb.append( " F:" );
+ sb.append( format( getFree(), 2 ) );
+ }
+
+ return sb.toString();
+ }
+
+ public String getTalkgroupID()
+ {
+ return getTalkgroupID( true );
+ }
+
+ public String getTalkgroupID( boolean format )
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( getArea() );
+
+ if( format )
+ {
+ sb.append( "-" );
+ }
+
+ sb.append( format( getHomeRepeater(), 2 ) );
+
+ if( format )
+ {
+ sb.append( "-" );
+ }
+
+ sb.append( format( getGroup(), 3 ) );
+
+ return sb.toString();
+ }
+
+ public Alias getTalkgroupIDAlias()
+ {
+ return mAliasList.getTalkgroupAlias( getTalkgroupID() );
+ }
+
+ public MessageType getType()
+ {
+ MessageType retVal = MessageType.UN_KNWN;
+
+ /* Group 255 when the channel equals the free is the IDLE message */
+ if( getGroup() == sIDLE && getChannel() == getFree() )
+ {
+ retVal = MessageType.SY_IDLE;
+ }
+ else
+ {
+ int channel = getChannel();
+
+ if( channel == sCALL_END )
+ {
+ retVal = MessageType.CA_ENDD;
+ }
+ else
+ {
+ retVal = MessageType.CA_STRT;
+ }
+ }
+
+ return retVal;
+ }
+
+ @Override
+ public String getFromID()
+ {
+ if( mMessageType == MessageType.CA_STRT ||
+ mMessageType == MessageType.CA_ENDD )
+ {
+ return getTalkgroupID();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public Alias getFromIDAlias()
+ {
+ if( mMessageType == MessageType.CA_STRT ||
+ mMessageType == MessageType.CA_ENDD )
+ {
+ return getTalkgroupIDAlias();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ @Override
+ public Plottable getPlottable()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * Provides a listing of aliases contained in the message.
+ */
+ public List getAliases()
+ {
+ List aliases = new ArrayList();
+
+ Alias from = getFromIDAlias();
+
+ if( from != null )
+ {
+ aliases.add( from );
+ }
+
+ Alias to = getToIDAlias();
+
+ if( to != null )
+ {
+ aliases.add( to );
+ }
+
+ return aliases;
+ }
+}
diff --git a/src/module/decode/ltrstandard/message/CallEndMessage.java b/src/module/decode/ltrstandard/message/CallEndMessage.java
deleted file mode 100644
index e5023139..00000000
--- a/src/module/decode/ltrstandard/message/CallEndMessage.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014-2016 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- ******************************************************************************/
-package module.decode.ltrstandard.message;
-
-import message.MessageDirection;
-import message.MessageType;
-import alias.Alias;
-import alias.AliasList;
-import bits.BinaryMessage;
-import edac.CRC;
-
-public class CallEndMessage extends LTRStandardMessage
-{
- public CallEndMessage( BinaryMessage message, MessageDirection direction,
- AliasList list, CRC crc )
- {
- super( message, direction, list, crc );
- }
-
- @Override
- public MessageType getMessageType()
- {
- return MessageType.CA_ENDD;
- }
-
- @Override
- public String getMessage()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( "END* AREA:" );
- sb.append( getArea() );
- sb.append( " LCN:" );
- sb.append( getChannelFormatted() );
- sb.append( " TG [" );
- sb.append( getToID() );
- sb.append( "/" );
- Alias endAlias = getToIDAlias();
- if( endAlias != null )
- {
- sb.append( getToIDAlias().getName() );
- }
- else
- {
- sb.append( "UNKNOWN" );
- }
- sb.append( "] FREE:" );
- sb.append( getFreeFormatted() );
-
- return sb.toString();
- }
-}
diff --git a/src/module/decode/ltrstandard/message/CallMessage.java b/src/module/decode/ltrstandard/message/CallMessage.java
deleted file mode 100644
index f7d44ca6..00000000
--- a/src/module/decode/ltrstandard/message/CallMessage.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014-2016 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- ******************************************************************************/
-package module.decode.ltrstandard.message;
-
-import message.MessageDirection;
-import message.MessageType;
-import alias.Alias;
-import alias.AliasList;
-import bits.BinaryMessage;
-import edac.CRC;
-
-public class CallMessage extends LTRStandardMessage
-{
- public CallMessage( BinaryMessage message, MessageDirection direction,
- AliasList list, CRC crc )
- {
- super( message, direction, list, crc );
- }
-
- @Override
- public MessageType getMessageType()
- {
- return MessageType.CA_STRT;
- }
-
- @Override
- public String getMessage()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( "CALL LCN:" );
- sb.append( getChannelFormatted() );
- sb.append( " TG [ " );
- sb.append( getToID() );
- sb.append( "/" );
-
- Alias alias = getToIDAlias();
-
- if( alias != null )
- {
- sb.append( getToIDAlias().getName() );
- }
- else
- {
- sb.append( "UNKNOWN" );
- }
-
- sb.append( " ] FREE:" );
- sb.append( getFreeFormatted() );
-
- return sb.toString();
- }
-}
diff --git a/src/module/decode/ltrstandard/message/IdleMessage.java b/src/module/decode/ltrstandard/message/IdleMessage.java
deleted file mode 100644
index ba8e4d97..00000000
--- a/src/module/decode/ltrstandard/message/IdleMessage.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014-2016 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- ******************************************************************************/
-package module.decode.ltrstandard.message;
-
-import message.MessageDirection;
-import message.MessageType;
-import alias.AliasList;
-import bits.BinaryMessage;
-import edac.CRC;
-
-public class IdleMessage extends LTRStandardMessage
-{
- public IdleMessage( BinaryMessage message, MessageDirection direction,
- AliasList list, CRC crc )
- {
- super( message, direction, list, crc );
- }
-
- @Override
- public MessageType getMessageType()
- {
- return MessageType.SY_IDLE;
- }
-
- @Override
- public String getMessage()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( "IDLE AREA:" );
- sb.append( getArea() );
- sb.append( " LCN:" );
- sb.append( getChannelFormatted() );
- sb.append( " HOME:" );
- sb.append( getHomeRepeaterFormatted() );
- sb.append( " GRP:" );
- sb.append( getGroupFormatted() );
- sb.append( " FREE:" );
- sb.append( getFreeFormatted() );
-
- return sb.toString();
- }
-}
diff --git a/src/module/decode/ltrstandard/message/LTRStandardMessage.java b/src/module/decode/ltrstandard/message/LTRStandardMessage.java
deleted file mode 100644
index 87f381b2..00000000
--- a/src/module/decode/ltrstandard/message/LTRStandardMessage.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014-2016 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- ******************************************************************************/
-package module.decode.ltrstandard.message;
-
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import message.Message;
-import message.MessageDirection;
-import message.MessageType;
-import module.decode.DecoderType;
-
-import org.apache.commons.lang3.StringUtils;
-
-import alias.Alias;
-import alias.AliasList;
-import bits.BinaryMessage;
-import edac.CRC;
-
-public abstract class LTRStandardMessage extends Message
-{
- protected SimpleDateFormat mDatestampFormatter =
- new SimpleDateFormat( "yyyyMMdd HHmmss" );
-
- public static final int[] SYNC = { 0,1,2,3,4,5,6,7,8 };
- public static final int[] AREA = { 9 };
- public static final int[] CHANNEL = { 10,11,12,13,14 };
- public static final int[] HOME_REPEATER = { 15,16,17,18,19 };
- public static final int[] GROUP = { 20,21,22,23,24,25,26,27 };
- public static final int[] FREE = { 28,29,30,31,32 };
- public static final int[] CHECKSUM = { 33,34,35,36,37,38,39 };
-
- protected BinaryMessage mMessage;
- protected MessageDirection mMessageDirection;
- protected AliasList mAliasList;
- protected CRC mCRC;
-
- public LTRStandardMessage( BinaryMessage message,
- MessageDirection direction,
- AliasList list,
- CRC crc )
- {
- mMessage = message;
- mMessageDirection = direction;
- mAliasList = list;
- mCRC = crc;
- }
-
- public abstract MessageType getMessageType();
-
- public boolean isValid()
- {
- return mCRC != CRC.FAILED_CRC && mCRC != CRC.FAILED_PARITY;
- }
-
- @Override
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
- sb.append( mDatestampFormatter.format(
- new Date( System.currentTimeMillis() ) ) );
- sb.append( " LTR " );
- sb.append( mMessageDirection.name() );
- sb.append( " [" );
- sb.append( mCRC.getAbbreviation() );
- sb.append( "] " );
- sb.append( getMessage() );
-
- return sb.toString();
- }
-
- @Override
- public String getBinaryMessage()
- {
- return mMessage.toString();
- }
-
- public CRC getCRC()
- {
- return mCRC;
- }
-
- public int getArea()
- {
- return mMessage.getInt( AREA );
- }
-
- public int getChannel()
- {
- return mMessage.getInt( CHANNEL );
- }
-
- public String getChannelFormatted()
- {
- return StringUtils.leftPad( String.valueOf( getChannel() ), 2, "0" );
- }
-
- public int getHomeRepeater()
- {
- return mMessage.getInt( HOME_REPEATER );
- }
-
- public String getHomeRepeaterFormatted()
- {
- return StringUtils.leftPad( String.valueOf( getHomeRepeater() ), 2, "0" );
- }
-
- public int getGroup()
- {
- return mMessage.getInt( GROUP );
- }
-
- public String getGroupFormatted()
- {
- return StringUtils.leftPad( String.valueOf( getGroup() ), 3, "0" );
- }
-
- public int getFree()
- {
- return mMessage.getInt( FREE );
- }
-
- public String getFreeFormatted()
- {
- return StringUtils.leftPad( String.valueOf( getFree() ), 2, "0" );
- }
-
- @Override
- public String getFromID()
- {
- return null;
- }
-
- @Override
- public Alias getFromIDAlias()
- {
- return null;
- }
-
- @Override
- public String getToID()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( getArea() );
- sb.append( "-" );
- sb.append( getHomeRepeaterFormatted() );
- sb.append( "-" );
- sb.append( getGroupFormatted() );
-
- return sb.toString();
- }
-
- @Override
- public Alias getToIDAlias()
- {
- return mAliasList.getTalkgroupAlias( getToID() );
- }
-
- public List getAliases()
- {
- List aliases = new ArrayList<>();
-
- Alias talkgroupAlias = getToIDAlias();
-
- if( talkgroupAlias != null )
- {
- aliases.add( talkgroupAlias );
- }
-
- return aliases;
- }
-
- public int getCRCChecksum()
- {
- return mMessage.getInt( CHECKSUM );
- }
-
- @Override
- public String getProtocol()
- {
- return DecoderType.LTR_STANDARD.getDisplayString();
- }
-
- @Override
- public String getEventType()
- {
- return getMessageType().getDisplayText();
- }
-
- @Override
- public String getErrorStatus()
- {
- return mCRC.getDisplayText();
- }
-}
diff --git a/src/module/decode/ltrstandard/message/UnknownMessage.java b/src/module/decode/ltrstandard/message/UnknownMessage.java
deleted file mode 100644
index b975c667..00000000
--- a/src/module/decode/ltrstandard/message/UnknownMessage.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014-2016 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- ******************************************************************************/
-package module.decode.ltrstandard.message;
-
-import message.MessageDirection;
-import message.MessageType;
-import alias.AliasList;
-import bits.BinaryMessage;
-import edac.CRC;
-
-public class UnknownMessage extends LTRStandardMessage
-{
- public UnknownMessage( BinaryMessage message, MessageDirection direction,
- AliasList list, CRC crc )
- {
- super( message, direction, list, crc );
- }
-
- @Override
- public MessageType getMessageType()
- {
- return MessageType.UN_KNWN;
- }
-
- @Override
- public String getMessage()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( "UNKNOWN AREA:" );
- sb.append( getArea() );
- sb.append( " LCN:" );
- sb.append( getChannelFormatted() );
- sb.append( " HOME:" );
- sb.append( getHomeRepeaterFormatted() );
- sb.append( " GRP:" );
- sb.append( getGroupFormatted() );
- sb.append( " FREE:" );
- sb.append( getFreeFormatted() );
-
- return sb.toString();
- }
-}
diff --git a/src/module/decode/mpt1327/MPT1327Decoder.java b/src/module/decode/mpt1327/MPT1327Decoder.java
index 799fd5a0..45bc3372 100644
--- a/src/module/decode/mpt1327/MPT1327Decoder.java
+++ b/src/module/decode/mpt1327/MPT1327Decoder.java
@@ -301,8 +301,8 @@ public class MPT1327Decoder extends Decoder
@Override
public void reset()
{
- mControlMessageFramer.reset();
- mTrafficMessageFramer.reset();
+ // TODO Auto-generated method stub
+
}
@Override
diff --git a/src/module/decode/mpt1327/MPT1327DecoderState.java b/src/module/decode/mpt1327/MPT1327DecoderState.java
index cc22c847..ad40aa52 100644
--- a/src/module/decode/mpt1327/MPT1327DecoderState.java
+++ b/src/module/decode/mpt1327/MPT1327DecoderState.java
@@ -26,11 +26,9 @@ import java.util.TreeSet;
import message.Message;
import module.decode.DecoderType;
-import module.decode.config.DecodeConfiguration;
import module.decode.event.CallEvent;
import module.decode.event.CallEvent.CallEventType;
import module.decode.mpt1327.MPT1327Message.IdentType;
-import module.decode.state.ChangeChannelTimeoutEvent;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderState;
import module.decode.state.DecoderStateEvent;
@@ -43,8 +41,6 @@ import org.slf4j.LoggerFactory;
import alias.Alias;
import alias.AliasList;
-import audio.metadata.Metadata;
-import audio.metadata.MetadataType;
import controller.channel.Channel.ChannelType;
import controller.channel.map.ChannelMap;
@@ -64,18 +60,15 @@ public class MPT1327DecoderState extends DecoderState
private ChannelMap mChannelMap;
private long mFrequency = 0;
- private long mCallTimeout;
- public MPT1327DecoderState( AliasList aliasList,
- ChannelMap channelMap,
- ChannelType channelType,
- long callTimeout )
+ public MPT1327DecoderState( AliasList aliasList, ChannelMap channelMap,
+ ChannelType channelType )
{
super( aliasList );
mChannelMap = channelMap;
+
mChannelType = channelType;
- mCallTimeout = callTimeout;
}
@Override
@@ -259,42 +252,8 @@ public class MPT1327DecoderState extends DecoderState
/* Traffic Channel Events */
case CLEAR:
- mChannelNumber = mpt.getChannel();
-
- broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) );
- break;
case MAINT:
- if( mChannelType == ChannelType.STANDARD )
- {
- /**
- * When we receive a MAINT message and we're configured
- * as a standard channel, we need to apply the call
- * timeout specified by the user. Otherwise we'll
- * be using the shorter default call timeout
- */
- broadcast( new ChangeChannelTimeoutEvent( this,
- mChannelType, mCallTimeout ) );
-
- if( mCurrentCallEvent == null )
- {
- mCurrentCallEvent = new MPT1327CallEvent.Builder( CallEventType.CALL )
- .aliasList( getAliasList() )
- .channel( String.valueOf( mChannelNumber ) )
- .details( "MONITORED TRAFFIC CHANNEL" )
- .frequency( mFrequency )
- .to( mpt.getToID() )
- .build();
-
- broadcast( mCurrentCallEvent );
- }
-
- broadcast(new Metadata(MetadataType.TO,
- mpt.getToID(), mpt.getToIDAlias(), true));
-
- broadcast( new DecoderStateEvent( this, Event.START, State.CALL ) );
-
- setToTalkgroup( mpt.getToID() );
- }
+ broadcast( new DecoderStateEvent( this, Event.END, State.END ) );
break;
default:
break;
@@ -317,25 +276,6 @@ public class MPT1327DecoderState extends DecoderState
mToTalkgroup = null;
broadcast( ChangedAttribute.TO_TALKGROUP );
-
- /**
- * If this is a standard channel, reset the fade timeout to the default
- * timeout. Once processing is underway, if we get a MAINT message,
- * this indicates we're processing a traffic channel as a standard
- * channel, so we'll issue a different call timeout then.
- */
- if( mChannelType == ChannelType.STANDARD )
- {
- broadcast( new ChangeChannelTimeoutEvent( this, mChannelType,
- DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000 ) );
-
- if( mCurrentCallEvent != null )
- {
- mCurrentCallEvent.end();
- broadcast( mCurrentCallEvent );
- mCurrentCallEvent = null;
- }
- }
}
public String getSite()
diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java
index 91ab112c..0ffe62ce 100644
--- a/src/module/decode/p25/P25DecoderState.java
+++ b/src/module/decode/p25/P25DecoderState.java
@@ -42,8 +42,6 @@ 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.PDUTypeUnknown;
-import module.decode.p25.message.pdu.confirmed.PacketData;
import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextAccept;
import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextReject;
import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextRequest;
@@ -120,6 +118,7 @@ import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdat
import module.decode.p25.reference.IPProtocol;
import module.decode.p25.reference.LinkControlOpcode;
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;
@@ -407,12 +406,12 @@ public class P25DecoderState extends DecoderState
{
if( hdu.isValid() )
{
- broadcast( new DecoderStateEvent( this, Event.START, State.CALL ) );
+ State state = hdu.isEncrypted() ? State.ENCRYPTED : State.CALL;
+
+ broadcast( new DecoderStateEvent( this, Event.START, state ) );
String to = hdu.getToID();
-
- updateTo( to );
-
+
if( mCurrentCallEvent == null )
{
mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.CALL )
@@ -426,6 +425,7 @@ public class P25DecoderState extends DecoderState
broadcast( mCurrentCallEvent );
}
+ updateTo( to );
}
}
@@ -437,7 +437,7 @@ public class P25DecoderState extends DecoderState
{
if( tdulc.getOpcode() == LinkControlOpcode.CALL_TERMINATION_OR_CANCELLATION )
{
- broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) );
+ broadcast( new DecoderStateEvent( this, Event.END, State.END ) );
if( mCurrentCallEvent != null )
{
@@ -615,14 +615,12 @@ public class P25DecoderState extends DecoderState
module.decode.p25.message.tdu.lc.GroupVoiceChannelUser gvcuser =
(module.decode.p25.message.tdu.lc.GroupVoiceChannelUser)tdulc;
- broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CALL ) );
+ broadcast( new DecoderStateEvent( this, Event.CONTINUATION,
+ ( gvcuser.isEncrypted() ? State.ENCRYPTED : State.CALL ) ) );
String from = gvcuser.getSourceAddress();
String to = gvcuser.getGroupAddress();
- updateFrom( from );
- updateTo( to );
-
if( mCurrentCallEvent == null )
{
mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
@@ -638,6 +636,9 @@ public class P25DecoderState extends DecoderState
broadcast( mCurrentCallEvent );
}
+
+ updateFrom( from );
+ updateTo( to );
}
break;
case MESSAGE_UPDATE:
@@ -844,8 +845,6 @@ public class P25DecoderState extends DecoderState
String to = tivcu.getAddress();
- updateTo( to );
-
if( mCurrentCallEvent == null )
{
mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.TELEPHONE_INTERCONNECT )
@@ -859,6 +858,8 @@ public class P25DecoderState extends DecoderState
broadcast( mCurrentCallEvent );
}
+
+ updateTo( to );
}
break;
case UNIT_AUTHENTICATION_COMMAND:
@@ -921,10 +922,7 @@ public class P25DecoderState extends DecoderState
(module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser)tdulc;
String from = uuvcu.getSourceAddress();
- updateFrom( from );
-
String to = uuvcu.getTargetAddress();
- updateTo( to );
if( mCurrentCallEvent != null )
{
@@ -938,6 +936,9 @@ public class P25DecoderState extends DecoderState
.to( to )
.build();
}
+
+ updateFrom( from );
+ updateTo( to );
}
break;
default:
@@ -949,14 +950,35 @@ public class P25DecoderState extends DecoderState
* 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 message )
+ private void logAlternateVendorMessage( P25Message p25 )
{
-// if( message.isValid() )
+// if( p25 instanceof TDULinkControlMessage )
// {
-// mLog.info( "PLEASE NOTIFY DEVELOPER - UNRECOGNIZED P25 VENDOR FORMAT -"
-// + " DUID:" + message.getDUID()
-// + " MSG:" + message.getBinaryMessage()
-// + " CLASS:" + message.getClass() );
+// 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() + "]" );
// }
}
@@ -967,11 +989,15 @@ public class P25DecoderState extends DecoderState
*/
private void processLDU( LDUMessage ldu )
{
- broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CALL ) );
+ State state = ( ldu.isValid() && ldu.isEncrypted() ) ?
+ State.ENCRYPTED : State.CALL;
+
+ broadcast( new DecoderStateEvent( this, Event.CONTINUATION, state ) );
if( mCurrentCallEvent == null )
{
- mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.CALL )
+ mCurrentCallEvent = new P25CallEvent.Builder(
+ ( state == State.CALL ) ? CallEventType.CALL : CallEventType.ENCRYPTED_CALL )
.aliasList( getAliasList() )
.channel( mCurrentChannel )
.frequency( mCurrentChannelFrequency )
@@ -1031,7 +1057,7 @@ public class P25DecoderState extends DecoderState
}
break;
case CALL_TERMINATION_OR_CANCELLATION:
- broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) );
+ broadcast( new DecoderStateEvent( this, Event.END, State.END ) );
mCurrentCallEvent = null;
if( !( ldu instanceof CallTermination ) )
@@ -1089,43 +1115,42 @@ public class P25DecoderState extends DecoderState
}
break;
case GROUP_VOICE_CHANNEL_UPDATE:
- if( ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate )
+ 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;
+ (module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate)ldu;
String userA = gvcu.getGroupAddressA();
+
+ if( !mCallDetectTalkgroups.contains( userA ) )
+ {
+ broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
+ .aliasList( getAliasList() )
+ .channel( gvcu.getChannelA() )
+ .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) )
+ .frequency( gvcu.getDownlinkFrequencyA() )
+ .to( userA )
+ .build() );
+
+ mCallDetectTalkgroups.add( userA );
+ }
+
String userB = gvcu.getGroupAddressB();
- if( mChannelType == ChannelType.STANDARD )
+ if( userB != null &&
+ !userB.contentEquals( "0000" ) &&
+ !mCallDetectTalkgroups.contains( userB ) )
{
- if( !mCallDetectTalkgroups.contains( userA ) )
- {
- broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
- .aliasList( getAliasList() )
- .channel( gvcu.getChannelA() )
- .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) )
- .frequency( gvcu.getDownlinkFrequencyA() )
- .to( userA )
- .build() );
-
- mCallDetectTalkgroups.add( userA );
- }
+ broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
+ .aliasList( getAliasList() )
+ .channel( gvcu.getChannelB() )
+ .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) )
+ .frequency( gvcu.getDownlinkFrequencyB() )
+ .to( userB )
+ .build() );
- if( userB != null &&
- !userB.contentEquals( "0000" ) &&
- !mCallDetectTalkgroups.contains( userB ) )
- {
- broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
- .aliasList( getAliasList() )
- .channel( gvcu.getChannelB() )
- .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) )
- .frequency( gvcu.getDownlinkFrequencyB() )
- .to( userB )
- .build() );
-
- mCallDetectTalkgroups.add( userB );
- }
+ mCallDetectTalkgroups.add( userB );
}
}
else
@@ -1134,27 +1159,25 @@ public class P25DecoderState extends DecoderState
}
break;
case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT:
- if( ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit )
+ 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;
String group = gvcue.getGroupAddress();
- if( mChannelType == ChannelType.STANDARD )
+ if( !mCallDetectTalkgroups.contains( group ) )
{
- if( !mCallDetectTalkgroups.contains( group ) )
- {
- broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
- .aliasList( getAliasList() )
- .channel( gvcue.getTransmitChannel() )
- .details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) )
- .frequency( gvcue.getDownlinkFrequency() )
- .to( group )
- .build() );
-
- mCallDetectTalkgroups.add( group );
- }
+ broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
+ .aliasList( getAliasList() )
+ .channel( gvcue.getTransmitChannel() )
+ .details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) )
+ .frequency( gvcue.getDownlinkFrequency() )
+ .to( group )
+ .build() );
+
+ mCallDetectTalkgroups.add( group );
}
}
else
@@ -1164,32 +1187,29 @@ public class P25DecoderState extends DecoderState
break;
case GROUP_VOICE_CHANNEL_USER:
/* Indicates the current user of the current channel */
- if( ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUser )
+ 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;
- updateFrom( gvcuser.getSourceAddress() );
-
- updateTo( gvcuser.getGroupAddress() );
-
- if( mChannelType == ChannelType.STANDARD )
+ if( mCurrentCallEvent.getCallEventType() != CallEventType.GROUP_CALL )
{
- if( mCurrentCallEvent.getCallEventType() != CallEventType.GROUP_CALL )
- {
- mCurrentCallEvent.setCallEventType( CallEventType.GROUP_CALL );
- broadcast( mCurrentCallEvent );
- }
-
- if( mCurrentCallEvent.getDetails() == null )
- {
- mCurrentCallEvent.setDetails(
- ( gvcuser.isEncrypted() ? "ENCRYPTED " : "" ) +
- ( gvcuser.isEmergency() ? "EMERGENCY " : "" ) );
-
- broadcast( mCurrentCallEvent );
- }
+ mCurrentCallEvent.setCallEventType( CallEventType.GROUP_CALL );
+ broadcast( mCurrentCallEvent );
}
+
+ if( mCurrentCallEvent.getDetails() == null )
+ {
+ mCurrentCallEvent.setDetails(
+ ( gvcuser.isEncrypted() ? "ENCRYPTED " : "" ) +
+ ( gvcuser.isEmergency() ? "EMERGENCY " : "" ) );
+
+ broadcast( mCurrentCallEvent );
+ }
+
+ updateFrom( gvcuser.getSourceAddress() );
+ updateTo( gvcuser.getGroupAddress() );
}
else
{
@@ -1385,8 +1405,6 @@ public class P25DecoderState extends DecoderState
TelephoneInterconnectVoiceChannelUser tivcu =
(TelephoneInterconnectVoiceChannelUser)ldu;
- updateTo( tivcu.getAddress() );
-
if( mCurrentCallEvent.getCallEventType() != CallEventType.TELEPHONE_INTERCONNECT )
{
mCurrentCallEvent.setCallEventType( CallEventType.TELEPHONE_INTERCONNECT );
@@ -1401,6 +1419,8 @@ public class P25DecoderState extends DecoderState
broadcast( mCurrentCallEvent );
}
+
+ updateTo( tivcu.getAddress() );
}
else
{
@@ -1466,10 +1486,6 @@ public class P25DecoderState extends DecoderState
UnitToUnitVoiceChannelUser uuvcu =
(UnitToUnitVoiceChannelUser)ldu;
- updateFrom( uuvcu.getSourceAddress() );
-
- updateTo( uuvcu.getTargetAddress() );
-
if( mCurrentCallEvent.getCallEventType() != CallEventType.UNIT_TO_UNIT_CALL )
{
mCurrentCallEvent.setCallEventType( CallEventType.UNIT_TO_UNIT_CALL );
@@ -1484,6 +1500,9 @@ public class P25DecoderState extends DecoderState
broadcast( mCurrentCallEvent );
}
+
+ updateFrom( uuvcu.getSourceAddress() );
+ updateTo( uuvcu.getTargetAddress() );
}
else
{
@@ -1502,7 +1521,7 @@ public class P25DecoderState extends DecoderState
private void processTSBK( TSBKMessage tsbk )
{
/* Trunking Signalling Block Messages - indicates Control Channel */
- broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) );
+ broadcast( new DecoderStateEvent( this, Event.START, State.CONTROL ) );
if( tsbk.getVendor() == Vendor.STANDARD )
{
@@ -1631,11 +1650,7 @@ public class P25DecoderState extends DecoderState
*/
private void processPDU( PDUMessage pdu )
{
- if( pdu instanceof PacketData || pdu instanceof PDUTypeUnknown )
- {
- broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.DATA ) );
- }
- else if( pdu instanceof PDUConfirmedMessage )
+ if( pdu instanceof PDUConfirmedMessage )
{
broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.DATA ) );
@@ -1643,7 +1658,7 @@ public class P25DecoderState extends DecoderState
switch( pduc.getPDUType() )
{
- case SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT:
+ case SN_ACTIVATE_TDS_CONTEXT_ACCEPT:
SNDCPActivateTDSContextAccept satca =
(SNDCPActivateTDSContextAccept)pduc;
@@ -1655,7 +1670,7 @@ public class P25DecoderState extends DecoderState
.to( satca.getLogicalLinkID() )
.build() );
break;
- case SNDCP_ACTIVATE_TDS_CONTEXT_REJECT:
+ case SN_ACTIVATE_TDS_CONTEXT_REJECT:
SNDCPActivateTDSContextReject satcr =
(SNDCPActivateTDSContextReject)pduc;
@@ -1680,7 +1695,7 @@ public class P25DecoderState extends DecoderState
.from( satcreq.getLogicalLinkID() )
.build() );
break;
- case SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT:
+ case SN_DEACTIVATE_TDS_CONTEXT_ACCEPT:
SNDCPDeactivateTDSContext sdtca =
(SNDCPDeactivateTDSContext)pduc;
@@ -1692,7 +1707,7 @@ public class P25DecoderState extends DecoderState
.from( sdtca.getLogicalLinkID() )
.build() );
break;
- case SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST:
+ case SN_DEACTIVATE_TDS_CONTEXT_REQUEST:
SNDCPDeactivateTDSContext sdtcreq =
(SNDCPDeactivateTDSContext)pduc;
@@ -1704,7 +1719,7 @@ public class P25DecoderState extends DecoderState
.from( sdtcreq.getLogicalLinkID() )
.build() );
break;
- case SNDCP_RF_CONFIRMED_DATA:
+ case SN_RF_CONFIRMED_DATA:
SNDCPUserData sud = (SNDCPUserData)pduc;
StringBuilder sbFrom = new StringBuilder();
@@ -1733,10 +1748,8 @@ public class P25DecoderState extends DecoderState
.to( pduc.getLogicalLinkID() )
.build() );
break;
- case SNDCP_RF_UNCONFIRMED_DATA:
- break;
default:
-// mLog.debug( "PDUC - Unrecognized Message: " + pduc.toString() );
+ mLog.debug( "PDUC - Unrecognized Message: " + pduc.toString() );
break;
}
}
@@ -3087,9 +3100,16 @@ public class P25DecoderState extends DecoderState
if( mToTalkgroup == null || !mToTalkgroup.contentEquals( to ) )
{
mToTalkgroup = to;
-
broadcast( ChangedAttribute.TO_TALKGROUP );
+ if( mCurrentCallEvent != null &&
+ ( mCurrentCallEvent.getToID() == null ||
+ !mCurrentCallEvent.getToID().contentEquals( to ) ) )
+ {
+ mCurrentCallEvent.setToID( to );
+ broadcast( mCurrentCallEvent );
+ }
+
if( hasAliasList() )
{
mToAlias = getAliasList().getTalkgroupAlias( mToTalkgroup );
@@ -3098,20 +3118,12 @@ public class P25DecoderState extends DecoderState
{
mToAlias = null;
}
-
+
broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
-
- /* Send audio metadata update */
- broadcast( new Metadata( MetadataType.TO, mToTalkgroup, mToAlias, true ) );
-
- if( mCurrentCallEvent != null &&
- ( mCurrentCallEvent.getToID() == null ||
- !mCurrentCallEvent.getToID().contentEquals( to ) ) )
- {
- mCurrentCallEvent.setToID( to );
- broadcast( mCurrentCallEvent );
- }
}
+
+ /* Send audio metadata update */
+ broadcast( new Metadata( MetadataType.TO, mToTalkgroup, mToAlias, true ) );
}
}
@@ -3127,6 +3139,14 @@ public class P25DecoderState extends DecoderState
mFromTalkgroup = from;
broadcast( ChangedAttribute.FROM_TALKGROUP );
+ if( mCurrentCallEvent != null &&
+ mCurrentCallEvent.getFromID() == null ||
+ !mCurrentCallEvent.getFromID().contentEquals( from ) )
+ {
+ mCurrentCallEvent.setFromID( from );
+ broadcast( mCurrentCallEvent );
+ }
+
if( hasAliasList() )
{
mFromAlias = getAliasList().getTalkgroupAlias( mFromTalkgroup );
@@ -3137,19 +3157,10 @@ public class P25DecoderState extends DecoderState
}
broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS );
-
- /* Send audio metadata update */
- broadcast( new Metadata( MetadataType.FROM, mFromTalkgroup, mFromAlias, true ) );
-
- if( mCurrentCallEvent != null &&
- mCurrentCallEvent.getFromID() == null ||
- !mCurrentCallEvent.getFromID().contentEquals( from ) )
- {
- mCurrentCallEvent.setFromID( from );
- broadcast( mCurrentCallEvent );
- }
}
+ /* Send audio metadata update */
+ broadcast( new Metadata( MetadataType.FROM, mFromTalkgroup, mFromAlias, true ) );
}
}
diff --git a/src/module/decode/p25/message/P25Message.java b/src/module/decode/p25/message/P25Message.java
index dc33108f..e8421211 100644
--- a/src/module/decode/p25/message/P25Message.java
+++ b/src/module/decode/p25/message/P25Message.java
@@ -87,8 +87,6 @@ public class P25Message extends Message
sb.append( " " );
sb.append( getDUID().getLabel() );
- sb.append( " DATA UNIT ID:" + mMessage.getInt( DUID ) );
-
return sb.toString();
}
diff --git a/src/module/decode/p25/message/pdu/PDUMessageFactory.java b/src/module/decode/p25/message/pdu/PDUMessageFactory.java
index 1e11796d..26b25b1c 100644
--- a/src/module/decode/p25/message/pdu/PDUMessageFactory.java
+++ b/src/module/decode/p25/message/pdu/PDUMessageFactory.java
@@ -1,6 +1,5 @@
package module.decode.p25.message.pdu;
-import module.decode.p25.message.pdu.confirmed.PacketData;
import module.decode.p25.message.pdu.osp.control.AdjacentStatusBroadcastExtended;
import module.decode.p25.message.pdu.osp.control.CallAlertExtended;
import module.decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended;
@@ -122,8 +121,7 @@ public class PDUMessageFactory
}
return new PDUMessage( message, duid, aliasList );
- case PACKET_DATA:
- return new PacketData( message, aliasList );
+
case UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL:
return new PDUMessage( message, duid, aliasList );
default:
diff --git a/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java b/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java
index e8c08bcb..b695dd79 100644
--- a/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java
+++ b/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java
@@ -48,27 +48,19 @@ public class PDUConfirmedMessageFactory
case UNENCRYPTED_USER_DATA:
switch( message.getPDUType() )
{
- case SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT:
+ case SN_ACTIVATE_TDS_CONTEXT_ACCEPT:
return new SNDCPActivateTDSContextAccept( message );
- case SNDCP_ACTIVATE_TDS_CONTEXT_REJECT:
+ case SN_ACTIVATE_TDS_CONTEXT_REJECT:
return new SNDCPActivateTDSContextReject( message );
case SN_ACTIVATE_TDS_CONTEXT_REQUEST:
return new SNDCPActivateTDSContextRequest( message );
- case SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT:
- case SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST:
+ case SN_DEACTIVATE_TDS_CONTEXT_ACCEPT:
+ case SN_DEACTIVATE_TDS_CONTEXT_REQUEST:
return new SNDCPDeactivateTDSContext( message );
- case SNDCP_RF_CONFIRMED_DATA:
+ case SN_RF_CONFIRMED_DATA:
return new SNDCPUserData( message );
- case SNDCP_RF_UNCONFIRMED_DATA:
+ case SN_RF_UNCONFIRMED_DATA:
break;
- case PDU_TYPE_6:
- case PDU_TYPE_7:
- case PDU_TYPE_8:
- case PDU_TYPE_9:
- case PDU_TYPE_10:
- case PDU_TYPE_11:
- return new PDUTypeUnknown( message );
- case UNKNOWN:
default:
break;
}
diff --git a/src/module/decode/p25/message/pdu/confirmed/PDUTypeUnknown.java b/src/module/decode/p25/message/pdu/confirmed/PDUTypeUnknown.java
deleted file mode 100644
index 0f08ca73..00000000
--- a/src/module/decode/p25/message/pdu/confirmed/PDUTypeUnknown.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package module.decode.p25.message.pdu.confirmed;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class PDUTypeUnknown extends PDUConfirmedMessage
-{
- public final static Logger mLog =
- LoggerFactory.getLogger( PDUTypeUnknown.class );
-
- public PDUTypeUnknown( PDUConfirmedMessage message )
- {
- super( message );
- }
-
- @Override
- public String getMessage()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( "NAC:" );
- sb.append( getNAC() );
- sb.append( " LLID:" );
- sb.append( getLogicalLinkID() );
- sb.append( " PACKET DATA UNIT CONFIRMED - " );
- sb.append( getPDUType().getLabel() );
- sb.append( " CRC[" );
- sb.append( getErrorStatus() );
- sb.append( "]" );
-
- sb.append( " PACKET #" );
- sb.append( getPacketSequenceNumber() );
-
- if( isFinalFragment() && getFragmentSequenceNumber() == 0 )
- {
- sb.append( ".C" );
- }
- else
- {
- sb.append( "." );
- sb.append( getFragmentSequenceNumber() );
-
- if( isFinalFragment() )
- {
- sb.append( "C" );
- }
- }
-
- sb.append( " " );
-
- sb.append( mMessage.toString() );
-
- return sb.toString();
- }
-}
diff --git a/src/module/decode/p25/message/pdu/confirmed/PacketData.java b/src/module/decode/p25/message/pdu/confirmed/PacketData.java
deleted file mode 100644
index e1b0af6d..00000000
--- a/src/module/decode/p25/message/pdu/confirmed/PacketData.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package module.decode.p25.message.pdu.confirmed;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import alias.AliasList;
-import bits.BinaryMessage;
-
-public class PacketData extends PDUConfirmedMessage
-{
- public final static Logger mLog =
- LoggerFactory.getLogger( PacketData.class );
-
- public static final int DATA_BLOCK_START = 176;
-
- public PacketData( BinaryMessage message, AliasList aliasList )
- {
- super( message, aliasList );
- }
-
- @Override
- public String getMessage()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( "NAC:" );
- sb.append( getNAC() );
- sb.append( " PDUC LLID:" );
- sb.append( getLogicalLinkID() );
-
-
- sb.append( " PACKET DATA" );
- sb.append( " CRC[" );
- sb.append( getErrorStatus() );
- sb.append( "]" );
-
- if( !mMessage.get( FINAL_FRAGMENT_FLAG ) )
- {
- sb.append( " RESENDING" );
- }
-
- sb.append( " PACKET #" );
- sb.append( getPacketSequenceNumber() );
-
- if( isFinalFragment() && getFragmentSequenceNumber() == 0 )
- {
- sb.append( ".C" );
- }
- else
- {
- sb.append( "." );
- sb.append( getFragmentSequenceNumber() );
-
- if( isFinalFragment() )
- {
- sb.append( "C" );
- }
- }
-
- sb.append( " " );
-
- sb.append( mMessage.toString() );
-
- return sb.toString();
- }
-}
diff --git a/src/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java b/src/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java
index ec8ec0b5..859303be 100644
--- a/src/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java
+++ b/src/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java
@@ -49,7 +49,7 @@ public class SNDCPDeactivateTDSContext extends PDUConfirmedMessage
public String getDeactivationType()
{
- if( getPDUType() == PDUType.SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST )
+ if( getPDUType() == PDUType.SN_DEACTIVATE_TDS_CONTEXT_REQUEST )
{
return mMessage.getInt( DEACTIVATION_TYPE ) == 0 ?
"ALL NSAPIS" : "THIS NSAPI";
diff --git a/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java b/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java
index 3e8ab315..e06ac784 100644
--- a/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java
+++ b/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java
@@ -1,7 +1,5 @@
package module.decode.p25.message.tsbk;
-import alias.AliasList;
-import bits.BinaryMessage;
import module.decode.p25.message.tsbk.motorola.ControlChannelBaseStationIdentification;
import module.decode.p25.message.tsbk.motorola.MotorolaOpcode;
import module.decode.p25.message.tsbk.motorola.MotorolaTSBKMessage;
@@ -21,7 +19,6 @@ 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.IdentifierUpdateTDMA;
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;
@@ -59,6 +56,8 @@ import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdat
import module.decode.p25.reference.DataUnitID;
import module.decode.p25.reference.Opcode;
import module.decode.p25.reference.Vendor;
+import alias.AliasList;
+import bits.BinaryMessage;
public class TSBKMessageFactory
{
@@ -120,8 +119,6 @@ public class TSBKMessageFactory
case IDENTIFIER_UPDATE_NON_VUHF:
return new IdentifierUpdateNonVUHF( message, duid,
aliasList );
- case IDENTIFIER_UPDATE_TDMA:
- return new IdentifierUpdateTDMA( message, duid, aliasList );
case IDENTIFIER_UPDATE_VHF_UHF_BANDS:
return new IdentifierUpdateVUHF( message, duid,
aliasList );
diff --git a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java
deleted file mode 100644
index ca31b7c6..00000000
--- a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java
+++ /dev/null
@@ -1,159 +0,0 @@
-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;
-
-public class IdentifierUpdateTDMA extends IdentifierUpdate
-{
- public static final int[] CHANNEL_TYPE = { 84,85,86,87 };
- public static final int TRANSMIT_OFFSET_SIGN = 88;
- public static final int[] TRANSMIT_OFFSET = { 89,90,91,92,93,94,95,96,97,98,
- 99,100,101 };
-
- public IdentifierUpdateTDMA( BinaryMessage message,
- DataUnitID duid,
- AliasList aliasList )
- {
- super( message, duid, aliasList );
- }
-
- @Override
- public String getEventType()
- {
- return Opcode.IDENTIFIER_UPDATE_TDMA.getDescription();
- }
-
- public String getMessage()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( getMessageStub() );
-
- sb.append( " IDEN:" + getIdentifier() );
- sb.append( " BASE:" + getBaseFrequency() );
- sb.append( " BW:" + getBandwidth() );
- sb.append( " SPACING:" + getChannelSpacing() );
- sb.append( " OFFSET:" + getTransmitOffset() );
- sb.append( " TYPE:" + getChannelType().toString() );
-
- return sb.toString();
- }
-
- public ChannelType getChannelType()
- {
- return ChannelType.fromValue( mMessage.getInt( CHANNEL_TYPE) );
- }
-
- /**
- * Channel bandwidth in hertz
- */
- public int getBandwidth()
- {
- return getChannelType().getBandwidth();
- }
-
- /**
- * Transmit offset in hertz
- */
- @Override
- public long getTransmitOffset()
- {
- long offset = mMessage.getLong( TRANSMIT_OFFSET ) *
- getChannelSpacing();
-
- if( mMessage.get( TRANSMIT_OFFSET_SIGN ) )
- {
- return offset;
- }
- else
- {
- return -offset;
- }
- }
-
- public enum ChannelType
- {
- TYPE_0( AccessType.FDMA, 12500, 1, Vocoder.HALF_RATE ),
- TYPE_1( AccessType.FDMA, 12500, 1, Vocoder.FULL_RATE ),
- TYPE_2( AccessType.FDMA, 6250, 1, Vocoder.HALF_RATE ),
- TYPE_3( AccessType.TDMA, 12500, 2, Vocoder.HALF_RATE ),
- TYPE_4( AccessType.TDMA, 25000, 4, Vocoder.HALF_RATE ),
- TYPE_5( AccessType.TDMA, 12500, 2, Vocoder.HALF_RATE ), //HD8PSK simulcast
- UNKNOWN( AccessType.UNKNOWN, 0, 0, Vocoder.HALF_RATE );
-
- private AccessType mAccessType;
- private int mBandwidth;
- private int mSlotsPerCarrier;
- private Vocoder mVocoder;
-
- private ChannelType( AccessType accessType,
- int bandwidth,
- int slots,
- Vocoder vocoder )
- {
- mAccessType = accessType;
- mBandwidth = bandwidth;
- mSlotsPerCarrier = slots;
- mVocoder = vocoder;
- }
-
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
-
- if( this.equals( TYPE_5 ) )
- {
- sb.append("H-D8PSK SIMULCAST " );
- }
-
- sb.append( mAccessType.name() );
- sb.append( " BW:" + mBandwidth );
- sb.append( " SLOT/CARRIER:" + mSlotsPerCarrier );
- sb.append( " VOCODER:" + mVocoder.name() );
-
- return sb.toString();
- }
-
- public static ChannelType fromValue( int value )
- {
- if( 0 <= value && value <= 5 )
- {
- return ChannelType.values()[ value ];
- }
-
- return ChannelType.UNKNOWN;
- }
-
- public AccessType getAccessType()
- {
- return mAccessType;
- }
-
- public int getBandwidth()
- {
- return mBandwidth;
- }
-
- public int getSlotsPerCarrier()
- {
- return mSlotsPerCarrier;
- }
-
- public Vocoder getVocoder()
- {
- return mVocoder;
- }
- }
-
- public enum AccessType
- {
- FDMA,TDMA,UNKNOWN;
- }
-
- public enum Vocoder
- {
- HALF_RATE, FULL_RATE;
- }
-}
diff --git a/src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java
index c59d8f12..50c09e73 100644
--- a/src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java
+++ b/src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java
@@ -1,10 +1,8 @@
package module.decode.p25.message.tsbk.osp.control;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
-import java.util.SimpleTimeZone;
+import java.util.GregorianCalendar;
import java.util.TimeZone;
import module.decode.p25.message.tsbk.TSBKMessage;
@@ -14,21 +12,20 @@ import alias.AliasList;
import bits.BinaryMessage;
/**
- * Sync Broadcast - used to broadcast FDMA-TDMA timing synchronization
- * information so that a subscriber unit that is time synchronized on an FDMA
- * control channel is also time synchronized with all TDMA traffic channels at
- * the site.
+ * Sync Broadcast - used to broadcast FDMA-TDMA synchronization timestamp so
+ * that a subscriber unit that is time synchronized on an FDMA control channel
+ * is also synchronized with a TDMA traffic channel.
*/
public class SyncBroadcast extends TSBKMessage
{
- public static final int[] RESERVED = { 80,81,82,83,84,85,86,87,88,89,90,91,92 };
- public static final int SYSTEM_TIME_NOT_LOCKED_TO_EXTERNAL_REFERENCE = 93;
- public static final int MICRO_SLOTS_TO_MINUTE_ROLLOVER_UNLOCKED = 94;
- public static final int[] LEAP_SECOND_CORRECTION = { 95,96 };
- public static final int LOCAL_TIME_OFFSET_VALID = 97;
+ public static final int US = 92;
+ public static final int IST = 93;
+ public static final int MMU = 94;
+ public static final int[] MC = { 95,96 };
+ public static final int VL = 97;
public static final int LOCAL_TIME_OFFSET_SIGN = 98;
- public static final int[] LOCAL_TIME_OFFSET_HOURS = { 99,100,101,102 };
- public static final int LOCAL_TIME_OFFSET_HALF_HOUR = 103;
+ public static final int[] LOCAL_TIME_OFFSET = { 99,100,101,102 };
+ public static final int LOCAL_TIME_OFFSET_HALF = 103;
public static final int[] YEAR = { 104,105,106,107,108,109,110 };
public static final int[] MONTH = { 111,112,113,114 };
@@ -39,10 +36,7 @@ public class SyncBroadcast extends TSBKMessage
139,140,141,142,143 };
public static final int[] TSBK_CRC = { 144,145,146,147,148,149,150,151,152,
153,154,155,156,157,158,159 };
-
- private static final DateFormat TIME_FORMATTER = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSS Z" );
- private static final TimeZone NO_TIME_ZONE = new SimpleTimeZone( 0, "NONE" );
-
+
public SyncBroadcast( BinaryMessage message,
DataUnitID duid,
AliasList aliasList )
@@ -62,41 +56,24 @@ public class SyncBroadcast extends TSBKMessage
sb.append( getMessageStub() );
- sb.append( " SYSTEM TIME" );
-
- if( isSystemTimeNotLockedToExternalReference() )
- {
- sb.append( " UNLOCKED" );
- }
-
- sb.append( ":" );
-
- TIME_FORMATTER.setTimeZone( getTimeZone() );
- sb.append( " " + TIME_FORMATTER.format( new Date( getSystemTime() ) ) );
-
- sb.append( " LEAP-SECOND CORRECTION:" + getLeapSecondCorrection() + "mS" );
-
- if( isMicroslotsLockedToMinuteRollover() )
- {
- sb.append( " MICROSLOT-MINUTE ROLLOVER:LOCKED" );
- }
- else
- {
- sb.append( " MICROSLOT-MINUTE ROLLOVER:UNLOCKED" );
- }
+ sb.append( " " + new Date( getTime() ).toString() );
+ sb.append( " TIME ZONE OFFSET:" + getTimeZone().toUpperCase() );
+ sb.append( " US:" + isUS() );
+ sb.append( " IST:" + isIST() );
+ sb.append( " MMU:" + isMMU() );
+ sb.append( " VL:" + isVL() );
return sb.toString();
}
/**
- * System Time (UTC) in milliseconds since java epoch
+ * Returns the time sync in java time (ie milliseconds since epoch )
*/
- public long getSystemTime()
+ public long getTime()
{
- Calendar cal = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) );
-
+ Calendar cal = new GregorianCalendar();
cal.clear();
-
+ cal.setTimeZone( TimeZone.getTimeZone( "GMT" ) );
cal.set( Calendar.YEAR, getYear() );
cal.set( Calendar.MONTH, getMonth() - 1 );
cal.set( Calendar.DAY_OF_MONTH, getDay() );
@@ -107,165 +84,102 @@ public class SyncBroadcast extends TSBKMessage
return cal.getTimeInMillis();
}
- /**
- * System Time not locked to external time reference indicator
- */
- public boolean isSystemTimeNotLockedToExternalReference()
+ public boolean isUS()
{
- return mMessage.get( SYSTEM_TIME_NOT_LOCKED_TO_EXTERNAL_REFERENCE );
+ return mMessage.get( US );
}
/**
- * Micro-slot-to-Minute rollover boundary lock indicator.
- *
- * Note: we invert the message value so that true indicates that the micro
- * slots and minute rollovers are locked, whereas the ICD uses false to
- * indicate a lock.
- *
- * @return true if the microslots rollover at each minute interval is locked
- * to the system time minute rollover.
- *
- * @return false if the microslots and minutes rollover are not locked. This
- * indicates that the microslots minute rollover is free rolling.
- *
+ * Daylight Savings Time
*/
- public boolean isMicroslotsLockedToMinuteRollover()
+ public boolean isIST()
{
- return !mMessage.get( MICRO_SLOTS_TO_MINUTE_ROLLOVER_UNLOCKED );
+ return mMessage.get( IST );
}
/**
- * Leap second correction value that should be applied to the system time.
- *
- * The leap-second correction field indicates the number of 2.5 millisecond
- * units that must be added to the system time to account for the insertion
- * of leap seconds when the system time is represented as UTC universal time.
- *
- * @return leap-second correction value in milliseconds 0.0-10.0
+ * No idea ...
*/
- public double getLeapSecondCorrection()
+ public boolean isMMU()
{
- return (double)mMessage.getInt( LEAP_SECOND_CORRECTION ) * 2.5d;
- }
-
-
-
- /**
- * Indicates the local time offset fields contain valid information.
- */
- public boolean isValidLocalTimeOffset()
- {
- return !mMessage.get( LOCAL_TIME_OFFSET_VALID );
+ return mMessage.get( MMU );
}
/**
- * Local time zone offset from UTC universal time (zulu) when the VALID
- * LOCAL TIME OFFSET flag indicates a valid offset. Otherwise, this method
- * returns a static +00:00 indicating no local time offset.
+ * Millenium ...?
*/
- public TimeZone getTimeZone()
+ public int getMC()
{
- if( isValidLocalTimeOffset() )
- {
- int offset = 0;
-
- offset += mMessage.getInt( LOCAL_TIME_OFFSET_HOURS ) * 3600000;
- offset += mMessage.get( LOCAL_TIME_OFFSET_HALF_HOUR ) ? 1800000 : 0;
- offset = mMessage.get( LOCAL_TIME_OFFSET_SIGN ) ? -offset : offset;
-
- return new SimpleTimeZone( offset, "LOCAL" );
- }
- else
- {
- return NO_TIME_ZONE;
- }
+ return mMessage.getInt( MC );
}
/**
- * System time - year.
- *
- * @return year in range 2000-2127
+ * No idea ....
*/
+ public boolean isVL()
+ {
+ return mMessage.get( VL );
+ }
+
+ /**
+ * Calculates the time zone from the local time offset field.
+ */
+ public String getTimeZone()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( mMessage.get( LOCAL_TIME_OFFSET_SIGN ) ? "-" : "+" );
+
+ int hours = mMessage.getInt( LOCAL_TIME_OFFSET );
+ sb.append( ( hours < 10 ? "0" : "" ) + String.valueOf( hours ) );
+
+ sb.append( ":" );
+
+ sb.append( mMessage.get( LOCAL_TIME_OFFSET_HALF ) ? "30" : "00" );
+
+ if( mMessage.get( IST ) )
+ {
+ sb.append( " DST" );
+ }
+
+ return sb.toString();
+ }
+
public int getYear()
{
return 2000 + mMessage.getInt( YEAR );
}
- /**
- * System time - month
- *
- * @return month 1-12 where 1=January and 12=December
- */
public int getMonth()
{
return mMessage.getInt( MONTH );
}
- /**
- * System time - day of month
- *
- * @return day of month in range 1-31
- */
public int getDay()
{
return mMessage.getInt( DAY );
}
- /**
- * System time - hours
- *
- * @return hours in range 0 - 23
- */
public int getHours()
{
return mMessage.getInt( HOURS );
}
- /**
- * System time - minutes
- *
- * @return minutes in range 0 - 59
- */
public int getMinutes()
{
return mMessage.getInt( MINUTES );
}
- /**
- * System time - milli-seconds
- *
- * Note: sub-millisecond values are rounded to nearest millisecond unit to
- * conform to java Date internal milli-second precision level.
- *
- * @return milli-seconds in range 0 - 59999;
- */
public int getMilliSeconds()
{
return (int)( (double)getMicroSlots() * 7.5 );
}
/**
- * TDMA Micro Slots.
- *
- * Number of 7.5 millisecond micro slots since the last minute rollover, or
- * since the last micro-slot counter rollover when the the micro-slot to
- * minute rollover is unlocked.
- *
- * @see isMicroslotsLockedToMinuteRollover() method.
- *
- * This value supports mobile subscriber timing alignment with a TDMA traffic
- * channel when a channel is granted from the control channels, so that when
- * the mobile changes from the control channel to the traffic channel, it is
- * already aligned with the traffic channel TDMA timing.
- *
- * Super frames occur every 360 ms (48 micro-slots) on FDMA and Ultra frames
- * occur every 4 super frames on TDMA.
- *
- * Valid micro-slot values range 0-7999 and represent 60,000
- * milliseconds ( 8000 x 7.5 ms).
- *
- * @return number of 7.5 mS micro-slots since last minute or micro-slot
- * rollover
+ * Micro Slots - each uslot = 7.5 milliseconds. The value ranges 0 - 7999
+ * and represents 60,000 milliseconds ( 8000 x 7.5 ms). Superframes occur
+ * every 360 ms (48 micro-slots) on FDMA and Ultraframes occur every 4
+ * super frames on TDMA.
*/
public int getMicroSlots()
{
diff --git a/src/module/decode/p25/reference/Opcode.java b/src/module/decode/p25/reference/Opcode.java
index d94afec8..4dbf8870 100644
--- a/src/module/decode/p25/reference/Opcode.java
+++ b/src/module/decode/p25/reference/Opcode.java
@@ -53,7 +53,7 @@ public enum Opcode
TDMA_SYNC_BROADCAST( "TDMA_SYNC_BCST ", "FDMA-TDMA Sync Broadcast", 48 ),
RESERVED_31( "RESERVED_31 ", "Reserved", 49 ),
RESERVED_32( "RESERVED_32 ", "Reserved", 50 ),
- IDENTIFIER_UPDATE_TDMA( "IDEN_UPDATE_TDMA", "Identifier Update for TDMA", 51 ),
+ RESERVED_33( "RESERVED_33 ", "Reserved", 51 ),
IDENTIFIER_UPDATE_VHF_UHF_BANDS( "IDEN_UPDATE_VUHF", "Identifier Update for VHF/UHF Bands", 52 ),
TIME_DATE_ANNOUNCEMENT( "TIME_DATE_ANNOUN", "Time and Date Announcement", 53 ),
ROAMING_ADDRESS_COMMAND( "ROAM_ADDR_CMD ", "Roaming Address Command", 54 ),
diff --git a/src/module/decode/p25/reference/PDUType.java b/src/module/decode/p25/reference/PDUType.java
index 20acb4ab..23f5da1e 100644
--- a/src/module/decode/p25/reference/PDUType.java
+++ b/src/module/decode/p25/reference/PDUType.java
@@ -3,18 +3,12 @@ package module.decode.p25.reference;
public enum PDUType
{
/* Outbound */
- SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT( "SNDCP-ACTIVATE TDS CONTEXT ACCEPT", 0 ),
- SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT( "SNDCP-DEACTIVATE TDS CONTEXT ACCEPT", 1 ),
- SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST( "SNDCP-DEACTIVATE TDS CONTEXT REQUEST", 2 ),
- SNDCP_ACTIVATE_TDS_CONTEXT_REJECT( "SNDCP-ACTIVATE TDS CONTEXT REJECT", 3 ),
- SNDCP_RF_UNCONFIRMED_DATA( "SNDCP-RF UNCONFIRMED DATA", 4 ),
- SNDCP_RF_CONFIRMED_DATA( "SNDCP-RF CONFIRMED DATA", 5 ),
- PDU_TYPE_6( "PDU TYPE 6 UNKNOWN", 6 ),
- PDU_TYPE_7( "PDU TYPE 7 UNKNOWN", 7 ),
- PDU_TYPE_8( "PDU TYPE 8 UNKNOWN", 8 ),
- PDU_TYPE_9( "PDU TYPE 9 UNKNOWN", 9 ),
- PDU_TYPE_10( "PDU TYPE 10 UNKNOWN", 10 ),
- PDU_TYPE_11( "PDU TYPE 11 UNKNOWN", 11 ),
+ SN_ACTIVATE_TDS_CONTEXT_ACCEPT( "SN-ACTIVATE TDS CONTEXT ACCEPT", 0 ),
+ SN_DEACTIVATE_TDS_CONTEXT_ACCEPT( "SN-DEACTIVATE TDS CONTEXT ACCEPT", 1 ),
+ SN_DEACTIVATE_TDS_CONTEXT_REQUEST( "SN-DEACTIVATE TDS CONTEXT REQUEST", 2 ),
+ SN_ACTIVATE_TDS_CONTEXT_REJECT( "SN-ACTIVATE TDS CONTEXT REJECT", 3 ),
+ SN_RF_UNCONFIRMED_DATA( "SN-RF UNCONFIRMED DATA", 4 ),
+ SN_RF_CONFIRMED_DATA( "SN-RF CONFIRMED DATA", 5 ),
/* Inbound */
SN_ACTIVATE_TDS_CONTEXT_REQUEST( "SN-ACTIVATE TDS CONTEXT REQUEST", 0 ),
@@ -42,7 +36,7 @@ public enum PDUType
public static PDUType fromValue( int value, boolean outbound )
{
- if( outbound && 0 <= value && value <= 11 )
+ if( outbound && 0 <= value && value <= 5 )
{
return values()[ value ];
}
diff --git a/src/module/decode/p25/reference/ServiceAccessPoint.java b/src/module/decode/p25/reference/ServiceAccessPoint.java
index 1040aa7c..ea1c735d 100644
--- a/src/module/decode/p25/reference/ServiceAccessPoint.java
+++ b/src/module/decode/p25/reference/ServiceAccessPoint.java
@@ -2,7 +2,7 @@ package module.decode.p25.reference;
public enum ServiceAccessPoint
{
- UNENCRYPTED_USER_DATA( "UNENCRYPTED USER DATA", 0 ),
+ UNENCRYPTED_USER_DATA( "USER DATA", 0 ),
ENCRYPTED_USER_DATA( "ENCRYPTED USER DATA", 1 ),
CIRCUIT_DATA( "CIRCUIT DATA", 2 ),
CIRCUIT_DATA_CONTROL( "CIRCUIT DATA CONTROL", 3 ),
diff --git a/src/module/decode/passport/PassportDecoder.java b/src/module/decode/passport/PassportDecoder.java
index e125541d..46381d8d 100644
--- a/src/module/decode/passport/PassportDecoder.java
+++ b/src/module/decode/passport/PassportDecoder.java
@@ -106,7 +106,6 @@ public class PassportDecoder extends Decoder
@Override
public void reset()
{
- mPassportMessageFramer.reset();
}
@Override
diff --git a/src/module/decode/state/ChangeChannelTimeoutEvent.java b/src/module/decode/state/ChangeChannelTimeoutEvent.java
deleted file mode 100644
index a2950171..00000000
--- a/src/module/decode/state/ChangeChannelTimeoutEvent.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package module.decode.state;
-
-import controller.channel.Channel.ChannelType;
-
-/**
- * Allows decoders to change the call timeout setting in the channel state
- */
-public class ChangeChannelTimeoutEvent extends DecoderStateEvent
-{
- private ChannelType mChannelType;
- private long mCallTimeout;
-
- public ChangeChannelTimeoutEvent( Object source, ChannelType channelType, long timeout )
- {
- super( source, Event.CHANGE_CALL_TIMEOUT, State.IDLE );
- mChannelType = channelType;
- mCallTimeout = timeout;
- }
-
- public ChannelType getChannelType()
- {
- return mChannelType;
- }
-
- public long getCallTimeout()
- {
- return mCallTimeout;
- }
-}
diff --git a/src/module/decode/state/ChannelList.java b/src/module/decode/state/ChannelList.java
index 5cd8f9b8..be9f9a89 100644
--- a/src/module/decode/state/ChannelList.java
+++ b/src/module/decode/state/ChannelList.java
@@ -136,13 +136,10 @@ public class ChannelList extends JPanel implements ChannelEventListener
public void setSelectedChannel( Channel channel )
{
- synchronized( mDisplayedPanels )
+ /* Send channel selection to each channel panel */
+ for( ChannelCollectionPanel panel: mDisplayedPanels.values() )
{
- /* Send channel selection to each channel panel */
- for( ChannelCollectionPanel panel: mDisplayedPanels.values() )
- {
- panel.setSelectedChannel( channel );
- }
+ panel.setSelectedChannel( channel );
}
}
@@ -261,6 +258,20 @@ public class ChannelList extends JPanel implements ChannelEventListener
idleMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE ) );
+ JMenu noTunerMenu = new JMenu( "No Tuner" );
+ channelsMenu.add( noTunerMenu );
+
+ noTunerMenu.add( new ColorSettingMenuItem( mSettingsManager,
+ ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ) );
+ noTunerMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
+ ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ) );
+ noTunerMenu.add( new JSeparator() );
+
+ noTunerMenu.add( new ColorSettingMenuItem( mSettingsManager,
+ ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ) );
+ noTunerMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
+ ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ) );
+
JMenu labelMenu = new JMenu( "Labels" );
colorMenu.add( labelMenu );
diff --git a/src/module/decode/state/ChannelState.java b/src/module/decode/state/ChannelState.java
index d75c904e..3b538349 100644
--- a/src/module/decode/state/ChannelState.java
+++ b/src/module/decode/state/ChannelState.java
@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
- * Copyright (C) 2014-2016 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
@@ -17,14 +17,20 @@
******************************************************************************/
package module.decode.state;
-import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import module.Module;
+import module.decode.config.DecodeConfiguration;
+import module.decode.event.CallEvent;
+import module.decode.event.ICallEventProvider;
+import module.decode.state.DecoderStateEvent.Event;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import sample.Listener;
import audio.metadata.IMetadataProvider;
import audio.metadata.Metadata;
import audio.metadata.MetadataReset;
@@ -34,13 +40,38 @@ import audio.squelch.SquelchState;
import controller.ThreadPoolManager;
import controller.ThreadPoolManager.ThreadType;
import controller.channel.Channel.ChannelType;
-import module.Module;
-import module.decode.config.DecodeConfiguration;
-import module.decode.event.CallEvent;
-import module.decode.event.ICallEventProvider;
-import module.decode.state.DecoderStateEvent.Event;
-import sample.Listener;
+/**
+ * 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
@@ -52,6 +83,8 @@ public class ChannelState extends Module implements ICallEventProvider,
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;
@@ -64,15 +97,12 @@ public class ChannelState extends Module implements ICallEventProvider,
private boolean mSquelchLocked = false;
private boolean mSelected = false;
- private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY;
- private long mTrafficChannelFadeTimeout =
- DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000;
private long mFadeTimeout;
- private long mEndTimeout;
+ private long mResetTimeout;
private StateMonitor mStateMonitor = new StateMonitor();
private AtomicBoolean mMonitoring = new AtomicBoolean();
- private ScheduledFuture> mStateMonitorFuture;
+ private ScheduledFuture> mRunningMonitor;
private ThreadPoolManager mThreadPoolManager;
private ChannelType mChannelType;
@@ -80,97 +110,32 @@ public class ChannelState extends Module implements ICallEventProvider,
private TrafficChannelStatusListener mTrafficChannelStatusListener;
private CallEvent mTrafficChannelCallEvent;
- /**
- * Channel state tracks the overall state of all processing modules and decoders
- * configured for the channel and provides squelch control and decoder state
- * reset events.
- *
- * Uses a state enumeration that defines allowable channel state transitions in
- * order to track a call or data decode event from start to finish. Uses a
- * timer to monitor for inactivity and to provide a FADE period that indicates
- * to the user that the activity has stopped while continuing to provide details
- * about the call, before the state is reset to IDLE.
- *
- * Since this class is multi-threaded between decoder events and the internal
- * timer that monitors for inactivity, this class uses the mState object for
- * thread synchronization.
- *
- * State Descriptions:
- * IDLE Normal state. No voice or data call activity
- * CALL/DATA/ENCRYPTED/CONTROL
- * Decoding states.
- * 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
- * TEARDOWN Indicates a traffic channel that will be torn down for reuse.
- */
public ChannelState( ThreadPoolManager manager, ChannelType channelType )
{
mThreadPoolManager = manager;
mChannelType = channelType;
}
- /**
- * Resets this channel state and prepares it for reuse.
- */
@Override
public void reset()
{
- broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) );
- broadcast( new MetadataReset() );
-
- mState = State.IDLE;
- broadcast( ChangedAttribute.CHANNEL_STATE );
}
@Override
public void start()
{
- //Start a monitoring task to check for FADE and END event timeouts
- if( mMonitoring.compareAndSet( false, true ) )
- {
- if( mStateMonitorFuture == null && mStateMonitor != null )
- {
- try
- {
- mStateMonitorFuture = mThreadPoolManager.scheduleFixedRate(
- ThreadType.DECODER, mStateMonitor, 20, TimeUnit.MILLISECONDS );
- }
- catch( RejectedExecutionException ree )
- {
- mLog.error( "state monitor scheduled task rejected", ree );
- mMonitoring.set( false );
- }
- }
- }
}
@Override
public void stop()
{
mSquelchLocked = false;
-
- //Shutdown the state montoring task
- if( mMonitoring.compareAndSet( true, false ) )
- {
- if( mStateMonitorFuture != null )
- {
- boolean success = mThreadPoolManager.cancel( mStateMonitorFuture );
-
- if( !success )
- {
- mLog.error("Couldn't stop monitoring scheduled future" );
- }
- }
-
- mStateMonitorFuture = null;
- }
}
public void dispose()
{
+ stopMonitor();
+
mCallEventListener = null;
mChangedAttributeListener = null;
mDecoderStateListener = null;
@@ -179,34 +144,9 @@ public class ChannelState extends Module implements ICallEventProvider,
mStateMonitor = null;
}
- private boolean isStandardChannel()
- {
- return mChannelType == ChannelType.STANDARD;
- }
-
- private boolean isTrafficChannel()
- {
- return mChannelType == ChannelType.TRAFFIC;
- }
-
- public void setStandardChannelTimeout( long milliseconds )
- {
- mStandardChannelFadeTimeout = milliseconds;
-
- if( mChannelType == ChannelType.STANDARD )
- {
- mFadeTimeout = mStandardChannelFadeTimeout;
- }
- }
-
public void setTrafficChannelTimeout( long milliseconds )
{
- mTrafficChannelFadeTimeout = milliseconds;
-
- if( mChannelType == ChannelType.TRAFFIC )
- {
- mFadeTimeout = mTrafficChannelFadeTimeout;
- }
+ mTrafficChannelTimeout = milliseconds;
}
public void setSelected( boolean selected )
@@ -224,18 +164,71 @@ public class ChannelState extends Module implements ICallEventProvider,
return mState;
}
+ private boolean isIdleOrEndingState()
+ {
+ return mState == State.FADE ||
+ mState == State.END ||
+ mState == State.IDLE;
+ }
+
+
+ /**
+ * 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 && mStateMonitor != 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 );
+ }
+
+ mRunningMonitor = null;
+ }
+ }
+
/**
* Updates the fade timeout threshold to the current time plus delay
*/
private void updateFadeTimeout()
{
- if( isTrafficChannel() )
+ /* 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 current = System.currentTimeMillis();
+
+ long timeout = current +
+ ( mChannelType == ChannelType.STANDARD ? FADE_TIMEOUT_DELAY :
+ mTrafficChannelTimeout );
+
+ if( timeout > mFadeTimeout )
{
- mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout;
- }
- else
- {
- mFadeTimeout = System.currentTimeMillis() + mStandardChannelFadeTimeout;
+ mFadeTimeout = timeout;
}
}
@@ -244,13 +237,13 @@ public class ChannelState extends Module implements ICallEventProvider,
*/
private void updateResetTimeout()
{
- if( isTrafficChannel() )
+ if( mChannelType == ChannelType.TRAFFIC )
{
- mEndTimeout = System.currentTimeMillis();
+ mResetTimeout = System.currentTimeMillis();
}
else
{
- mEndTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY;
+ mResetTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY;
}
}
@@ -292,120 +285,120 @@ public class ChannelState extends Module implements ICallEventProvider,
*/
protected void setState( State state )
{
- synchronized( mState )
+ if( mState == state )
{
- if( state == mState )
+ switch( state )
{
- if( State.CALL_STATES.contains( state ) )
- {
+ case CALL:
+ case CONTROL:
+ case DATA:
+ case ENCRYPTED:
updateFadeTimeout();
- }
+ break;
+ default:
+ break;
}
- else if( state != mState && mState.canChangeTo( state ) )
+ }
+ else if( mState.canChangeTo( state ) )
+ {
+ switch( state )
{
- switch( state )
- {
- case CONTROL:
- //Don't allow traffic channels to be control channels,
- //otherwise they can't transition to call tear down
- if( isStandardChannel() )
- {
- broadcast( SquelchState.SQUELCH );
- updateFadeTimeout();
- mState = state;
- broadcast( ChangedAttribute.CHANNEL_STATE );
- }
- break;
- case DATA:
- case ENCRYPTED:
+ case CONTROL:
+ //Don't allow traffic channels to be control channels,
+ //otherwise they won't get call tear down
+ if( mChannelType != ChannelType.TRAFFIC )
+ {
broadcast( SquelchState.SQUELCH );
updateFadeTimeout();
+ startMonitor();
mState = state;
broadcast( ChangedAttribute.CHANNEL_STATE );
- break;
- case CALL:
- broadcast( SquelchState.UNSQUELCH );
- updateFadeTimeout();
- mState = state;
- broadcast( ChangedAttribute.CHANNEL_STATE );
- break;
- case FADE:
- processFadeState();
- break;
- case IDLE:
- processIdleState();
- break;
- case TEARDOWN:
- processTeardownState();
- break;
- case RESET:
- mState = State.IDLE;
- break;
- default:
- break;
+ }
+ break;
+ case DATA:
+ case ENCRYPTED:
+ broadcast( SquelchState.SQUELCH );
+ updateFadeTimeout();
+ startMonitor();
+ mState = state;
+ broadcast( ChangedAttribute.CHANNEL_STATE );
+ break;
+ case CALL:
+ broadcast( SquelchState.UNSQUELCH );
+ updateFadeTimeout();
+ startMonitor();
+ mState = state;
+ broadcast( ChangedAttribute.CHANNEL_STATE );
+ break;
+ case FADE:
+ broadcast( SquelchState.SQUELCH );
+ updateResetTimeout();
+ startMonitor();
+ mState = state;
+ broadcast( ChangedAttribute.CHANNEL_STATE );
+ break;
+ case END:
+ broadcast( SquelchState.SQUELCH );
+ broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) );
+ broadcast( new MetadataReset() );
+ mState = state;
+ broadcast( ChangedAttribute.CHANNEL_STATE );
+ 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;
+ }
+ }
+
+ mState = state;
+ broadcast( ChangedAttribute.CHANNEL_STATE );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ 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
{
-// mLog.debug( "Can't change from [" + mState + "] to [" + state + "]" );
+ if( mFadeTimeout <= System.currentTimeMillis() )
+ {
+ setState( State.FADE );
+ }
}
}
}
-
- /**
- * Sets the state and processes related actions
- */
- private void processFadeState()
- {
- updateResetTimeout();
- mState = State.FADE;
-
- broadcast( SquelchState.SQUELCH );
- broadcast( ChangedAttribute.CHANNEL_STATE );
- }
-
- private void processIdleState()
- {
- broadcast( SquelchState.SQUELCH );
-
- if( mState == State.FADE )
- {
- broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) );
- broadcast( new MetadataReset() );
- }
-
- mState = State.IDLE;
-
- broadcast( ChangedAttribute.CHANNEL_STATE );
- }
-
- private void processTeardownState()
- {
- broadcast( SquelchState.SQUELCH );
-
- mState = State.TEARDOWN;
-
- broadcast( ChangedAttribute.CHANNEL_STATE );
-
- if( mTrafficChannelCallEvent != null )
- {
- mTrafficChannelCallEvent.end();
-
- broadcast( mTrafficChannelCallEvent );
- mTrafficChannelCallEvent = null;
- }
-
- if( mTrafficChannelStatusListener != null )
- {
- mTrafficChannelStatusListener.callEnd();
- mTrafficChannelStatusListener = null;
- }
- else
- {
- mLog.error( "Traffic channel status listener was null - couldn't "
- + "teardown the channel");
- }
- }
/**
* Broadcasts the call event to the registered listener
@@ -597,95 +590,35 @@ public class ChannelState extends Module implements ICallEventProvider,
broadcast( SquelchState.UNSQUELCH );
mSquelchLocked = true;
break;
- case CHANGE_CALL_TIMEOUT:
- if( event instanceof ChangeChannelTimeoutEvent )
- {
- ChangeChannelTimeoutEvent timeout = (ChangeChannelTimeoutEvent)event;
-
- if( timeout.getChannelType() == ChannelType.STANDARD )
- {
- setStandardChannelTimeout( timeout.getCallTimeout() );
- }
- else
- {
- setTrafficChannelTimeout( timeout.getCallTimeout() );
- }
- }
case CONTINUATION:
+ if( isIdleOrEndingState() )
+ {
+ setState( event.getState() );
+ }
+ else
+ {
+ updateFadeTimeout();
+ }
+ break;
case DECODE:
- case START:
- if( State.CALL_STATES.contains( event.getState() ) )
+ if( getState() == State.IDLE )
{
setState( event.getState() );
}
break;
case END:
- if( isTrafficChannel() )
- {
- setState( State.TEARDOWN );
- }
- else
- {
- setState( State.FADE );
- }
+ setState( State.FADE );
break;
case RESET:
/* Channel State does not respond to reset events */
break;
+ case START:
+ setState( event.getState() );
+ break;
default:
break;
}
}
}
}
-
- /**
- * Monitors decoder state events to automatically transition the channel
- * state to IDLE (standard channel) or to TEARDOWN (traffic channel) when
- * decoding stops or the monitored channel returns to a no signal state.
- *
- * Provides a FADE transition state to allow for momentary decoding dropouts
- * and to allow the user access to call details for a fade period upon
- * call end.
- */
- public class StateMonitor implements Runnable
- {
- @Override
- public void run()
- {
- try
- {
- synchronized( mState )
- {
- if( State.CALL_STATES.contains( mState ) &&
- mFadeTimeout <= System.currentTimeMillis() )
- {
- mState = State.FADE;
- processFadeState();
- }
- else if( mState == State.FADE &&
- mEndTimeout <= System.currentTimeMillis() )
- {
- if( isTrafficChannel() )
- {
- processTeardownState();
- }
- else
- {
- processIdleState();
- }
- }
- }
- }
- catch( Exception e )
- {
- mLog.error( "Exception thrown while state monitor is running " +
- "- state [" + getState() +
- "] current [" + System.currentTimeMillis() +
- "] mResetTimeout [" + mEndTimeout +
- "] mFadeTimeout [" + mFadeTimeout +
- "]", e );
- }
- }
- }
}
diff --git a/src/module/decode/state/DecoderStateEvent.java b/src/module/decode/state/DecoderStateEvent.java
index fa60b7d8..19623bbc 100644
--- a/src/module/decode/state/DecoderStateEvent.java
+++ b/src/module/decode/state/DecoderStateEvent.java
@@ -56,7 +56,6 @@ public class DecoderStateEvent
public enum Event
{
ALWAYS_UNSQUELCH,
- CHANGE_CALL_TIMEOUT,
CONTINUATION,
DECODE,
END,
diff --git a/src/module/decode/state/State.java b/src/module/decode/state/State.java
index 141c5ea6..2820429d 100644
--- a/src/module/decode/state/State.java
+++ b/src/module/decode/state/State.java
@@ -1,10 +1,5 @@
package module.decode.state;
-import java.util.EnumSet;
-
-/**
- * Details the set of states for a channel and the allowable transition states
- */
public enum State
{
IDLE( "IDLE" )
@@ -12,8 +7,7 @@ public enum State
@Override
public boolean canChangeTo( State state )
{
- return state != TEARDOWN &&
- state != RESET;
+ return true;
}
},
CALL( "CALL" )
@@ -21,11 +15,11 @@ public enum State
@Override
public boolean canChangeTo( State state )
{
- return state == CONTROL ||
+ return state == CALL ||
+ state == CONTROL ||
state == DATA ||
state == ENCRYPTED ||
- state == FADE ||
- state == TEARDOWN;
+ state == FADE;
}
},
DATA( "DATA" )
@@ -35,9 +29,9 @@ public enum State
{
return state == CALL ||
state == CONTROL ||
- state == ENCRYPTED ||
- state == FADE ||
- state == TEARDOWN;
+ state == DATA ||
+ state == State.ENCRYPTED ||
+ state == FADE;
}
},
ENCRYPTED( "ENCRYPTED" )
@@ -45,7 +39,7 @@ public enum State
@Override
public boolean canChangeTo( State state )
{
- return state == FADE || state == TEARDOWN;
+ return state == FADE;
}
},
CONTROL( "CONTROL" )
@@ -53,8 +47,7 @@ public enum State
@Override
public boolean canChangeTo( State state )
{
- return state == IDLE ||
- state == FADE;
+ return true;
}
},
FADE( "FADE" )
@@ -62,36 +55,20 @@ public enum State
@Override
public boolean canChangeTo( State state )
{
- return state != FADE &&
- state != RESET;
- }
+ return state != FADE; //All states except fade allowed
+ }
},
- RESET( "RESET" )
+ END( "END" )
{
- @Override
- public boolean canChangeTo(State state)
- {
- return state == IDLE;
- }
- },
-
- TEARDOWN( "TEARDOWN" )
- {
- @Override
- public boolean canChangeTo(State state)
- {
- return state == RESET;
- }
- };
+ @Override
+ public boolean canChangeTo( State state )
+ {
+ return state != FADE; //Fade is only disallowed state
+ }
+ };
private String mDisplayValue;
- public static final EnumSet CALL_STATES =
- EnumSet.of( CALL, CONTROL, DATA, ENCRYPTED );
-
- public static final EnumSet IDLE_STATES =
- EnumSet.of( IDLE, FADE );
-
private State( String displayValue )
{
mDisplayValue = displayValue;
diff --git a/src/org/jdesktop/swingx/JXMapViewer.java b/src/org/jdesktop/swingx/JXMapViewer.java
index e0bd81f9..81ffc083 100644
--- a/src/org/jdesktop/swingx/JXMapViewer.java
+++ b/src/org/jdesktop/swingx/JXMapViewer.java
@@ -22,9 +22,10 @@ import java.awt.image.BufferedImage;
import java.beans.DesignMode;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
+import java.net.URL;
import java.util.Set;
-import javax.swing.ImageIcon;
+import javax.imageio.ImageIO;
import javax.swing.JPanel;
import org.jdesktop.swingx.mapviewer.GeoPosition;
@@ -125,13 +126,13 @@ public class JXMapViewer extends JPanel implements DesignMode
// make a dummy loading image
try
{
- ImageIcon imageIcon = new ImageIcon( "images/loading.png" );
- this.setLoadingImage( imageIcon.getImage() );
+ URL url = JXMapViewer.class.getResource("mapviewer/resources/loading.png");
+ this.setLoadingImage(ImageIO.read(url));
}
catch (Throwable ex)
{
- mLog.error( "could not load 'loading.png'" );
+ mLog.error( "JXMapViewer could not load 'loading.png'" );
BufferedImage img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
diff --git a/src/properties/SystemProperties.java b/src/properties/SystemProperties.java
index a36a171b..bf4b33f4 100644
--- a/src/properties/SystemProperties.java
+++ b/src/properties/SystemProperties.java
@@ -82,13 +82,18 @@ public class SystemProperties
{
out = new FileOutputStream( propsPath.toString() );
- String comments = "SDRTrunk - SDR Trunking Decoder Application Settings";
+ String comments =
+ "SDRTrunk - SDR Trunking Decoder Application Settings";
mProperties.store( out, comments );
+
+ mLog.info( "SystemProperties - saved current properties [" +
+ propsPath.toString() + "]" );
}
catch( Exception e )
{
- mLog.error( "exception while saving application properties", e );
+ mLog.error( "SystemProperties - exception while saving " +
+ "application properties", e );
}
finally
{
@@ -141,7 +146,8 @@ public class SystemProperties
}
catch ( IOException e )
{
- mLog.error( "exception while creating app folder [" + folder + "]", e );
+ mLog.error( "SystemProperties - exception while creating " +
+ "app folder [" + folder + "]", e );
}
}
@@ -152,11 +158,11 @@ public class SystemProperties
{
if( mPropertiesPath == null )
{
- mLog.info( "no properties file loaded - using defaults" );
+ mLog.info( "SystemProperties - no properties file loaded - using defaults" );
}
else
{
- mLog.info( "application properties loaded [" + mPropertiesPath.toString() + "]" );
+ mLog.info( "SystemProperties - application properties loaded [" + mPropertiesPath.toString() + "]" );
}
}
@@ -178,7 +184,7 @@ public class SystemProperties
}
catch ( FileNotFoundException e )
{
- mLog.error( "exception while opening inputstream on " +
+ mLog.error( "SDRTrunk - exception while opening inputstream on " +
"application properties file", e );
}
@@ -190,7 +196,7 @@ public class SystemProperties
}
catch ( IOException e )
{
- mLog.error( "exception while loading properties " +
+ mLog.error( "SDRTrunk - exception while loading properties " +
"inputstream into SystemProperties", e );
}
finally
@@ -206,7 +212,8 @@ public class SystemProperties
}
}
- mLog.info( "loaded [" + propertiesPath.toString() + "]" );
+ mLog.info( "SystemProperties - loaded [" +
+ propertiesPath.toString() + "]" );
}
/**
diff --git a/src/settings/ColorSetting.java b/src/settings/ColorSetting.java
index d0cb3e1f..6040fce4 100644
--- a/src/settings/ColorSetting.java
+++ b/src/settings/ColorSetting.java
@@ -123,7 +123,7 @@ public class ColorSetting extends Setting
"Control Gradient Middle", "Channel Control State Gradient Middle" ),
CHANNEL_STATE_GRADIENT_TOP_DATA( Color.BLACK,
"Data Gradient Top", "Channel Data State Gradient Top" ),
- CHANNEL_STATE_GRADIENT_MIDDLE_DATA( new Color( 0xCC00CC ),
+ CHANNEL_STATE_GRADIENT_MIDDLE_DATA( Color.BLUE,
"Data Gradient Middle", "Channel Data State Gradient Middle" ),
CHANNEL_STATE_GRADIENT_TOP_FADE( Color.BLACK,
"Fade Gradient Top", "Channel Fade State Gradient Top" ),
diff --git a/src/settings/SettingsManager.java b/src/settings/SettingsManager.java
index df430aa6..ee25bd78 100644
--- a/src/settings/SettingsManager.java
+++ b/src/settings/SettingsManager.java
@@ -437,7 +437,8 @@ public class SettingsManager
}
/* Something happened ... the above should always return an icon */
- mLog.error( "couldn't return an icon named [" + name + "] of heigh [" + height + "]" );
+ mLog.error( "SettingsManager - couldn't return an icon named [" +
+ name + "] of heigh [" + height + "]" );
}
return null;
@@ -635,7 +636,8 @@ public class SettingsManager
return r820TConfig;
default:
- throw new IllegalArgumentException( "tuner type is unrecognized [" +
+ throw new IllegalArgumentException( "TunerConfiguration"
+ + "Directory - tuner type is unrecognized [" +
type.toString() + "]" );
}
}
@@ -709,7 +711,8 @@ public class SettingsManager
}
catch( Exception e )
{
- mLog.error( "couldn't create file to save settings [" + filePath.toString() + "]", e );
+ mLog.error( "SettingsManager - couldn't create file to save "
+ + "settings [" + filePath.toString() + "]", e );
}
OutputStream out = null;
@@ -730,12 +733,14 @@ public class SettingsManager
}
catch ( JAXBException e )
{
- mLog.error( "jaxb exception while saving settings", e );
+ mLog.error( "SettingsManager - jaxb exception while saving " +
+ "settings", e );
}
}
catch ( Exception e )
{
- mLog.error( "couldn't open outputstream to save settings [" + filePath.toString() + "]" );
+ mLog.error( "SettingsManager - coulcn't open outputstream to " +
+ "save settings [" + filePath.toString() + "]" );
}
finally
{
@@ -761,7 +766,8 @@ public class SettingsManager
{
if( Files.exists( settingsPath ) )
{
- mLog.info( "loading settings file [" + settingsPath.toString() + "]" );
+ mLog.info( "SettingsManager - loading settings file [" +
+ settingsPath.toString() + "]" );
JAXBContext context = null;
@@ -781,13 +787,13 @@ public class SettingsManager
}
catch ( JAXBException e )
{
- mLog.error( "jaxb exception while loading " +
+ mLog.error( "SettingsManager - jaxb exception while loading " +
"settings", e );
}
}
catch ( Exception e )
{
- mLog.error( "coulcn't open inputstream to " +
+ mLog.error( "SettingsManager - coulcn't open inputstream to " +
"load settings [" + settingsPath.toString() + "]", e );
}
finally
@@ -800,7 +806,7 @@ public class SettingsManager
}
catch ( IOException e )
{
- mLog.error( "exception while closing " +
+ mLog.error( "SettingsManager - exception while closing " +
"the settings file inputstream reader", e );
}
}
@@ -808,7 +814,8 @@ public class SettingsManager
}
else
{
- mLog.info( "settings does not exist [" + settingsPath.toString() + "]" );
+ mLog.info( "SettingsManager - settings does not exist [" +
+ settingsPath.toString() + "]" );
}
if( mSettings == null )
diff --git a/src/source/mixer/MixerChannelConfiguration.java b/src/source/mixer/MixerChannelConfiguration.java
index b8520bcc..0be3b358 100644
--- a/src/source/mixer/MixerChannelConfiguration.java
+++ b/src/source/mixer/MixerChannelConfiguration.java
@@ -23,14 +23,6 @@ public class MixerChannelConfiguration
return mMixerChannel;
}
- public boolean matches( String mixer, String channels )
- {
- return mixer != null &&
- channels != null &&
- mMixer.getMixerInfo().getName().contentEquals( mixer ) &&
- mMixerChannel.name().contentEquals( channels );
- }
-
public String toString()
{
StringBuilder sb = new StringBuilder();
diff --git a/src/source/tuner/TunerChannel.java b/src/source/tuner/TunerChannel.java
index 625d7a98..7ff6e32d 100644
--- a/src/source/tuner/TunerChannel.java
+++ b/src/source/tuner/TunerChannel.java
@@ -97,13 +97,9 @@ public class TunerChannel implements Comparable
}
@Override
- public int compareTo( TunerChannel otherTunerChannel )
+ public int compareTo( TunerChannel otherLock )
{
- if( this.equals( otherTunerChannel ) )
- {
- return 0;
- }
- else if( mFrequency < otherTunerChannel.getFrequency() )
+ if( mFrequency < otherLock.getFrequency() )
{
return -1;
}
diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java
index 207d9eb9..7e844301 100644
--- a/src/spectrum/DFTProcessor.java
+++ b/src/spectrum/DFTProcessor.java
@@ -30,7 +30,6 @@ import org.jtransforms.fft.FloatFFT_1D;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import properties.SystemProperties;
import sample.Buffer;
import sample.Listener;
import sample.SampleType;
@@ -62,8 +61,6 @@ public class DFTProcessor implements Listener,
private ScheduledExecutorService mScheduler = Executors
.newScheduledThreadPool( 1, new NamingThreadFactory( "spectrum dft" ) );
- public static final String FRAME_RATE_PROPERTY = "spectral.display.frame.rate";
-
private DFTSize mDFTSize = DFTSize.FFT04096;
private DFTSize mNewDFTSize = DFTSize.FFT04096;
@@ -92,16 +89,7 @@ public class DFTProcessor implements Listener,
public DFTProcessor( SampleType sampleType )
{
setSampleType( sampleType );
-
- loadSettings();
- }
-
- private void loadSettings()
- {
- int frameRate = SystemProperties.getInstance()
- .get( DFTProcessor.FRAME_RATE_PROPERTY, 20 );
-
- setFrameRate( frameRate );
+ setFrameRate( 20 );
}
public void dispose()
@@ -183,10 +171,6 @@ public class DFTProcessor implements Listener,
mFrameRate = framesPerSecond;
- SystemProperties properties = SystemProperties.getInstance();
- properties.set( FRAME_RATE_PROPERTY, String.valueOf( mFrameRate ) );
- properties.save();
-
calculateConsumptionRate();
restart();
diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java
index f8d1acd9..9d76b912 100644
--- a/src/spectrum/SpectralDisplayPanel.java
+++ b/src/spectrum/SpectralDisplayPanel.java
@@ -54,7 +54,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import playlist.PlaylistManager;
-import properties.SystemProperties;
import sample.Listener;
import sample.SampleType;
import sample.complex.ComplexBuffer;
@@ -100,10 +99,8 @@ public class SpectralDisplayPanel extends JPanel
private static DecimalFormat sCURSOR_FORMAT = new DecimalFormat( "000.00000" );
- public static final String FFT_SIZE_PROPERTY = "spectral.display.dft.size";
public static final int NO_ZOOM = 0;
public static final int MAX_ZOOM = 6;
-
private DFTSize mDFTSize = DFTSize.FFT04096;
private int mZoom = 0;
@@ -158,36 +155,6 @@ public class SpectralDisplayPanel extends JPanel
}
init();
-
- loadSettings();
- }
-
- private void loadSettings()
- {
- SystemProperties properties = SystemProperties.getInstance();
-
- String rawSize = properties.get( FFT_SIZE_PROPERTY, DFTSize.FFT04096.name() );
-
- DFTSize size = null;
-
- if( rawSize != null )
- {
- try
- {
- size = DFTSize.valueOf( rawSize );
- }
- catch( Exception e )
- {
- //Do nothing, we couldn't parse the stored value
- }
- }
-
- if( size == null )
- {
- size = DFTSize.FFT04096;
- }
-
- setDFTSize( size );
}
public void dispose()
@@ -232,10 +199,6 @@ public class SpectralDisplayPanel extends JPanel
mDFTProcessor.setDFTSize( size );
mOverlayPanel.setDFTSize( size );
mDFTSize = size;
-
- SystemProperties properties = SystemProperties.getInstance();
- properties.set( FFT_SIZE_PROPERTY, size.name() );
- properties.save();
setZoom( 0, 0, 0 );
}
diff --git a/src/spectrum/WaterfallPanel.java b/src/spectrum/WaterfallPanel.java
index 336e8c6c..2aa7e37a 100644
--- a/src/spectrum/WaterfallPanel.java
+++ b/src/spectrum/WaterfallPanel.java
@@ -121,8 +121,6 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
mMemoryImageSource.setAnimated( true );
mWaterfallImage = createImage( mMemoryImageSource );
-
- repaint();
}
/**