P25 Decoder updates - work in progress.

Updated jmbe library to work with version 0.3.1
This commit is contained in:
denny
2015-11-15 10:32:46 -05:00
parent e27ca12ec8
commit a6c7c4f5d1
10 changed files with 542 additions and 256 deletions

View File

@@ -24,6 +24,6 @@
<classpathentry kind="lib" path="imports/logback-classic-1.1.2.jar"/> <classpathentry kind="lib" path="imports/logback-classic-1.1.2.jar"/>
<classpathentry kind="lib" path="imports/logback-core-1.1.2.jar"/> <classpathentry kind="lib" path="imports/logback-core-1.1.2.jar"/>
<classpathentry kind="lib" path="imports/commons-io-2.4.jar"/> <classpathentry kind="lib" path="imports/commons-io-2.4.jar"/>
<classpathentry kind="lib" path="imports/jmbe-interface-0.3.0.jar"/> <classpathentry kind="lib" path="imports/jmbe-interface-0.3.1.jar"/>
<classpathentry kind="output" path="bin"/> <classpathentry kind="output" path="bin"/>
</classpath> </classpath>

View File

@@ -42,7 +42,7 @@ import controller.config.Configuration;
public abstract class DecodeConfiguration extends Configuration public abstract class DecodeConfiguration extends Configuration
{ {
public static final int DEFAULT_CALL_TIMEOUT_SECONDS = 45; 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 CALL_TIMEOUT_MAXIMUM = 300; //5 minutes
public static final int TRAFFIC_CHANNEL_LIMIT_DEFAULT = 3; public static final int TRAFFIC_CHANNEL_LIMIT_DEFAULT = 3;

View File

@@ -273,6 +273,7 @@ public abstract class CallEvent implements Comparable<CallEvent>
ENCRYPTED_CALL( "Encrypted Call" ), ENCRYPTED_CALL( "Encrypted Call" ),
FUNCTION( "Function" ), FUNCTION( "Function" ),
GPS( "GPS" ), GPS( "GPS" ),
GROUP_CALL( "Group Call" ),
ID_ANI( "ANI" ), ID_ANI( "ANI" ),
ID_UNIQUE( "Unique ID" ), ID_UNIQUE( "Unique ID" ),
PAGE( "Page" ), PAGE( "Page" ),
@@ -283,7 +284,8 @@ public abstract class CallEvent implements Comparable<CallEvent>
SDM( "Short Data Message" ), SDM( "Short Data Message" ),
STATION_ID( "Station ID" ), STATION_ID( "Station ID" ),
STATUS( "Status" ), STATUS( "Status" ),
TELEPHONE_CALL( "Telephone Call" ), TELEPHONE_INTERCONNECT( "Telephone Interconnect" ),
UNIT_TO_UNIT_CALL( "Unit To Unit Call" ),
UNKNOWN( "Unknown" ), UNKNOWN( "Unknown" ),
INVALID( "Invalid" ); INVALID( "Invalid" );

View File

@@ -34,13 +34,13 @@ public class P25DecoderPanel extends DecoderPanel
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private JLabel mProtocol; 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 mTo = new JLabel( " " );
private JLabel mToAlias = 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 mSystem = new JLabel( "SYS:" );
private JLabel mSite = new JLabel( "Site:" ); private JLabel mSite = new JLabel( "Site:" );
private JLabel mSiteAlias = new JLabel( "" ); private JLabel mSiteAlias = new JLabel( "" );
@@ -65,21 +65,21 @@ public class P25DecoderPanel extends DecoderPanel
mProtocol.setFont( mFontDecoder ); mProtocol.setFont( mFontDecoder );
mProtocol.setForeground( mColorLabelDecoder ); 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.setFont( mFontDecoder );
mTo.setForeground( mColorLabelDecoder ); mTo.setForeground( mColorLabelDecoder );
mToAlias.setFont( mFontDecoder ); mToAlias.setFont( mFontDecoder );
mToAlias.setForeground( mColorLabelDecoder ); 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.setFont( mFontDetails );
mSystem.setForeground( mColorLabelDecoder ); mSystem.setForeground( mColorLabelDecoder );
@@ -90,13 +90,13 @@ public class P25DecoderPanel extends DecoderPanel
mSite.setForeground( mColorLabelDecoder ); mSite.setForeground( mColorLabelDecoder );
add( mProtocol ); add( mProtocol );
add( mFrom );
add( mFromAlias, "wrap" );
add( mNAC );
add( mTo ); add( mTo );
add( mToAlias, "wrap" ); add( mToAlias, "wrap" );
add( mNAC );
add( mFrom );
add( mFromAlias, "wrap" );
add( mSystem ); add( mSystem );
add( mSite ); add( mSite );
add( mSiteAlias, "wrap" ); add( mSiteAlias, "wrap" );
@@ -133,7 +133,14 @@ public class P25DecoderPanel extends DecoderPanel
mSiteAlias.setText( state.getSiteAlias() ); mSiteAlias.setText( state.getSiteAlias() );
break; break;
case FROM_TALKGROUP: case FROM_TALKGROUP:
mFrom.setText( state.getFromTalkgroup() ); if( state.getFromTalkgroup() == null )
{
mFrom.setText( " " );
}
else
{
mFrom.setText( state.getFromTalkgroup() );
}
break; break;
case FROM_TALKGROUP_ALIAS: case FROM_TALKGROUP_ALIAS:
Alias from = state.getFromAlias(); Alias from = state.getFromAlias();
@@ -146,12 +153,19 @@ public class P25DecoderPanel extends DecoderPanel
} }
else else
{ {
mFromAlias.setText( null ); mFromAlias.setText( " " );
mFromAlias.setIcon( null ); mFromAlias.setIcon( null );
} }
break; break;
case TO_TALKGROUP: case TO_TALKGROUP:
mTo.setText( state.getToTalkgroup() ); if( state.getToTalkgroup() == null )
{
mTo.setText( " " );
}
else
{
mTo.setText( state.getToTalkgroup() );
}
break; break;
case TO_TALKGROUP_ALIAS: case TO_TALKGROUP_ALIAS:
Alias to = state.getToAlias(); Alias to = state.getToAlias();
@@ -164,7 +178,7 @@ public class P25DecoderPanel extends DecoderPanel
} }
else else
{ {
mToAlias.setText( null ); mToAlias.setText( " " );
mToAlias.setIcon( null ); mToAlias.setIcon( null );
} }
break; break;

