mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 09:07:27 -05:00
Compare commits
22 Commits
v0.2.0-bet
...
v0.2.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8a20b5adb | ||
|
|
c670f1d855 | ||
|
|
1dc0125ef4 | ||
|
|
225e747d6e | ||
|
|
36f9953156 | ||
|
|
47c452e7e6 | ||
|
|
06c8bba97e | ||
|
|
a25bb83dea | ||
|
|
8e11cb4554 | ||
|
|
2892e4609e | ||
|
|
d6e161f3e9 | ||
|
|
ba952af721 | ||
|
|
d9be346d7f | ||
|
|
133adec27a | ||
|
|
981a8fec55 | ||
|
|
9c94266adb | ||
|
|
4460f1ee30 | ||
|
|
ddfefe4b85 | ||
|
|
debdc39a5f | ||
|
|
6f76f6fca4 | ||
|
|
1290b8c4d3 | ||
|
|
b95b3ff415 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
/*.class
|
/*.class
|
||||||
/classes*
|
/classes*
|
||||||
/product*
|
/product*
|
||||||
|
/bin/
|
||||||
|
|||||||
31
README.md
31
README.md
@@ -1,19 +1,22 @@
|
|||||||
# sdrtrunk
|
# sdrtrunk #
|
||||||
A cross-platform java application for decoding, monitoring, recording and streaming trunked mobile and related radio protocols using Software Defined Radios (SDR).
|
A cross-platform java application for decoding, monitoring and recording trunked
|
||||||
|
mobile and related radio protocols using Software Defined Radios (SDR).
|
||||||
|
|
||||||
|
# Documentation #
|
||||||
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted)
|
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted)
|
||||||
* [User's Manual](https://github.com/DSheirer/sdrtrunk/wiki/UserManual)
|
* [User's Manual (Version 0.2.0)](https://github.com/DSheirer/sdrtrunk/wiki/UserManual)
|
||||||
* [Download](https://drive.google.com/folderview?id=0B7BHsssXUq8eOHBTNndCczZTd0k&usp=sharing)
|
* [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:
|
||||||
|
|
||||||
* [Support](https://groups.google.com/forum/#!forum/sdrtrunk)
|
* [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)
|
||||||
|
|
||||||

|

|
||||||
<script>
|
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
||||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
||||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
|
||||||
|
|
||||||
ga('create', 'UA-67489234-1', 'auto');
|
|
||||||
ga('send', 'pageview');
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<property name="major" value="0"/>
|
<property name="major" value="0"/>
|
||||||
<property name="minor" value="2"/>
|
<property name="minor" value="2"/>
|
||||||
<property name="patch" value="0"/>
|
<property name="patch" value="0"/>
|
||||||
<property name="release" value="beta.3"/>
|
<property name="release" value="beta.5"/>
|
||||||
<property name="filename" value="sdrtrunk_${major}.${minor}.${patch}-${release}"/>
|
<property name="filename" value="sdrtrunk_${major}.${minor}.${patch}-${release}"/>
|
||||||
|
|
||||||
<!-- Build properties -->
|
<!-- Build properties -->
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import audio.inverted.AudioType;
|
|||||||
public class TalkgroupID extends AliasID
|
public class TalkgroupID extends AliasID
|
||||||
{
|
{
|
||||||
private String mTalkgroup;
|
private String mTalkgroup;
|
||||||
private AudioType mAudioType;
|
|
||||||
|
|
||||||
public TalkgroupID()
|
public TalkgroupID()
|
||||||
{
|
{
|
||||||
@@ -44,20 +43,9 @@ public class TalkgroupID extends AliasID
|
|||||||
mTalkgroup = talkgroup;
|
mTalkgroup = talkgroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
@XmlAttribute
|
|
||||||
public AudioType getAudioType()
|
|
||||||
{
|
|
||||||
return mAudioType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAudioType( AudioType type )
|
|
||||||
{
|
|
||||||
mAudioType = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasAudioType()
|
public boolean hasAudioType()
|
||||||
{
|
{
|
||||||
return mAudioType != null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString()
|
public String toString()
|
||||||
@@ -66,13 +54,6 @@ public class TalkgroupID extends AliasID
|
|||||||
|
|
||||||
sb.append( "Talkgroup: " + mTalkgroup );
|
sb.append( "Talkgroup: " + mTalkgroup );
|
||||||
|
|
||||||
if( mAudioType != null )
|
|
||||||
{
|
|
||||||
sb.append( " [Audio " );
|
|
||||||
sb.append( mAudioType.getShortDisplayString() );
|
|
||||||
sb.append( "]" );
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* SDR Trunk
|
* SDR Trunk
|
||||||
* Copyright (C) 2014 Dennis Sheirer
|
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,9 +20,7 @@ package alias.id.talkgroup;
|
|||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
|
|
||||||
import javax.swing.DefaultComboBoxModel;
|
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JComboBox;
|
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JOptionPane;
|
import javax.swing.JOptionPane;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
@@ -30,7 +28,6 @@ import javax.swing.JTextArea;
|
|||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
import net.miginfocom.swing.MigLayout;
|
||||||
import audio.inverted.AudioType;
|
|
||||||
import controller.ConfigurableNode;
|
import controller.ConfigurableNode;
|
||||||
|
|
||||||
public class TalkgroupIDEditor extends JPanel implements ActionListener
|
public class TalkgroupIDEditor extends JPanel implements ActionListener
|
||||||
@@ -38,19 +35,15 @@ public class TalkgroupIDEditor extends JPanel implements ActionListener
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private TalkgroupIDNode mTalkgroupIDNode;
|
private TalkgroupIDNode mTalkgroupIDNode;
|
||||||
|
|
||||||
private JLabel mLabelName;
|
|
||||||
private JTextField mTextTalkgroupID;
|
private JTextField mTextTalkgroupID;
|
||||||
private JLabel mLabelAudio;
|
|
||||||
private JComboBox<AudioType> mComboAudioTypes;
|
|
||||||
|
|
||||||
private String mHelpText = "Enter a formatted talkgroup identifier.\n\n"
|
private String mHelpText = "Enter a formatted talkgroup identifier.\n\n"
|
||||||
|
+ "P25 Talkgroup Format: hexadecimal value, 4 characters for "
|
||||||
|
+ "talkgroup or 6 characters for a mobile radio\n\n"
|
||||||
+ "LTR Talkgroup Format: A-HH-TTT (A=Area, H=Home Repeater, "
|
+ "LTR Talkgroup Format: A-HH-TTT (A=Area, H=Home Repeater, "
|
||||||
+ "T=Talkgroup)\n\n"
|
+ "T=Talkgroup)\n\n"
|
||||||
+ "Passport Talkgroup Format: xxxxx (up to 5-digit number)\n\n"
|
+ "Passport Talkgroup Format: xxxxx (up to 5-digit number)\n\n"
|
||||||
+ "Wildcard: use one or more asterisks (*) for any talkgroup digits.\n\n"
|
+ "Wildcard: use one or more asterisks (*) for any talkgroup digits.";
|
||||||
+ "Audio: if the talkgroup uses simple frequency inversion, specify"
|
|
||||||
+ " the inversion frequency from the dropdown list to automatically"
|
|
||||||
+ " un-invert audio for this specific talkgroup";
|
|
||||||
|
|
||||||
public TalkgroupIDEditor( TalkgroupIDNode talkgroupIDNode )
|
public TalkgroupIDEditor( TalkgroupIDNode talkgroupIDNode )
|
||||||
{
|
{
|
||||||
@@ -72,22 +65,6 @@ public class TalkgroupIDEditor extends JPanel implements ActionListener
|
|||||||
|
|
||||||
add( mTextTalkgroupID, "growx,push" );
|
add( mTextTalkgroupID, "growx,push" );
|
||||||
|
|
||||||
add( new JLabel( "Audio:" ) );
|
|
||||||
|
|
||||||
mComboAudioTypes = new JComboBox<AudioType>();
|
|
||||||
|
|
||||||
mComboAudioTypes.setModel(
|
|
||||||
new DefaultComboBoxModel<AudioType>( AudioType.values() ) );
|
|
||||||
|
|
||||||
AudioType audioType = mTalkgroupIDNode.getTalkgroupID().getAudioType();
|
|
||||||
|
|
||||||
if( audioType != null )
|
|
||||||
{
|
|
||||||
mComboAudioTypes.setSelectedItem( audioType );
|
|
||||||
}
|
|
||||||
|
|
||||||
add( mComboAudioTypes, "growx,push" );
|
|
||||||
|
|
||||||
JButton btnSave = new JButton( "Save" );
|
JButton btnSave = new JButton( "Save" );
|
||||||
btnSave.addActionListener( TalkgroupIDEditor.this );
|
btnSave.addActionListener( TalkgroupIDEditor.this );
|
||||||
add( btnSave, "growx,push" );
|
add( btnSave, "growx,push" );
|
||||||
@@ -117,14 +94,6 @@ public class TalkgroupIDEditor extends JPanel implements ActionListener
|
|||||||
|
|
||||||
((ConfigurableNode)mTalkgroupIDNode.getParent()).sort();
|
((ConfigurableNode)mTalkgroupIDNode.getParent()).sort();
|
||||||
|
|
||||||
AudioType selected = mComboAudioTypes
|
|
||||||
.getItemAt( mComboAudioTypes.getSelectedIndex() );
|
|
||||||
|
|
||||||
if( selected != null )
|
|
||||||
{
|
|
||||||
mTalkgroupIDNode.getTalkgroupID().setAudioType( selected );
|
|
||||||
}
|
|
||||||
|
|
||||||
mTalkgroupIDNode.save();
|
mTalkgroupIDNode.save();
|
||||||
|
|
||||||
mTalkgroupIDNode.show();
|
mTalkgroupIDNode.show();
|
||||||
@@ -139,9 +108,6 @@ public class TalkgroupIDEditor extends JPanel implements ActionListener
|
|||||||
{
|
{
|
||||||
mTextTalkgroupID.setText(
|
mTextTalkgroupID.setText(
|
||||||
mTalkgroupIDNode.getTalkgroupID().getTalkgroup() );
|
mTalkgroupIDNode.getTalkgroupID().getTalkgroup() );
|
||||||
|
|
||||||
mComboAudioTypes.setSelectedItem(
|
|
||||||
mTalkgroupIDNode.getTalkgroupID().getAudioType() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mTalkgroupIDNode.refresh();
|
mTalkgroupIDNode.refresh();
|
||||||
|
|||||||
@@ -48,8 +48,11 @@ public class AudioChannelPanel extends JPanel
|
|||||||
private JLabel mFromLabel = new JLabel( "FROM:" );
|
private JLabel mFromLabel = new JLabel( "FROM:" );
|
||||||
private JLabel mFrom = new JLabel( "" );
|
private JLabel mFrom = new JLabel( "" );
|
||||||
private JLabel mFromAlias = new JLabel( "" );
|
private JLabel mFromAlias = new JLabel( "" );
|
||||||
|
|
||||||
|
private enum AudioStatus { STARTED,STOPPED };
|
||||||
|
private AudioStatus mAudioStatus = AudioStatus.STOPPED;
|
||||||
|
|
||||||
private boolean mConfigured = false;
|
private AudioMetadata mAudioMetadata;
|
||||||
|
|
||||||
public AudioChannelPanel( SettingsManager settingsManager, AudioOutput audioOutput )
|
public AudioChannelPanel( SettingsManager settingsManager, AudioOutput audioOutput )
|
||||||
{
|
{
|
||||||
@@ -134,15 +137,19 @@ public class AudioChannelPanel extends JPanel
|
|||||||
{
|
{
|
||||||
switch( audioEvent.getType() )
|
switch( audioEvent.getType() )
|
||||||
{
|
{
|
||||||
case AUDIO_STOPPED:
|
case AUDIO_STARTED:
|
||||||
EventQueue.invokeLater( new Runnable()
|
synchronized( mAudioStatus )
|
||||||
{
|
{
|
||||||
@Override
|
mAudioStatus = AudioStatus.STARTED;
|
||||||
public void run()
|
updateLabels();
|
||||||
{
|
}
|
||||||
resetLabels();
|
break;
|
||||||
}
|
case AUDIO_STOPPED:
|
||||||
} );
|
synchronized( mAudioStatus )
|
||||||
|
{
|
||||||
|
mAudioStatus = AudioStatus.STOPPED;
|
||||||
|
resetLabels();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case AUDIO_MUTED:
|
case AUDIO_MUTED:
|
||||||
case AUDIO_UNMUTED:
|
case AUDIO_UNMUTED:
|
||||||
@@ -166,13 +173,55 @@ public class AudioChannelPanel extends JPanel
|
|||||||
*/
|
*/
|
||||||
private void resetLabels()
|
private void resetLabels()
|
||||||
{
|
{
|
||||||
mFrom.setText( "" );
|
EventQueue.invokeLater( new Runnable()
|
||||||
updateAlias( mFromAlias, null );
|
{
|
||||||
|
@Override
|
||||||
mTo.setText( "" );
|
public void run()
|
||||||
updateAlias( mToAlias, null );
|
{
|
||||||
|
mFrom.setText( "" );
|
||||||
mConfigured = false;
|
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( "-----" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -214,32 +263,17 @@ public class AudioChannelPanel extends JPanel
|
|||||||
@Override
|
@Override
|
||||||
public void receive( AudioMetadata audioMetadata )
|
public void receive( AudioMetadata audioMetadata )
|
||||||
{
|
{
|
||||||
if( !mConfigured || audioMetadata.isUpdated() )
|
mAudioMetadata = audioMetadata;
|
||||||
|
|
||||||
|
if( audioMetadata.isUpdated() )
|
||||||
{
|
{
|
||||||
final Metadata from = audioMetadata.getMetadata( MetadataType.FROM );
|
synchronized( mAudioStatus )
|
||||||
|
|
||||||
final Metadata to = audioMetadata.getMetadata( MetadataType.TO );
|
|
||||||
|
|
||||||
EventQueue.invokeLater( new Runnable()
|
|
||||||
{
|
{
|
||||||
@Override
|
if( mAudioStatus == AudioStatus.STARTED )
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if( from != null )
|
updateLabels();
|
||||||
{
|
|
||||||
mFrom.setText( from.getValue() );
|
|
||||||
updateAlias( mFromAlias, from.getAlias() );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( to != null )
|
|
||||||
{
|
|
||||||
mTo.setText( to.getValue() );
|
|
||||||
updateAlias( mToAlias, to.getAlias() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} );
|
}
|
||||||
|
|
||||||
mConfigured = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ package audio;
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@@ -41,7 +40,6 @@ import source.mixer.MixerChannel;
|
|||||||
import source.mixer.MixerChannelConfiguration;
|
import source.mixer.MixerChannelConfiguration;
|
||||||
import source.mixer.MixerManager;
|
import source.mixer.MixerManager;
|
||||||
import audio.AudioEvent.Type;
|
import audio.AudioEvent.Type;
|
||||||
import audio.AudioPanel.MixerSelectionItem;
|
|
||||||
import audio.output.AudioOutput;
|
import audio.output.AudioOutput;
|
||||||
import audio.output.MonoAudioOutput;
|
import audio.output.MonoAudioOutput;
|
||||||
import audio.output.StereoAudioOutput;
|
import audio.output.StereoAudioOutput;
|
||||||
@@ -184,7 +182,8 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
|
|
||||||
for( AudioOutputConnection connection: mAudioOutputConnections )
|
for( AudioOutputConnection connection: mAudioOutputConnections )
|
||||||
{
|
{
|
||||||
if( connection.isConnected() && connection.isInactive() )
|
if( connection.isInactive() &&
|
||||||
|
mChannelConnectionMap.containsKey( connection.getSource() ) )
|
||||||
{
|
{
|
||||||
mChannelConnectionMap.remove( connection.getSource() );
|
mChannelConnectionMap.remove( connection.getSource() );
|
||||||
connection.disconnect();
|
connection.disconnect();
|
||||||
@@ -392,34 +391,41 @@ public class AudioManager implements Listener<AudioPacket>, IAudioController
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
disconnectInactiveChannelAssignments();
|
try
|
||||||
|
|
||||||
if( mAudioPacketQueue != null )
|
|
||||||
{
|
{
|
||||||
List<AudioPacket> packets = new ArrayList<AudioPacket>();
|
disconnectInactiveChannelAssignments();
|
||||||
|
|
||||||
mAudioPacketQueue.drainTo( packets );
|
|
||||||
|
|
||||||
for( AudioPacket packet: packets )
|
if( mAudioPacketQueue != null )
|
||||||
{
|
{
|
||||||
/* Don't process any packet's marked as do not monitor */
|
List<AudioPacket> packets = new ArrayList<AudioPacket>();
|
||||||
if( !packet.getAudioMetadata().isDoNotMonitor() &&
|
|
||||||
packet.getType() == AudioPacket.Type.AUDIO )
|
|
||||||
{
|
|
||||||
AudioOutputConnection connection = getConnection( packet );
|
|
||||||
|
|
||||||
if( connection != null )
|
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 )
|
||||||
{
|
{
|
||||||
connection.receive( packet );
|
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 soure and an audio
|
* Audio output connection manages a connection between a source and an audio
|
||||||
* output and maintains current state information about the audio activity
|
* output and maintains current state information about the audio activity
|
||||||
* received form the source.
|
* received form the source.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package audio.output;
|
package audio.output;
|
||||||
|
|
||||||
import java.util.concurrent.LinkedTransferQueue;
|
import java.util.concurrent.LinkedTransferQueue;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
import javax.sound.sampled.FloatControl;
|
import javax.sound.sampled.FloatControl;
|
||||||
|
import javax.sound.sampled.LineListener;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -11,18 +11,15 @@ import org.slf4j.LoggerFactory;
|
|||||||
import sample.Broadcaster;
|
import sample.Broadcaster;
|
||||||
import sample.Listener;
|
import sample.Listener;
|
||||||
import audio.AudioEvent;
|
import audio.AudioEvent;
|
||||||
import audio.AudioEvent.Type;
|
|
||||||
import audio.AudioPacket;
|
import audio.AudioPacket;
|
||||||
import audio.metadata.AudioMetadata;
|
import audio.metadata.AudioMetadata;
|
||||||
import controller.ThreadPoolManager;
|
import controller.ThreadPoolManager;
|
||||||
|
|
||||||
public abstract class AudioOutput implements Listener<AudioPacket>
|
public abstract class AudioOutput implements Listener<AudioPacket>, LineListener
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( AudioOutput.class );
|
private final static Logger mLog = LoggerFactory.getLogger( AudioOutput.class );
|
||||||
|
|
||||||
private AtomicBoolean mSquelched = new AtomicBoolean( false );
|
|
||||||
private Listener<AudioMetadata> mAudioMetadataListener;
|
private Listener<AudioMetadata> mAudioMetadataListener;
|
||||||
|
|
||||||
protected Broadcaster<AudioEvent> mAudioEventBroadcaster = new Broadcaster<>();
|
protected Broadcaster<AudioEvent> mAudioEventBroadcaster = new Broadcaster<>();
|
||||||
protected LinkedTransferQueue<AudioPacket> mBuffer = new LinkedTransferQueue<>();
|
protected LinkedTransferQueue<AudioPacket> mBuffer = new LinkedTransferQueue<>();
|
||||||
protected ThreadPoolManager mThreadPoolManager;
|
protected ThreadPoolManager mThreadPoolManager;
|
||||||
@@ -74,19 +71,6 @@ public abstract class AudioOutput implements Listener<AudioPacket>
|
|||||||
mAudioEventBroadcaster.broadcast( audioEvent );
|
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
|
* Registers a single listener to receive audio start and audio stop events
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
import javax.sound.sampled.BooleanControl;
|
import javax.sound.sampled.BooleanControl;
|
||||||
import javax.sound.sampled.Control;
|
import javax.sound.sampled.Control;
|
||||||
import javax.sound.sampled.FloatControl;
|
import javax.sound.sampled.FloatControl;
|
||||||
|
import javax.sound.sampled.LineEvent;
|
||||||
import javax.sound.sampled.LineUnavailableException;
|
import javax.sound.sampled.LineUnavailableException;
|
||||||
import javax.sound.sampled.Mixer;
|
import javax.sound.sampled.Mixer;
|
||||||
import javax.sound.sampled.SourceDataLine;
|
import javax.sound.sampled.SourceDataLine;
|
||||||
@@ -50,11 +51,9 @@ public class MonoAudioOutput extends AudioOutput
|
|||||||
private SourceDataLine mOutput;
|
private SourceDataLine mOutput;
|
||||||
private FloatControl mGainControl;
|
private FloatControl mGainControl;
|
||||||
private BooleanControl mMuteControl;
|
private BooleanControl mMuteControl;
|
||||||
|
|
||||||
private AudioEvent mAudioStartEvent;
|
private AudioEvent mAudioStartEvent;
|
||||||
private AudioEvent mAudioStopEvent;
|
private AudioEvent mAudioStopEvent;
|
||||||
private AudioEvent mAudioContinuationEvent;
|
private AudioEvent mAudioContinuationEvent;
|
||||||
|
|
||||||
private ScheduledFuture<?> mProcessorTask;
|
private ScheduledFuture<?> mProcessorTask;
|
||||||
|
|
||||||
public MonoAudioOutput( ThreadPoolManager threadPoolManager, Mixer mixer )
|
public MonoAudioOutput( ThreadPoolManager threadPoolManager, Mixer mixer )
|
||||||
@@ -76,6 +75,7 @@ public class MonoAudioOutput extends AudioOutput
|
|||||||
if( mOutput != null )
|
if( mOutput != null )
|
||||||
{
|
{
|
||||||
mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO, BUFFER_SIZE );
|
mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO, BUFFER_SIZE );
|
||||||
|
mOutput.addLineListener( this );
|
||||||
mCanProcessAudio = true;
|
mCanProcessAudio = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -162,17 +162,13 @@ public class MonoAudioOutput extends AudioOutput
|
|||||||
|
|
||||||
if( packets.size() > 0 )
|
if( packets.size() > 0 )
|
||||||
{
|
{
|
||||||
broadcast( mAudioContinuationEvent );
|
|
||||||
|
|
||||||
for( AudioPacket packet: packets )
|
for( AudioPacket packet: packets )
|
||||||
{
|
{
|
||||||
|
broadcast( mAudioContinuationEvent );
|
||||||
|
|
||||||
if( mCanProcessAudio )
|
if( mCanProcessAudio )
|
||||||
{
|
{
|
||||||
if( isSquelched() )
|
if( packet.getType() == Type.AUDIO )
|
||||||
{
|
|
||||||
checkStop();
|
|
||||||
}
|
|
||||||
else if( packet.getType() == Type.AUDIO )
|
|
||||||
{
|
{
|
||||||
float[] samples = packet.getAudioBuffer().getSamples();
|
float[] samples = packet.getAudioBuffer().getSamples();
|
||||||
|
|
||||||
@@ -216,24 +212,20 @@ public class MonoAudioOutput extends AudioOutput
|
|||||||
|
|
||||||
private void checkStart()
|
private void checkStart()
|
||||||
{
|
{
|
||||||
if( mCanProcessAudio && !isSquelched() &&
|
if( mCanProcessAudio &&
|
||||||
!mOutput.isRunning() && mOutput.available() <= START_THRESHOLD )
|
!mOutput.isRunning() && mOutput.available() <= START_THRESHOLD )
|
||||||
{
|
{
|
||||||
mOutput.start();
|
mOutput.start();
|
||||||
|
|
||||||
mAudioEventBroadcaster.broadcast( mAudioStartEvent );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkStop()
|
private void checkStop()
|
||||||
{
|
{
|
||||||
if( mCanProcessAudio && mOutput.isRunning() &&
|
if( mCanProcessAudio && mOutput.isRunning() &&
|
||||||
( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) )
|
mOutput.available() >= STOP_THRESHOLD )
|
||||||
{
|
{
|
||||||
mOutput.drain();
|
mOutput.drain();
|
||||||
mOutput.stop();
|
mOutput.stop();
|
||||||
|
|
||||||
mAudioEventBroadcaster.broadcast( mAudioStopEvent );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,4 +264,19 @@ public class MonoAudioOutput extends AudioOutput
|
|||||||
{
|
{
|
||||||
return mGainControl != null;
|
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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||||||
import javax.sound.sampled.BooleanControl;
|
import javax.sound.sampled.BooleanControl;
|
||||||
import javax.sound.sampled.Control;
|
import javax.sound.sampled.Control;
|
||||||
import javax.sound.sampled.FloatControl;
|
import javax.sound.sampled.FloatControl;
|
||||||
|
import javax.sound.sampled.LineEvent;
|
||||||
import javax.sound.sampled.LineUnavailableException;
|
import javax.sound.sampled.LineUnavailableException;
|
||||||
import javax.sound.sampled.Mixer;
|
import javax.sound.sampled.Mixer;
|
||||||
import javax.sound.sampled.SourceDataLine;
|
import javax.sound.sampled.SourceDataLine;
|
||||||
@@ -40,9 +41,9 @@ public class StereoAudioOutput extends AudioOutput
|
|||||||
private final static int BUFFER_SIZE = 96000;
|
private final static int BUFFER_SIZE = 96000;
|
||||||
|
|
||||||
/* The queue processor will run every 20 milliseconds checking for inbound
|
/* The queue processor will run every 20 milliseconds checking for inbound
|
||||||
* audio and automatically starting or stopping audio playback. We set the
|
* audio and automatically start or stop audio playback. We set the
|
||||||
* data line available space threshold to start when we're 20 ms away from
|
* 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 SAMPLE_SIZE_40_MS = BUFFER_SIZE / 25;
|
||||||
private final static int START_THRESHOLD = SAMPLE_SIZE_40_MS;
|
private final static int START_THRESHOLD = SAMPLE_SIZE_40_MS;
|
||||||
private final static int STOP_THRESHOLD = BUFFER_SIZE - SAMPLE_SIZE_40_MS;
|
private final static int STOP_THRESHOLD = BUFFER_SIZE - SAMPLE_SIZE_40_MS;
|
||||||
@@ -50,13 +51,10 @@ public class StereoAudioOutput extends AudioOutput
|
|||||||
private SourceDataLine mOutput;
|
private SourceDataLine mOutput;
|
||||||
private FloatControl mGainControl;
|
private FloatControl mGainControl;
|
||||||
private BooleanControl mMuteControl;
|
private BooleanControl mMuteControl;
|
||||||
|
|
||||||
private AudioEvent mAudioStartEvent;
|
private AudioEvent mAudioStartEvent;
|
||||||
private AudioEvent mAudioStopEvent;
|
private AudioEvent mAudioStopEvent;
|
||||||
private AudioEvent mAudioContinuationEvent;
|
private AudioEvent mAudioContinuationEvent;
|
||||||
|
|
||||||
private MixerChannel mMixerChannel;
|
private MixerChannel mMixerChannel;
|
||||||
|
|
||||||
private ScheduledFuture<?> mProcessorTask;
|
private ScheduledFuture<?> mProcessorTask;
|
||||||
|
|
||||||
public StereoAudioOutput( ThreadPoolManager threadPoolManager,
|
public StereoAudioOutput( ThreadPoolManager threadPoolManager,
|
||||||
@@ -83,6 +81,7 @@ public class StereoAudioOutput extends AudioOutput
|
|||||||
if( mOutput != null )
|
if( mOutput != null )
|
||||||
{
|
{
|
||||||
mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO, BUFFER_SIZE );
|
mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO, BUFFER_SIZE );
|
||||||
|
mOutput.addLineListener( this );
|
||||||
mCanProcessAudio = true;
|
mCanProcessAudio = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -202,17 +201,14 @@ public class StereoAudioOutput extends AudioOutput
|
|||||||
|
|
||||||
if( packets.size() > 0 )
|
if( packets.size() > 0 )
|
||||||
{
|
{
|
||||||
broadcast( mAudioContinuationEvent );
|
|
||||||
|
|
||||||
for( AudioPacket packet: packets )
|
for( AudioPacket packet: packets )
|
||||||
{
|
{
|
||||||
|
broadcast( mAudioContinuationEvent );
|
||||||
|
|
||||||
if( mCanProcessAudio )
|
if( mCanProcessAudio )
|
||||||
{
|
{
|
||||||
if( isSquelched() )
|
if( packet.getType() == Type.AUDIO )
|
||||||
{
|
|
||||||
checkStop();
|
|
||||||
}
|
|
||||||
else if( packet.getType() == Type.AUDIO )
|
|
||||||
{
|
{
|
||||||
ByteBuffer buffer = convert(
|
ByteBuffer buffer = convert(
|
||||||
packet.getAudioBuffer().getSamples() );
|
packet.getAudioBuffer().getSamples() );
|
||||||
@@ -245,24 +241,20 @@ public class StereoAudioOutput extends AudioOutput
|
|||||||
|
|
||||||
private void checkStart()
|
private void checkStart()
|
||||||
{
|
{
|
||||||
if( mCanProcessAudio && !isSquelched() &&
|
if( mCanProcessAudio &&
|
||||||
!mOutput.isRunning() && mOutput.available() <= START_THRESHOLD )
|
!mOutput.isRunning() && mOutput.available() <= START_THRESHOLD )
|
||||||
{
|
{
|
||||||
mOutput.start();
|
mOutput.start();
|
||||||
|
|
||||||
mAudioEventBroadcaster.broadcast( mAudioStartEvent );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkStop()
|
private void checkStop()
|
||||||
{
|
{
|
||||||
if( mCanProcessAudio && mOutput.isRunning() &&
|
if( mCanProcessAudio && mOutput.isRunning() &&
|
||||||
( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) )
|
mOutput.available() >= STOP_THRESHOLD )
|
||||||
{
|
{
|
||||||
mOutput.drain();
|
mOutput.drain();
|
||||||
mOutput.stop();
|
mOutput.stop();
|
||||||
|
|
||||||
mAudioEventBroadcaster.broadcast( mAudioStopEvent );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -301,4 +293,20 @@ public class StereoAudioOutput extends AudioOutput
|
|||||||
{
|
{
|
||||||
return mGainControl != null;
|
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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,16 @@ public class MessageFramer implements Listener<Boolean>,
|
|||||||
mMessageLength = messageLength;
|
mMessageLength = messageLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void reset()
|
||||||
|
{
|
||||||
|
for( MessageAssembler assembler: mMessageAssemblers )
|
||||||
|
{
|
||||||
|
assembler.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
mMessageAssemblers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
public void dispose()
|
public void dispose()
|
||||||
{
|
{
|
||||||
mBroadcaster.dispose();
|
mBroadcaster.dispose();
|
||||||
|
|||||||
@@ -64,11 +64,13 @@ public class ThreadPoolManager
|
|||||||
mExecutor.schedule( command, delay, unit );
|
mExecutor.schedule( command, delay, unit );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancel( ScheduledFuture<?> task )
|
public boolean cancel( ScheduledFuture<?> task )
|
||||||
{
|
{
|
||||||
task.cancel( true );
|
boolean success = task.cancel( true );
|
||||||
|
|
||||||
mTasks.remove( task );
|
mTasks.remove( task );
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getTaskCount( ThreadType type )
|
public int getTaskCount( ThreadType type )
|
||||||
|
|||||||
@@ -809,8 +809,7 @@ public class Channel extends Configuration
|
|||||||
{
|
{
|
||||||
if( mProcessingChain == null )
|
if( mProcessingChain == null )
|
||||||
{
|
{
|
||||||
mProcessingChain = new ProcessingChain(
|
mProcessingChain = new ProcessingChain();
|
||||||
mResourceManager.getThreadPoolManager() );
|
|
||||||
|
|
||||||
/* Get the optional alias list for the decode modules to use */
|
/* Get the optional alias list for the decode modules to use */
|
||||||
AliasList aliasList = mResourceManager.getPlaylistManager()
|
AliasList aliasList = mResourceManager.getPlaylistManager()
|
||||||
@@ -901,6 +900,8 @@ public class Channel extends Configuration
|
|||||||
{
|
{
|
||||||
if( !mEnabled && mProcessingChain != null )
|
if( !mEnabled && mProcessingChain != null )
|
||||||
{
|
{
|
||||||
|
fireChannelEvent( Event.CHANNEL_PROCESSING_STOPPED );
|
||||||
|
|
||||||
mProcessingChain.stop();
|
mProcessingChain.stop();
|
||||||
|
|
||||||
if( mChannelType == ChannelType.STANDARD )
|
if( mChannelType == ChannelType.STANDARD )
|
||||||
@@ -910,8 +911,6 @@ public class Channel extends Configuration
|
|||||||
}
|
}
|
||||||
|
|
||||||
mSelected = false;
|
mSelected = false;
|
||||||
|
|
||||||
fireChannelEvent( Event.CHANNEL_PROCESSING_STOPPED );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,8 +99,7 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
private Broadcaster<RealBuffer> mRealBufferBroadcaster = new Broadcaster<>();
|
private Broadcaster<RealBuffer> mRealBufferBroadcaster = new Broadcaster<>();
|
||||||
private Broadcaster<SquelchState> mSquelchStateBroadcaster = new Broadcaster<>();
|
private Broadcaster<SquelchState> mSquelchStateBroadcaster = new Broadcaster<>();
|
||||||
|
|
||||||
private ThreadPoolManager mThreadPoolManager;
|
// private ThreadPoolManager mThreadPoolManager;
|
||||||
private ScheduledFuture<?> mBufferProcessingTask;
|
|
||||||
private AtomicBoolean mRunning = new AtomicBoolean();
|
private AtomicBoolean mRunning = new AtomicBoolean();
|
||||||
|
|
||||||
protected Source mSource;
|
protected Source mSource;
|
||||||
@@ -109,14 +108,8 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
|
|
||||||
public ProcessingChain()
|
public ProcessingChain()
|
||||||
{
|
{
|
||||||
this( new ThreadPoolManager() );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProcessingChain( ThreadPoolManager threadManager )
|
|
||||||
{
|
|
||||||
mThreadPoolManager = threadManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispose()
|
public void dispose()
|
||||||
{
|
{
|
||||||
stop();
|
stop();
|
||||||
@@ -128,14 +121,6 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
|
|
||||||
mModules.clear();
|
mModules.clear();
|
||||||
|
|
||||||
// mComplexReceiver.dispose();
|
|
||||||
// mComplexReceiver = null;
|
|
||||||
//
|
|
||||||
// mRealReceiver.dispose();
|
|
||||||
// mRealReceiver = null;
|
|
||||||
|
|
||||||
mThreadPoolManager = null;
|
|
||||||
mBufferProcessingTask = null;
|
|
||||||
mFrequencyCorrectionController = null;
|
mFrequencyCorrectionController = null;
|
||||||
|
|
||||||
mAudioPacketBroadcaster.dispose();
|
mAudioPacketBroadcaster.dispose();
|
||||||
@@ -496,12 +481,6 @@ public class ProcessingChain implements IChannelEventListener
|
|||||||
mSource = null;
|
mSource = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( mBufferProcessingTask != null )
|
|
||||||
{
|
|
||||||
mThreadPoolManager.cancel( mBufferProcessingTask );
|
|
||||||
mBufferProcessingTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop each of the modules */
|
/* Stop each of the modules */
|
||||||
for( Module module: mModules )
|
for( Module module: mModules )
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -228,7 +228,8 @@ public class DecoderFactory
|
|||||||
Sync sync = mptConfig.getSync();
|
Sync sync = mptConfig.getSync();
|
||||||
|
|
||||||
modules.add( new MPT1327Decoder( aliasList, sync ) );
|
modules.add( new MPT1327Decoder( aliasList, sync ) );
|
||||||
modules.add( new MPT1327DecoderState( aliasList, channelMap, channelType ) );
|
modules.add( new MPT1327DecoderState( aliasList, channelMap,
|
||||||
|
channelType, mptConfig.getCallTimeout() * 1000 ) );
|
||||||
|
|
||||||
if( channelType == ChannelType.STANDARD )
|
if( channelType == ChannelType.STANDARD )
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -310,6 +310,11 @@ public abstract class CallEvent implements Comparable<CallEvent>
|
|||||||
@Override
|
@Override
|
||||||
public int compareTo( CallEvent other )
|
public int compareTo( CallEvent other )
|
||||||
{
|
{
|
||||||
|
if( other != null && other == this )
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
return Long.compare( mEventStart, other.getEventStartTime() );
|
return Long.compare( mEventStart, other.getEventStartTime() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ public class LTRNetDecoder extends Decoder implements IRealBufferListener, Instr
|
|||||||
@Override
|
@Override
|
||||||
public void reset()
|
public void reset()
|
||||||
{
|
{
|
||||||
|
mLTRMessageFramer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ public class LTRStandardDecoder extends Decoder implements IRealBufferListener
|
|||||||
@Override
|
@Override
|
||||||
public void reset()
|
public void reset()
|
||||||
{
|
{
|
||||||
|
mLTRMessageFramer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -153,7 +153,9 @@ public class LTRStandardDecoderState extends DecoderState
|
|||||||
case CA_ENDD:
|
case CA_ENDD:
|
||||||
CallEndMessage end = (CallEndMessage)message;
|
CallEndMessage end = (CallEndMessage)message;
|
||||||
|
|
||||||
int repeater = end.getHomeRepeater();
|
//Home channel is 31 for call end -- use the free channel
|
||||||
|
//as the call end channel
|
||||||
|
int repeater = end.getFree();
|
||||||
|
|
||||||
mLCNTracker.processCallChannel( repeater );
|
mLCNTracker.processCallChannel( repeater );
|
||||||
|
|
||||||
|
|||||||
@@ -301,8 +301,8 @@ public class MPT1327Decoder extends Decoder
|
|||||||
@Override
|
@Override
|
||||||
public void reset()
|
public void reset()
|
||||||
{
|
{
|
||||||
// TODO Auto-generated method stub
|
mControlMessageFramer.reset();
|
||||||
|
mTrafficMessageFramer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -26,9 +26,11 @@ import java.util.TreeSet;
|
|||||||
|
|
||||||
import message.Message;
|
import message.Message;
|
||||||
import module.decode.DecoderType;
|
import module.decode.DecoderType;
|
||||||
|
import module.decode.config.DecodeConfiguration;
|
||||||
import module.decode.event.CallEvent;
|
import module.decode.event.CallEvent;
|
||||||
import module.decode.event.CallEvent.CallEventType;
|
import module.decode.event.CallEvent.CallEventType;
|
||||||
import module.decode.mpt1327.MPT1327Message.IdentType;
|
import module.decode.mpt1327.MPT1327Message.IdentType;
|
||||||
|
import module.decode.state.ChangeChannelTimeoutEvent;
|
||||||
import module.decode.state.ChangedAttribute;
|
import module.decode.state.ChangedAttribute;
|
||||||
import module.decode.state.DecoderState;
|
import module.decode.state.DecoderState;
|
||||||
import module.decode.state.DecoderStateEvent;
|
import module.decode.state.DecoderStateEvent;
|
||||||
@@ -41,6 +43,8 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import alias.Alias;
|
import alias.Alias;
|
||||||
import alias.AliasList;
|
import alias.AliasList;
|
||||||
|
import audio.metadata.Metadata;
|
||||||
|
import audio.metadata.MetadataType;
|
||||||
import controller.channel.Channel.ChannelType;
|
import controller.channel.Channel.ChannelType;
|
||||||
import controller.channel.map.ChannelMap;
|
import controller.channel.map.ChannelMap;
|
||||||
|
|
||||||
@@ -60,15 +64,18 @@ public class MPT1327DecoderState extends DecoderState
|
|||||||
private ChannelMap mChannelMap;
|
private ChannelMap mChannelMap;
|
||||||
|
|
||||||
private long mFrequency = 0;
|
private long mFrequency = 0;
|
||||||
|
private long mCallTimeout;
|
||||||
|
|
||||||
public MPT1327DecoderState( AliasList aliasList, ChannelMap channelMap,
|
public MPT1327DecoderState( AliasList aliasList,
|
||||||
ChannelType channelType )
|
ChannelMap channelMap,
|
||||||
|
ChannelType channelType,
|
||||||
|
long callTimeout )
|
||||||
{
|
{
|
||||||
super( aliasList );
|
super( aliasList );
|
||||||
|
|
||||||
mChannelMap = channelMap;
|
mChannelMap = channelMap;
|
||||||
|
|
||||||
mChannelType = channelType;
|
mChannelType = channelType;
|
||||||
|
mCallTimeout = callTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -252,8 +259,42 @@ public class MPT1327DecoderState extends DecoderState
|
|||||||
|
|
||||||
/* Traffic Channel Events */
|
/* Traffic Channel Events */
|
||||||
case CLEAR:
|
case CLEAR:
|
||||||
|
mChannelNumber = mpt.getChannel();
|
||||||
|
|
||||||
|
broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) );
|
||||||
|
break;
|
||||||
case MAINT:
|
case MAINT:
|
||||||
broadcast( new DecoderStateEvent( this, Event.END, State.END ) );
|
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() );
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -276,6 +317,25 @@ public class MPT1327DecoderState extends DecoderState
|
|||||||
|
|
||||||
mToTalkgroup = null;
|
mToTalkgroup = null;
|
||||||
broadcast( ChangedAttribute.TO_TALKGROUP );
|
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()
|
public String getSite()
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ import module.decode.p25.message.ldu.lc.TelephoneInterconnectVoiceChannelUser;
|
|||||||
import module.decode.p25.message.ldu.lc.UnitToUnitVoiceChannelUser;
|
import module.decode.p25.message.ldu.lc.UnitToUnitVoiceChannelUser;
|
||||||
import module.decode.p25.message.pdu.PDUMessage;
|
import module.decode.p25.message.pdu.PDUMessage;
|
||||||
import module.decode.p25.message.pdu.confirmed.PDUConfirmedMessage;
|
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.SNDCPActivateTDSContextAccept;
|
||||||
import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextReject;
|
import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextReject;
|
||||||
import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextRequest;
|
import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextRequest;
|
||||||
@@ -405,12 +407,12 @@ public class P25DecoderState extends DecoderState
|
|||||||
{
|
{
|
||||||
if( hdu.isValid() )
|
if( hdu.isValid() )
|
||||||
{
|
{
|
||||||
State state = hdu.isEncrypted() ? State.ENCRYPTED : State.CALL;
|
broadcast( new DecoderStateEvent( this, Event.START, State.CALL ) );
|
||||||
|
|
||||||
broadcast( new DecoderStateEvent( this, Event.START, state ) );
|
|
||||||
|
|
||||||
String to = hdu.getToID();
|
String to = hdu.getToID();
|
||||||
|
|
||||||
|
updateTo( to );
|
||||||
|
|
||||||
if( mCurrentCallEvent == null )
|
if( mCurrentCallEvent == null )
|
||||||
{
|
{
|
||||||
mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.CALL )
|
mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.CALL )
|
||||||
@@ -424,7 +426,6 @@ public class P25DecoderState extends DecoderState
|
|||||||
broadcast( mCurrentCallEvent );
|
broadcast( mCurrentCallEvent );
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTo( to );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,7 +437,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
{
|
{
|
||||||
if( tdulc.getOpcode() == LinkControlOpcode.CALL_TERMINATION_OR_CANCELLATION )
|
if( tdulc.getOpcode() == LinkControlOpcode.CALL_TERMINATION_OR_CANCELLATION )
|
||||||
{
|
{
|
||||||
broadcast( new DecoderStateEvent( this, Event.END, State.END ) );
|
broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) );
|
||||||
|
|
||||||
if( mCurrentCallEvent != null )
|
if( mCurrentCallEvent != null )
|
||||||
{
|
{
|
||||||
@@ -614,12 +615,14 @@ public class P25DecoderState extends DecoderState
|
|||||||
module.decode.p25.message.tdu.lc.GroupVoiceChannelUser gvcuser =
|
module.decode.p25.message.tdu.lc.GroupVoiceChannelUser gvcuser =
|
||||||
(module.decode.p25.message.tdu.lc.GroupVoiceChannelUser)tdulc;
|
(module.decode.p25.message.tdu.lc.GroupVoiceChannelUser)tdulc;
|
||||||
|
|
||||||
broadcast( new DecoderStateEvent( this, Event.CONTINUATION,
|
broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CALL ) );
|
||||||
( gvcuser.isEncrypted() ? State.ENCRYPTED : State.CALL ) ) );
|
|
||||||
|
|
||||||
String from = gvcuser.getSourceAddress();
|
String from = gvcuser.getSourceAddress();
|
||||||
String to = gvcuser.getGroupAddress();
|
String to = gvcuser.getGroupAddress();
|
||||||
|
|
||||||
|
updateFrom( from );
|
||||||
|
updateTo( to );
|
||||||
|
|
||||||
if( mCurrentCallEvent == null )
|
if( mCurrentCallEvent == null )
|
||||||
{
|
{
|
||||||
mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
|
mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
|
||||||
@@ -635,9 +638,6 @@ public class P25DecoderState extends DecoderState
|
|||||||
|
|
||||||
broadcast( mCurrentCallEvent );
|
broadcast( mCurrentCallEvent );
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFrom( from );
|
|
||||||
updateTo( to );
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MESSAGE_UPDATE:
|
case MESSAGE_UPDATE:
|
||||||
@@ -844,6 +844,8 @@ public class P25DecoderState extends DecoderState
|
|||||||
|
|
||||||
String to = tivcu.getAddress();
|
String to = tivcu.getAddress();
|
||||||
|
|
||||||
|
updateTo( to );
|
||||||
|
|
||||||
if( mCurrentCallEvent == null )
|
if( mCurrentCallEvent == null )
|
||||||
{
|
{
|
||||||
mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.TELEPHONE_INTERCONNECT )
|
mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.TELEPHONE_INTERCONNECT )
|
||||||
@@ -857,8 +859,6 @@ public class P25DecoderState extends DecoderState
|
|||||||
|
|
||||||
broadcast( mCurrentCallEvent );
|
broadcast( mCurrentCallEvent );
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTo( to );
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case UNIT_AUTHENTICATION_COMMAND:
|
case UNIT_AUTHENTICATION_COMMAND:
|
||||||
@@ -921,7 +921,10 @@ public class P25DecoderState extends DecoderState
|
|||||||
(module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser)tdulc;
|
(module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser)tdulc;
|
||||||
|
|
||||||
String from = uuvcu.getSourceAddress();
|
String from = uuvcu.getSourceAddress();
|
||||||
|
updateFrom( from );
|
||||||
|
|
||||||
String to = uuvcu.getTargetAddress();
|
String to = uuvcu.getTargetAddress();
|
||||||
|
updateTo( to );
|
||||||
|
|
||||||
if( mCurrentCallEvent != null )
|
if( mCurrentCallEvent != null )
|
||||||
{
|
{
|
||||||
@@ -935,9 +938,6 @@ public class P25DecoderState extends DecoderState
|
|||||||
.to( to )
|
.to( to )
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFrom( from );
|
|
||||||
updateTo( to );
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -949,35 +949,14 @@ public class P25DecoderState extends DecoderState
|
|||||||
* Log optional vendor format messages that we don't yet support so that we
|
* Log optional vendor format messages that we don't yet support so that we
|
||||||
* can understand those messages and eventually add support.
|
* can understand those messages and eventually add support.
|
||||||
*/
|
*/
|
||||||
private void logAlternateVendorMessage( P25Message p25 )
|
private void logAlternateVendorMessage( P25Message message )
|
||||||
{
|
{
|
||||||
// if( p25 instanceof TDULinkControlMessage )
|
// if( message.isValid() )
|
||||||
// {
|
// {
|
||||||
// TDULinkControlMessage tdulc = (TDULinkControlMessage)p25;
|
// mLog.info( "PLEASE NOTIFY DEVELOPER - UNRECOGNIZED P25 VENDOR FORMAT -"
|
||||||
//
|
// + " DUID:" + message.getDUID()
|
||||||
// mLog.debug( "TDULC - VENDOR [" + tdulc.getVendor() +
|
// + " MSG:" + message.getBinaryMessage()
|
||||||
// "] SPECIFIC FORMAT FOR " + tdulc.getVendorOpcode().getLabel() +
|
// + " CLASS:" + message.getClass() );
|
||||||
// " " + 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() + "]" );
|
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -988,15 +967,11 @@ public class P25DecoderState extends DecoderState
|
|||||||
*/
|
*/
|
||||||
private void processLDU( LDUMessage ldu )
|
private void processLDU( LDUMessage ldu )
|
||||||
{
|
{
|
||||||
State state = ( ldu.isValid() && ldu.isEncrypted() ) ?
|
broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CALL ) );
|
||||||
State.ENCRYPTED : State.CALL;
|
|
||||||
|
|
||||||
broadcast( new DecoderStateEvent( this, Event.CONTINUATION, state ) );
|
|
||||||
|
|
||||||
if( mCurrentCallEvent == null )
|
if( mCurrentCallEvent == null )
|
||||||
{
|
{
|
||||||
mCurrentCallEvent = new P25CallEvent.Builder(
|
mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.CALL )
|
||||||
( state == State.CALL ) ? CallEventType.CALL : CallEventType.ENCRYPTED_CALL )
|
|
||||||
.aliasList( getAliasList() )
|
.aliasList( getAliasList() )
|
||||||
.channel( mCurrentChannel )
|
.channel( mCurrentChannel )
|
||||||
.frequency( mCurrentChannelFrequency )
|
.frequency( mCurrentChannelFrequency )
|
||||||
@@ -1056,7 +1031,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CALL_TERMINATION_OR_CANCELLATION:
|
case CALL_TERMINATION_OR_CANCELLATION:
|
||||||
broadcast( new DecoderStateEvent( this, Event.END, State.END ) );
|
broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) );
|
||||||
mCurrentCallEvent = null;
|
mCurrentCallEvent = null;
|
||||||
|
|
||||||
if( !( ldu instanceof CallTermination ) )
|
if( !( ldu instanceof CallTermination ) )
|
||||||
@@ -1114,42 +1089,43 @@ public class P25DecoderState extends DecoderState
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GROUP_VOICE_CHANNEL_UPDATE:
|
case GROUP_VOICE_CHANNEL_UPDATE:
|
||||||
if( mChannelType == ChannelType.STANDARD &&
|
if( ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate )
|
||||||
ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate )
|
|
||||||
{
|
{
|
||||||
module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate gvcu =
|
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();
|
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();
|
String userB = gvcu.getGroupAddressB();
|
||||||
|
|
||||||
if( userB != null &&
|
if( mChannelType == ChannelType.STANDARD )
|
||||||
!userB.contentEquals( "0000" ) &&
|
|
||||||
!mCallDetectTalkgroups.contains( userB ) )
|
|
||||||
{
|
{
|
||||||
broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
|
if( !mCallDetectTalkgroups.contains( userA ) )
|
||||||
.aliasList( getAliasList() )
|
{
|
||||||
.channel( gvcu.getChannelB() )
|
broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
|
||||||
.details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) )
|
.aliasList( getAliasList() )
|
||||||
.frequency( gvcu.getDownlinkFrequencyB() )
|
.channel( gvcu.getChannelA() )
|
||||||
.to( userB )
|
.details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) )
|
||||||
.build() );
|
.frequency( gvcu.getDownlinkFrequencyA() )
|
||||||
|
.to( userA )
|
||||||
|
.build() );
|
||||||
|
|
||||||
|
mCallDetectTalkgroups.add( userA );
|
||||||
|
}
|
||||||
|
|
||||||
mCallDetectTalkgroups.add( userB );
|
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 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1158,25 +1134,27 @@ public class P25DecoderState extends DecoderState
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT:
|
case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT:
|
||||||
if( mChannelType == ChannelType.STANDARD &&
|
if( ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit )
|
||||||
ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit )
|
|
||||||
{
|
{
|
||||||
module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit gvcue =
|
module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit gvcue =
|
||||||
(module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit)ldu;
|
(module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit)ldu;
|
||||||
|
|
||||||
String group = gvcue.getGroupAddress();
|
String group = gvcue.getGroupAddress();
|
||||||
|
|
||||||
if( !mCallDetectTalkgroups.contains( group ) )
|
if( mChannelType == ChannelType.STANDARD )
|
||||||
{
|
{
|
||||||
broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
|
if( !mCallDetectTalkgroups.contains( group ) )
|
||||||
.aliasList( getAliasList() )
|
{
|
||||||
.channel( gvcue.getTransmitChannel() )
|
broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
|
||||||
.details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) )
|
.aliasList( getAliasList() )
|
||||||
.frequency( gvcue.getDownlinkFrequency() )
|
.channel( gvcue.getTransmitChannel() )
|
||||||
.to( group )
|
.details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) )
|
||||||
.build() );
|
.frequency( gvcue.getDownlinkFrequency() )
|
||||||
|
.to( group )
|
||||||
mCallDetectTalkgroups.add( group );
|
.build() );
|
||||||
|
|
||||||
|
mCallDetectTalkgroups.add( group );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1186,29 +1164,32 @@ public class P25DecoderState extends DecoderState
|
|||||||
break;
|
break;
|
||||||
case GROUP_VOICE_CHANNEL_USER:
|
case GROUP_VOICE_CHANNEL_USER:
|
||||||
/* Indicates the current user of the current channel */
|
/* Indicates the current user of the current channel */
|
||||||
if( mChannelType == ChannelType.STANDARD &&
|
if( ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUser )
|
||||||
ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUser )
|
|
||||||
{
|
{
|
||||||
module.decode.p25.message.ldu.lc.GroupVoiceChannelUser gvcuser =
|
module.decode.p25.message.ldu.lc.GroupVoiceChannelUser gvcuser =
|
||||||
(module.decode.p25.message.ldu.lc.GroupVoiceChannelUser)ldu;
|
(module.decode.p25.message.ldu.lc.GroupVoiceChannelUser)ldu;
|
||||||
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFrom( gvcuser.getSourceAddress() );
|
updateFrom( gvcuser.getSourceAddress() );
|
||||||
|
|
||||||
updateTo( gvcuser.getGroupAddress() );
|
updateTo( gvcuser.getGroupAddress() );
|
||||||
|
|
||||||
|
if( mChannelType == ChannelType.STANDARD )
|
||||||
|
{
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1404,6 +1385,8 @@ public class P25DecoderState extends DecoderState
|
|||||||
TelephoneInterconnectVoiceChannelUser tivcu =
|
TelephoneInterconnectVoiceChannelUser tivcu =
|
||||||
(TelephoneInterconnectVoiceChannelUser)ldu;
|
(TelephoneInterconnectVoiceChannelUser)ldu;
|
||||||
|
|
||||||
|
updateTo( tivcu.getAddress() );
|
||||||
|
|
||||||
if( mCurrentCallEvent.getCallEventType() != CallEventType.TELEPHONE_INTERCONNECT )
|
if( mCurrentCallEvent.getCallEventType() != CallEventType.TELEPHONE_INTERCONNECT )
|
||||||
{
|
{
|
||||||
mCurrentCallEvent.setCallEventType( CallEventType.TELEPHONE_INTERCONNECT );
|
mCurrentCallEvent.setCallEventType( CallEventType.TELEPHONE_INTERCONNECT );
|
||||||
@@ -1418,8 +1401,6 @@ public class P25DecoderState extends DecoderState
|
|||||||
|
|
||||||
broadcast( mCurrentCallEvent );
|
broadcast( mCurrentCallEvent );
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTo( tivcu.getAddress() );
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1485,6 +1466,10 @@ public class P25DecoderState extends DecoderState
|
|||||||
UnitToUnitVoiceChannelUser uuvcu =
|
UnitToUnitVoiceChannelUser uuvcu =
|
||||||
(UnitToUnitVoiceChannelUser)ldu;
|
(UnitToUnitVoiceChannelUser)ldu;
|
||||||
|
|
||||||
|
updateFrom( uuvcu.getSourceAddress() );
|
||||||
|
|
||||||
|
updateTo( uuvcu.getTargetAddress() );
|
||||||
|
|
||||||
if( mCurrentCallEvent.getCallEventType() != CallEventType.UNIT_TO_UNIT_CALL )
|
if( mCurrentCallEvent.getCallEventType() != CallEventType.UNIT_TO_UNIT_CALL )
|
||||||
{
|
{
|
||||||
mCurrentCallEvent.setCallEventType( CallEventType.UNIT_TO_UNIT_CALL );
|
mCurrentCallEvent.setCallEventType( CallEventType.UNIT_TO_UNIT_CALL );
|
||||||
@@ -1499,9 +1484,6 @@ public class P25DecoderState extends DecoderState
|
|||||||
|
|
||||||
broadcast( mCurrentCallEvent );
|
broadcast( mCurrentCallEvent );
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFrom( uuvcu.getSourceAddress() );
|
|
||||||
updateTo( uuvcu.getTargetAddress() );
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1520,7 +1502,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
private void processTSBK( TSBKMessage tsbk )
|
private void processTSBK( TSBKMessage tsbk )
|
||||||
{
|
{
|
||||||
/* Trunking Signalling Block Messages - indicates Control Channel */
|
/* Trunking Signalling Block Messages - indicates Control Channel */
|
||||||
broadcast( new DecoderStateEvent( this, Event.START, State.CONTROL ) );
|
broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) );
|
||||||
|
|
||||||
if( tsbk.getVendor() == Vendor.STANDARD )
|
if( tsbk.getVendor() == Vendor.STANDARD )
|
||||||
{
|
{
|
||||||
@@ -1649,7 +1631,11 @@ public class P25DecoderState extends DecoderState
|
|||||||
*/
|
*/
|
||||||
private void processPDU( PDUMessage pdu )
|
private void processPDU( PDUMessage pdu )
|
||||||
{
|
{
|
||||||
if( pdu instanceof PDUConfirmedMessage )
|
if( pdu instanceof PacketData || pdu instanceof PDUTypeUnknown )
|
||||||
|
{
|
||||||
|
broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.DATA ) );
|
||||||
|
}
|
||||||
|
else if( pdu instanceof PDUConfirmedMessage )
|
||||||
{
|
{
|
||||||
broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.DATA ) );
|
broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.DATA ) );
|
||||||
|
|
||||||
@@ -1657,7 +1643,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
|
|
||||||
switch( pduc.getPDUType() )
|
switch( pduc.getPDUType() )
|
||||||
{
|
{
|
||||||
case SN_ACTIVATE_TDS_CONTEXT_ACCEPT:
|
case SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT:
|
||||||
SNDCPActivateTDSContextAccept satca =
|
SNDCPActivateTDSContextAccept satca =
|
||||||
(SNDCPActivateTDSContextAccept)pduc;
|
(SNDCPActivateTDSContextAccept)pduc;
|
||||||
|
|
||||||
@@ -1669,7 +1655,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
.to( satca.getLogicalLinkID() )
|
.to( satca.getLogicalLinkID() )
|
||||||
.build() );
|
.build() );
|
||||||
break;
|
break;
|
||||||
case SN_ACTIVATE_TDS_CONTEXT_REJECT:
|
case SNDCP_ACTIVATE_TDS_CONTEXT_REJECT:
|
||||||
SNDCPActivateTDSContextReject satcr =
|
SNDCPActivateTDSContextReject satcr =
|
||||||
(SNDCPActivateTDSContextReject)pduc;
|
(SNDCPActivateTDSContextReject)pduc;
|
||||||
|
|
||||||
@@ -1694,7 +1680,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
.from( satcreq.getLogicalLinkID() )
|
.from( satcreq.getLogicalLinkID() )
|
||||||
.build() );
|
.build() );
|
||||||
break;
|
break;
|
||||||
case SN_DEACTIVATE_TDS_CONTEXT_ACCEPT:
|
case SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT:
|
||||||
SNDCPDeactivateTDSContext sdtca =
|
SNDCPDeactivateTDSContext sdtca =
|
||||||
(SNDCPDeactivateTDSContext)pduc;
|
(SNDCPDeactivateTDSContext)pduc;
|
||||||
|
|
||||||
@@ -1706,7 +1692,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
.from( sdtca.getLogicalLinkID() )
|
.from( sdtca.getLogicalLinkID() )
|
||||||
.build() );
|
.build() );
|
||||||
break;
|
break;
|
||||||
case SN_DEACTIVATE_TDS_CONTEXT_REQUEST:
|
case SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST:
|
||||||
SNDCPDeactivateTDSContext sdtcreq =
|
SNDCPDeactivateTDSContext sdtcreq =
|
||||||
(SNDCPDeactivateTDSContext)pduc;
|
(SNDCPDeactivateTDSContext)pduc;
|
||||||
|
|
||||||
@@ -1718,7 +1704,7 @@ public class P25DecoderState extends DecoderState
|
|||||||
.from( sdtcreq.getLogicalLinkID() )
|
.from( sdtcreq.getLogicalLinkID() )
|
||||||
.build() );
|
.build() );
|
||||||
break;
|
break;
|
||||||
case SN_RF_CONFIRMED_DATA:
|
case SNDCP_RF_CONFIRMED_DATA:
|
||||||
SNDCPUserData sud = (SNDCPUserData)pduc;
|
SNDCPUserData sud = (SNDCPUserData)pduc;
|
||||||
|
|
||||||
StringBuilder sbFrom = new StringBuilder();
|
StringBuilder sbFrom = new StringBuilder();
|
||||||
@@ -1747,10 +1733,10 @@ public class P25DecoderState extends DecoderState
|
|||||||
.to( pduc.getLogicalLinkID() )
|
.to( pduc.getLogicalLinkID() )
|
||||||
.build() );
|
.build() );
|
||||||
break;
|
break;
|
||||||
case SN_RF_UNCONFIRMED_DATA:
|
case SNDCP_RF_UNCONFIRMED_DATA:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mLog.debug( "PDUC - Unrecognized Message: " + pduc.toString() );
|
// mLog.debug( "PDUC - Unrecognized Message: " + pduc.toString() );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3101,16 +3087,9 @@ public class P25DecoderState extends DecoderState
|
|||||||
if( mToTalkgroup == null || !mToTalkgroup.contentEquals( to ) )
|
if( mToTalkgroup == null || !mToTalkgroup.contentEquals( to ) )
|
||||||
{
|
{
|
||||||
mToTalkgroup = to;
|
mToTalkgroup = to;
|
||||||
|
|
||||||
broadcast( ChangedAttribute.TO_TALKGROUP );
|
broadcast( ChangedAttribute.TO_TALKGROUP );
|
||||||
|
|
||||||
if( mCurrentCallEvent != null &&
|
|
||||||
( mCurrentCallEvent.getToID() == null ||
|
|
||||||
!mCurrentCallEvent.getToID().contentEquals( to ) ) )
|
|
||||||
{
|
|
||||||
mCurrentCallEvent.setToID( to );
|
|
||||||
broadcast( mCurrentCallEvent );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( hasAliasList() )
|
if( hasAliasList() )
|
||||||
{
|
{
|
||||||
mToAlias = getAliasList().getTalkgroupAlias( mToTalkgroup );
|
mToAlias = getAliasList().getTalkgroupAlias( mToTalkgroup );
|
||||||
@@ -3119,12 +3098,20 @@ public class P25DecoderState extends DecoderState
|
|||||||
{
|
{
|
||||||
mToAlias = null;
|
mToAlias = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send audio metadata update */
|
broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
|
||||||
broadcast( new Metadata( MetadataType.TO, mToTalkgroup, mToAlias, true ) );
|
|
||||||
|
/* 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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3140,14 +3127,6 @@ public class P25DecoderState extends DecoderState
|
|||||||
mFromTalkgroup = from;
|
mFromTalkgroup = from;
|
||||||
broadcast( ChangedAttribute.FROM_TALKGROUP );
|
broadcast( ChangedAttribute.FROM_TALKGROUP );
|
||||||
|
|
||||||
if( mCurrentCallEvent != null &&
|
|
||||||
mCurrentCallEvent.getFromID() == null ||
|
|
||||||
!mCurrentCallEvent.getFromID().contentEquals( from ) )
|
|
||||||
{
|
|
||||||
mCurrentCallEvent.setFromID( from );
|
|
||||||
broadcast( mCurrentCallEvent );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( hasAliasList() )
|
if( hasAliasList() )
|
||||||
{
|
{
|
||||||
mFromAlias = getAliasList().getTalkgroupAlias( mFromTalkgroup );
|
mFromAlias = getAliasList().getTalkgroupAlias( mFromTalkgroup );
|
||||||
@@ -3158,10 +3137,19 @@ public class P25DecoderState extends DecoderState
|
|||||||
}
|
}
|
||||||
|
|
||||||
broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS );
|
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 ) );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,8 @@ public class P25Message extends Message
|
|||||||
sb.append( " " );
|
sb.append( " " );
|
||||||
sb.append( getDUID().getLabel() );
|
sb.append( getDUID().getLabel() );
|
||||||
|
|
||||||
|
sb.append( " DATA UNIT ID:" + mMessage.getInt( DUID ) );
|
||||||
|
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package module.decode.p25.message.pdu;
|
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.AdjacentStatusBroadcastExtended;
|
||||||
import module.decode.p25.message.pdu.osp.control.CallAlertExtended;
|
import module.decode.p25.message.pdu.osp.control.CallAlertExtended;
|
||||||
import module.decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended;
|
import module.decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended;
|
||||||
@@ -121,7 +122,8 @@ public class PDUMessageFactory
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new PDUMessage( message, duid, aliasList );
|
return new PDUMessage( message, duid, aliasList );
|
||||||
|
case PACKET_DATA:
|
||||||
|
return new PacketData( message, aliasList );
|
||||||
case UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL:
|
case UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL:
|
||||||
return new PDUMessage( message, duid, aliasList );
|
return new PDUMessage( message, duid, aliasList );
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -48,19 +48,27 @@ public class PDUConfirmedMessageFactory
|
|||||||
case UNENCRYPTED_USER_DATA:
|
case UNENCRYPTED_USER_DATA:
|
||||||
switch( message.getPDUType() )
|
switch( message.getPDUType() )
|
||||||
{
|
{
|
||||||
case SN_ACTIVATE_TDS_CONTEXT_ACCEPT:
|
case SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT:
|
||||||
return new SNDCPActivateTDSContextAccept( message );
|
return new SNDCPActivateTDSContextAccept( message );
|
||||||
case SN_ACTIVATE_TDS_CONTEXT_REJECT:
|
case SNDCP_ACTIVATE_TDS_CONTEXT_REJECT:
|
||||||
return new SNDCPActivateTDSContextReject( message );
|
return new SNDCPActivateTDSContextReject( message );
|
||||||
case SN_ACTIVATE_TDS_CONTEXT_REQUEST:
|
case SN_ACTIVATE_TDS_CONTEXT_REQUEST:
|
||||||
return new SNDCPActivateTDSContextRequest( message );
|
return new SNDCPActivateTDSContextRequest( message );
|
||||||
case SN_DEACTIVATE_TDS_CONTEXT_ACCEPT:
|
case SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT:
|
||||||
case SN_DEACTIVATE_TDS_CONTEXT_REQUEST:
|
case SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST:
|
||||||
return new SNDCPDeactivateTDSContext( message );
|
return new SNDCPDeactivateTDSContext( message );
|
||||||
case SN_RF_CONFIRMED_DATA:
|
case SNDCP_RF_CONFIRMED_DATA:
|
||||||
return new SNDCPUserData( message );
|
return new SNDCPUserData( message );
|
||||||
case SN_RF_UNCONFIRMED_DATA:
|
case SNDCP_RF_UNCONFIRMED_DATA:
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
66
src/module/decode/p25/message/pdu/confirmed/PacketData.java
Normal file
66
src/module/decode/p25/message/pdu/confirmed/PacketData.java
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,7 +49,7 @@ public class SNDCPDeactivateTDSContext extends PDUConfirmedMessage
|
|||||||
|
|
||||||
public String getDeactivationType()
|
public String getDeactivationType()
|
||||||
{
|
{
|
||||||
if( getPDUType() == PDUType.SN_DEACTIVATE_TDS_CONTEXT_REQUEST )
|
if( getPDUType() == PDUType.SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST )
|
||||||
{
|
{
|
||||||
return mMessage.getInt( DEACTIVATION_TYPE ) == 0 ?
|
return mMessage.getInt( DEACTIVATION_TYPE ) == 0 ?
|
||||||
"ALL NSAPIS" : "THIS NSAPI";
|
"ALL NSAPIS" : "THIS NSAPI";
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ public enum DataUnitID
|
|||||||
HDU ( 0, 712, false, "HDU ", "Header Data Unit" ),
|
HDU ( 0, 712, false, "HDU ", "Header Data Unit" ),
|
||||||
TDU ( 3, 64, false, "TDU ", "Simple Terminator Data Unit" ),
|
TDU ( 3, 64, false, "TDU ", "Simple Terminator Data Unit" ),
|
||||||
LDU1 ( 5, 1632, true, "LDU1 ", "Logical Link Data Unit 1" ),
|
LDU1 ( 5, 1632, true, "LDU1 ", "Logical Link Data Unit 1" ),
|
||||||
|
VSELP1( 6, 1680, false, "VSEL1", "Motorola VSELP Unit 1" ),
|
||||||
TSBK1( 7, 260, false, "TSBK1", "Trunking Signaling Block" ),
|
TSBK1( 7, 260, false, "TSBK1", "Trunking Signaling Block" ),
|
||||||
TSBK2( 7, 260, false, "TSBK2", "Trunking Signaling Block" ),
|
TSBK2( 7, 260, false, "TSBK2", "Trunking Signaling Block" ),
|
||||||
TSBK3( 7, 260, false, "TSBK3", "Trunking Signaling Block" ),
|
TSBK3( 7, 260, false, "TSBK3", "Trunking Signaling Block" ),
|
||||||
|
VSELP2( 9, 1680, false, "VSEL1", "Motorola VSELP Unit 1" ),
|
||||||
LDU2 ( 10, 1632, true, "LDU2 ", "Logical Link Data Unit 2" ),
|
LDU2 ( 10, 1632, true, "LDU2 ", "Logical Link Data Unit 2" ),
|
||||||
PDU0 ( 12, 260, false, "PDU0 ", "Packet Header Data Unit" ),
|
PDU0 ( 12, 260, false, "PDU0 ", "Packet Header Data Unit" ),
|
||||||
PDU1 ( 12, 356, false, "PDU1 ", "Packet Data Unit-Unconfirmed" ),
|
PDU1 ( 12, 356, false, "PDU1 ", "Packet Data Unit-Unconfirmed" ),
|
||||||
|
|||||||
@@ -3,12 +3,18 @@ package module.decode.p25.reference;
|
|||||||
public enum PDUType
|
public enum PDUType
|
||||||
{
|
{
|
||||||
/* Outbound */
|
/* Outbound */
|
||||||
SN_ACTIVATE_TDS_CONTEXT_ACCEPT( "SN-ACTIVATE TDS CONTEXT ACCEPT", 0 ),
|
SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT( "SNDCP-ACTIVATE TDS CONTEXT ACCEPT", 0 ),
|
||||||
SN_DEACTIVATE_TDS_CONTEXT_ACCEPT( "SN-DEACTIVATE TDS CONTEXT ACCEPT", 1 ),
|
SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT( "SNDCP-DEACTIVATE TDS CONTEXT ACCEPT", 1 ),
|
||||||
SN_DEACTIVATE_TDS_CONTEXT_REQUEST( "SN-DEACTIVATE TDS CONTEXT REQUEST", 2 ),
|
SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST( "SNDCP-DEACTIVATE TDS CONTEXT REQUEST", 2 ),
|
||||||
SN_ACTIVATE_TDS_CONTEXT_REJECT( "SN-ACTIVATE TDS CONTEXT REJECT", 3 ),
|
SNDCP_ACTIVATE_TDS_CONTEXT_REJECT( "SNDCP-ACTIVATE TDS CONTEXT REJECT", 3 ),
|
||||||
SN_RF_UNCONFIRMED_DATA( "SN-RF UNCONFIRMED DATA", 4 ),
|
SNDCP_RF_UNCONFIRMED_DATA( "SNDCP-RF UNCONFIRMED DATA", 4 ),
|
||||||
SN_RF_CONFIRMED_DATA( "SN-RF CONFIRMED DATA", 5 ),
|
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 ),
|
||||||
|
|
||||||
/* Inbound */
|
/* Inbound */
|
||||||
SN_ACTIVATE_TDS_CONTEXT_REQUEST( "SN-ACTIVATE TDS CONTEXT REQUEST", 0 ),
|
SN_ACTIVATE_TDS_CONTEXT_REQUEST( "SN-ACTIVATE TDS CONTEXT REQUEST", 0 ),
|
||||||
@@ -36,7 +42,7 @@ public enum PDUType
|
|||||||
|
|
||||||
public static PDUType fromValue( int value, boolean outbound )
|
public static PDUType fromValue( int value, boolean outbound )
|
||||||
{
|
{
|
||||||
if( outbound && 0 <= value && value <= 5 )
|
if( outbound && 0 <= value && value <= 11 )
|
||||||
{
|
{
|
||||||
return values()[ value ];
|
return values()[ value ];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package module.decode.p25.reference;
|
|||||||
|
|
||||||
public enum ServiceAccessPoint
|
public enum ServiceAccessPoint
|
||||||
{
|
{
|
||||||
UNENCRYPTED_USER_DATA( "USER DATA", 0 ),
|
UNENCRYPTED_USER_DATA( "UNENCRYPTED USER DATA", 0 ),
|
||||||
ENCRYPTED_USER_DATA( "ENCRYPTED USER DATA", 1 ),
|
ENCRYPTED_USER_DATA( "ENCRYPTED USER DATA", 1 ),
|
||||||
CIRCUIT_DATA( "CIRCUIT DATA", 2 ),
|
CIRCUIT_DATA( "CIRCUIT DATA", 2 ),
|
||||||
CIRCUIT_DATA_CONTROL( "CIRCUIT DATA CONTROL", 3 ),
|
CIRCUIT_DATA_CONTROL( "CIRCUIT DATA CONTROL", 3 ),
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ public class PassportDecoder extends Decoder
|
|||||||
@Override
|
@Override
|
||||||
public void reset()
|
public void reset()
|
||||||
{
|
{
|
||||||
|
mPassportMessageFramer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
29
src/module/decode/state/ChangeChannelTimeoutEvent.java
Normal file
29
src/module/decode/state/ChangeChannelTimeoutEvent.java
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -253,20 +253,6 @@ public class ChannelList extends JPanel implements ChannelEventListener
|
|||||||
idleMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
idleMenu.add( new ColorSettingResetMenuItem( mSettingsManager,
|
||||||
ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE ) );
|
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" );
|
JMenu labelMenu = new JMenu( "Labels" );
|
||||||
colorMenu.add( labelMenu );
|
colorMenu.add( labelMenu );
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* SDR Trunk
|
* SDR Trunk
|
||||||
* Copyright (C) 2014,2015 Dennis Sheirer
|
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,16 +22,9 @@ import java.util.concurrent.ScheduledFuture;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import sample.Listener;
|
|
||||||
import audio.metadata.IMetadataProvider;
|
import audio.metadata.IMetadataProvider;
|
||||||
import audio.metadata.Metadata;
|
import audio.metadata.Metadata;
|
||||||
import audio.metadata.MetadataReset;
|
import audio.metadata.MetadataReset;
|
||||||
@@ -41,38 +34,13 @@ import audio.squelch.SquelchState;
|
|||||||
import controller.ThreadPoolManager;
|
import controller.ThreadPoolManager;
|
||||||
import controller.ThreadPoolManager.ThreadType;
|
import controller.ThreadPoolManager.ThreadType;
|
||||||
import controller.channel.Channel.ChannelType;
|
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,
|
public class ChannelState extends Module implements ICallEventProvider,
|
||||||
IChangedAttributeProvider, IDecoderStateEventListener,
|
IChangedAttributeProvider, IDecoderStateEventListener,
|
||||||
IDecoderStateEventProvider, IMetadataProvider, ISquelchStateProvider
|
IDecoderStateEventProvider, IMetadataProvider, ISquelchStateProvider
|
||||||
@@ -84,8 +52,6 @@ public class ChannelState extends Module implements ICallEventProvider,
|
|||||||
public static final long RESET_TIMEOUT_DELAY = 2000;
|
public static final long RESET_TIMEOUT_DELAY = 2000;
|
||||||
|
|
||||||
private State mState = State.IDLE;
|
private State mState = State.IDLE;
|
||||||
private long mTrafficChannelTimeout =
|
|
||||||
DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS;
|
|
||||||
|
|
||||||
private Listener<CallEvent> mCallEventListener;
|
private Listener<CallEvent> mCallEventListener;
|
||||||
private Listener<ChangedAttribute> mChangedAttributeListener;
|
private Listener<ChangedAttribute> mChangedAttributeListener;
|
||||||
@@ -98,12 +64,15 @@ public class ChannelState extends Module implements ICallEventProvider,
|
|||||||
private boolean mSquelchLocked = false;
|
private boolean mSquelchLocked = false;
|
||||||
private boolean mSelected = 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 mFadeTimeout;
|
||||||
private long mResetTimeout;
|
private long mEndTimeout;
|
||||||
|
|
||||||
private StateMonitor mStateMonitor = new StateMonitor();
|
private StateMonitor mStateMonitor = new StateMonitor();
|
||||||
private AtomicBoolean mMonitoring = new AtomicBoolean();
|
private AtomicBoolean mMonitoring = new AtomicBoolean();
|
||||||
private ScheduledFuture<?> mRunningMonitor;
|
private ScheduledFuture<?> mStateMonitorFuture;
|
||||||
|
|
||||||
private ThreadPoolManager mThreadPoolManager;
|
private ThreadPoolManager mThreadPoolManager;
|
||||||
private ChannelType mChannelType;
|
private ChannelType mChannelType;
|
||||||
@@ -111,32 +80,97 @@ public class ChannelState extends Module implements ICallEventProvider,
|
|||||||
private TrafficChannelStatusListener mTrafficChannelStatusListener;
|
private TrafficChannelStatusListener mTrafficChannelStatusListener;
|
||||||
private CallEvent mTrafficChannelCallEvent;
|
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 )
|
public ChannelState( ThreadPoolManager manager, ChannelType channelType )
|
||||||
{
|
{
|
||||||
mThreadPoolManager = manager;
|
mThreadPoolManager = manager;
|
||||||
mChannelType = channelType;
|
mChannelType = channelType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets this channel state and prepares it for reuse.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void reset()
|
public void reset()
|
||||||
{
|
{
|
||||||
|
broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) );
|
||||||
|
broadcast( new MetadataReset() );
|
||||||
|
|
||||||
|
mState = State.IDLE;
|
||||||
|
broadcast( ChangedAttribute.CHANNEL_STATE );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start()
|
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
|
@Override
|
||||||
public void stop()
|
public void stop()
|
||||||
{
|
{
|
||||||
mSquelchLocked = false;
|
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()
|
public void dispose()
|
||||||
{
|
{
|
||||||
stopMonitor();
|
|
||||||
|
|
||||||
mCallEventListener = null;
|
mCallEventListener = null;
|
||||||
mChangedAttributeListener = null;
|
mChangedAttributeListener = null;
|
||||||
mDecoderStateListener = null;
|
mDecoderStateListener = null;
|
||||||
@@ -145,9 +179,34 @@ public class ChannelState extends Module implements ICallEventProvider,
|
|||||||
mStateMonitor = null;
|
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 )
|
public void setTrafficChannelTimeout( long milliseconds )
|
||||||
{
|
{
|
||||||
mTrafficChannelTimeout = milliseconds;
|
mTrafficChannelFadeTimeout = milliseconds;
|
||||||
|
|
||||||
|
if( mChannelType == ChannelType.TRAFFIC )
|
||||||
|
{
|
||||||
|
mFadeTimeout = mTrafficChannelFadeTimeout;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelected( boolean selected )
|
public void setSelected( boolean selected )
|
||||||
@@ -165,79 +224,18 @@ public class ChannelState extends Module implements ICallEventProvider,
|
|||||||
return mState;
|
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 )
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mRunningMonitor = mThreadPoolManager.scheduleFixedRate(
|
|
||||||
ThreadType.DECODER, mStateMonitor, 20, TimeUnit.MILLISECONDS );
|
|
||||||
}
|
|
||||||
catch( RejectedExecutionException ree )
|
|
||||||
{
|
|
||||||
mLog.error( "state monitor scheduled task rejected", ree );
|
|
||||||
mMonitoring.set( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
* Updates the fade timeout threshold to the current time plus delay
|
||||||
*/
|
*/
|
||||||
private void updateFadeTimeout()
|
private void updateFadeTimeout()
|
||||||
{
|
{
|
||||||
/* Standard channel uses the FADE_TIMEOUT_DELAY and traffic channels
|
if( isTrafficChannel() )
|
||||||
* 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 = timeout;
|
mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mFadeTimeout = System.currentTimeMillis() + mStandardChannelFadeTimeout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,13 +244,13 @@ public class ChannelState extends Module implements ICallEventProvider,
|
|||||||
*/
|
*/
|
||||||
private void updateResetTimeout()
|
private void updateResetTimeout()
|
||||||
{
|
{
|
||||||
if( mChannelType == ChannelType.TRAFFIC )
|
if( isTrafficChannel() )
|
||||||
{
|
{
|
||||||
mResetTimeout = System.currentTimeMillis();
|
mEndTimeout = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mResetTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY;
|
mEndTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,134 +292,118 @@ public class ChannelState extends Module implements ICallEventProvider,
|
|||||||
*/
|
*/
|
||||||
protected void setState( State state )
|
protected void setState( State state )
|
||||||
{
|
{
|
||||||
if( mState == state )
|
synchronized( mState )
|
||||||
{
|
{
|
||||||
switch( state )
|
if( state == mState )
|
||||||
{
|
{
|
||||||
case CALL:
|
if( State.CALL_STATES.contains( state ) )
|
||||||
case CONTROL:
|
{
|
||||||
case DATA:
|
|
||||||
case ENCRYPTED:
|
|
||||||
updateFadeTimeout();
|
updateFadeTimeout();
|
||||||
break;
|
}
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
else if( state != mState && mState.canChangeTo( state ) )
|
||||||
else if( mState.canChangeTo( state ) )
|
|
||||||
{
|
|
||||||
switch( state )
|
|
||||||
{
|
{
|
||||||
case CONTROL:
|
switch( state )
|
||||||
//Don't allow traffic channels to be control channels,
|
{
|
||||||
//otherwise they won't get call tear down
|
case CONTROL:
|
||||||
if( mChannelType != ChannelType.TRAFFIC )
|
//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:
|
||||||
broadcast( SquelchState.SQUELCH );
|
broadcast( SquelchState.SQUELCH );
|
||||||
updateFadeTimeout();
|
updateFadeTimeout();
|
||||||
startMonitor();
|
|
||||||
mState = state;
|
mState = state;
|
||||||
broadcast( ChangedAttribute.CHANNEL_STATE );
|
broadcast( ChangedAttribute.CHANNEL_STATE );
|
||||||
}
|
break;
|
||||||
break;
|
case CALL:
|
||||||
case DATA:
|
broadcast( SquelchState.UNSQUELCH );
|
||||||
case ENCRYPTED:
|
updateFadeTimeout();
|
||||||
broadcast( SquelchState.SQUELCH );
|
mState = state;
|
||||||
updateFadeTimeout();
|
broadcast( ChangedAttribute.CHANNEL_STATE );
|
||||||
startMonitor();
|
break;
|
||||||
mState = state;
|
case FADE:
|
||||||
broadcast( ChangedAttribute.CHANNEL_STATE );
|
processFadeState();
|
||||||
break;
|
break;
|
||||||
case CALL:
|
case IDLE:
|
||||||
broadcast( SquelchState.UNSQUELCH );
|
processIdleState();
|
||||||
updateFadeTimeout();
|
break;
|
||||||
startMonitor();
|
case TEARDOWN:
|
||||||
mState = state;
|
processTeardownState();
|
||||||
broadcast( ChangedAttribute.CHANNEL_STATE );
|
break;
|
||||||
break;
|
case RESET:
|
||||||
case FADE:
|
mState = State.IDLE;
|
||||||
broadcast( SquelchState.SQUELCH );
|
break;
|
||||||
updateResetTimeout();
|
default:
|
||||||
startMonitor();
|
break;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// mLog.debug( "Can't change from [" + mState + "] to [" + state + "]" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
else
|
||||||
{
|
{
|
||||||
mLog.debug( "Can't change from [" + mState + "] to [" + state + "]" );
|
mLog.error( "Traffic channel status listener was null - couldn't "
|
||||||
}
|
+ "teardown the channel");
|
||||||
}
|
|
||||||
|
|
||||||
public class StateMonitor implements Runnable
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if( getState() == State.FADE )
|
|
||||||
{
|
|
||||||
if( mResetTimeout <= System.currentTimeMillis() )
|
|
||||||
{
|
|
||||||
setState( State.END );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if( getState() == State.END )
|
|
||||||
{
|
|
||||||
setState( State.IDLE );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if( mFadeTimeout <= System.currentTimeMillis() )
|
|
||||||
{
|
|
||||||
setState( State.FADE );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
mLog.error( "Exception thrown while state monitor is running " +
|
|
||||||
"- state [" + getState() +
|
|
||||||
"] current [" + System.currentTimeMillis() +
|
|
||||||
"] mResetTimeout [" + mResetTimeout +
|
|
||||||
"] mFadeTimeout [" + mFadeTimeout +
|
|
||||||
"]", e );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -615,35 +597,95 @@ public class ChannelState extends Module implements ICallEventProvider,
|
|||||||
broadcast( SquelchState.UNSQUELCH );
|
broadcast( SquelchState.UNSQUELCH );
|
||||||
mSquelchLocked = true;
|
mSquelchLocked = true;
|
||||||
break;
|
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:
|
case CONTINUATION:
|
||||||
if( isIdleOrEndingState() )
|
|
||||||
{
|
|
||||||
setState( event.getState() );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
updateFadeTimeout();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DECODE:
|
case DECODE:
|
||||||
if( getState() == State.IDLE )
|
case START:
|
||||||
|
if( State.CALL_STATES.contains( event.getState() ) )
|
||||||
{
|
{
|
||||||
setState( event.getState() );
|
setState( event.getState() );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case END:
|
case END:
|
||||||
setState( State.FADE );
|
if( isTrafficChannel() )
|
||||||
|
{
|
||||||
|
setState( State.TEARDOWN );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setState( State.FADE );
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RESET:
|
case RESET:
|
||||||
/* Channel State does not respond to reset events */
|
/* Channel State does not respond to reset events */
|
||||||
break;
|
break;
|
||||||
case START:
|
|
||||||
setState( event.getState() );
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ import javax.swing.JLabel;
|
|||||||
import javax.swing.JMenu;
|
import javax.swing.JMenu;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
import module.decode.DecoderFactory;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import controller.channel.Channel;
|
||||||
|
import controller.channel.ChannelUtils;
|
||||||
|
import module.decode.DecoderFactory;
|
||||||
|
import net.miginfocom.swing.MigLayout;
|
||||||
import playlist.PlaylistManager;
|
import playlist.PlaylistManager;
|
||||||
import record.RecorderType;
|
import record.RecorderType;
|
||||||
import record.config.RecordConfiguration;
|
import record.config.RecordConfiguration;
|
||||||
@@ -29,8 +30,6 @@ import settings.ColorSetting.ColorSettingName;
|
|||||||
import settings.Setting;
|
import settings.Setting;
|
||||||
import settings.SettingChangeListener;
|
import settings.SettingChangeListener;
|
||||||
import settings.SettingsManager;
|
import settings.SettingsManager;
|
||||||
import controller.channel.Channel;
|
|
||||||
import controller.channel.ChannelUtils;
|
|
||||||
|
|
||||||
public class ChannelStatePanel extends JPanel
|
public class ChannelStatePanel extends JPanel
|
||||||
implements Listener<ChangedAttribute>, SettingChangeListener
|
implements Listener<ChangedAttribute>, SettingChangeListener
|
||||||
@@ -236,26 +235,37 @@ public class ChannelStatePanel extends JPanel
|
|||||||
Graphics2D g2 = (Graphics2D)g.create();
|
Graphics2D g2 = (Graphics2D)g.create();
|
||||||
|
|
||||||
Paint p = null;
|
Paint p = null;
|
||||||
|
|
||||||
|
final Channel channel = mChannel;
|
||||||
|
|
||||||
switch( mChannel.getChannelState().getState() )
|
final ChannelState channelState = ( channel == null ? null : channel.getChannelState() );
|
||||||
|
|
||||||
|
if( channelState != null )
|
||||||
{
|
{
|
||||||
case CALL:
|
switch( channelState.getState() )
|
||||||
p = getGradient( mColorTopCall, mColorMiddleCall );
|
{
|
||||||
break;
|
case CALL:
|
||||||
case CONTROL:
|
p = getGradient( mColorTopCall, mColorMiddleCall );
|
||||||
p = getGradient( mColorTopControl, mColorMiddleControl );
|
break;
|
||||||
break;
|
case CONTROL:
|
||||||
case DATA:
|
p = getGradient( mColorTopControl, mColorMiddleControl );
|
||||||
p = getGradient( mColorTopData, mColorMiddleData );
|
break;
|
||||||
break;
|
case DATA:
|
||||||
case FADE:
|
p = getGradient( mColorTopData, mColorMiddleData );
|
||||||
p = getGradient( mColorTopFade, mColorMiddleFade );
|
break;
|
||||||
break;
|
case FADE:
|
||||||
case END:
|
p = getGradient( mColorTopFade, mColorMiddleFade );
|
||||||
case IDLE:
|
break;
|
||||||
default:
|
case IDLE:
|
||||||
p = getGradient( mColorTopIdle, mColorMiddleIdle );
|
default:
|
||||||
break;
|
p = getGradient( mColorTopIdle, mColorMiddleIdle );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( p == null )
|
||||||
|
{
|
||||||
|
p = getGradient( mColorTopIdle, mColorMiddleIdle );
|
||||||
}
|
}
|
||||||
|
|
||||||
g2.setPaint( p );
|
g2.setPaint( p );
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ public class DecoderStateEvent
|
|||||||
public enum Event
|
public enum Event
|
||||||
{
|
{
|
||||||
ALWAYS_UNSQUELCH,
|
ALWAYS_UNSQUELCH,
|
||||||
|
CHANGE_CALL_TIMEOUT,
|
||||||
CONTINUATION,
|
CONTINUATION,
|
||||||
DECODE,
|
DECODE,
|
||||||
END,
|
END,
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
package module.decode.state;
|
package module.decode.state;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Details the set of states for a channel and the allowable transition states
|
||||||
|
*/
|
||||||
public enum State
|
public enum State
|
||||||
{
|
{
|
||||||
IDLE( "IDLE" )
|
IDLE( "IDLE" )
|
||||||
@@ -7,7 +12,8 @@ public enum State
|
|||||||
@Override
|
@Override
|
||||||
public boolean canChangeTo( State state )
|
public boolean canChangeTo( State state )
|
||||||
{
|
{
|
||||||
return true;
|
return state != TEARDOWN &&
|
||||||
|
state != RESET;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
CALL( "CALL" )
|
CALL( "CALL" )
|
||||||
@@ -15,11 +21,11 @@ public enum State
|
|||||||
@Override
|
@Override
|
||||||
public boolean canChangeTo( State state )
|
public boolean canChangeTo( State state )
|
||||||
{
|
{
|
||||||
return state == CALL ||
|
return state == CONTROL ||
|
||||||
state == CONTROL ||
|
|
||||||
state == DATA ||
|
state == DATA ||
|
||||||
state == ENCRYPTED ||
|
state == ENCRYPTED ||
|
||||||
state == FADE;
|
state == FADE ||
|
||||||
|
state == TEARDOWN;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DATA( "DATA" )
|
DATA( "DATA" )
|
||||||
@@ -29,9 +35,9 @@ public enum State
|
|||||||
{
|
{
|
||||||
return state == CALL ||
|
return state == CALL ||
|
||||||
state == CONTROL ||
|
state == CONTROL ||
|
||||||
state == DATA ||
|
state == ENCRYPTED ||
|
||||||
state == State.ENCRYPTED ||
|
state == FADE ||
|
||||||
state == FADE;
|
state == TEARDOWN;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ENCRYPTED( "ENCRYPTED" )
|
ENCRYPTED( "ENCRYPTED" )
|
||||||
@@ -39,7 +45,7 @@ public enum State
|
|||||||
@Override
|
@Override
|
||||||
public boolean canChangeTo( State state )
|
public boolean canChangeTo( State state )
|
||||||
{
|
{
|
||||||
return state == FADE;
|
return state == FADE || state == TEARDOWN;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
CONTROL( "CONTROL" )
|
CONTROL( "CONTROL" )
|
||||||
@@ -47,7 +53,8 @@ public enum State
|
|||||||
@Override
|
@Override
|
||||||
public boolean canChangeTo( State state )
|
public boolean canChangeTo( State state )
|
||||||
{
|
{
|
||||||
return true;
|
return state == IDLE ||
|
||||||
|
state == FADE;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
FADE( "FADE" )
|
FADE( "FADE" )
|
||||||
@@ -55,20 +62,36 @@ public enum State
|
|||||||
@Override
|
@Override
|
||||||
public boolean canChangeTo( State state )
|
public boolean canChangeTo( State state )
|
||||||
{
|
{
|
||||||
return state != FADE; //All states except fade allowed
|
return state != FADE &&
|
||||||
}
|
state != RESET;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
END( "END" )
|
RESET( "RESET" )
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public boolean canChangeTo( State state )
|
public boolean canChangeTo(State state)
|
||||||
{
|
{
|
||||||
return state != FADE; //Fade is only disallowed state
|
return state == IDLE;
|
||||||
}
|
}
|
||||||
};
|
},
|
||||||
|
|
||||||
|
TEARDOWN( "TEARDOWN" )
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public boolean canChangeTo(State state)
|
||||||
|
{
|
||||||
|
return state == RESET;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private String mDisplayValue;
|
private String mDisplayValue;
|
||||||
|
|
||||||
|
public static final EnumSet<State> CALL_STATES =
|
||||||
|
EnumSet.of( CALL, CONTROL, DATA, ENCRYPTED );
|
||||||
|
|
||||||
|
public static final EnumSet<State> IDLE_STATES =
|
||||||
|
EnumSet.of( IDLE, FADE );
|
||||||
|
|
||||||
private State( String displayValue )
|
private State( String displayValue )
|
||||||
{
|
{
|
||||||
mDisplayValue = displayValue;
|
mDisplayValue = displayValue;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import record.config.RecordConfiguration;
|
import record.config.RecordConfiguration;
|
||||||
|
import sample.Broadcaster;
|
||||||
import sample.Listener;
|
import sample.Listener;
|
||||||
import source.config.SourceConfigTuner;
|
import source.config.SourceConfigTuner;
|
||||||
import source.tuner.TunerChannel;
|
import source.tuner.TunerChannel;
|
||||||
@@ -96,10 +97,21 @@ public class TrafficChannelManager extends Module
|
|||||||
mPreviousDoNotMonitorCallEvent = null;
|
mPreviousDoNotMonitorCallEvent = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a channel by either reusing an existing channel or constructing
|
||||||
|
* a new one, limited by the total number of constructed channels allowed.
|
||||||
|
*
|
||||||
|
* Note: you must enforce thread safety on the mTrafficChannelsInUse
|
||||||
|
* external to this method.
|
||||||
|
*
|
||||||
|
* @param channelNumber
|
||||||
|
* @param tunerChannel
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
private Channel getChannel( String channelNumber, TunerChannel tunerChannel )
|
private Channel getChannel( String channelNumber, TunerChannel tunerChannel )
|
||||||
{
|
{
|
||||||
Channel channel = null;
|
Channel channel = null;
|
||||||
|
|
||||||
if( !mTrafficChannelsInUse.containsKey( channelNumber ) )
|
if( !mTrafficChannelsInUse.containsKey( channelNumber ) )
|
||||||
{
|
{
|
||||||
for( Channel configuredChannel: mTrafficChannelPool )
|
for( Channel configuredChannel: mTrafficChannelPool )
|
||||||
@@ -154,7 +166,7 @@ public class TrafficChannelManager extends Module
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,72 +176,79 @@ public class TrafficChannelManager extends Module
|
|||||||
*/
|
*/
|
||||||
private void process( TrafficChannelAllocationEvent event )
|
private void process( TrafficChannelAllocationEvent event )
|
||||||
{
|
{
|
||||||
if( mCallEventListener != null )
|
CallEvent callEvent = event.getCallEvent();
|
||||||
{
|
|
||||||
synchronized( mCallEventListener )
|
|
||||||
{
|
|
||||||
CallEvent callEvent = event.getCallEvent();
|
|
||||||
|
|
||||||
/* Check for duplicate events and suppress */
|
/* Check for duplicate events and suppress */
|
||||||
if( mTrafficChannelsInUse.containsKey( callEvent.getChannel() ) )
|
synchronized( mTrafficChannelsInUse )
|
||||||
|
{
|
||||||
|
if( mTrafficChannelsInUse.containsKey( callEvent.getChannel() ) )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long frequency = callEvent.getFrequency();
|
||||||
|
|
||||||
|
/* Check the from/to aliases for do not monitor priority */
|
||||||
|
if( isDoNotMonitor( callEvent ) )
|
||||||
|
{
|
||||||
|
callEvent.setCallEventType( CallEventType.CALL_DO_NOT_MONITOR );
|
||||||
|
|
||||||
|
if( isSameCallEvent( mPreviousDoNotMonitorCallEvent, callEvent ) )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
long frequency = callEvent.getFrequency();
|
|
||||||
|
|
||||||
/* Check the from/to aliases for do not monitor priority */
|
|
||||||
if( isDoNotMonitor( callEvent ) )
|
|
||||||
{
|
{
|
||||||
callEvent.setCallEventType( CallEventType.CALL_DO_NOT_MONITOR );
|
mPreviousDoNotMonitorCallEvent = callEvent;
|
||||||
|
|
||||||
if( isSameCallEvent( mPreviousDoNotMonitorCallEvent, callEvent ) )
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mPreviousDoNotMonitorCallEvent = callEvent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if( frequency > 0 )
|
}
|
||||||
{
|
else if( frequency > 0 )
|
||||||
Channel channel = getChannel( callEvent.getChannel(),
|
{
|
||||||
new TunerChannel( Type.TRAFFIC, frequency,
|
Channel channel = getChannel( callEvent.getChannel(),
|
||||||
mDecodeConfiguration.getDecoderType()
|
new TunerChannel( Type.TRAFFIC, frequency,
|
||||||
.getChannelBandwidth() ) );
|
mDecodeConfiguration.getDecoderType()
|
||||||
|
.getChannelBandwidth() ) );
|
||||||
|
|
||||||
if( channel != null )
|
if( channel != null )
|
||||||
|
{
|
||||||
|
if( channel.isProcessing() )
|
||||||
{
|
{
|
||||||
if( channel.isProcessing() )
|
callEvent.setDetails( "TRAFFIC CHANNEL ALLOCATED" );
|
||||||
{
|
|
||||||
ChannelState state = channel.getChannelState();
|
ChannelState state = channel.getChannelState();
|
||||||
|
|
||||||
/* Register as a listener to be notified when the call
|
/* Register as a listener to be notified when the call
|
||||||
* is completed */
|
* is completed */
|
||||||
state.configureAsTrafficChannel(
|
state.configureAsTrafficChannel(
|
||||||
new TrafficChannelStatusListener( this,
|
new TrafficChannelStatusListener( this,
|
||||||
callEvent.getChannel() ), event );
|
callEvent.getChannel() ), event );
|
||||||
|
|
||||||
/* Set state to call so that audio squelch state is correct */
|
/* Set state to call so that audio squelch state is correct */
|
||||||
state.setState( State.CALL );
|
state.setState( State.CALL );
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
callEvent.setCallEventType( CallEventType.CALL_DETECT );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
callEvent.setCallEventType( CallEventType.CALL_DETECT );
|
callEvent.setCallEventType( CallEventType.CALL_DETECT );
|
||||||
|
callEvent.setDetails( "CHANNEL PROCESSING ERROR" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
callEvent.setCallEventType( CallEventType.CALL_DETECT );
|
callEvent.setCallEventType( CallEventType.CALL_DETECT );
|
||||||
|
callEvent.setDetails( "NO TUNER AVAILABLE" );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
mCallEventListener.receive( callEvent );
|
else
|
||||||
|
{
|
||||||
|
callEvent.setCallEventType( CallEventType.CALL_DETECT );
|
||||||
|
callEvent.setDetails( "UNKNOWN FREQUENCY" );
|
||||||
|
}
|
||||||
|
|
||||||
|
final Listener<CallEvent> listener = mCallEventListener;
|
||||||
|
|
||||||
|
if( listener != null )
|
||||||
|
{
|
||||||
|
listener.receive( callEvent );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -370,14 +389,18 @@ public class TrafficChannelManager extends Module
|
|||||||
*/
|
*/
|
||||||
public void callEnd( String channelNumber )
|
public void callEnd( String channelNumber )
|
||||||
{
|
{
|
||||||
if( channelNumber != null && mTrafficChannelsInUse.containsKey( channelNumber ) )
|
synchronized( mTrafficChannelsInUse )
|
||||||
{
|
{
|
||||||
Channel channel = mTrafficChannelsInUse.get( channelNumber );
|
if( channelNumber != null &&
|
||||||
|
mTrafficChannelsInUse.containsKey( channelNumber ) )
|
||||||
mTrafficChannelsInUse.remove( channelNumber );
|
{
|
||||||
|
Channel channel = mTrafficChannelsInUse.get( channelNumber );
|
||||||
|
|
||||||
|
mTrafficChannelsInUse.remove( channelNumber );
|
||||||
|
|
||||||
/* Disable the channel and broadcast a notification */
|
/* Disable the channel and broadcast a notification */
|
||||||
channel.setEnabled( false, true );
|
channel.setEnabled( false, true );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,9 +97,13 @@ public class TunerChannel implements Comparable<TunerChannel>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo( TunerChannel otherLock )
|
public int compareTo( TunerChannel otherTunerChannel )
|
||||||
{
|
{
|
||||||
if( mFrequency < otherLock.getFrequency() )
|
if( this.equals( otherTunerChannel ) )
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if( mFrequency < otherTunerChannel.getFrequency() )
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -246,10 +246,6 @@ public abstract class TunerController implements Tunable
|
|||||||
{
|
{
|
||||||
mTunedChannels.remove( tunerChannelSource.getTunerChannel() );
|
mTunedChannels.remove( tunerChannelSource.getTunerChannel() );
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
mLog.error( "Request to release channel that is not in current tuned channels set" );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user