diff --git a/.classpath b/.classpath
index 73e0ed80..e69bc147 100644
--- a/.classpath
+++ b/.classpath
@@ -24,6 +24,6 @@
-
+
diff --git a/imports/jmbe-interface-0.3.0.jar b/imports/jmbe-interface-0.3.1.jar
similarity index 89%
rename from imports/jmbe-interface-0.3.0.jar
rename to imports/jmbe-interface-0.3.1.jar
index e6a8fcfd..5b63b640 100644
Binary files a/imports/jmbe-interface-0.3.0.jar and b/imports/jmbe-interface-0.3.1.jar differ
diff --git a/src/module/decode/config/DecodeConfiguration.java b/src/module/decode/config/DecodeConfiguration.java
index b6b59f04..95070a08 100644
--- a/src/module/decode/config/DecodeConfiguration.java
+++ b/src/module/decode/config/DecodeConfiguration.java
@@ -42,7 +42,7 @@ import controller.config.Configuration;
public abstract class DecodeConfiguration extends Configuration
{
public static final int DEFAULT_CALL_TIMEOUT_SECONDS = 45;
- public static final int CALL_TIMEOUT_MINIMUM = 10;
+ public static final int CALL_TIMEOUT_MINIMUM = 1;
public static final int CALL_TIMEOUT_MAXIMUM = 300; //5 minutes
public static final int TRAFFIC_CHANNEL_LIMIT_DEFAULT = 3;
diff --git a/src/module/decode/event/CallEvent.java b/src/module/decode/event/CallEvent.java
index cbe07cfc..000cb27c 100644
--- a/src/module/decode/event/CallEvent.java
+++ b/src/module/decode/event/CallEvent.java
@@ -273,6 +273,7 @@ public abstract class CallEvent implements Comparable
ENCRYPTED_CALL( "Encrypted Call" ),
FUNCTION( "Function" ),
GPS( "GPS" ),
+ GROUP_CALL( "Group Call" ),
ID_ANI( "ANI" ),
ID_UNIQUE( "Unique ID" ),
PAGE( "Page" ),
@@ -283,7 +284,8 @@ public abstract class CallEvent implements Comparable
SDM( "Short Data Message" ),
STATION_ID( "Station ID" ),
STATUS( "Status" ),
- TELEPHONE_CALL( "Telephone Call" ),
+ TELEPHONE_INTERCONNECT( "Telephone Interconnect" ),
+ UNIT_TO_UNIT_CALL( "Unit To Unit Call" ),
UNKNOWN( "Unknown" ),
INVALID( "Invalid" );
diff --git a/src/module/decode/p25/P25DecoderPanel.java b/src/module/decode/p25/P25DecoderPanel.java
index 5db8144a..ae3d9396 100644
--- a/src/module/decode/p25/P25DecoderPanel.java
+++ b/src/module/decode/p25/P25DecoderPanel.java
@@ -34,13 +34,13 @@ public class P25DecoderPanel extends DecoderPanel
private static final long serialVersionUID = 1L;
private JLabel mProtocol;
- private JLabel mFrom = new JLabel( " " );
- private JLabel mFromAlias = new JLabel( " " );
-
- private JLabel mNAC = new JLabel( "NAC:" );
private JLabel mTo = new JLabel( " " );
private JLabel mToAlias = new JLabel( " " );
+ private JLabel mNAC = new JLabel( "NAC:" );
+ private JLabel mFrom = new JLabel( " " );
+ private JLabel mFromAlias = new JLabel( " " );
+
private JLabel mSystem = new JLabel( "SYS:" );
private JLabel mSite = new JLabel( "Site:" );
private JLabel mSiteAlias = new JLabel( "" );
@@ -65,21 +65,21 @@ public class P25DecoderPanel extends DecoderPanel
mProtocol.setFont( mFontDecoder );
mProtocol.setForeground( mColorLabelDecoder );
- mFrom.setFont( mFontDecoder );
- mFrom.setForeground( mColorLabelDecoder );
-
- mFromAlias.setFont( mFontDecoder );
- mFromAlias.setForeground( mColorLabelDecoder );
-
- mNAC.setFont( mFontDetails );
- mNAC.setForeground( mColorLabelDecoder );
-
mTo.setFont( mFontDecoder );
mTo.setForeground( mColorLabelDecoder );
mToAlias.setFont( mFontDecoder );
mToAlias.setForeground( mColorLabelDecoder );
+ mNAC.setFont( mFontDetails );
+ mNAC.setForeground( mColorLabelDecoder );
+
+ mFrom.setFont( mFontDecoder );
+ mFrom.setForeground( mColorLabelDecoder );
+
+ mFromAlias.setFont( mFontDecoder );
+ mFromAlias.setForeground( mColorLabelDecoder );
+
mSystem.setFont( mFontDetails );
mSystem.setForeground( mColorLabelDecoder );
@@ -90,13 +90,13 @@ public class P25DecoderPanel extends DecoderPanel
mSite.setForeground( mColorLabelDecoder );
add( mProtocol );
- add( mFrom );
- add( mFromAlias, "wrap" );
-
- add( mNAC );
add( mTo );
add( mToAlias, "wrap" );
+ add( mNAC );
+ add( mFrom );
+ add( mFromAlias, "wrap" );
+
add( mSystem );
add( mSite );
add( mSiteAlias, "wrap" );
@@ -133,7 +133,14 @@ public class P25DecoderPanel extends DecoderPanel
mSiteAlias.setText( state.getSiteAlias() );
break;
case FROM_TALKGROUP:
- mFrom.setText( state.getFromTalkgroup() );
+ if( state.getFromTalkgroup() == null )
+ {
+ mFrom.setText( " " );
+ }
+ else
+ {
+ mFrom.setText( state.getFromTalkgroup() );
+ }
break;
case FROM_TALKGROUP_ALIAS:
Alias from = state.getFromAlias();
@@ -146,12 +153,19 @@ public class P25DecoderPanel extends DecoderPanel
}
else
{
- mFromAlias.setText( null );
+ mFromAlias.setText( " " );
mFromAlias.setIcon( null );
}
break;
case TO_TALKGROUP:
- mTo.setText( state.getToTalkgroup() );
+ if( state.getToTalkgroup() == null )
+ {
+ mTo.setText( " " );
+ }
+ else
+ {
+ mTo.setText( state.getToTalkgroup() );
+ }
break;
case TO_TALKGROUP_ALIAS:
Alias to = state.getToAlias();
@@ -164,7 +178,7 @@ public class P25DecoderPanel extends DecoderPanel
}
else
{
- mToAlias.setText( null );
+ mToAlias.setText( " " );
mToAlias.setIcon( null );
}
break;
diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java
index f510e2a6..f7dfae15 100644
--- a/src/module/decode/p25/P25DecoderState.java
+++ b/src/module/decode/p25/P25DecoderState.java
@@ -113,6 +113,7 @@ import module.decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest;
import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrant;
import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate;
import module.decode.p25.reference.IPProtocol;
+import module.decode.p25.reference.LinkControlOpcode;
import module.decode.p25.reference.Response;
import module.decode.p25.reference.Service;
import module.decode.p25.reference.Vendor;
@@ -128,6 +129,8 @@ import org.slf4j.LoggerFactory;
import alias.Alias;
import alias.AliasList;
+import audio.metadata.Metadata;
+import audio.metadata.MetadataType;
import controller.channel.Channel.ChannelType;
public class P25DecoderState extends DecoderState
@@ -169,9 +172,11 @@ public class P25DecoderState extends DecoderState
private String mCurrentChannel = "CURRENT";
private long mCurrentChannelFrequency = 0;
- private P25CallEvent mCurrentCallEvent;
private ChannelType mChannelType;
private Modulation mModulation;
+
+ private P25CallEvent mCurrentCallEvent;
+ private List mCallDetectTalkgroups = new ArrayList<>();
public P25DecoderState( AliasList aliasList,
ChannelType channelType,
@@ -221,8 +226,26 @@ public class P25DecoderState extends DecoderState
*/
private void resetState()
{
- updateFrom( null );
- updateTo( null );
+ mFromTalkgroup = null;
+ broadcast( ChangedAttribute.FROM_TALKGROUP );
+
+ mFromAlias = null;
+ broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS );
+
+ mToTalkgroup = null;
+ broadcast( ChangedAttribute.TO_TALKGROUP );
+
+ mToAlias = null;
+ broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
+
+ mCallDetectTalkgroups.clear();
+
+ if( mCurrentCallEvent != null )
+ {
+ mCurrentCallEvent.end();
+ }
+
+ mCurrentCallEvent = null;
}
/**
@@ -248,22 +271,16 @@ public class P25DecoderState extends DecoderState
processLDU( (LDUMessage)message );
}
- /* Terminator Data Unit with Link Control Message */
- else if( message instanceof TDULinkControlMessage )
- {
- processTDULC( (TDULinkControlMessage)message );
- }
-
/* Trunking Signalling Messages */
else if( message instanceof TSBKMessage )
{
processTSBK( (TSBKMessage)message );
}
- /* Header Data Unit Message - preceeds voice LDUs */
- else if( message instanceof HDUMessage )
+ /* Terminator Data Unit with Link Control Message */
+ else if( message instanceof TDULinkControlMessage )
{
- processHDU( (HDUMessage)message );
+ processTDULC( (TDULinkControlMessage)message );
}
/* Packet Data Unit Messages */
@@ -272,6 +289,12 @@ public class P25DecoderState extends DecoderState
processPDU( (PDUMessage)message );
}
+ /* Header Data Unit Message - preceeds voice LDUs */
+ else if( message instanceof HDUMessage )
+ {
+ processHDU( (HDUMessage)message );
+ }
+
/* Terminator Data Unit, or default message if CRC failed */
else if( message instanceof TDUMessage )
{
@@ -286,7 +309,10 @@ public class P25DecoderState extends DecoderState
*/
private void processTDU( TDUMessage tdu )
{
- broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) );
+// if( mCurrentCallEvent == null )
+// {
+// broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) );
+// }
}
/**
@@ -296,56 +322,49 @@ public class P25DecoderState extends DecoderState
{
if( hdu.isValid() )
{
- State state = State.CALL;
-
- if( hdu.isEncrypted() )
- {
- state = State.ENCRYPTED;
- }
+ State state = hdu.isEncrypted() ? State.ENCRYPTED : State.CALL;
broadcast( new DecoderStateEvent( this, Event.START, state ) );
- updateTo( hdu.getToID() );
+ String to = hdu.getToID();
- if( mChannelType == ChannelType.STANDARD )
+ if( mCurrentCallEvent == null )
{
- StringBuilder sb = new StringBuilder();
-
- CallEventType type = CallEventType.CALL;
-
- if( hdu.isEncrypted() )
- {
- type = CallEventType.ENCRYPTED_CALL;
-
- sb.append( "ENCRYPTED WITH " );
- sb.append( hdu.getEncryption().name() );
- sb.append( " KEY:" );
- sb.append( hdu.getKeyID() );
- }
-
- P25CallEvent event = new P25CallEvent.Builder( type )
+ mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.CALL )
.aliasList( getAliasList() )
.channel( mCurrentChannel )
- .details( "Trigger:HDU" )
+ .details( ( hdu.isEncrypted() ? "ENCRYPTED " : "" ) )
.frequency( mCurrentChannelFrequency )
- .to( hdu.getToID() )
- .details( sb.toString() )
+ .to( to )
.build();
-
- mCurrentCallEvent = event;
-
- broadcast( event );
+
+ broadcast( mCurrentCallEvent );
}
+
+ updateTo( to );
}
}
/**
- * Terminator Data Unit with Link Control - indicates an end of the call
- * sequences with embedded link control messages
+ * Terminator Data Unit with Link Control - transmitted multiple times at
+ * beginning or end of call sequence and includes embedded link control messages
*/
private void processTDULC( TDULinkControlMessage tdulc )
{
- broadcast( new DecoderStateEvent( this, Event.END, State.END ) );
+ if( tdulc.getOpcode() == LinkControlOpcode.CALL_TERMINATION_OR_CANCELLATION )
+ {
+ broadcast( new DecoderStateEvent( this, Event.END, State.END ) );
+
+ if( mCurrentCallEvent != null )
+ {
+ mCurrentCallEvent.end();
+ mCurrentCallEvent = null;
+ }
+ }
+ else
+ {
+ broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CALL ) );
+ }
switch( tdulc.getOpcode() )
{
@@ -384,6 +403,7 @@ public class P25DecoderState extends DecoderState
}
break;
case CALL_TERMINATION_OR_CANCELLATION:
+ /* This opcode as handled at the beginning of the method */
break;
case CHANNEL_IDENTIFIER_UPDATE:
//TODO: does the activity summary need this message?
@@ -434,20 +454,42 @@ public class P25DecoderState extends DecoderState
}
break;
case GROUP_VOICE_CHANNEL_UPDATE:
- /* Reflects other call activity on the system: CALL DETECT */
- if( mChannelType == ChannelType.STANDARD &&
- tdulc instanceof GroupVoiceChannelUpdate )
+ /* Used only on trunked systems on the outbound channel, to
+ * reflect user activity on other channels. We process this
+ * as a call detect */
+ if( tdulc instanceof GroupVoiceChannelUpdate )
{
GroupVoiceChannelUpdate gvcu = (GroupVoiceChannelUpdate)tdulc;
+
+ String groupA = gvcu.getGroupAddressA();
+
+ if( !mCallDetectTalkgroups.contains( groupA ) )
+ {
+ broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
+ .aliasList( getAliasList() )
+ .channel( gvcu.getChannelA() )
+ .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) )
+ .frequency( gvcu.getDownlinkFrequencyA() )
+ .to( groupA )
+ .build() );
+
+ mCallDetectTalkgroups.add( groupA );
+ }
- broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
- .aliasList( getAliasList() )
- .channel( gvcu.getChannelA() )
- .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) )
- .frequency( gvcu.getDownlinkFrequencyA() )
- .from( gvcu.getGroupAddressA() )
- .to( gvcu.getGroupAddressB() )
- .build() );
+ String groupB = gvcu.getGroupAddressB();
+
+ if( !mCallDetectTalkgroups.contains( groupB ) )
+ {
+ broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
+ .aliasList( getAliasList() )
+ .channel( gvcu.getChannelB() )
+ .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) )
+ .frequency( gvcu.getDownlinkFrequencyB() )
+ .to( groupB )
+ .build() );
+
+ mCallDetectTalkgroups.add( groupB );
+ }
}
break;
case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT:
@@ -458,31 +500,52 @@ public class P25DecoderState extends DecoderState
GroupVoiceChannelUpdateExplicit gvcue =
(GroupVoiceChannelUpdateExplicit)tdulc;
- broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
- .aliasList( getAliasList() )
- .channel( gvcue.getTransmitChannel() )
- .details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) )
- .frequency( gvcue.getDownlinkFrequency() )
- .to( gvcue.getGroupAddress() )
- .build() );
+ String group = gvcue.getGroupAddress();
+
+ if( !mCallDetectTalkgroups.contains( group ) )
+ {
+ broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
+ .aliasList( getAliasList() )
+ .channel( gvcue.getTransmitChannel() )
+ .details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) )
+ .frequency( gvcue.getDownlinkFrequency() )
+ .to( group )
+ .build() );
+
+ mCallDetectTalkgroups.add( group );
+ }
}
break;
case GROUP_VOICE_CHANNEL_USER:
- if( mChannelType == ChannelType.STANDARD &&
- tdulc instanceof module.decode.p25.message.tdu.lc.GroupVoiceChannelUser )
+ /* Used on a traffic channel to reflect current call entities */
+ if( tdulc instanceof module.decode.p25.message.tdu.lc.GroupVoiceChannelUser )
{
module.decode.p25.message.tdu.lc.GroupVoiceChannelUser gvcuser =
(module.decode.p25.message.tdu.lc.GroupVoiceChannelUser)tdulc;
+
+ broadcast( new DecoderStateEvent( this, Event.CONTINUATION,
+ ( gvcuser.isEncrypted() ? State.ENCRYPTED : State.CALL ) ) );
- broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
- .aliasList( getAliasList() )
- .channel( mCurrentChannel )
- .details( ( gvcuser.isEncrypted() ? "ENCRYPTED" : "" ) +
- ( gvcuser.isEmergency() ? " EMERGENCY" : "") )
- .frequency( mCurrentChannelFrequency )
- .from( gvcuser.getSourceAddress() )
- .to( gvcuser.getGroupAddress() )
- .build() );
+ String from = gvcuser.getSourceAddress();
+ String to = gvcuser.getGroupAddress();
+
+ if( mCurrentCallEvent == null )
+ {
+ mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
+ .aliasList( getAliasList() )
+ .channel( mCurrentChannel )
+ .details( ( gvcuser.isEncrypted() ? "ENCRYPTED " : "" ) +
+ ( gvcuser.isEmergency() ? "EMERGENCY " : "") )
+ .frequency( mCurrentChannelFrequency )
+ .from( from )
+ .to( to )
+ .build();
+
+ broadcast( mCurrentCallEvent );
+ }
+
+ updateFrom( from );
+ updateTo( to );
}
break;
case MESSAGE_UPDATE:
@@ -687,16 +750,23 @@ public class P25DecoderState extends DecoderState
module.decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser tivcu =
(module.decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser)tdulc;
- broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
- .aliasList( getAliasList() )
- .channel( mCurrentChannel )
- .details( "TELEPHONE INTERCONNECT " +
- ( tivcu.isEncrypted() ? "ENCRYPTED" : "" ) +
- ( tivcu.isEmergency() ? " EMERGENCY" : "") )
- .frequency( mCurrentChannelFrequency )
- .from( null )
- .to( tivcu.getAddress() )
- .build() );
+ String to = tivcu.getAddress();
+
+ if( mCurrentCallEvent == null )
+ {
+ mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.TELEPHONE_INTERCONNECT )
+ .aliasList( getAliasList() )
+ .channel( mCurrentChannel )
+ .details( ( tivcu.isEncrypted() ? "ENCRYPTED" : "" ) +
+ ( tivcu.isEmergency() ? " EMERGENCY" : "") )
+ .frequency( mCurrentChannelFrequency )
+ .to( to )
+ .build();
+
+ broadcast( mCurrentCallEvent );
+ }
+
+ updateTo( to );
}
break;
case UNIT_AUTHENTICATION_COMMAND:
@@ -752,21 +822,30 @@ public class P25DecoderState extends DecoderState
}
break;
case UNIT_TO_UNIT_VOICE_CHANNEL_USER:
- if( mChannelType == ChannelType.STANDARD &&
- tdulc instanceof module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser )
+ /* Used on traffic channels to indicate the current call entities */
+ if( tdulc instanceof module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser )
{
module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser uuvcu =
(module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser)tdulc;
+
+ String from = uuvcu.getSourceAddress();
+ String to = uuvcu.getTargetAddress();
- broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
- .aliasList( getAliasList() )
- .channel( mCurrentChannel )
- .details( ( uuvcu.isEncrypted() ? "ENCRYPTED" : "" ) +
- ( uuvcu.isEmergency() ? " EMERGENCY" : "" ) )
- .frequency( mCurrentChannelFrequency )
- .from( uuvcu.getSourceAddress() )
- .to( uuvcu.getTargetAddress() )
- .build() );
+ if( mCurrentCallEvent != null )
+ {
+ mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.UNIT_TO_UNIT_CALL )
+ .aliasList( getAliasList() )
+ .channel( mCurrentChannel )
+ .details( ( uuvcu.isEncrypted() ? "ENCRYPTED " : "" ) +
+ ( uuvcu.isEmergency() ? "EMERGENCY " : "" ) )
+ .frequency( mCurrentChannelFrequency )
+ .from( from )
+ .to( to )
+ .build();
+ }
+
+ updateFrom( from );
+ updateTo( to );
}
break;
default:
@@ -780,36 +859,41 @@ public class P25DecoderState extends DecoderState
*/
private void logAlternateVendorMessage( P25Message p25 )
{
- if( p25 instanceof TDULinkControlMessage )
- {
- TDULinkControlMessage tdulc = (TDULinkControlMessage)p25;
-
- mLog.debug( "TDULC - VENDOR [" + tdulc.getVendor() +
- "] SPECIFIC FORMAT FOR " + tdulc.getVendorOpcode().getLabel() +
- " " + tdulc.getBinaryMessage() );
- }
- else if( p25 instanceof LDU1Message )
- {
- LDU1Message ldu1 = (LDU1Message)p25;
-
- mLog.debug( "LDU1 - VENDOR [" + ldu1.getVendor() +
- "] SPECIFIC FORMAT FOR " + ldu1.getVendorOpcode().getLabel() +
- " " + ldu1.getBinaryMessage() );
- }
- else if( p25 instanceof PDUMessage )
- {
- PDUMessage pdu = (PDUMessage)p25;
-
- mLog.debug( "PDU - VENDOR [" + pdu.getVendor() +
- "] SPECIFIC FORMAT FOR " + pdu.getVendorOpcode().getLabel() +
- " " + pdu.getBinaryMessage() );
- }
- else
- {
- mLog.debug( "P25 - ALTERNATE VENDOR MESSAGE [" + p25.getClass() + "]" );
- }
+// if( p25 instanceof TDULinkControlMessage )
+// {
+// TDULinkControlMessage tdulc = (TDULinkControlMessage)p25;
+//
+// mLog.debug( "TDULC - VENDOR [" + tdulc.getVendor() +
+// "] SPECIFIC FORMAT FOR " + tdulc.getVendorOpcode().getLabel() +
+// " " + tdulc.getBinaryMessage() );
+// }
+// else if( p25 instanceof LDU1Message )
+// {
+// LDU1Message ldu1 = (LDU1Message)p25;
+//
+// mLog.debug( "LDU1 - VENDOR [" + ldu1.getVendor() +
+// "] SPECIFIC FORMAT FOR " + ldu1.getVendorOpcode().getLabel() +
+// " " + ldu1.getBinaryMessage() );
+// }
+// else if( p25 instanceof PDUMessage )
+// {
+// PDUMessage pdu = (PDUMessage)p25;
+//
+// mLog.debug( "PDU - VENDOR [" + pdu.getVendor() +
+// "] SPECIFIC FORMAT FOR " + pdu.getVendorOpcode().getLabel() +
+// " " + pdu.getBinaryMessage() );
+// }
+// else
+// {
+// mLog.debug( "P25 - ALTERNATE VENDOR MESSAGE [" + p25.getClass() + "]" );
+// }
}
+ /**
+ * Processes LDU voice frame messages. Sends continuation events to keep
+ * the channel state synchronized and processes the embedded link control
+ * messages to capture/broadcast peripheral events like paging.
+ */
private void processLDU( LDUMessage ldu )
{
State state = ( ldu.isValid() && ldu.isEncrypted() ) ?
@@ -817,6 +901,18 @@ public class P25DecoderState extends DecoderState
broadcast( new DecoderStateEvent( this, Event.CONTINUATION, state ) );
+ if( mCurrentCallEvent == null )
+ {
+ mCurrentCallEvent = new P25CallEvent.Builder(
+ ( state == State.CALL ) ? CallEventType.CALL : CallEventType.ENCRYPTED_CALL )
+ .aliasList( getAliasList() )
+ .channel( mCurrentChannel )
+ .frequency( mCurrentChannelFrequency )
+ .build();
+
+ broadcast( mCurrentCallEvent );
+ }
+
if( ldu instanceof LDU1Message )
{
switch( ((LDU1Message)ldu).getOpcode() )
@@ -919,18 +1015,27 @@ public class P25DecoderState extends DecoderState
{
module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate gvcu =
(module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate)ldu;
-
- broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
+
+ 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( gvcu.getGroupAddressA() )
+ .to( userA )
.build() );
+
+ mCallDetectTalkgroups.add( userA );
+ }
String userB = gvcu.getGroupAddressB();
- if( userB != null && !userB.contentEquals( "0000" ) )
+ if( userB != null &&
+ !userB.contentEquals( "0000" ) &&
+ !mCallDetectTalkgroups.contains( userB ) )
{
broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
.aliasList( getAliasList() )
@@ -939,6 +1044,8 @@ public class P25DecoderState extends DecoderState
.frequency( gvcu.getDownlinkFrequencyB() )
.to( userB )
.build() );
+
+ mCallDetectTalkgroups.add( userB );
}
}
else
@@ -953,13 +1060,20 @@ public class P25DecoderState extends DecoderState
module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit gvcue =
(module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit)ldu;
- broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
+ String group = gvcue.getGroupAddress();
+
+ if( !mCallDetectTalkgroups.contains( group ) )
+ {
+ broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
.aliasList( getAliasList() )
.channel( gvcue.getTransmitChannel() )
.details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) )
.frequency( gvcue.getDownlinkFrequency() )
- .to( gvcue.getGroupAddress() )
+ .to( group )
.build() );
+
+ mCallDetectTalkgroups.add( group );
+ }
}
else
{
@@ -973,17 +1087,24 @@ public class P25DecoderState extends DecoderState
{
module.decode.p25.message.ldu.lc.GroupVoiceChannelUser gvcuser =
(module.decode.p25.message.ldu.lc.GroupVoiceChannelUser)ldu;
-
- broadcast( new P25CallEvent.Builder( CallEventType.CALL )
- .aliasList( getAliasList() )
- .channel( mCurrentChannel )
- .details( "Trigger:LDU-GROUP_VOICE_CHANNEL_USER " +
- ( gvcuser.isEncrypted() ? "ENCRYPTED" : "" ) +
- ( gvcuser.isEmergency() ? " EMERGENCY" : "") )
- .frequency( mCurrentChannelFrequency )
- .from( gvcuser.getSourceAddress() )
- .to( gvcuser.getGroupAddress() )
- .build() );
+
+ 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() );
+ updateTo( gvcuser.getGroupAddress() );
}
else
{
@@ -1178,18 +1299,23 @@ public class P25DecoderState extends DecoderState
{
TelephoneInterconnectVoiceChannelUser tivcu =
(TelephoneInterconnectVoiceChannelUser)ldu;
-
- broadcast( new P25CallEvent.Builder( CallEventType.CALL )
- .aliasList( getAliasList() )
- .channel( mCurrentChannel )
- .details( "Trigger: LDU-TEL_INT_VOIC_CHAN_USER " +
- "TELEPHONE INTERCONNECT " +
- ( tivcu.isEncrypted() ? "ENCRYPTED" : "" ) +
- ( tivcu.isEmergency() ? " EMERGENCY" : "") )
- .frequency( mCurrentChannelFrequency )
- .from( null )
- .to( tivcu.getAddress() )
- .build() );
+
+ if( mCurrentCallEvent.getCallEventType() != CallEventType.TELEPHONE_INTERCONNECT )
+ {
+ mCurrentCallEvent.setCallEventType( CallEventType.TELEPHONE_INTERCONNECT );
+ broadcast( mCurrentCallEvent );
+ }
+
+ if( mCurrentCallEvent.getDetails() == null )
+ {
+ mCurrentCallEvent.setDetails(
+ ( tivcu.isEncrypted() ? "ENCRYPTED " : "" ) +
+ ( tivcu.isEmergency() ? "EMERGENCY " : "" ) );
+
+ broadcast( mCurrentCallEvent );
+ }
+
+ updateTo( tivcu.getAddress() );
}
else
{
@@ -1254,17 +1380,24 @@ public class P25DecoderState extends DecoderState
{
UnitToUnitVoiceChannelUser uuvcu =
(UnitToUnitVoiceChannelUser)ldu;
-
- broadcast( new P25CallEvent.Builder( CallEventType.CALL )
- .aliasList( getAliasList() )
- .channel( mCurrentChannel )
- .details( "Trigger:LDU-U2U_VOICE_CHAN_USER " +
- ( uuvcu.isEncrypted() ? "ENCRYPTED" : "" ) +
- ( uuvcu.isEmergency() ? " EMERGENCY" : "" ) )
- .frequency( mCurrentChannelFrequency )
- .from( uuvcu.getSourceAddress() )
- .to( uuvcu.getTargetAddress() )
- .build() );
+
+ if( mCurrentCallEvent.getCallEventType() != CallEventType.UNIT_TO_UNIT_CALL )
+ {
+ mCurrentCallEvent.setCallEventType( CallEventType.UNIT_TO_UNIT_CALL );
+ broadcast( mCurrentCallEvent );
+ }
+
+ if( mCurrentCallEvent.getDetails() == null )
+ {
+ mCurrentCallEvent.setDetails(
+ ( uuvcu.isEncrypted() ? "ENCRYPTED " : "" ) +
+ ( uuvcu.isEmergency() ? "EMERGENCY " : "" ) );
+
+ broadcast( mCurrentCallEvent );
+ }
+
+ updateFrom( uuvcu.getSourceAddress() );
+ updateTo( uuvcu.getTargetAddress() );
}
else
{
@@ -2366,7 +2499,7 @@ public class P25DecoderState extends DecoderState
case GROUP_VOICE_CHANNEL_GRANT:
GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)message;
- event = new P25CallEvent.Builder( CallEventType.CALL )
+ event = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
.aliasList( getAliasList() )
.channel( gvcg.getChannel() )
.details( "Trigger: TSBK-G_V_CH_GRNT " +
@@ -2381,7 +2514,7 @@ public class P25DecoderState extends DecoderState
GroupVoiceChannelGrantUpdate gvcgu =
(GroupVoiceChannelGrantUpdate)message;
- event = new P25CallEvent.Builder( CallEventType.CALL )
+ event = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
.aliasList( getAliasList() )
.channel( gvcgu.getChannel1() )
.details( "Trigger: TSBK-G_V_CH_GRNT_UP " +
@@ -2394,7 +2527,7 @@ public class P25DecoderState extends DecoderState
if( gvcgu.hasChannelNumber2() )
{
/* We handle this channel grant independent of the first event */
- P25CallEvent event2 = new P25CallEvent.Builder( CallEventType.CALL )
+ P25CallEvent event2 = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
.aliasList( getAliasList() )
.channel( gvcgu.getChannel2() )
.details( "Trigger: TSBK-G_V_CH_GRNT_UP#2 " +
@@ -2411,7 +2544,7 @@ public class P25DecoderState extends DecoderState
GroupVoiceChannelGrantUpdateExplicit gvcgue =
(GroupVoiceChannelGrantUpdateExplicit)message;
- event = new P25CallEvent.Builder( CallEventType.CALL )
+ event = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
.aliasList( getAliasList() )
.channel( gvcgue.getTransmitChannelIdentifier() + "-" +
gvcgue.getTransmitChannelNumber() )
@@ -2452,7 +2585,7 @@ public class P25DecoderState extends DecoderState
TelephoneInterconnectVoiceChannelGrant tivcg =
(TelephoneInterconnectVoiceChannelGrant)message;
- event = new P25CallEvent.Builder( CallEventType.TELEPHONE_CALL )
+ event = new P25CallEvent.Builder( CallEventType.TELEPHONE_INTERCONNECT )
.aliasList( getAliasList() )
.channel( tivcg.getChannel() )
.details( "Trigger: TSBK-T_INT_V_CH_GRNT " +
@@ -2467,7 +2600,7 @@ public class P25DecoderState extends DecoderState
TelephoneInterconnectVoiceChannelGrantUpdate tivcgu =
(TelephoneInterconnectVoiceChannelGrantUpdate)message;
- event = new P25CallEvent.Builder( CallEventType.TELEPHONE_CALL )
+ event = new P25CallEvent.Builder( CallEventType.TELEPHONE_INTERCONNECT )
.aliasList( getAliasList() )
.channel( tivcgu.getChannelIdentifier() + "-" +
tivcgu.getChannelNumber() )
@@ -2482,7 +2615,7 @@ public class P25DecoderState extends DecoderState
UnitToUnitVoiceChannelGrant uuvcg =
(UnitToUnitVoiceChannelGrant)message;
- event = new P25CallEvent.Builder( CallEventType.CALL )
+ event = new P25CallEvent.Builder( CallEventType.UNIT_TO_UNIT_CALL )
.aliasList( getAliasList() )
.channel( uuvcg.getChannelIdentifier() + "-" +
uuvcg.getChannelNumber() )
@@ -2498,7 +2631,7 @@ public class P25DecoderState extends DecoderState
UnitToUnitVoiceChannelGrantUpdate uuvcgu =
(UnitToUnitVoiceChannelGrantUpdate)message;
- event = new P25CallEvent.Builder( CallEventType.CALL )
+ event = new P25CallEvent.Builder( CallEventType.UNIT_TO_UNIT_CALL )
.aliasList( getAliasList() )
.channel( uuvcgu.getChannelIdentifier() + "-" +
uuvcgu.getChannelNumber() )
@@ -2514,7 +2647,7 @@ public class P25DecoderState extends DecoderState
break;
}
- if( event != null )
+ if( event != null && event.getCallEventType() != CallEventType.DATA_CALL )
{
broadcast( new TrafficChannelAllocationEvent( this, event ) );
}
@@ -2525,24 +2658,38 @@ public class P25DecoderState extends DecoderState
*/
private void updateTo( String to )
{
- if( to != null &&
+ mLog.debug( "Updating to [" + to + "]" );
+ if( to != null &&
!to.contentEquals( "0000" ) &&
!to.contentEquals( "000000" ) &&
( mToTalkgroup == null || !mToTalkgroup.contentEquals( to ) ) )
{
mToTalkgroup = to;
broadcast( ChangedAttribute.TO_TALKGROUP );
-
+
+ if( mCurrentCallEvent != null &&
+ ( mCurrentCallEvent.getToID() == null ||
+ !mCurrentCallEvent.getToID().contentEquals( to ) ) )
+ {
+ mCurrentCallEvent.setToID( to );
+ broadcast( mCurrentCallEvent );
+ }
+
if( hasAliasList() )
{
- mToAlias = getAliasList().getTalkgroupAlias( to );
+ mToAlias = getAliasList().getTalkgroupAlias( mToTalkgroup );
+ mLog.debug( "To Alias: " + mToAlias );
}
else
{
+ mLog.debug( "To Alias: no alias list" );
mToAlias = null;
}
broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
+
+ /* Send audio metadata update */
+ broadcast( new Metadata( MetadataType.TO, to, mToAlias, true ) );
}
}
@@ -2551,18 +2698,25 @@ public class P25DecoderState extends DecoderState
*/
private void updateFrom( String from )
{
- if( from != null &&
+ if( from != null &&
!from.contentEquals( "0000" ) &&
!from.contentEquals( "000000" ) &&
( mFromTalkgroup == null || !mFromTalkgroup.contentEquals( from ) ) )
{
mFromTalkgroup = from;
-
broadcast( ChangedAttribute.FROM_TALKGROUP );
+ if( mCurrentCallEvent != null &&
+ mCurrentCallEvent.getFromID() == null ||
+ !mCurrentCallEvent.getFromID().contentEquals( from ) )
+ {
+ mCurrentCallEvent.setFromID( from );
+ broadcast( mCurrentCallEvent );
+ }
+
if( hasAliasList() )
{
- mFromAlias = getAliasList().getTalkgroupAlias( from );
+ mFromAlias = getAliasList().getTalkgroupAlias( mFromTalkgroup );
}
else
{
@@ -2570,6 +2724,9 @@ public class P25DecoderState extends DecoderState
}
broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS );
+
+ /* Send audio metadata update */
+ broadcast( new Metadata( MetadataType.FROM, from, mFromAlias, true ) );
}
}
@@ -2959,6 +3116,17 @@ public class P25DecoderState extends DecoderState
mToTalkgroup = allocationEvent.getCallEvent().getToID();
broadcast( ChangedAttribute.TO_TALKGROUP );
+
+ if( allocationEvent.getCallEvent().hasAliasList() )
+ {
+ AliasList aliasList = allocationEvent.getCallEvent().getAliasList();
+
+ mFromAlias = aliasList.getTalkgroupAlias( mFromTalkgroup );
+ broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS );
+
+ mToAlias = aliasList.getTalkgroupAlias( mToTalkgroup );
+ broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
+ }
}
}
break;
diff --git a/src/module/decode/p25/P25Editor.java b/src/module/decode/p25/P25Editor.java
index 347b235f..dc567fb4 100644
--- a/src/module/decode/p25/P25Editor.java
+++ b/src/module/decode/p25/P25Editor.java
@@ -17,12 +17,12 @@
******************************************************************************/
package module.decode.p25;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
+import javax.swing.JSlider;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
import module.decode.DecodeEditor;
import module.decode.config.DecodeConfiguration;
@@ -43,6 +43,10 @@ public class P25Editor extends DecodeEditor
private static final long serialVersionUID = 1L;
private JComboBox mComboModulation;
+ private JLabel mCallTimeoutLabel;
+ private JSlider mCallTimeout;
+ private JLabel mTrafficChannelPoolSizeLabel;
+ private JSlider mTrafficChannelPoolSize;
public P25Editor( DecodeConfiguration config )
{
@@ -60,23 +64,62 @@ public class P25Editor extends DecodeEditor
mComboModulation.setSelectedItem( ((DecodeConfigP25Phase1)mConfig).getModulation() );
- mComboModulation.addActionListener( new ActionListener()
- {
- @Override
- public void actionPerformed( ActionEvent e )
- {
- P25_LSMDecoder.Modulation selected = mComboModulation
- .getItemAt( mComboModulation.getSelectedIndex() );
-
- if( selected != null )
- {
- ((DecodeConfigP25Phase1)mConfig).setModulation( selected );
- }
- }
- } );
-
add( new JLabel( "Modulation:" ) );
add( mComboModulation, "wrap" );
+
+ mCallTimeout = new JSlider( JSlider.HORIZONTAL,
+ DecodeConfiguration.CALL_TIMEOUT_MINIMUM,
+ DecodeConfiguration.CALL_TIMEOUT_MAXIMUM,
+ DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS );
+
+ mCallTimeout.setMajorTickSpacing( 90 );
+ mCallTimeout.setMinorTickSpacing( 10 );
+ mCallTimeout.setPaintTicks( true );
+
+ mCallTimeout.setLabelTable( mCallTimeout.createStandardLabels( 100, 100 ) );
+ mCallTimeout.setPaintLabels( true );
+
+ mCallTimeoutLabel = new JLabel( "Call Timeout: " + mCallTimeout.getValue() + " " );
+
+ mCallTimeout.addChangeListener( new ChangeListener()
+ {
+ @Override
+ public void stateChanged( ChangeEvent e )
+ {
+ mCallTimeoutLabel.setText( "Call Timeout: " + mCallTimeout.getValue() );
+ }
+ } );
+
+ add( mCallTimeoutLabel );
+ add( mCallTimeout, "wrap,grow" );
+
+ mTrafficChannelPoolSize = new JSlider( JSlider.HORIZONTAL,
+ DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MINIMUM,
+ DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MAXIMUM,
+ DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT );
+
+ mTrafficChannelPoolSize.setMajorTickSpacing( 10 );
+ mTrafficChannelPoolSize.setMinorTickSpacing( 1 );
+ mTrafficChannelPoolSize.setPaintTicks( true );
+
+ mTrafficChannelPoolSize.setLabelTable( mTrafficChannelPoolSize.createStandardLabels( 10, 10 ) );
+ mTrafficChannelPoolSize.setPaintLabels( true );
+
+ mTrafficChannelPoolSizeLabel = new JLabel( "Traffic Channel Pool Size: " + mTrafficChannelPoolSize.getValue() + " " );
+
+ mTrafficChannelPoolSize.addChangeListener( new ChangeListener()
+ {
+ @Override
+ public void stateChanged( ChangeEvent e )
+ {
+ mTrafficChannelPoolSizeLabel.setText( "Traffic Channel Pool Size: " + mTrafficChannelPoolSize.getValue() );
+ }
+ } );
+
+ add( mTrafficChannelPoolSizeLabel );
+ add( mTrafficChannelPoolSize, "wrap,grow" );
+
+ reset();
}
/**
@@ -99,4 +142,28 @@ public class P25Editor extends DecodeEditor
}
}
}
+
+ @Override
+ public void save()
+ {
+ DecodeConfigP25Phase1 config = (DecodeConfigP25Phase1)mConfig;
+
+ config.setModulation( (Modulation)mComboModulation.getSelectedItem() );
+
+ config.setCallTimeout( mCallTimeout.getValue() );
+
+ config.setTrafficChannelPoolSize( mTrafficChannelPoolSize.getValue() );
+ }
+
+ @Override
+ public void reset()
+ {
+ DecodeConfigP25Phase1 config = (DecodeConfigP25Phase1)mConfig;
+
+ mComboModulation.setSelectedItem( config.getModulation() );
+
+ mCallTimeout.setValue( config.getCallTimeout() );
+
+ mTrafficChannelPoolSize.setValue( config.getTrafficChannelPoolSize() );
+ }
}
diff --git a/src/module/decode/p25/audio/P25AudioModule.java b/src/module/decode/p25/audio/P25AudioModule.java
index 84e860be..7ff3b446 100644
--- a/src/module/decode/p25/audio/P25AudioModule.java
+++ b/src/module/decode/p25/audio/P25AudioModule.java
@@ -12,8 +12,6 @@ import module.decode.p25.message.tdu.lc.TDULinkControlMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import controller.channel.ChannelEvent;
-import controller.channel.IChannelEventListener;
import sample.Listener;
import audio.AudioFormats;
import audio.AudioPacket;
@@ -21,9 +19,14 @@ import audio.IAudioPacketProvider;
import audio.metadata.AudioMetadata;
import audio.metadata.IMetadataListener;
import audio.metadata.Metadata;
+import audio.squelch.ISquelchStateListener;
+import audio.squelch.SquelchState;
+import controller.channel.ChannelEvent;
+import controller.channel.IChannelEventListener;
public class P25AudioModule extends Module implements Listener,
- IAudioPacketProvider, IMessageListener, IChannelEventListener, IMetadataListener
+ IAudioPacketProvider, IMessageListener, IChannelEventListener,
+ IMetadataListener, ISquelchStateListener
{
private final static Logger mLog = LoggerFactory.getLogger( P25AudioModule.class );
@@ -40,6 +43,7 @@ public class P25AudioModule extends Module implements Listener,
private Listener mAudioPacketListener;
private AudioMetadata mAudioMetadata;
private ChannelEventListener mChannelEventListener = new ChannelEventListener();
+ private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
public P25AudioModule( boolean record )
{
@@ -55,6 +59,12 @@ public class P25AudioModule extends Module implements Listener,
return this;
}
+ @Override
+ public Listener getSquelchStateListener()
+ {
+ return mSquelchStateListener;
+ }
+
@Override
public void dispose()
{
@@ -105,7 +115,7 @@ public class P25AudioModule extends Module implements Listener,
for( byte[] frame: ((LDUMessage)message).getIMBEFrames() )
{
float[] audio = mAudioConverter.decode( frame );
-
+
mAudioPacketListener.receive(
new AudioPacket( audio, mAudioMetadata ) );
}
@@ -134,8 +144,10 @@ public class P25AudioModule extends Module implements Listener,
library = (AudioConversionLibrary)temp.newInstance();
- if( ( library.getMajorVersion() == 0 && library.getMinorVersion() >= 3 ) ||
- library.getMajorVersion() >= 1 )
+ if( ( library.getMajorVersion() == 0 &&
+ library.getMinorVersion() >= 3 &&
+ library.getBuildVersion() >= 1 ) ||
+ library.getMajorVersion() >= 1 )
{
mAudioConverter = library.getAudioConverter( IMBE_CODEC,
AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO );
@@ -163,7 +175,7 @@ public class P25AudioModule extends Module implements Listener,
}
else
{
- mLog.debug( "JMBE library version 0.3.x or higher is required" );
+ mLog.debug( "JMBE library version 0.3.1 or higher is required - found: " + library.getVersion() );
}
}
catch ( ClassNotFoundException e1 )
@@ -239,4 +251,25 @@ public class P25AudioModule extends Module implements Listener,
}
}
}
+
+ /**
+ * Wrapper for squelch state listener. Internally, the P25 audio module
+ * doesn't have a squelch state. If there are IMBE audio frames, we have
+ * audio. We use this listener to signal to the recorder manager that the
+ * channel state is resetting and it should end the recording.
+ */
+ public class SquelchStateListener implements Listener
+ {
+ @Override
+ public void receive( SquelchState state )
+ {
+ if( state == SquelchState.SQUELCH && mAudioPacketListener != null )
+ {
+ mAudioPacketListener.receive( new AudioPacket( AudioPacket.Type.END,
+ mAudioMetadata.copyOf() ) );
+
+ mAudioMetadata.reset();
+ }
+ }
+ }
}
diff --git a/src/module/decode/state/ChannelState.java b/src/module/decode/state/ChannelState.java
index 1510d490..05e96f44 100644
--- a/src/module/decode/state/ChannelState.java
+++ b/src/module/decode/state/ChannelState.java
@@ -163,9 +163,11 @@ public class ChannelState extends Module implements ICallEventProvider,
return mState;
}
- private boolean isEndingState()
+ private boolean isIdleOrEndingState()
{
- return mState == State.FADE || mState == State.END;
+ return mState == State.FADE ||
+ mState == State.END ||
+ mState == State.IDLE;
}
@@ -568,7 +570,7 @@ public class ChannelState extends Module implements ICallEventProvider,
mSquelchLocked = true;
break;
case CONTINUATION:
- if( isEndingState() )
+ if( isIdleOrEndingState() )
{
setState( event.getState() );
}
diff --git a/src/record/RecorderManager.java b/src/record/RecorderManager.java
index 53e37469..e247036b 100644
--- a/src/record/RecorderManager.java
+++ b/src/record/RecorderManager.java
@@ -59,37 +59,37 @@ public class RecorderManager implements Listener
{
if( audioPacket.hasAudioMetadata() &&
audioPacket.getAudioMetadata().isRecordable() )
+ {
+ String identifier = audioPacket.getAudioMetadata().getIdentifier();
+
+ if( mRecorders.containsKey( identifier ) )
{
- String identifier = audioPacket.getAudioMetadata().getIdentifier();
+ RealBufferWaveRecorder recorder = mRecorders.get( identifier );
- if( mRecorders.containsKey( identifier ) )
+ if( audioPacket.getType() == AudioPacket.Type.AUDIO )
{
- RealBufferWaveRecorder recorder = mRecorders.get( identifier );
-
- if( audioPacket.getType() == AudioPacket.Type.AUDIO )
- {
- recorder.receive( audioPacket.getAudioBuffer() );
- }
- else if( audioPacket.getType() == AudioPacket.Type.END )
- {
- mRecorders.remove( identifier );
- recorder.stop();
- }
- }
- else if( audioPacket.getType() == AudioPacket.Type.AUDIO )
- {
- String filePrefix = getFilePrefix( audioPacket );
-
- RealBufferWaveRecorder recorder =
- new RealBufferWaveRecorder( mThreadPoolManager,
- AUDIO_SAMPLE_RATE, filePrefix );
-
- recorder.start();
-
recorder.receive( audioPacket.getAudioBuffer() );
- mRecorders.put( identifier, recorder );
+ }
+ else if( audioPacket.getType() == AudioPacket.Type.END )
+ {
+ mRecorders.remove( identifier );
+ recorder.stop();
}
}
+ else if( audioPacket.getType() == AudioPacket.Type.AUDIO )
+ {
+ String filePrefix = getFilePrefix( audioPacket );
+
+ RealBufferWaveRecorder recorder =
+ new RealBufferWaveRecorder( mThreadPoolManager,
+ AUDIO_SAMPLE_RATE, filePrefix );
+
+ recorder.start();
+
+ recorder.receive( audioPacket.getAudioBuffer() );
+ mRecorders.put( identifier, recorder );
+ }
+ }
}
/**