View File

@@ -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.UnitToUnitVoiceChannelGrant;
import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate; import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdate;
import module.decode.p25.reference.IPProtocol; import module.decode.p25.reference.IPProtocol;
import module.decode.p25.reference.LinkControlOpcode;
import module.decode.p25.reference.Response; import module.decode.p25.reference.Response;
import module.decode.p25.reference.Service; import module.decode.p25.reference.Service;
import module.decode.p25.reference.Vendor; import module.decode.p25.reference.Vendor;
@@ -128,6 +129,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;
public class P25DecoderState extends DecoderState public class P25DecoderState extends DecoderState
@@ -169,9 +172,11 @@ public class P25DecoderState extends DecoderState
private String mCurrentChannel = "CURRENT"; private String mCurrentChannel = "CURRENT";
private long mCurrentChannelFrequency = 0; private long mCurrentChannelFrequency = 0;
private P25CallEvent mCurrentCallEvent;
private ChannelType mChannelType; private ChannelType mChannelType;
private Modulation mModulation; private Modulation mModulation;
private P25CallEvent mCurrentCallEvent;
private List<String> mCallDetectTalkgroups = new ArrayList<>();
public P25DecoderState( AliasList aliasList, public P25DecoderState( AliasList aliasList,
ChannelType channelType, ChannelType channelType,
@@ -221,8 +226,26 @@ public class P25DecoderState extends DecoderState
*/ */
private void resetState() private void resetState()
{ {
updateFrom( null ); mFromTalkgroup = null;
updateTo( 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 ); processLDU( (LDUMessage)message );
} }
/* Terminator Data Unit with Link Control Message */
else if( message instanceof TDULinkControlMessage )
{
processTDULC( (TDULinkControlMessage)message );
}
/* Trunking Signalling Messages */ /* Trunking Signalling Messages */
else if( message instanceof TSBKMessage ) else if( message instanceof TSBKMessage )
{ {
processTSBK( (TSBKMessage)message ); processTSBK( (TSBKMessage)message );
} }
/* Header Data Unit Message - preceeds voice LDUs */ /* Terminator Data Unit with Link Control Message */
else if( message instanceof HDUMessage ) else if( message instanceof TDULinkControlMessage )
{ {
processHDU( (HDUMessage)message ); processTDULC( (TDULinkControlMessage)message );
} }
/* Packet Data Unit Messages */ /* Packet Data Unit Messages */
@@ -272,6 +289,12 @@ public class P25DecoderState extends DecoderState
processPDU( (PDUMessage)message ); 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 */ /* Terminator Data Unit, or default message if CRC failed */
else if( message instanceof TDUMessage ) else if( message instanceof TDUMessage )
{ {
@@ -286,7 +309,10 @@ public class P25DecoderState extends DecoderState
*/ */
private void processTDU( TDUMessage tdu ) 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() ) if( hdu.isValid() )
{ {
State state = State.CALL; State state = hdu.isEncrypted() ? State.ENCRYPTED : State.CALL;
if( hdu.isEncrypted() )
{
state = State.ENCRYPTED;
}
broadcast( new DecoderStateEvent( this, Event.START, state ) ); 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(); mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.CALL )
CallEventType type = CallEventType.CALL;
if( hdu.isEncrypted() )
{
type = CallEventType.ENCRYPTED_CALL;
sb.append( "ENCRYPTED WITH " );
sb.append( hdu.getEncryption().name() );
sb.append( " KEY:" );
sb.append( hdu.getKeyID() );
}
P25CallEvent event = new P25CallEvent.Builder( type )
.aliasList( getAliasList() ) .aliasList( getAliasList() )
.channel( mCurrentChannel ) .channel( mCurrentChannel )
.details( "Trigger:HDU" ) .details( ( hdu.isEncrypted() ? "ENCRYPTED " : "" ) )
.frequency( mCurrentChannelFrequency ) .frequency( mCurrentChannelFrequency )
.to( hdu.getToID() ) .to( to )
.details( sb.toString() )
.build(); .build();
mCurrentCallEvent = event; broadcast( mCurrentCallEvent );
broadcast( event );
} }
updateTo( to );
} }
} }
/** /**
* Terminator Data Unit with Link Control - indicates an end of the call * Terminator Data Unit with Link Control - transmitted multiple times at
* sequences with embedded link control messages * beginning or end of call sequence and includes embedded link control messages
*/ */
private void processTDULC( TDULinkControlMessage tdulc ) 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() ) switch( tdulc.getOpcode() )
{ {
@@ -384,6 +403,7 @@ public class P25DecoderState extends DecoderState
} }
break; break;
case CALL_TERMINATION_OR_CANCELLATION: case CALL_TERMINATION_OR_CANCELLATION:
/* This opcode as handled at the beginning of the method */
break; break;
case CHANNEL_IDENTIFIER_UPDATE: case CHANNEL_IDENTIFIER_UPDATE:
//TODO: does the activity summary need this message? //TODO: does the activity summary need this message?
@@ -434,20 +454,42 @@ public class P25DecoderState extends DecoderState
} }
break; break;
case GROUP_VOICE_CHANNEL_UPDATE: case GROUP_VOICE_CHANNEL_UPDATE:
/* Reflects other call activity on the system: CALL DETECT */ /* Used only on trunked systems on the outbound channel, to
if( mChannelType == ChannelType.STANDARD && * reflect user activity on other channels. We process this
tdulc instanceof GroupVoiceChannelUpdate ) * as a call detect */
if( tdulc instanceof GroupVoiceChannelUpdate )
{ {
GroupVoiceChannelUpdate gvcu = (GroupVoiceChannelUpdate)tdulc; 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 ) String groupB = gvcu.getGroupAddressB();
.aliasList( getAliasList() )
.channel( gvcu.getChannelA() ) if( !mCallDetectTalkgroups.contains( groupB ) )
.details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) {
.frequency( gvcu.getDownlinkFrequencyA() ) broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
.from( gvcu.getGroupAddressA() ) .aliasList( getAliasList() )
.to( gvcu.getGroupAddressB() ) .channel( gvcu.getChannelB() )
.build() ); .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) )
.frequency( gvcu.getDownlinkFrequencyB() )
.to( groupB )
.build() );
mCallDetectTalkgroups.add( groupB );
}
} }
break; break;
case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT:
@@ -458,31 +500,52 @@ public class P25DecoderState extends DecoderState
GroupVoiceChannelUpdateExplicit gvcue = GroupVoiceChannelUpdateExplicit gvcue =
(GroupVoiceChannelUpdateExplicit)tdulc; (GroupVoiceChannelUpdateExplicit)tdulc;
broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) String group = gvcue.getGroupAddress();
.aliasList( getAliasList() )
.channel( gvcue.getTransmitChannel() ) if( !mCallDetectTalkgroups.contains( group ) )
.details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) ) {
.frequency( gvcue.getDownlinkFrequency() ) broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
.to( gvcue.getGroupAddress() ) .aliasList( getAliasList() )
.build() ); .channel( gvcue.getTransmitChannel() )
.details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) )
.frequency( gvcue.getDownlinkFrequency() )
.to( group )
.build() );
mCallDetectTalkgroups.add( group );
}
} }
break; break;
case GROUP_VOICE_CHANNEL_USER: case GROUP_VOICE_CHANNEL_USER:
if( mChannelType == ChannelType.STANDARD && /* Used on a traffic channel to reflect current call entities */
tdulc instanceof module.decode.p25.message.tdu.lc.GroupVoiceChannelUser ) 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 gvcuser =
(module.decode.p25.message.tdu.lc.GroupVoiceChannelUser)tdulc; (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 ) String from = gvcuser.getSourceAddress();
.aliasList( getAliasList() ) String to = gvcuser.getGroupAddress();
.channel( mCurrentChannel )
.details( ( gvcuser.isEncrypted() ? "ENCRYPTED" : "" ) + if( mCurrentCallEvent == null )
( gvcuser.isEmergency() ? " EMERGENCY" : "") ) {
.frequency( mCurrentChannelFrequency ) mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
.from( gvcuser.getSourceAddress() ) .aliasList( getAliasList() )
.to( gvcuser.getGroupAddress() ) .channel( mCurrentChannel )
.build() ); .details( ( gvcuser.isEncrypted() ? "ENCRYPTED " : "" ) +
( gvcuser.isEmergency() ? "EMERGENCY " : "") )
.frequency( mCurrentChannelFrequency )
.from( from )
.to( to )
.build();
broadcast( mCurrentCallEvent );
}
updateFrom( from );
updateTo( to );
} }
break; break;
case MESSAGE_UPDATE: 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 tivcu =
(module.decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser)tdulc; (module.decode.p25.message.tdu.lc.TelephoneInterconnectVoiceChannelUser)tdulc;
broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) String to = tivcu.getAddress();
.aliasList( getAliasList() )
.channel( mCurrentChannel ) if( mCurrentCallEvent == null )
.details( "TELEPHONE INTERCONNECT " + {
( tivcu.isEncrypted() ? "ENCRYPTED" : "" ) + mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.TELEPHONE_INTERCONNECT )
( tivcu.isEmergency() ? " EMERGENCY" : "") ) .aliasList( getAliasList() )
.frequency( mCurrentChannelFrequency ) .channel( mCurrentChannel )
.from( null ) .details( ( tivcu.isEncrypted() ? "ENCRYPTED" : "" ) +
.to( tivcu.getAddress() ) ( tivcu.isEmergency() ? " EMERGENCY" : "") )
.build() ); .frequency( mCurrentChannelFrequency )
.to( to )
.build();
broadcast( mCurrentCallEvent );
}
updateTo( to );
} }
break; break;
case UNIT_AUTHENTICATION_COMMAND: case UNIT_AUTHENTICATION_COMMAND:
@@ -752,21 +822,30 @@ public class P25DecoderState extends DecoderState
} }
break; break;
case UNIT_TO_UNIT_VOICE_CHANNEL_USER: case UNIT_TO_UNIT_VOICE_CHANNEL_USER:
if( mChannelType == ChannelType.STANDARD && /* Used on traffic channels to indicate the current call entities */
tdulc instanceof module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser ) 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 uuvcu =
(module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser)tdulc; (module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser)tdulc;
String from = uuvcu.getSourceAddress();
String to = uuvcu.getTargetAddress();
broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) if( mCurrentCallEvent != null )
.aliasList( getAliasList() ) {
.channel( mCurrentChannel ) mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.UNIT_TO_UNIT_CALL )
.details( ( uuvcu.isEncrypted() ? "ENCRYPTED" : "" ) + .aliasList( getAliasList() )
( uuvcu.isEmergency() ? " EMERGENCY" : "" ) ) .channel( mCurrentChannel )
.frequency( mCurrentChannelFrequency ) .details( ( uuvcu.isEncrypted() ? "ENCRYPTED " : "" ) +
.from( uuvcu.getSourceAddress() ) ( uuvcu.isEmergency() ? "EMERGENCY " : "" ) )
.to( uuvcu.getTargetAddress() ) .frequency( mCurrentChannelFrequency )
.build() ); .from( from )
.to( to )
.build();
}
updateFrom( from );
updateTo( to );
} }
break; break;
default: default:
@@ -780,36 +859,41 @@ public class P25DecoderState extends DecoderState
*/ */
private void logAlternateVendorMessage( P25Message p25 ) private void logAlternateVendorMessage( P25Message p25 )
{ {
if( p25 instanceof TDULinkControlMessage ) // if( p25 instanceof TDULinkControlMessage )
{ // {
TDULinkControlMessage tdulc = (TDULinkControlMessage)p25; // TDULinkControlMessage tdulc = (TDULinkControlMessage)p25;
//
mLog.debug( "TDULC - VENDOR [" + tdulc.getVendor() + // mLog.debug( "TDULC - VENDOR [" + tdulc.getVendor() +
"] SPECIFIC FORMAT FOR " + tdulc.getVendorOpcode().getLabel() + // "] SPECIFIC FORMAT FOR " + tdulc.getVendorOpcode().getLabel() +
" " + tdulc.getBinaryMessage() ); // " " + tdulc.getBinaryMessage() );
} // }
else if( p25 instanceof LDU1Message ) // else if( p25 instanceof LDU1Message )
{ // {
LDU1Message ldu1 = (LDU1Message)p25; // LDU1Message ldu1 = (LDU1Message)p25;
//
mLog.debug( "LDU1 - VENDOR [" + ldu1.getVendor() + // mLog.debug( "LDU1 - VENDOR [" + ldu1.getVendor() +
"] SPECIFIC FORMAT FOR " + ldu1.getVendorOpcode().getLabel() + // "] SPECIFIC FORMAT FOR " + ldu1.getVendorOpcode().getLabel() +
" " + ldu1.getBinaryMessage() ); // " " + ldu1.getBinaryMessage() );
} // }
else if( p25 instanceof PDUMessage ) // else if( p25 instanceof PDUMessage )
{ // {
PDUMessage pdu = (PDUMessage)p25; // PDUMessage pdu = (PDUMessage)p25;
//
mLog.debug( "PDU - VENDOR [" + pdu.getVendor() + // mLog.debug( "PDU - VENDOR [" + pdu.getVendor() +
"] SPECIFIC FORMAT FOR " + pdu.getVendorOpcode().getLabel() + // "] SPECIFIC FORMAT FOR " + pdu.getVendorOpcode().getLabel() +
" " + pdu.getBinaryMessage() ); // " " + pdu.getBinaryMessage() );
} // }
else // else
{ // {
mLog.debug( "P25 - ALTERNATE VENDOR MESSAGE [" + p25.getClass() + "]" ); // 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 ) private void processLDU( LDUMessage ldu )
{ {
State state = ( ldu.isValid() && ldu.isEncrypted() ) ? State state = ( ldu.isValid() && ldu.isEncrypted() ) ?
@@ -817,6 +901,18 @@ public class P25DecoderState extends DecoderState
broadcast( new DecoderStateEvent( this, Event.CONTINUATION, state ) ); 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 ) if( ldu instanceof LDU1Message )
{ {
switch( ((LDU1Message)ldu).getOpcode() ) 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 gvcu =
(module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate)ldu; (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() ) .aliasList( getAliasList() )
.channel( gvcu.getChannelA() ) .channel( gvcu.getChannelA() )
.details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) )
.frequency( gvcu.getDownlinkFrequencyA() ) .frequency( gvcu.getDownlinkFrequencyA() )
.to( gvcu.getGroupAddressA() ) .to( userA )
.build() ); .build() );
mCallDetectTalkgroups.add( userA );
}
String userB = gvcu.getGroupAddressB(); 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 ) broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT )
.aliasList( getAliasList() ) .aliasList( getAliasList() )
@@ -939,6 +1044,8 @@ public class P25DecoderState extends DecoderState
.frequency( gvcu.getDownlinkFrequencyB() ) .frequency( gvcu.getDownlinkFrequencyB() )
.to( userB ) .to( userB )
.build() ); .build() );
mCallDetectTalkgroups.add( userB );
} }
} }
else 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 gvcue =
(module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit)ldu; (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() ) .aliasList( getAliasList() )
.channel( gvcue.getTransmitChannel() ) .channel( gvcue.getTransmitChannel() )
.details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) ) .details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) )
.frequency( gvcue.getDownlinkFrequency() ) .frequency( gvcue.getDownlinkFrequency() )
.to( gvcue.getGroupAddress() ) .to( group )
.build() ); .build() );
mCallDetectTalkgroups.add( group );
}
} }
else 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 gvcuser =
(module.decode.p25.message.ldu.lc.GroupVoiceChannelUser)ldu; (module.decode.p25.message.ldu.lc.GroupVoiceChannelUser)ldu;
broadcast( new P25CallEvent.Builder( CallEventType.CALL ) if( mCurrentCallEvent.getCallEventType() != CallEventType.GROUP_CALL )
.aliasList( getAliasList() ) {
.channel( mCurrentChannel ) mCurrentCallEvent.setCallEventType( CallEventType.GROUP_CALL );
.details( "Trigger:LDU-GROUP_VOICE_CHANNEL_USER " + broadcast( mCurrentCallEvent );
( gvcuser.isEncrypted() ? "ENCRYPTED" : "" ) + }
( gvcuser.isEmergency() ? " EMERGENCY" : "") )
.frequency( mCurrentChannelFrequency ) if( mCurrentCallEvent.getDetails() == null )
.from( gvcuser.getSourceAddress() ) {
.to( gvcuser.getGroupAddress() ) mCurrentCallEvent.setDetails(
.build() ); ( gvcuser.isEncrypted() ? "ENCRYPTED " : "" ) +
( gvcuser.isEmergency() ? "EMERGENCY " : "" ) );
broadcast( mCurrentCallEvent );
}
updateFrom( gvcuser.getSourceAddress() );
updateTo( gvcuser.getGroupAddress() );
} }
else else
{ {
@@ -1178,18 +1299,23 @@ public class P25DecoderState extends DecoderState
{ {
TelephoneInterconnectVoiceChannelUser tivcu = TelephoneInterconnectVoiceChannelUser tivcu =
(TelephoneInterconnectVoiceChannelUser)ldu; (TelephoneInterconnectVoiceChannelUser)ldu;
broadcast( new P25CallEvent.Builder( CallEventType.CALL ) if( mCurrentCallEvent.getCallEventType() != CallEventType.TELEPHONE_INTERCONNECT )
.aliasList( getAliasList() ) {
.channel( mCurrentChannel ) mCurrentCallEvent.setCallEventType( CallEventType.TELEPHONE_INTERCONNECT );
.details( "Trigger: LDU-TEL_INT_VOIC_CHAN_USER " + broadcast( mCurrentCallEvent );
"TELEPHONE INTERCONNECT " + }
( tivcu.isEncrypted() ? "ENCRYPTED" : "" ) +
( tivcu.isEmergency() ? " EMERGENCY" : "") ) if( mCurrentCallEvent.getDetails() == null )
.frequency( mCurrentChannelFrequency ) {
.from( null ) mCurrentCallEvent.setDetails(
.to( tivcu.getAddress() ) ( tivcu.isEncrypted() ? "ENCRYPTED " : "" ) +
.build() ); ( tivcu.isEmergency() ? "EMERGENCY " : "" ) );
broadcast( mCurrentCallEvent );
}
updateTo( tivcu.getAddress() );
} }
else else
{ {
@@ -1254,17 +1380,24 @@ public class P25DecoderState extends DecoderState
{ {
UnitToUnitVoiceChannelUser uuvcu = UnitToUnitVoiceChannelUser uuvcu =
(UnitToUnitVoiceChannelUser)ldu; (UnitToUnitVoiceChannelUser)ldu;
broadcast( new P25CallEvent.Builder( CallEventType.CALL ) if( mCurrentCallEvent.getCallEventType() != CallEventType.UNIT_TO_UNIT_CALL )
.aliasList( getAliasList() ) {
.channel( mCurrentChannel ) mCurrentCallEvent.setCallEventType( CallEventType.UNIT_TO_UNIT_CALL );
.details( "Trigger:LDU-U2U_VOICE_CHAN_USER " + broadcast( mCurrentCallEvent );
( uuvcu.isEncrypted() ? "ENCRYPTED" : "" ) + }
( uuvcu.isEmergency() ? " EMERGENCY" : "" ) )
.frequency( mCurrentChannelFrequency ) if( mCurrentCallEvent.getDetails() == null )
.from( uuvcu.getSourceAddress() ) {
.to( uuvcu.getTargetAddress() ) mCurrentCallEvent.setDetails(
.build() ); ( uuvcu.isEncrypted() ? "ENCRYPTED " : "" ) +
( uuvcu.isEmergency() ? "EMERGENCY " : "" ) );
broadcast( mCurrentCallEvent );
}
updateFrom( uuvcu.getSourceAddress() );
updateTo( uuvcu.getTargetAddress() );
} }
else else
{ {
@@ -2366,7 +2499,7 @@ public class P25DecoderState extends DecoderState
case GROUP_VOICE_CHANNEL_GRANT: case GROUP_VOICE_CHANNEL_GRANT:
GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)message; GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)message;
event = new P25CallEvent.Builder( CallEventType.CALL ) event = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
.aliasList( getAliasList() ) .aliasList( getAliasList() )
.channel( gvcg.getChannel() ) .channel( gvcg.getChannel() )
.details( "Trigger: TSBK-G_V_CH_GRNT " + .details( "Trigger: TSBK-G_V_CH_GRNT " +
@@ -2381,7 +2514,7 @@ public class P25DecoderState extends DecoderState
GroupVoiceChannelGrantUpdate gvcgu = GroupVoiceChannelGrantUpdate gvcgu =
(GroupVoiceChannelGrantUpdate)message; (GroupVoiceChannelGrantUpdate)message;
event = new P25CallEvent.Builder( CallEventType.CALL ) event = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
.aliasList( getAliasList() ) .aliasList( getAliasList() )
.channel( gvcgu.getChannel1() ) .channel( gvcgu.getChannel1() )
.details( "Trigger: TSBK-G_V_CH_GRNT_UP " + .details( "Trigger: TSBK-G_V_CH_GRNT_UP " +
@@ -2394,7 +2527,7 @@ public class P25DecoderState extends DecoderState
if( gvcgu.hasChannelNumber2() ) if( gvcgu.hasChannelNumber2() )
{ {
/* We handle this channel grant independent of the first event */ /* 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() ) .aliasList( getAliasList() )
.channel( gvcgu.getChannel2() ) .channel( gvcgu.getChannel2() )
.details( "Trigger: TSBK-G_V_CH_GRNT_UP#2 " + .details( "Trigger: TSBK-G_V_CH_GRNT_UP#2 " +
@@ -2411,7 +2544,7 @@ public class P25DecoderState extends DecoderState
GroupVoiceChannelGrantUpdateExplicit gvcgue = GroupVoiceChannelGrantUpdateExplicit gvcgue =
(GroupVoiceChannelGrantUpdateExplicit)message; (GroupVoiceChannelGrantUpdateExplicit)message;
event = new P25CallEvent.Builder( CallEventType.CALL ) event = new P25CallEvent.Builder( CallEventType.GROUP_CALL )
.aliasList( getAliasList() ) .aliasList( getAliasList() )
.channel( gvcgue.getTransmitChannelIdentifier() + "-" + .channel( gvcgue.getTransmitChannelIdentifier() + "-" +
gvcgue.getTransmitChannelNumber() ) gvcgue.getTransmitChannelNumber() )
@@ -2452,7 +2585,7 @@ public class P25DecoderState extends DecoderState
TelephoneInterconnectVoiceChannelGrant tivcg = TelephoneInterconnectVoiceChannelGrant tivcg =
(TelephoneInterconnectVoiceChannelGrant)message; (TelephoneInterconnectVoiceChannelGrant)message;
event = new P25CallEvent.Builder( CallEventType.TELEPHONE_CALL ) event = new P25CallEvent.Builder( CallEventType.TELEPHONE_INTERCONNECT )
.aliasList( getAliasList() ) .aliasList( getAliasList() )
.channel( tivcg.getChannel() ) .channel( tivcg.getChannel() )
.details( "Trigger: TSBK-T_INT_V_CH_GRNT " + .details( "Trigger: TSBK-T_INT_V_CH_GRNT " +
@@ -2467,7 +2600,7 @@ public class P25DecoderState extends DecoderState
TelephoneInterconnectVoiceChannelGrantUpdate tivcgu = TelephoneInterconnectVoiceChannelGrantUpdate tivcgu =
(TelephoneInterconnectVoiceChannelGrantUpdate)message; (TelephoneInterconnectVoiceChannelGrantUpdate)message;
event = new P25CallEvent.Builder( CallEventType.TELEPHONE_CALL ) event = new P25CallEvent.Builder( CallEventType.TELEPHONE_INTERCONNECT )
.aliasList( getAliasList() ) .aliasList( getAliasList() )
.channel( tivcgu.getChannelIdentifier() + "-" + .channel( tivcgu.getChannelIdentifier() + "-" +
tivcgu.getChannelNumber() ) tivcgu.getChannelNumber() )
@@ -2482,7 +2615,7 @@ public class P25DecoderState extends DecoderState
UnitToUnitVoiceChannelGrant uuvcg = UnitToUnitVoiceChannelGrant uuvcg =
(UnitToUnitVoiceChannelGrant)message; (UnitToUnitVoiceChannelGrant)message;
event = new P25CallEvent.Builder( CallEventType.CALL ) event = new P25CallEvent.Builder( CallEventType.UNIT_TO_UNIT_CALL )
.aliasList( getAliasList() ) .aliasList( getAliasList() )
.channel( uuvcg.getChannelIdentifier() + "-" + .channel( uuvcg.getChannelIdentifier() + "-" +
uuvcg.getChannelNumber() ) uuvcg.getChannelNumber() )
@@ -2498,7 +2631,7 @@ public class P25DecoderState extends DecoderState
UnitToUnitVoiceChannelGrantUpdate uuvcgu = UnitToUnitVoiceChannelGrantUpdate uuvcgu =
(UnitToUnitVoiceChannelGrantUpdate)message; (UnitToUnitVoiceChannelGrantUpdate)message;
event = new P25CallEvent.Builder( CallEventType.CALL ) event = new P25CallEvent.Builder( CallEventType.UNIT_TO_UNIT_CALL )
.aliasList( getAliasList() ) .aliasList( getAliasList() )
.channel( uuvcgu.getChannelIdentifier() + "-" + .channel( uuvcgu.getChannelIdentifier() + "-" +
uuvcgu.getChannelNumber() ) uuvcgu.getChannelNumber() )
@@ -2514,7 +2647,7 @@ public class P25DecoderState extends DecoderState
break; break;
} }
if( event != null ) if( event != null && event.getCallEventType() != CallEventType.DATA_CALL )
{ {
broadcast( new TrafficChannelAllocationEvent( this, event ) ); broadcast( new TrafficChannelAllocationEvent( this, event ) );
} }
@@ -2525,24 +2658,38 @@ public class P25DecoderState extends DecoderState
*/ */
private void updateTo( String to ) private void updateTo( String to )
{ {
if( to != null && mLog.debug( "Updating to [" + to + "]" );
if( to != null &&
!to.contentEquals( "0000" ) && !to.contentEquals( "0000" ) &&
!to.contentEquals( "000000" ) && !to.contentEquals( "000000" ) &&
( mToTalkgroup == null || !mToTalkgroup.contentEquals( to ) ) ) ( 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( to ); mToAlias = getAliasList().getTalkgroupAlias( mToTalkgroup );
mLog.debug( "To Alias: " + mToAlias );
} }
else else
{ {
mLog.debug( "To Alias: no alias list" );
mToAlias = null; mToAlias = null;
} }
broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); 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 ) private void updateFrom( String from )
{ {
if( from != null && if( from != null &&
!from.contentEquals( "0000" ) && !from.contentEquals( "0000" ) &&
!from.contentEquals( "000000" ) && !from.contentEquals( "000000" ) &&
( mFromTalkgroup == null || !mFromTalkgroup.contentEquals( from ) ) ) ( mFromTalkgroup == null || !mFromTalkgroup.contentEquals( from ) ) )
{ {
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( from ); mFromAlias = getAliasList().getTalkgroupAlias( mFromTalkgroup );
} }
else else
{ {
@@ -2570,6 +2724,9 @@ public class P25DecoderState extends DecoderState
} }
broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); 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(); mToTalkgroup = allocationEvent.getCallEvent().getToID();
broadcast( ChangedAttribute.TO_TALKGROUP ); 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; break;

View File

@@ -17,12 +17,12 @@
******************************************************************************/ ******************************************************************************/
package module.decode.p25; package module.decode.p25;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox; import javax.swing.JComboBox;
import javax.swing.JLabel; 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.DecodeEditor;
import module.decode.config.DecodeConfiguration; import module.decode.config.DecodeConfiguration;
@@ -43,6 +43,10 @@ public class P25Editor extends DecodeEditor
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private JComboBox<P25_LSMDecoder.Modulation> mComboModulation; private JComboBox<P25_LSMDecoder.Modulation> mComboModulation;
private JLabel mCallTimeoutLabel;
private JSlider mCallTimeout;
private JLabel mTrafficChannelPoolSizeLabel;
private JSlider mTrafficChannelPoolSize;
public P25Editor( DecodeConfiguration config ) public P25Editor( DecodeConfiguration config )
{ {
@@ -60,23 +64,62 @@ public class P25Editor extends DecodeEditor
mComboModulation.setSelectedItem( ((DecodeConfigP25Phase1)mConfig).getModulation() ); 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( new JLabel( "Modulation:" ) );
add( mComboModulation, "wrap" ); 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() );
}
} }

View File

@@ -12,8 +12,6 @@ import module.decode.p25.message.tdu.lc.TDULinkControlMessage;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import controller.channel.ChannelEvent;
import controller.channel.IChannelEventListener;
import sample.Listener; import sample.Listener;
import audio.AudioFormats; import audio.AudioFormats;
import audio.AudioPacket; import audio.AudioPacket;
@@ -21,9 +19,14 @@ import audio.IAudioPacketProvider;
import audio.metadata.AudioMetadata; import audio.metadata.AudioMetadata;
import audio.metadata.IMetadataListener; import audio.metadata.IMetadataListener;
import audio.metadata.Metadata; 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<Message>, public class P25AudioModule extends Module implements Listener<Message>,
IAudioPacketProvider, IMessageListener, IChannelEventListener, IMetadataListener IAudioPacketProvider, IMessageListener, IChannelEventListener,
IMetadataListener, ISquelchStateListener
{ {
private final static Logger mLog = LoggerFactory.getLogger( P25AudioModule.class ); private final static Logger mLog = LoggerFactory.getLogger( P25AudioModule.class );
@@ -40,6 +43,7 @@ public class P25AudioModule extends Module implements Listener<Message>,
private Listener<AudioPacket> mAudioPacketListener; private Listener<AudioPacket> mAudioPacketListener;
private AudioMetadata mAudioMetadata; private AudioMetadata mAudioMetadata;
private ChannelEventListener mChannelEventListener = new ChannelEventListener(); private ChannelEventListener mChannelEventListener = new ChannelEventListener();
private SquelchStateListener mSquelchStateListener = new SquelchStateListener();
public P25AudioModule( boolean record ) public P25AudioModule( boolean record )
{ {
@@ -55,6 +59,12 @@ public class P25AudioModule extends Module implements Listener<Message>,
return this; return this;
} }
@Override
public Listener<SquelchState> getSquelchStateListener()
{
return mSquelchStateListener;
}
@Override @Override
public void dispose() public void dispose()
{ {
@@ -105,7 +115,7 @@ public class P25AudioModule extends Module implements Listener<Message>,
for( byte[] frame: ((LDUMessage)message).getIMBEFrames() ) for( byte[] frame: ((LDUMessage)message).getIMBEFrames() )
{ {
float[] audio = mAudioConverter.decode( frame ); float[] audio = mAudioConverter.decode( frame );
mAudioPacketListener.receive( mAudioPacketListener.receive(
new AudioPacket( audio, mAudioMetadata ) ); new AudioPacket( audio, mAudioMetadata ) );
} }
@@ -134,8 +144,10 @@ public class P25AudioModule extends Module implements Listener<Message>,
library = (AudioConversionLibrary)temp.newInstance(); library = (AudioConversionLibrary)temp.newInstance();
if( ( library.getMajorVersion() == 0 && library.getMinorVersion() >= 3 ) || if( ( library.getMajorVersion() == 0 &&
library.getMajorVersion() >= 1 ) library.getMinorVersion() >= 3 &&
library.getBuildVersion() >= 1 ) ||
library.getMajorVersion() >= 1 )
{ {
mAudioConverter = library.getAudioConverter( IMBE_CODEC, mAudioConverter = library.getAudioConverter( IMBE_CODEC,
AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO ); AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO );
@@ -163,7 +175,7 @@ public class P25AudioModule extends Module implements Listener<Message>,
} }
else 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 ) catch ( ClassNotFoundException e1 )
@@ -239,4 +251,25 @@ public class P25AudioModule extends Module implements Listener<Message>,
} }
} }
} }
/**
* 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<SquelchState>
{
@Override
public void receive( SquelchState state )
{
if( state == SquelchState.SQUELCH && mAudioPacketListener != null )
{
mAudioPacketListener.receive( new AudioPacket( AudioPacket.Type.END,
mAudioMetadata.copyOf() ) );
mAudioMetadata.reset();
}
}
}
} }

View File

@@ -163,9 +163,11 @@ public class ChannelState extends Module implements ICallEventProvider,
return mState; 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; mSquelchLocked = true;
break; break;
case CONTINUATION: case CONTINUATION:
if( isEndingState() ) if( isIdleOrEndingState() )
{ {
setState( event.getState() ); setState( event.getState() );
} }

View File

@@ -59,37 +59,37 @@ public class RecorderManager implements Listener<AudioPacket>
{ {
if( audioPacket.hasAudioMetadata() && if( audioPacket.hasAudioMetadata() &&
audioPacket.getAudioMetadata().isRecordable() ) 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() ); 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 );
}
}
} }
/** /**