P25 Work in progress. Adds CRC checking to NID/DUID. Adds limiter to trellis decoder to prevent it from decoding an excessive error message. Updates channel state with initial handling/generation of call events.

This commit is contained in:
dennis.sheirer
2014-12-14 02:34:19 +00:00
parent 0d8da161e1
commit 170688b95e
42 changed files with 1099 additions and 390 deletions

View File

@@ -58,7 +58,7 @@ public class BaseNode extends DefaultMutableTreeNode
return null;
}
public Color getForegroundColor()
public Color getBackgroundColor()
{
return null;
}

View File

@@ -54,11 +54,32 @@ public class ControllerTreeCellRenderer extends DefaultTreeCellRenderer
setIcon( icon );
Color foreground = node.getForegroundColor();
Color background = node.getBackgroundColor();
if( foreground != null )
if( background != null )
{
setForeground( foreground );
setBackground( background );
setOpaque( true );
if( selected )
{
if( background == Color.CYAN )
{
setForeground( Color.ORANGE );
}
else if( background == Color.GREEN )
{
setForeground( Color.MAGENTA );
}
else if( background == Color.RED )
{
setForeground( Color.YELLOW );
}
}
}
else
{
setOpaque( false );
}
}

View File

@@ -127,7 +127,7 @@ public abstract class CallEvent
public abstract Alias getToIDAlias();
public abstract int getChannel();
public abstract String getChannel();
public abstract long getFrequency();
@@ -216,6 +216,7 @@ public abstract class CallEvent
CALL_UNIQUE_ID( "UID Call" ),
CALL_NO_TUNER( "Call - No Tuner" ),
CALL_TIMEOUT( "Call Timeout" ),
DEREGISTER( "Deregister" ),
EMERGENCY( "EMERGENCY" ),
GPS( "GPS" ),
ID_ANI( "ANI" ),

View File

@@ -296,16 +296,7 @@ public class CallEventModel extends AbstractTableModel
case TO_ALIAS:
return mEvents.get( rowIndex ).getToIDAlias();
case CHANNEL:
int channel = mEvents.get( rowIndex ).getChannel();
if( channel != 0 )
{
return channel;
}
else
{
return null;
}
return mEvents.get( rowIndex ).getChannel();
case FREQUENCY:
long frequency = mEvents.get( rowIndex ).getFrequency();

View File

@@ -104,7 +104,7 @@ public class ChannelNode extends ConfigurableNode implements ChannelEventListene
}
@Override
public Color getForegroundColor()
public Color getBackgroundColor()
{
if( getChannel().getEnabled() )
{

View File

@@ -64,7 +64,7 @@ public class SiteNode extends ConfigurableNode
}
@Override
public Color getForegroundColor()
public Color getBackgroundColor()
{
@SuppressWarnings( "unchecked" )
Enumeration<DefaultMutableTreeNode> nodes = children();
@@ -73,9 +73,9 @@ public class SiteNode extends ConfigurableNode
{
ChannelNode child = (ChannelNode)nodes.nextElement();
if( child.getForegroundColor() != null )
if( child.getBackgroundColor() != null )
{
return Color.BLUE;
return Color.CYAN;
}
}

View File

@@ -64,7 +64,7 @@ public class SystemNode extends ConfigurableNode
}
@Override
public Color getForegroundColor()
public Color getBackgroundColor()
{
@SuppressWarnings( "unchecked" )
Enumeration<DefaultMutableTreeNode> nodes = children();
@@ -73,9 +73,9 @@ public class SystemNode extends ConfigurableNode
{
SiteNode child = (SiteNode)nodes.nextElement();
if( child.getForegroundColor() != null )
if( child.getBackgroundColor() != null )
{
return Color.BLUE;
return Color.CYAN;
}
}

View File

@@ -1,13 +0,0 @@
package crc;
import java.util.BitSet;
public class BCH
{
public BCH( int n, int k, int t, boolean[] polynomial, boolean sys )
{
int[] exponents = new int[ n - k + 1 ];
}
}

View File

@@ -0,0 +1,179 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package crc;
import java.util.BitSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import bits.BitSetBuffer;
/**
* P25 Data Unit ID CRC check utility. Corrects any single bit errors in
* either the message or the crc checksum.
*/
public class CRCP25NetworkID
{
private final static Logger mLog = LoggerFactory.getLogger( CRCP25NetworkID.class );
private static final int MESSAGE_START = 0;
private static final int CRC_START = 16;
private static final int CRC_END = 64;
private static long[] CHECKSUMS = new long[]
{
0xCD930BDD3B2Al, //NAC Bit 11 (MSB)
0xAB5A8E33A6BEl, //NAC Bit 10
0x983E4CC4E874l, //NAC Bit 9
0x4C1F2662743Al, //NAC Bit 8
0xEB9C98EC0136l, //NAC Bit 7
0xB85D47AB3BB0l, //NAC Bit 6
0x5C2EA3D59DD8l, //NAC Bit 5
0x2E1751EACEECl, //NAC Bit 4
0x170BA8F56776l, //NAC Bit 3
0xC616DFA78890l, //NAC Bit 2
0x630B6FD3C448l, //NAC Bit 1
0x3185B7E9E224l, //NAC Bit 0 (LSB)
0x18C2DBF4F112l, //DUID Bit 3 (MSB)
0xC1F2662743A2l, //DUID Bit 2
0xAD6A38CE9AFBl, //DUID Bit 1
0x9B2617BA7657l, //DUID Bit 0 (LSB)
//Single bit errors in the CRC Checksum
0x800000000000l,
0x400000000000l,
0x200000000000l,
0x100000000000l,
0x080000000000l,
0x040000000000l,
0x020000000000l,
0x010000000000l,
0x008000000000l,
0x004000000000l,
0x002000000000l,
0x001000000000l,
0x000800000000l,
0x000400000000l,
0x000200000000l,
0x000100000000l,
0x000080000000l,
0x000040000000l,
0x000020000000l,
0x000010000000l,
0x000008000000l,
0x000004000000l,
0x000002000000l,
0x000001000000l,
0x000000800000l,
0x000000400000l,
0x000000200000l,
0x000000100000l,
0x000000080000l,
0x000000040000l,
0x000000020000l,
0x000000010000l,
0x000000008000l,
0x000000004000l,
0x000000002000l,
0x000000001000l,
0x000000000800l,
0x000000000400l,
0x000000000200l,
0x000000000100l,
0x000000000080l,
0x000000000040l,
0x000000000020l,
0x000000000010l,
0x000000000008l,
0x000000000004l,
0x000000000002l,
0x000000000001l,
};
/**
*
*/
public static CRC checkAndCorrect( BitSetBuffer message )
{
long calculated = 0; //Starting value
/* Iterate the set bits and XOR running checksum with lookup value */
for (int i = message.nextSetBit( MESSAGE_START );
i >= MESSAGE_START && i < CRC_START;
i = message.nextSetBit( i+1 ) )
{
calculated ^= CHECKSUMS[ i - MESSAGE_START ];
}
long checksum = getChecksum( message );
if( calculated == checksum )
{
return CRC.PASSED;
}
else
{
int errorLocation = findBitError( calculated ^ checksum );
if( errorLocation >= 0 )
{
message.flip( errorLocation );
return CRC.CORRECTED;
}
}
return CRC.FAILED_CRC;
}
/**
* Returns the long value of the 48 bit crc checksum and parity bit
*/
public static long getChecksum( BitSet msg )
{
long checksum = 0;
for( int x = CRC_START; x < CRC_END; x++ )
{
if( msg.get( x ) )
{
checksum += Long.rotateLeft( 1, CRC_END - 1 - x );
}
}
return checksum;
}
/**
* Identifies any single bit error position that matches the checksum error.
*/
public static int findBitError( long checksumError )
{
/* One bit errors */
for( int x = 0; x < 64; x++ )
{
if( CHECKSUMS[ x ] == checksumError )
{
return x;
}
}
return -1;
}
}

View File

@@ -57,9 +57,9 @@ public class FleetsyncCallEvent extends CallEvent
}
@Override
public int getChannel()
public String getChannel()
{
return 0;
return null;
}
@Override

View File

@@ -57,9 +57,9 @@ public class LJ1200CallEvent extends CallEvent
}
@Override
public int getChannel()
public String getChannel()
{
return 0;
return null;
}
@Override

View File

@@ -25,7 +25,7 @@ import decode.ltrstandard.LTRStandardMessage;
public class LTRCallEvent extends CallEvent
{
private int mChannel;
private String mChannel;
private long mFrequency;
private int mValidCallMessages = 1;
@@ -33,7 +33,7 @@ public class LTRCallEvent extends CallEvent
AliasList aliasList,
String fromID,
String toID,
int channel,
String channel,
long frequency,
String details )
{
@@ -141,7 +141,7 @@ public class LTRCallEvent extends CallEvent
}
@Override
public int getChannel()
public String getChannel()
{
return mChannel;
}
@@ -162,7 +162,7 @@ public class LTRCallEvent extends CallEvent
private String mFromID;
private String mToID;
private String mDetails;
private int mChannel;
private String mChannel;
private long mFrequency;
public Builder( CallEventType callEventType )
@@ -182,7 +182,7 @@ public class LTRCallEvent extends CallEvent
return this;
}
public Builder channel( int channel )
public Builder channel( String channel )
{
mChannel = channel;
return this;

View File

@@ -166,7 +166,7 @@ public class LTRNetChannelState extends ChannelState
mCallEventModel.add(
new LTRCallEvent.Builder( CallEventType.CALL_DETECT )
.aliasList( mAliasList )
.channel( ltr.getChannel() )
.channel( String.valueOf( ltr.getChannel() ) )
.to( ltr.getTalkgroupID() )
.build() );
}
@@ -288,7 +288,7 @@ public class LTRNetChannelState extends ChannelState
LTRCallEvent event =
new LTRCallEvent.Builder( CallEventType.REGISTER )
.aliasList( mAliasList )
.channel( ltr.getChannel() )
.channel( String.valueOf( ltr.getChannel() ) )
.frequency( mProcessingChain.getChannel()
.getTunerChannel().getFrequency() )
.to( ltr.getTalkgroupID() )
@@ -354,7 +354,7 @@ public class LTRNetChannelState extends ChannelState
{
event = new LTRCallEvent.Builder( CallEventType.REGISTER )
.aliasList( mAliasList )
.channel( message.getChannel() )
.channel( String.valueOf( message.getChannel() ) )
.frequency( mProcessingChain.getChannel()
.getTunerChannel().getFrequency() )
.to( message.getTalkgroupID() )
@@ -366,7 +366,7 @@ public class LTRNetChannelState extends ChannelState
{
event = new LTRCallEvent.Builder( CallEventType.CALL )
.aliasList( mAliasList )
.channel( message.getChannel() )
.channel( String.valueOf( message.getChannel() ) )
.frequency( mProcessingChain.getChannel()
.getTunerChannel().getFrequency() )
.to( message.getTalkgroupID() )

View File

@@ -120,7 +120,7 @@ public class LTRChannelState extends ChannelState
new LTRCallEvent.Builder( CallEventType.CALL )
.to( ltr.getTalkgroupID() )
.aliasList( getAliasList() )
.channel( ltr.getChannel() )
.channel( String.valueOf( ltr.getChannel() ) )
.frequency( ( ltr.getChannel() == mChannelNumber ) ?
mProcessingChain.getChannel()
.getTunerChannel().getFrequency() : 0 )

View File

@@ -57,9 +57,9 @@ public class MDCCallEvent extends CallEvent
}
@Override
public int getChannel()
public String getChannel()
{
return 0;
return null;
}
@Override

View File

@@ -24,14 +24,14 @@ import decode.DecoderType;
public class MPT1327CallEvent extends CallEvent
{
private int mChannel;
private String mChannel;
private long mFrequency;
public MPT1327CallEvent( CallEventType callEventType,
AliasList aliasList,
String fromID,
String toID,
int channel,
String channel,
long frequency,
String details )
{
@@ -65,7 +65,7 @@ public class MPT1327CallEvent extends CallEvent
}
@Override
public int getChannel()
public String getChannel()
{
return mChannel;
}
@@ -86,7 +86,7 @@ public class MPT1327CallEvent extends CallEvent
private String mFromID;
private String mToID;
private String mDetails;
private int mChannel;
private String mChannel;
private long mFrequency;
public Builder( CallEventType callEventType )
@@ -106,7 +106,7 @@ public class MPT1327CallEvent extends CallEvent
return this;
}
public Builder channel( int channel )
public Builder channel( String channel )
{
mChannel = channel;
return this;

View File

@@ -115,7 +115,7 @@ public class MPT1327ChannelState extends ChannelState
mCallEventModel.add(
new MPT1327CallEvent.Builder( CallEventType.REGISTER )
.aliasList( getProcessingChain().getAliasList() )
.channel( mChannelNumber )
.channel( String.valueOf( mChannelNumber ) )
.details( "REGISTERED ON NETWORK" )
.frequency( mChannelMap.getFrequency( mChannelNumber ) )
.from( mpt.getToID() )
@@ -127,7 +127,7 @@ public class MPT1327ChannelState extends ChannelState
mCallEventModel.add(
new MPT1327CallEvent.Builder( CallEventType.ACKNOWLEDGE )
.aliasList( getProcessingChain().getAliasList() )
.channel( mChannelNumber )
.channel( String.valueOf( mChannelNumber ) )
.details( "ACK " + identType.getLabel() )
.frequency( mChannelMap.getFrequency( mChannelNumber ) )
.from( mpt.getFromID() )
@@ -139,7 +139,7 @@ public class MPT1327ChannelState extends ChannelState
mCallEventModel.add(
new MPT1327CallEvent.Builder( CallEventType.REQUEST )
.aliasList( getProcessingChain().getAliasList() )
.channel( mChannelNumber )
.channel( String.valueOf( mChannelNumber ) )
.details( ( (MPT1327Message) message ).getRequestString() )
.frequency( mChannelMap.getFrequency( mChannelNumber ) )
.from( mpt.getFromID() )
@@ -150,7 +150,7 @@ public class MPT1327ChannelState extends ChannelState
mCallEventModel.add(
new MPT1327CallEvent.Builder( CallEventType.STATUS )
.aliasList( getProcessingChain().getAliasList() )
.channel( mChannelNumber )
.channel( String.valueOf( mChannelNumber ) )
.details( mpt.getStatusMessage() )
.frequency( mChannelMap.getFrequency( mChannelNumber ) )
.from( mpt.getFromID() )
@@ -233,7 +233,7 @@ public class MPT1327ChannelState extends ChannelState
MPT1327CallEvent callStartEvent =
new MPT1327CallEvent.Builder( type )
.aliasList( traffic.getProcessingChain().getAliasList() )
.channel( channelNumber )
.channel( String.valueOf( channelNumber ) )
.frequency( mChannelMap.getFrequency( channelNumber ) )
.from( mpt.getFromID() )
.to( mpt.getToID() )
@@ -293,7 +293,7 @@ public class MPT1327ChannelState extends ChannelState
mCallEventModel.add(
new MPT1327CallEvent.Builder( type )
.aliasList( getAliasList() )
.channel( mChannelNumber )
.channel( String.valueOf( mChannelNumber ) )
.frequency( mChannelMap.getFrequency( mChannelNumber ) )
.from( mFromTalkgroup )
.to( mToTalkgroup )

View File

@@ -24,16 +24,16 @@ import decode.DecoderType;
public class P25CallEvent extends CallEvent
{
private int mChannel;
private String mChannel;
private long mFrequency;
public P25CallEvent( CallEventType callEventType,
AliasList aliasList,
String fromID,
String toID,
int channel,
long frequency,
String details )
AliasList aliasList,
String fromID,
String toID,
String channel,
long frequency,
String details )
{
super( DecoderType.MPT1327, callEventType,
aliasList, fromID, toID, details );
@@ -42,11 +42,11 @@ public class P25CallEvent extends CallEvent
mFrequency = frequency;
}
private Alias getAlias( String mpt1327Talkgroup )
private Alias getAlias( String talkgroup )
{
if( hasAliasList() )
{
return getAliasList().getMPT1327Alias( mpt1327Talkgroup );
return getAliasList().getTalkgroupAlias( talkgroup );
}
return null;
@@ -65,7 +65,7 @@ public class P25CallEvent extends CallEvent
}
@Override
public int getChannel()
public String getChannel()
{
return mChannel;
}
@@ -86,7 +86,7 @@ public class P25CallEvent extends CallEvent
private String mFromID;
private String mToID;
private String mDetails;
private int mChannel;
private String mChannel;
private long mFrequency;
public Builder( CallEventType callEventType )
@@ -106,7 +106,7 @@ public class P25CallEvent extends CallEvent
return this;
}
public Builder channel( int channel )
public Builder channel( String channel )
{
mChannel = channel;
return this;

View File

@@ -22,12 +22,21 @@ import alias.Alias;
import alias.AliasList;
import audio.SquelchListener;
import audio.SquelchListener.SquelchState;
import controller.activity.CallEvent.CallEventType;
import controller.channel.ProcessingChain;
import controller.state.AuxChannelState;
import controller.state.ChannelState;
import decode.p25.message.P25Message;
import decode.p25.message.pdu.osp.control.RFSSStatusBroadcastExtended;
import decode.p25.message.tsbk.TSBKMessage;
import decode.p25.message.tsbk.osp.control.LocationRegistrationResponse;
import decode.p25.message.tsbk.osp.control.RFSSStatusBroadcast;
import decode.p25.message.tsbk.osp.control.UnitDeregistrationAcknowledge;
import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrant;
import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdate;
import decode.p25.message.tsbk.osp.voice.GroupVoiceChannelGrantUpdateExplicit;
import decode.p25.message.tsbk.osp.voice.UnitToUnitAnswerRequest;
import decode.p25.reference.Vendor;
public class P25ChannelState extends ChannelState
{
@@ -123,11 +132,118 @@ public class P25ChannelState extends ChannelState
{
mSiteAlias = alias.getName();
broadcastChange( ChangedAttribute.SITE_ALIAS );
}
else
{
mSiteAlias = null;
}
}
broadcastChange( ChangedAttribute.SITE );
broadcastChange( ChangedAttribute.SITE_ALIAS );
}
}
else if( message instanceof TSBKMessage )
{
TSBKMessage tsbk = (TSBKMessage)message;
setState( State.CONTROL );
if( tsbk.getVendor() == Vendor.STANDARD )
{
switch( tsbk.getOpcode() )
{
case GROUP_VOICE_CHANNEL_GRANT:
GroupVoiceChannelGrant gvcg = (GroupVoiceChannelGrant)tsbk;
mCallEventModel.add(
new P25CallEvent.Builder( CallEventType.CALL )
.aliasList( mAliasList )
.channel( gvcg.getChannelIdentifier() + "-" + gvcg.getChannel() )
.details( ( gvcg.isEncrypted() ? "ENCRYPTED" : "" ) +
( gvcg.isEmergency() ? " EMERGENCY" : "") )
.frequency( gvcg.getDownlinkFrequency() )
.from( gvcg.getSourceAddress() )
.to( gvcg.getGroupAddress() )
.build() );
break;
case GROUP_VOICE_CHANNEL_GRANT_UPDATE:
GroupVoiceChannelGrantUpdate gvcgu =
(GroupVoiceChannelGrantUpdate)tsbk;
mCallEventModel.add(
new P25CallEvent.Builder( CallEventType.CALL )
.aliasList( mAliasList )
.channel( gvcgu.getChannelID1() + "-" + gvcgu.getChannelNumber1() )
.details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) +
( gvcgu.isEmergency() ? " EMERGENCY" : "") )
.frequency( gvcgu.getDownlinkFrequency() )
.to( gvcgu.getGroupAddress1() )
.build() );
if( gvcgu.getChannelNumber2() != 0 )
{
mCallEventModel.add(
new P25CallEvent.Builder( CallEventType.CALL )
.aliasList( mAliasList )
.channel( gvcgu.getChannelID2() + "-" + gvcgu.getChannelNumber2() )
.details( ( gvcgu.isEncrypted() ? "ENCRYPTED" : "" ) +
( gvcgu.isEmergency() ? " EMERGENCY" : "") )
.frequency( gvcgu.getDownlinkFrequency() )
.to( gvcgu.getGroupAddress2() )
.build() );
}
break;
case GROUP_VOICE_CHANNEL_GRANT_UPDATE_EXPLICIT:
GroupVoiceChannelGrantUpdateExplicit gvcgue =
(GroupVoiceChannelGrantUpdateExplicit)tsbk;
mCallEventModel.add(
new P25CallEvent.Builder( CallEventType.CALL )
.aliasList( mAliasList )
.channel( gvcgue.getTransmitChannelID() + "-" +
gvcgue.getTransmitChannelNumber() )
.details( ( gvcgue.isEncrypted() ? "ENCRYPTED" : "" ) +
( gvcgue.isEmergency() ? " EMERGENCY" : "") )
.frequency( gvcgue.getDownlinkFrequency() )
.to( gvcgue.getGroupAddress() )
.build() );
break;
case UNIT_TO_UNIT_ANSWER_REQUEST:
UnitToUnitAnswerRequest utuar =
(UnitToUnitAnswerRequest)tsbk;
mCallEventModel.add(
new P25CallEvent.Builder( CallEventType.PAGE )
.aliasList( mAliasList )
.details( ( utuar.isEmergency() ? "EMERGENCY" : "" ) )
.from( utuar.getSourceAddress() )
.to( utuar.getTargetAddress() )
.build() );
break;
case LOCATION_REGISTRATION_RESPONSE:
LocationRegistrationResponse lrr =
(LocationRegistrationResponse)tsbk;
mCallEventModel.add(
new P25CallEvent.Builder( CallEventType.REGISTER )
.aliasList( mAliasList )
.details( "REG:" + lrr.getResponse().name() +
" SITE: " + lrr.getRFSSID() + "-" +
lrr.getSiteID() )
.from( lrr.getTargetAddress() )
.to( lrr.getGroupAddress() )
.build() );
break;
case UNIT_DEREGISTRATION_ACKNOWLEDGE:
UnitDeregistrationAcknowledge udr =
(UnitDeregistrationAcknowledge)tsbk;
mCallEventModel.add(
new P25CallEvent.Builder( CallEventType.DEREGISTER )
.aliasList( mAliasList )
.from( udr.getSourceID() )
.build() );
break;
}
}
}
}

View File

@@ -45,7 +45,7 @@ public class P25Panel extends ChannelStatePanel
private JLabel mSystem = new JLabel( "SYS:" );
private JLabel mSite = new JLabel( "Site:" );
private JLabel mSiteAlias = new JLabel( "WACN:" );
private JLabel mSiteAlias = new JLabel( "" );
public P25Panel( Channel channel )

View File

@@ -1,17 +1,20 @@
package decode.p25;
import java.util.ArrayList;
import java.util.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import bits.BitSetBuffer;
import dsp.fsk.Dibit;
import dsp.fsk.P25MessageFramer;
public class TrellisHalfRate
{
private final static Logger mLog = LoggerFactory.getLogger( TrellisHalfRate.class );
public final static int MAX_ERROR_THRESHOLD = 7;
private final static Logger mLog =
LoggerFactory.getLogger( TrellisHalfRate.class );
private ArrayList<ConstellationNode> mConstellationNodes =
new ArrayList<ConstellationNode>();
@@ -94,7 +97,7 @@ public class TrellisHalfRate
return sb.toString();
}
public void decode( BitSetBuffer message, int start, int end )
public boolean decode( BitSetBuffer message, int start, int end )
{
/* load each of the nodes with de-interleaved constellations */
for( int index = 0; index < 49; index++ )
@@ -104,20 +107,24 @@ public class TrellisHalfRate
mConstellationNodes.get( index ).setConstellation( c );
}
// mLog.debug( constellationsToString( " LOADED: ") );
//
/* test to see if constellations are correct - otherwise correct them */
ConstellationNode firstNode = mConstellationNodes.get( 0 );
if( !firstNode.hasStateDibit( Dibit.D00_PLUS_1 ) || !firstNode.isCorrect() )
{
firstNode.correctTo( Dibit.D00_PLUS_1 );
}
int errorCount = firstNode.getErrorCount();
// mLog.debug( constellationsToString( "CORRECTD: " ) );
//
// mLog.debug( inputsToString() );
//
if( errorCount > 0 )
{
if( errorCount < MAX_ERROR_THRESHOLD )
{
/* repair the errors */
firstNode.correctTo( Dibit.D00_PLUS_1 );
}
else
{
return false;
}
}
/* clear constellations from original message */
message.clear( start, end );
@@ -137,6 +144,8 @@ public class TrellisHalfRate
message.set( start + ( index * 2 ) + 1 );
}
}
return true;
}
private Constellation getConstellation( BitSetBuffer message, int index )
@@ -301,24 +310,18 @@ public class TrellisHalfRate
* @return - true - all node connections to the right are correct
* false - one or more nodes to the right are incorrect
*/
public boolean isCorrect()
public int getErrorCount()
{
if( mCorrect )
{
return mCorrect;
}
if( mConnectedNode == null )
{
mCorrect = true;
return 0;
}
else
{
mCorrect = mConnectedNode.isCorrect() &&
mConstellation.getInput() == mConnectedNode.getStateDibit();
}
mCorrect = mConstellation.getInput() == mConnectedNode.getStateDibit();
return mCorrect;
return mConnectedNode.getErrorCount() + ( mCorrect ? 0 : 1 );
}
public Dibit getStateDibit()
@@ -495,15 +498,17 @@ public class TrellisHalfRate
*/
public static void main( String[] args )
{
String original = "00100110000001111000101111110011111000011110100111100001000100100010110000001101100100100010001000100010001000101100111000100010111110001000100010001000100010001000100010001000100010001000100010001000100010001000100010001000101100001110000101000110011100001110";
BitSetBuffer buffer = new BitSetBuffer( original.length() );
Random random = new Random();
BitSetBuffer buffer = new BitSetBuffer( 196 );
try
{
for( int x = 0; x < original.length(); x++ )
for( int x = 0; x < 196; x++ )
{
if( original.substring( x, x + 1 ).contentEquals( "0" ) )
int number = random.nextInt( 2 );
if( number == 1 )
{
buffer.add( false );
}
@@ -518,12 +523,11 @@ public class TrellisHalfRate
e.printStackTrace();
}
mLog.debug( "ORIGINAL: " + original );
mLog.debug( " BUFFER: " + buffer.toString() );
TrellisHalfRate t = new TrellisHalfRate();
t.decode( buffer, 64, original.length() );
t.decode( buffer, 0, 196 );
mLog.debug( " DECODED: " + buffer.toString() );
mLog.debug( "Finished!" );
}

View File

@@ -12,7 +12,7 @@ public class P25MessageFilterSet extends FilterSet<Message>
addFilter( new PDUMessageFilter() );
addFilter( new TDUMessageFilter() );
addFilter( new TSBKMessageFilter() );
addFilter( new TSBKMessageFilterSet() );
}
@Override

View File

@@ -1,54 +0,0 @@
package decode.p25.message.filter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import message.Message;
import decode.p25.message.tsbk.TSBKMessage;
import decode.p25.reference.Opcode;
import filter.Filter;
import filter.FilterElement;
public class TSBKMessageFilter extends Filter<Message>
{
private HashMap<Opcode,FilterElement<Opcode>> mFilterElements =
new HashMap<Opcode,FilterElement<Opcode>>();
public TSBKMessageFilter()
{
super( "TSBK Trunking Signalling Block" );
for( Opcode opcode: Opcode.values() )
{
mFilterElements.put( opcode, new FilterElement<Opcode>( opcode ) );
}
}
@Override
public boolean passes( Message message )
{
if( mEnabled && canProcess( message ) )
{
TSBKMessage tsbk = (TSBKMessage)message;
Opcode opcode = tsbk.getOpcode();
return mFilterElements.get( opcode ).isEnabled();
}
return false;
}
@Override
public boolean canProcess( Message message )
{
return message instanceof TSBKMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mFilterElements.values() );
}
}

View File

@@ -0,0 +1,200 @@
package decode.p25.message.filter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import message.Message;
import decode.p25.message.tsbk.TSBKMessage;
import decode.p25.message.tsbk.motorola.MotorolaOpcode;
import decode.p25.message.tsbk.vendor.VendorOpcode;
import decode.p25.reference.Opcode;
import decode.p25.reference.Vendor;
import filter.Filter;
import filter.FilterElement;
import filter.FilterSet;
public class TSBKMessageFilterSet extends FilterSet<Message>
{
public TSBKMessageFilterSet()
{
super( "TSBK Trunking Signalling Block" );
addFilter( new StandardOpcodeFilter() );
addFilter( new MotorolaOpcodeFilter() );
addFilter( new VendorOpcodeFilter() );
}
// @Override
// public boolean passes( Message message )
// {
// if( mEnabled && canProcess( message ) )
// {
// TSBKMessage tsbk = (TSBKMessage)message;
//
// switch( tsbk.getVendor() )
// {
// case STANDARD:
// return
// }
// Opcode opcode = tsbk.getOpcode();
//
// return mFilterElements.get( opcode ).isEnabled();
// }
//
// return false;
// }
@Override
public boolean canProcess( Message message )
{
return message instanceof TSBKMessage;
}
public class StandardOpcodeFilter extends Filter<Message>
{
private HashMap<Opcode,FilterElement<Opcode>> mStandardElements =
new HashMap<Opcode,FilterElement<Opcode>>();
public StandardOpcodeFilter()
{
super( "Standard Opcode Filter" );
for( Opcode opcode: Opcode.values() )
{
if( opcode != Opcode.UNKNOWN )
{
mStandardElements.put( opcode,
new FilterElement<Opcode>( opcode ) );
}
}
}
@Override
public boolean passes( Message message )
{
if( mEnabled && canProcess( message ) )
{
Opcode opcode = ((TSBKMessage)message).getOpcode();
if( mStandardElements.containsKey( opcode ) )
{
return mStandardElements.get( opcode ).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess( Message message )
{
return ((TSBKMessage)message).getVendor() == Vendor.STANDARD;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mStandardElements.values() );
}
}
public class MotorolaOpcodeFilter extends Filter<Message>
{
private HashMap<MotorolaOpcode,FilterElement<MotorolaOpcode>> mMotorolaElements =
new HashMap<MotorolaOpcode,FilterElement<MotorolaOpcode>>();
public MotorolaOpcodeFilter()
{
super( "Motorola Opcode Filter" );
for( MotorolaOpcode opcode: MotorolaOpcode.values() )
{
if( opcode != MotorolaOpcode.UNKNOWN )
{
mMotorolaElements.put( opcode,
new FilterElement<MotorolaOpcode>( opcode ) );
}
}
}
@Override
public boolean passes( Message message )
{
if( mEnabled && canProcess( message ) )
{
MotorolaOpcode opcode = ((TSBKMessage)message).getMotorolaOpcode();
if( mMotorolaElements.containsKey( opcode ) )
{
return mMotorolaElements.get( opcode ).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess( Message message )
{
return ((TSBKMessage)message).getVendor() == Vendor.MOTOROLA;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mMotorolaElements.values() );
}
}
public class VendorOpcodeFilter extends Filter<Message>
{
private HashMap<VendorOpcode,FilterElement<VendorOpcode>> mVendorElements =
new HashMap<VendorOpcode,FilterElement<VendorOpcode>>();
public VendorOpcodeFilter()
{
super( "Vendor Opcode Filter" );
for( VendorOpcode opcode: VendorOpcode.values() )
{
if( opcode != VendorOpcode.UNKNOWN )
{
mVendorElements.put( opcode,
new FilterElement<VendorOpcode>( opcode ) );
}
}
}
@Override
public boolean passes( Message message )
{
if( mEnabled && canProcess( message ) )
{
MotorolaOpcode opcode = ((TSBKMessage)message).getMotorolaOpcode();
if( mVendorElements.containsKey( opcode ) )
{
return mVendorElements.get( opcode ).isEnabled();
}
}
return false;
}
@Override
public boolean canProcess( Message message )
{
Vendor vendor = ((TSBKMessage)message).getVendor();
return vendor != Vendor.STANDARD && vendor != Vendor.MOTOROLA;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>( mVendorElements.values() );
}
}
}

View File

@@ -2,15 +2,20 @@ package decode.p25.message.tsbk;
import alias.AliasList;
import bits.BitSetBuffer;
import decode.p25.message.tsbk.osp.control.IdentifierUpdate;
import decode.p25.message.tsbk.osp.control.IdentifierUpdateReceiver;
import decode.p25.reference.DataUnitID;
public abstract class ChannelGrant extends ServiceMessage
public abstract class ChannelGrant extends ServiceMessage
implements IdentifierUpdateReceiver
{
public static final int[] PRIORITY = { 85,86,87 };
public static final int[] CHANNEL_ID = { 88,89,90,91 };
public static final int[] CHANNEL_NUMBER = { 92,93,94,95,96,97,98,99,100,
101,102,103 };
private IdentifierUpdate mIdentifierUpdate;
public ChannelGrant( BitSetBuffer message,
DataUnitID duid,
AliasList aliasList )
@@ -26,13 +31,40 @@ public abstract class ChannelGrant extends ServiceMessage
return mMessage.getInt( PRIORITY );
}
public int getChannelID()
public int getChannelIdentifier()
{
return mMessage.getInt( CHANNEL_ID );
}
public int getChannelNumber()
public int getChannel()
{
return mMessage.getInt( CHANNEL_NUMBER );
}
@Override
public void setIdentifierMessage( int identifier, IdentifierUpdate message )
{
mIdentifierUpdate = message;
}
@Override
public int[] getIdentifiers()
{
int[] identifiers = new int[ 1 ];
identifiers[ 0 ] = getChannelIdentifier();
return identifiers;
}
public long getDownlinkFrequency()
{
return calculateDownlink( mIdentifierUpdate, getChannel() );
}
public long getUplinkFrequency()
{
return calculateUplink( mIdentifierUpdate, getChannel() );
}
}

View File

@@ -41,15 +41,19 @@ public abstract class GroupChannelGrant extends ChannelGrant
sb.append( " EMERGENCY" );
}
sb.append( " CHAN:" );
sb.append( getChannelID() + "/" + getChannelNumber() );
sb.append( " FROM:" );
sb.append( " SOURCE UNIT:" );
sb.append( getSourceAddress() );
sb.append( " GRP:" );
sb.append( " GROUP:" );
sb.append( getGroupAddress() );
sb.append( " CHAN:" );
sb.append( getChannelIdentifier() + "-" + getChannel() );
sb.append( " DN:" + getDownlinkFrequency() );
sb.append( " UP:" + getUplinkFrequency() );
return sb.toString();
}

View File

@@ -2,6 +2,7 @@ package decode.p25.message.tsbk;
import alias.AliasList;
import bits.BitSetBuffer;
import decode.p25.message.tsbk.osp.control.IdentifierUpdate;
import decode.p25.reference.DataUnitID;
public abstract class GroupChannelGrantExplicit extends ChannelGrant
@@ -15,6 +16,9 @@ public abstract class GroupChannelGrantExplicit extends ChannelGrant
public static final int[] GROUP_ADDRESS = { 128,129,130,131,132,133,134,135,
136,137,138,139,140,141,142,143 };
private IdentifierUpdate mTransmitIdentifierUpdate;
private IdentifierUpdate mReceiveIdentifierUpdate;
public GroupChannelGrantExplicit( BitSetBuffer message,
DataUnitID duid,
AliasList aliasList )
@@ -69,4 +73,38 @@ public abstract class GroupChannelGrantExplicit extends ChannelGrant
{
return mMessage.getHex( GROUP_ADDRESS, 4 );
}
@Override
public void setIdentifierMessage( int identifier, IdentifierUpdate message )
{
if( identifier == getTransmitChannelID() )
{
mTransmitIdentifierUpdate = message;
}
if( identifier == getReceiveChannelID() )
{
mReceiveIdentifierUpdate = message;
}
}
@Override
public int[] getIdentifiers()
{
int[] identifiers = new int[ 2 ];
identifiers[ 0 ] = getTransmitChannelID();
identifiers[ 1 ] = getReceiveChannelID();
return identifiers;
}
public long getDownlinkFrequency()
{
return calculateDownlink( mTransmitIdentifierUpdate, getChannel() );
}
public long getUplinkFrequency()
{
return calculateUplink( mReceiveIdentifierUpdate, getChannel() );
}
}

View File

@@ -3,7 +3,8 @@ package decode.p25.message.tsbk;
import alias.AliasList;
import bits.BitSetBuffer;
import decode.p25.message.P25Message;
import decode.p25.message.tsbk.osp.control.IdentifierUpdate;
import decode.p25.message.tsbk.motorola.MotorolaOpcode;
import decode.p25.message.tsbk.vendor.VendorOpcode;
import decode.p25.reference.DataUnitID;
import decode.p25.reference.Opcode;
import decode.p25.reference.Vendor;
@@ -56,6 +57,16 @@ public class TSBKMessage extends P25Message
return Opcode.fromValue( mMessage.getInt( OPCODE ) );
}
public MotorolaOpcode getMotorolaOpcode()
{
return MotorolaOpcode.fromValue( mMessage.getInt( OPCODE ) );
}
public VendorOpcode getVendorOpcode()
{
return VendorOpcode.fromValue( mMessage.getInt( OPCODE ) );
}
@Override
public String getMessage()
{

View File

@@ -196,9 +196,9 @@ public class TSBKMessageFactory
switch( motorolaOpcode )
{
case CHANNEL_CWID_UPDATE:
case OP0B:
return new ChannelCWIDUpdate( message, duid, aliasList );
case SYSTEM_LOADING:
case OP09:
return new SystemLoading( message, duid, aliasList );
default:
}

View File

@@ -34,7 +34,7 @@ public class ChannelCWIDUpdate extends MotorolaTSBKMessage
@Override
public String getEventType()
{
return MotorolaOpcode.CHANNEL_CWID_UPDATE.getDescription();
return MotorolaOpcode.OP0B.getDescription();
}
public String getMessage()
@@ -58,94 +58,31 @@ public class ChannelCWIDUpdate extends MotorolaTSBKMessage
public String getCWID()
{
StringBuilder sb = new StringBuilder();
int char1 = mMessage.getInt( CHARACTER_1 );
if( char1 != 0 )
{
sb.append( (char)( char1 + 46 ) );
}
else
{
return null;
}
int char2 = mMessage.getInt( CHARACTER_2 );
if( char2 != 0 )
{
sb.append( (char)( char2 + 46 ) );
}
else
{
return sb.toString();
}
int char3 = mMessage.getInt( CHARACTER_3 );
if( char3 != 0 )
{
sb.append( (char)( char3 + 46 ) );
}
else
{
return sb.toString();
}
int char4 = mMessage.getInt( CHARACTER_4 );
if( char4 != 0 )
{
sb.append( (char)( char4 + 46 ) );
}
else
{
return sb.toString();
}
int char5 = mMessage.getInt( CHARACTER_5 );
if( char5 != 0 )
{
sb.append( (char)( char5 + 46 ) );
}
else
{
return sb.toString();
}
int char6 = mMessage.getInt( CHARACTER_6 );
if( char6 != 0 )
{
sb.append( (char)( char6 + 46 ) );
}
else
{
return sb.toString();
}
int char7 = mMessage.getInt( CHARACTER_7 );
if( char7 != 0 )
{
sb.append( (char)( char7 + 46 ) );
}
else
{
return sb.toString();
}
int char8 = mMessage.getInt( CHARACTER_8 );
if( char8 != 0 )
{
sb.append( (char)( char8 + 46 ) );
}
sb.append( getCharacter( CHARACTER_1 ) );
sb.append( getCharacter( CHARACTER_2 ) );
sb.append( getCharacter( CHARACTER_3 ) );
sb.append( getCharacter( CHARACTER_4 ) );
sb.append( getCharacter( CHARACTER_5 ) );
sb.append( getCharacter( CHARACTER_6 ) );
sb.append( getCharacter( CHARACTER_7 ) );
sb.append( getCharacter( CHARACTER_8 ) );
return sb.toString();
}
private String getCharacter( int[] field )
{
int value = mMessage.getInt( field );
if( value != 0 )
{
return String.valueOf( (char)( value + 43 ) );
}
return null;
}
public int getIdentifier()
{
return mMessage.getInt( IDENTIFIER );

View File

@@ -2,9 +2,70 @@ package decode.p25.message.tsbk.motorola;
public enum MotorolaOpcode
{
UNKNOWN_OPCODE_5( "MOTO OPCODE 05 ", "Unknown Opcode 0x05", 0x05 ),
SYSTEM_LOADING( "SYSTEM_LOADING ", "System Loading", 0x09 ),
CHANNEL_CWID_UPDATE( "CHAN_CWID_UPDATE", "Channel FCC Callsign Update", 0x0B ),
OP00( "OP00:UNKNOWN ", "Opcode 0x00 Unknown", 0x00 ),
OP01( "OP01:UNKNOWN ", "Opcode 0x01 Unknown", 0x01 ),
OP02( "OP02:UNKNOWN ", "Opcode 0x02 Unknown", 0x02 ),
OP03( "OP03:UNKNOWN ", "Opcode 0x03 Unknown", 0x03 ),
OP04( "OP04:UNKNOWN ", "Opcode 0x04 Unknown", 0x04 ),
OP05( "OP05:UNKNOWN ", "Opcode 0x05 Unknown", 0x05 ),
OP06( "OP06:UNKNOWN ", "Opcode 0x06 Unknown", 0x06 ),
OP07( "OP07:UNKNOWN ", "Opcode 0x07 Unknown", 0x07 ),
OP08( "OP08:UNKNOWN ", "Opcode 0x08 Unknown", 0x08 ),
OP09( "OP09:SYSTEM_LOAD", "Opcode 0x09 System Loading", 0x09 ),
OP0A( "OP0A:UNKNOWN ", "Opcode 0x0A Unknown", 0x0A ),
OP0B( "OP0B:CWID_UPDATE", "Opcode 0x0B FCC Callsign", 0x0B ),
OP0C( "OP0C:UNKNOWN ", "Opcode 0x0C Unknown", 0x0C ),
OP0D( "OP0D:UNKNOWN ", "Opcode 0x0D Unknown", 0x0D ),
OP0E( "OP0E:UNKNOWN ", "Opcode 0x0E Unknown", 0x0E ),
OP0F( "OP0F:UNKNOWN ", "Opcode 0x0F Unknown", 0x0F ),
OP10( "OP10:UNKNOWN ", "Opcode 0x10 Unknown", 0x10 ),
OP11( "OP11:UNKNOWN ", "Opcode 0x11 Unknown", 0x11 ),
OP12( "OP12:UNKNOWN ", "Opcode 0x12 Unknown", 0x12 ),
OP13( "OP13:UNKNOWN ", "Opcode 0x13 Unknown", 0x13 ),
OP14( "OP14:UNKNOWN ", "Opcode 0x14 Unknown", 0x14 ),
OP15( "OP15:UNKNOWN ", "Opcode 0x15 Unknown", 0x15 ),
OP16( "OP16:UNKNOWN ", "Opcode 0x16 Unknown", 0x16 ),
OP17( "OP17:UNKNOWN ", "Opcode 0x17 Unknown", 0x17 ),
OP18( "OP18:UNKNOWN ", "Opcode 0x18 Unknown", 0x18 ),
OP19( "OP19:UNKNOWN ", "Opcode 0x19 Unknown", 0x19 ),
OP1A( "OP1A:UNKNOWN ", "Opcode 0x1A Unknown", 0x1A ),
OP1B( "OP1B:UNKNOWN ", "Opcode 0x1B Unknown", 0x1B ),
OP1C( "OP1C:UNKNOWN ", "Opcode 0x1C Unknown", 0x1C ),
OP1D( "OP1D:UNKNOWN ", "Opcode 0x1D Unknown", 0x1D ),
OP1E( "OP1E:UNKNOWN ", "Opcode 0x1E Unknown", 0x1E ),
OP1F( "OP1F:UNKNOWN ", "Opcode 0x1F Unknown", 0x1F ),
OP20( "OP20:UNKNOWN ", "Opcode 0x20 Unknown", 0x20 ),
OP21( "OP21:UNKNOWN ", "Opcode 0x21 Unknown", 0x21 ),
OP22( "OP22:UNKNOWN ", "Opcode 0x22 Unknown", 0x22 ),
OP23( "OP23:UNKNOWN ", "Opcode 0x23 Unknown", 0x23 ),
OP24( "OP24:UNKNOWN ", "Opcode 0x24 Unknown", 0x24 ),
OP25( "OP25:UNKNOWN ", "Opcode 0x25 Unknown", 0x25 ),
OP26( "OP26:UNKNOWN ", "Opcode 0x26 Unknown", 0x26 ),
OP27( "OP27:UNKNOWN ", "Opcode 0x27 Unknown", 0x27 ),
OP28( "OP28:UNKNOWN ", "Opcode 0x28 Unknown", 0x28 ),
OP29( "OP29:UNKNOWN ", "Opcode 0x29 Unknown", 0x29 ),
OP2A( "OP2A:UNKNOWN ", "Opcode 0x2A Unknown", 0x2A ),
OP2B( "OP2B:UNKNOWN ", "Opcode 0x2B Unknown", 0x2B ),
OP2C( "OP2C:UNKNOWN ", "Opcode 0x2C Unknown", 0x2C ),
OP2D( "OP2D:UNKNOWN ", "Opcode 0x2D Unknown", 0x2D ),
OP2E( "OP2E:UNKNOWN ", "Opcode 0x2E Unknown", 0x2E ),
OP2F( "OP2F:UNKNOWN ", "Opcode 0x2F Unknown", 0x2F ),
OP30( "OP30:UNKNOWN ", "Opcode 0x30 Unknown", 0x30 ),
OP31( "OP31:UNKNOWN ", "Opcode 0x31 Unknown", 0x31 ),
OP32( "OP32:UNKNOWN ", "Opcode 0x32 Unknown", 0x32 ),
OP33( "OP33:UNKNOWN ", "Opcode 0x33 Unknown", 0x33 ),
OP34( "OP34:UNKNOWN ", "Opcode 0x34 Unknown", 0x34 ),
OP35( "OP35:UNKNOWN ", "Opcode 0x35 Unknown", 0x35 ),
OP36( "OP36:UNKNOWN ", "Opcode 0x36 Unknown", 0x36 ),
OP37( "OP37:UNKNOWN ", "Opcode 0x37 Unknown", 0x37 ),
OP38( "OP38:UNKNOWN ", "Opcode 0x38 Unknown", 0x38 ),
OP39( "OP39:UNKNOWN ", "Opcode 0x39 Unknown", 0x39 ),
OP3A( "OP3A:UNKNOWN ", "Opcode 0x3A Unknown", 0x3A ),
OP3B( "OP3B:UNKNOWN ", "Opcode 0x3B Unknown", 0x3B ),
OP3C( "OP3C:UNKNOWN ", "Opcode 0x3C Unknown", 0x3C ),
OP3D( "OP3D:UNKNOWN ", "Opcode 0x3D Unknown", 0x3D ),
OP3E( "OP3E:UNKNOWN ", "Opcode 0x3E Unknown", 0x3E ),
OP3F( "OP3F:UNKNOWN ", "Opcode 0x3F Unknown", 0x3F ),
UNKNOWN( "UNKNOWN ", "Unknown", -1 );
private String mLabel;
@@ -23,6 +84,11 @@ public enum MotorolaOpcode
return mLabel;
}
public String toString()
{
return getLabel();
}
public String getDescription()
{
return mDescription;
@@ -35,14 +101,9 @@ public enum MotorolaOpcode
public static MotorolaOpcode fromValue( int value )
{
switch( value )
if( 0 <= value && value <= 0x3F )
{
case 0x05:
return UNKNOWN_OPCODE_5;
case 0x09:
return SYSTEM_LOADING;
case 0x0B:
return CHANNEL_CWID_UPDATE;
return MotorolaOpcode.values()[ value ];
}
return UNKNOWN;

View File

@@ -16,7 +16,7 @@ public class SystemLoading extends MotorolaTSBKMessage
@Override
public String getEventType()
{
return MotorolaOpcode.SYSTEM_LOADING.getLabel();
return MotorolaOpcode.OP09.getLabel();
}
@Override

View File

@@ -52,7 +52,7 @@ public class LocationRegistrationResponse extends TSBKMessage
public String getGroupAddress()
{
return mMessage.getHex( GROUP_ADDRESS, 6 );
return mMessage.getHex( GROUP_ADDRESS, 4 );
}
public String getRFSSID()

View File

@@ -54,7 +54,8 @@ public class TimeAndDateAnnouncement extends TSBKMessage
{
sb.append( " DATE-TIME:" );
}
sb.append( " " );
sb.append( mTimeFormatter.format( new Date( getDateTimestamp() ) ) );
return sb.toString();

View File

@@ -20,4 +20,5 @@ public class GroupVoiceChannelGrant extends GroupChannelGrant
{
return Opcode.GROUP_VOICE_CHANNEL_GRANT.getDescription();
}
}

View File

@@ -30,7 +30,7 @@ public class TelephoneInterconnectVoiceChannelGrant extends UnitChannelGrant
}
sb.append( " CHAN:" );
sb.append( getChannelID() + "/" + getChannelNumber() );
sb.append( getChannelIdentifier() + "/" + getChannel() );
sb.append( " CALL TIMER:" );
sb.append( getCallTimer() );

View File

@@ -3,6 +3,7 @@ package decode.p25.message.tsbk.osp.voice;
import alias.AliasList;
import bits.BitSetBuffer;
import decode.p25.message.tsbk.UnitChannelGrant;
import decode.p25.message.tsbk.osp.control.IdentifierUpdate;
import decode.p25.reference.DataUnitID;
import decode.p25.reference.Opcode;
@@ -20,4 +21,28 @@ public class UnitToUnitVoiceChannelGrant extends UnitChannelGrant
{
return Opcode.UNIT_TO_UNIT_VOICE_CHANNEL_GRANT.getDescription();
}
public String getMessage()
{
StringBuilder sb = new StringBuilder();
sb.append( getMessageStub() );
if( isEmergency() )
{
sb.append( " EMERGENCY" );
}
sb.append( " SOURCE UNIT:" );
sb.append( getSourceAddress() );
sb.append( " TARGET UNIT:" );
sb.append( getTargetAddress() );
sb.append( " CHAN:" + getChannelIdentifier() + "-" + getChannel() );
sb.append( " DN:" + getDownlinkFrequency() );
sb.append( " UP:" + getUplinkFrequency() );
return sb.toString();
}
}

View File

@@ -0,0 +1,111 @@
package decode.p25.message.tsbk.vendor;
public enum VendorOpcode
{
OP00( "OPCODE 00 ", "VENDOR OPCODE 0x00", 0x00 ),
OP01( "OPCODE 01 ", "VENDOR OPCODE 0x01", 0x01 ),
OP02( "OPCODE 02 ", "VENDOR OPCODE 0x02", 0x02 ),
OP03( "OPCODE 03 ", "VENDOR OPCODE 0x03", 0x03 ),
OP04( "OPCODE 04 ", "VENDOR OPCODE 0x04", 0x04 ),
OP05( "OPCODE 05 ", "VENDOR OPCODE 0x05", 0x05 ),
OP06( "OPCODE 06 ", "VENDOR OPCODE 0x06", 0x06 ),
OP07( "OPCODE 07 ", "VENDOR OPCODE 0x07", 0x07 ),
OP08( "OPCODE 08 ", "VENDOR OPCODE 0x08", 0x08 ),
OP09( "OPCODE 09 ", "VENDOR OPCODE 0x09", 0x09 ),
OP0A( "OPCODE 0A ", "VENDOR OPCODE 0x0A", 0x0A ),
OP0B( "OPCODE 0B ", "VENDOR OPCODE 0x0B", 0x0B ),
OP0C( "OPCODE 0C ", "VENDOR OPCODE 0x0C", 0x0C ),
OP0D( "OPCODE 0D ", "VENDOR OPCODE 0x0D", 0x0D ),
OP0E( "OPCODE 0E ", "VENDOR OPCODE 0x0E", 0x0E ),
OP0F( "OPCODE 0F ", "VENDOR OPCODE 0x0F", 0x0F ),
OP10( "OPCODE 10 ", "VENDOR OPCODE 0x10", 0x10 ),
OP11( "OPCODE 11 ", "VENDOR OPCODE 0x11", 0x11 ),
OP12( "OPCODE 12 ", "VENDOR OPCODE 0x12", 0x12 ),
OP13( "OPCODE 13 ", "VENDOR OPCODE 0x13", 0x13 ),
OP14( "OPCODE 14 ", "VENDOR OPCODE 0x14", 0x14 ),
OP15( "OPCODE 15 ", "VENDOR OPCODE 0x15", 0x15 ),
OP16( "OPCODE 16 ", "VENDOR OPCODE 0x16", 0x16 ),
OP17( "OPCODE 17 ", "VENDOR OPCODE 0x17", 0x17 ),
OP18( "OPCODE 18 ", "VENDOR OPCODE 0x18", 0x18 ),
OP19( "OPCODE 19 ", "VENDOR OPCODE 0x19", 0x19 ),
OP1A( "OPCODE 1A ", "VENDOR OPCODE 0x1A", 0x1A ),
OP1B( "OPCODE 1B ", "VENDOR OPCODE 0x1B", 0x1B ),
OP1C( "OPCODE 1C ", "VENDOR OPCODE 0x1C", 0x1C ),
OP1D( "OPCODE 1D ", "VENDOR OPCODE 0x1D", 0x1D ),
OP1E( "OPCODE 1E ", "VENDOR OPCODE 0x1E", 0x1E ),
OP1F( "OPCODE 1F ", "VENDOR OPCODE 0x1F", 0x1F ),
OP20( "OPCODE 20 ", "VENDOR OPCODE 0x20", 0x20 ),
OP21( "OPCODE 21 ", "VENDOR OPCODE 0x21", 0x21 ),
OP22( "OPCODE 22 ", "VENDOR OPCODE 0x22", 0x22 ),
OP23( "OPCODE 23 ", "VENDOR OPCODE 0x23", 0x23 ),
OP24( "OPCODE 24 ", "VENDOR OPCODE 0x24", 0x24 ),
OP25( "OPCODE 25 ", "VENDOR OPCODE 0x25", 0x25 ),
OP26( "OPCODE 26 ", "VENDOR OPCODE 0x26", 0x26 ),
OP27( "OPCODE 27 ", "VENDOR OPCODE 0x27", 0x27 ),
OP28( "OPCODE 28 ", "VENDOR OPCODE 0x28", 0x28 ),
OP29( "OPCODE 29 ", "VENDOR OPCODE 0x29", 0x29 ),
OP2A( "OPCODE 2A ", "VENDOR OPCODE 0x2A", 0x2A ),
OP2B( "OPCODE 2B ", "VENDOR OPCODE 0x2B", 0x2B ),
OP2C( "OPCODE 2C ", "VENDOR OPCODE 0x2C", 0x2C ),
OP2D( "OPCODE 2D ", "VENDOR OPCODE 0x2D", 0x2D ),
OP2E( "OPCODE 2E ", "VENDOR OPCODE 0x2E", 0x2E ),
OP2F( "OPCODE 2F ", "VENDOR OPCODE 0x2F", 0x2F ),
OP30( "OPCODE 30 ", "VENDOR OPCODE 0x30", 0x30 ),
OP31( "OPCODE 31 ", "VENDOR OPCODE 0x31", 0x31 ),
OP32( "OPCODE 32 ", "VENDOR OPCODE 0x32", 0x32 ),
OP33( "OPCODE 33 ", "VENDOR OPCODE 0x33", 0x33 ),
OP34( "OPCODE 34 ", "VENDOR OPCODE 0x34", 0x34 ),
OP35( "OPCODE 35 ", "VENDOR OPCODE 0x35", 0x35 ),
OP36( "OPCODE 36 ", "VENDOR OPCODE 0x36", 0x36 ),
OP37( "OPCODE 37 ", "VENDOR OPCODE 0x37", 0x37 ),
OP38( "OPCODE 38 ", "VENDOR OPCODE 0x38", 0x38 ),
OP39( "OPCODE 39 ", "VENDOR OPCODE 0x39", 0x39 ),
OP3A( "OPCODE 3A ", "VENDOR OPCODE 0x3A", 0x3A ),
OP3B( "OPCODE 3B ", "VENDOR OPCODE 0x3B", 0x3B ),
OP3C( "OPCODE 3C ", "VENDOR OPCODE 0x3C", 0x3C ),
OP3D( "OPCODE 3D ", "VENDOR OPCODE 0x3D", 0x3D ),
OP3E( "OPCODE 3E ", "VENDOR OPCODE 0x3E", 0x3E ),
OP3F( "OPCODE 3F ", "VENDOR OPCODE 0x3F", 0x3F ),
UNKNOWN( "UNKNOWN ", "Unknown", -1 );
private String mLabel;
private String mDescription;
private int mCode;
private VendorOpcode( String label, String description, int code )
{
mLabel = label;
mDescription = description;
mCode = code;
}
public String getLabel()
{
return mLabel;
}
public String toString()
{
return getLabel();
}
public String getDescription()
{
return mDescription;
}
public int getCode()
{
return mCode;
}
public static VendorOpcode fromValue( int value )
{
if( 0 <= value && value <= 0x3F )
{
return VendorOpcode.values()[ value ];
}
return UNKNOWN;
}
}

View File

@@ -2,13 +2,13 @@ package decode.p25.reference;
public enum ServiceAccessPoint
{
SAP_0( "0", 0 ),
SAP_1( "1", 1 ),
SAP_2( "2", 2 ),
SAP_3( "3", 3 ),
SAP_4( "4", 4 ),
SAP_5( "5", 5 ),
SAP_6( "6", 6 ),
UNENCRYPTED_USER_DATA( "0", 0 ),
ENCRYPTED_USER_DATA( "1", 1 ),
CIRCUIT_DATA( "2", 2 ),
CIRCUIT_DATA_CONTROL( "3", 3 ),
PACKET_DATA( "4", 4 ),
ADDRESS_RESOLUTION_PROTOCOL( "5", 5 ),
SNDCP_PACKET_DATA_CONTROL( "6", 6 ),
SAP_7( "7", 7 ),
SAP_8( "8", 8 ),
SAP_9( "9", 9 ),
@@ -33,17 +33,17 @@ public enum ServiceAccessPoint
SAP_28( "28", 28 ),
SAP_29( "29", 29 ),
SAP_30( "30", 30 ),
SAP_31( "31", 31 ),
SAP_32( "32", 32 ),
SAP_33( "33", 33 ),
SAP_34( "34", 34 ),
SAP_35( "35", 35 ),
SAP_36( "36", 36 ),
SAP_37( "37", 37 ),
SAP_38( "38", 38 ),
SAP_39( "39", 39 ),
SAP_40( "40", 40 ),
SAP_41( "41", 41 ),
EXTENDED_ADDRESS( "31", 31 ),
REGISTRATION_AND_AUTHORIZATION( "32", 32 ),
CHANNEL_REASSIGNMENT( "33", 33 ),
SYSTEM_CONFIGURATION( "34", 34 ),
MR_LOOPBACK( "35", 35 ),
MR_STATISTICS( "36", 36 ),
MR_OUT_OF_SERVICE( "37", 37 ),
MR_PAGING( "38", 38 ),
MR_CONFIGURATION( "39", 39 ),
UNENCRYPTED_KEY_MANAGEMENT_MESSAGE( "40", 40 ),
ENCRYPTED_KEY_MANAGEMENT_MESSAGE( "41", 41 ),
SAP_42( "42", 42 ),
SAP_43( "43", 43 ),
SAP_44( "44", 44 ),

View File

@@ -24,7 +24,7 @@ import decode.DecoderType;
public class PassportCallEvent extends CallEvent
{
private int mChannel;
private String mChannel;
private long mFrequency;
private int mValidCallMessages = 1;
@@ -32,7 +32,7 @@ public class PassportCallEvent extends CallEvent
AliasList aliasList,
String fromID,
String toID,
int channel,
String channel,
long frequency,
String details )
{
@@ -99,7 +99,7 @@ public class PassportCallEvent extends CallEvent
}
@Override
public int getChannel()
public String getChannel()
{
return mChannel;
}
@@ -120,7 +120,7 @@ public class PassportCallEvent extends CallEvent
private String mFromID;
private String mToID;
private String mDetails;
private int mChannel;
private String mChannel;
private long mFrequency;
public Builder( CallEventType callEventType )
@@ -140,7 +140,7 @@ public class PassportCallEvent extends CallEvent
return this;
}
public Builder channel( int channel )
public Builder channel( String channel )
{
mChannel = channel;
return this;

View File

@@ -146,7 +146,7 @@ public class PassportChannelState extends ChannelState
mCurrentCallEvent = new PassportCallEvent
.Builder( CallEventType.CALL )
.aliasList( mAliasList )
.channel( mChannelNumber )
.channel( String.valueOf( mChannelNumber ) )
.frequency( passport.getLCNFrequency() )
.to( String.valueOf( passport.getTalkgroupID() ) )
.build();
@@ -179,7 +179,7 @@ public class PassportChannelState extends ChannelState
PassportCallEvent event = new PassportCallEvent
.Builder( CallEventType.CALL_DETECT )
.aliasList( mAliasList )
.channel( lcn )
.channel( String.valueOf( lcn ) )
.details( "Site: " + passport.getSite() )
.frequency( passport.getLCNFrequency() )
.to( tg )
@@ -220,7 +220,7 @@ public class PassportChannelState extends ChannelState
mCallEventModel.add( new PassportCallEvent
.Builder( CallEventType.REGISTER )
.aliasList( mAliasList )
.channel( passport.getLCN() )
.channel( String.valueOf( passport.getLCN() ) )
.frequency( passport.getLCNFrequency() )
.to( passport.getToID() )
.build() );

View File

@@ -12,6 +12,8 @@ import alias.AliasList;
import bits.BitSetBuffer;
import bits.BitSetFullException;
import bits.SyncPatternMatcher;
import crc.CRC;
import crc.CRCP25NetworkID;
import decode.p25.P25Interleave;
import decode.p25.TrellisHalfRate;
import decode.p25.message.P25Message;
@@ -218,13 +220,22 @@ public class P25MessageFramer implements Listener<Dibit>
switch( mDUID )
{
case NID:
int value = mMessage.getInt( P25Message.DUID );
DataUnitID duid = DataUnitID.fromValue( value );
if( duid != DataUnitID.UNKN )
CRC crc = CRCP25NetworkID.checkAndCorrect( mMessage );
if( crc != CRC.FAILED_CRC )
{
setDUID( duid );
int value = mMessage.getInt( P25Message.DUID );
DataUnitID duid = DataUnitID.fromValue( value );
if( duid != DataUnitID.UNKN )
{
setDUID( duid );
}
else
{
mComplete = true;
}
}
else
{
@@ -247,155 +258,187 @@ public class P25MessageFramer implements Listener<Dibit>
/* Remove interleaving */
P25Interleave.deinterleave( mMessage, PDU0_BEGIN, PDU0_END );
/* Remove trellis encoding */
mHalfRate.decode( mMessage, PDU0_BEGIN, PDU0_END );
setDUID( DataUnitID.PDU1 );
mMessage.setPointer( PDU1_BEGIN );
/* Remove trellis encoding - abort processing if we have an
* unsuccessful decode due to excessive errors */
if( mHalfRate.decode( mMessage, PDU0_BEGIN, PDU0_END ) )
{
setDUID( DataUnitID.PDU1 );
mMessage.setPointer( PDU1_BEGIN );
}
else
{
mComplete = true;
}
break;
case PDU1:
/* Remove interleaving */
P25Interleave.deinterleave( mMessage, PDU1_BEGIN, PDU1_END );
/* Remove trellis encoding */
mHalfRate.decode( mMessage, PDU1_BEGIN, PDU1_END );
if( mMessage.getInt( PDUMessage.BLOCKS_TO_FOLLOW ) == 1 )
/* Remove trellis encoding - abort processing if we have an
* unsuccessful decode due to excessive errors */
if( mHalfRate.decode( mMessage, PDU1_BEGIN, PDU1_END ) )
{
mMessage.setSize( PDU2_BEGIN );
PDUMessage pduMessage1 = PDUMessageFactory.getMessage(
mMessage.copy(), DataUnitID.PDU1, mAliasList );
if( mMessage.getInt( PDUMessage.BLOCKS_TO_FOLLOW ) == 1 )
{
mMessage.setSize( PDU2_BEGIN );
PDUMessage pduMessage1 = PDUMessageFactory.getMessage(
mMessage.copy(), DataUnitID.PDU1, mAliasList );
dispatch( pduMessage1 );
mComplete = true;
dispatch( pduMessage1 );
mComplete = true;
}
else
{
setDUID( DataUnitID.PDU2 );
mMessage.setPointer( PDU2_BEGIN );
}
}
else
{
setDUID( DataUnitID.PDU2 );
mMessage.setPointer( PDU2_BEGIN );
mComplete = true;
}
break;
case PDU2:
/* Remove interleaving */
P25Interleave.deinterleave( mMessage, PDU2_BEGIN, PDU2_END );
/* Remove trellis encoding */
mHalfRate.decode( mMessage, PDU2_BEGIN, PDU2_END );
if( mMessage.getInt( PDUMessage.BLOCKS_TO_FOLLOW ) == 2 )
/* Remove trellis encoding - abort processing if we have an
* unsuccessful decode due to excessive errors */
if( mHalfRate.decode( mMessage, PDU2_BEGIN, PDU2_END ) )
{
mMessage.setSize( PDU3_BEGIN );
PDUMessage pduMessage2 = PDUMessageFactory.getMessage(
mMessage.copy(), DataUnitID.PDU2, mAliasList );
if( mMessage.getInt( PDUMessage.BLOCKS_TO_FOLLOW ) == 2 )
{
mMessage.setSize( PDU3_BEGIN );
PDUMessage pduMessage2 = PDUMessageFactory.getMessage(
mMessage.copy(), DataUnitID.PDU2, mAliasList );
dispatch( pduMessage2 );
mComplete = true;
dispatch( pduMessage2 );
mComplete = true;
}
else
{
setDUID( DataUnitID.PDU3 );
mMessage.setPointer( PDU3_BEGIN );
}
}
else
{
setDUID( DataUnitID.PDU3 );
mMessage.setPointer( PDU3_BEGIN );
mComplete = true;
}
break;
case PDU3:
/* Remove interleaving */
P25Interleave.deinterleave( mMessage, PDU3_BEGIN, PDU3_END );
/* Remove trellis encoding */
mHalfRate.decode( mMessage, PDU3_BEGIN, PDU3_END );
mMessage.setSize( PDU3_DECODED_END );
PDUMessage pduMessage3 = PDUMessageFactory.getMessage(
mMessage.copy(), DataUnitID.PDU3, mAliasList );
/* Remove trellis encoding - abort processing if we have an
* unsuccessful decode due to excessive errors */
if( mHalfRate.decode( mMessage, PDU3_BEGIN, PDU3_END ) )
{
mMessage.setSize( PDU3_DECODED_END );
PDUMessage pduMessage3 = PDUMessageFactory.getMessage(
mMessage.copy(), DataUnitID.PDU3, mAliasList );
dispatch( pduMessage3 );
dispatch( pduMessage3 );
}
mComplete = true;
break;
case TDU:
mComplete = true;
dispatch( new TDUMessage( mMessage.copy(), mDUID, mAliasList ) );
mComplete = true;
break;
case TDULC:
mComplete = true;
dispatch( new P25Message( mMessage.copy(), mDUID, mAliasList ) );
mComplete = true;
break;
case TSBK1:
/* Remove interleaving */
P25Interleave.deinterleave( mMessage, TSBK_BEGIN, TSBK_END );
/* Remove trellis encoding */
mHalfRate.decode( mMessage, TSBK_BEGIN, TSBK_END );
BitSetBuffer tsbkBuffer1 = mMessage.copy();
tsbkBuffer1.setSize( TSBK_DECODED_END );
TSBKMessage tsbkMessage1 = TSBKMessageFactory.getMessage(
tsbkBuffer1, DataUnitID.TSBK1, mAliasList );
if( tsbkMessage1.isLastBlock() )
/* Remove trellis encoding - abort processing if we have an
* unsuccessful decode due to excessive errors */
if( mHalfRate.decode( mMessage, TSBK_BEGIN, TSBK_END ) )
{
mComplete = true;
BitSetBuffer tsbkBuffer1 = mMessage.copy();
tsbkBuffer1.setSize( TSBK_DECODED_END );
TSBKMessage tsbkMessage1 = TSBKMessageFactory.getMessage(
tsbkBuffer1, DataUnitID.TSBK1, mAliasList );
if( tsbkMessage1.isLastBlock() )
{
mComplete = true;
}
else
{
setDUID( DataUnitID.TSBK2 );
mMessage.setPointer( TSBK_BEGIN );
}
dispatch( tsbkMessage1 );
}
else
{
setDUID( DataUnitID.TSBK2 );
mMessage.setPointer( TSBK_BEGIN );
mComplete = true;
}
dispatch( tsbkMessage1 );
break;
case TSBK2:
/* Remove interleaving */
P25Interleave.deinterleave( mMessage, TSBK_BEGIN, TSBK_END );
/* Remove trellis encoding */
mHalfRate.decode( mMessage, TSBK_BEGIN, TSBK_END );
BitSetBuffer tsbkBuffer2 = mMessage.copy();
tsbkBuffer2.setSize( TSBK_DECODED_END );
TSBKMessage tsbkMessage2 = TSBKMessageFactory.getMessage(
tsbkBuffer2, DataUnitID.TSBK2, mAliasList );
if( tsbkMessage2.isLastBlock() )
/* Remove trellis encoding - abort processing if we have an
* unsuccessful decode due to excessive errors */
if( mHalfRate.decode( mMessage, TSBK_BEGIN, TSBK_END ) )
{
mComplete = true;
BitSetBuffer tsbkBuffer2 = mMessage.copy();
tsbkBuffer2.setSize( TSBK_DECODED_END );
TSBKMessage tsbkMessage2 = TSBKMessageFactory.getMessage(
tsbkBuffer2, DataUnitID.TSBK2, mAliasList );
if( tsbkMessage2.isLastBlock() )
{
mComplete = true;
}
else
{
setDUID( DataUnitID.TSBK3 );
mMessage.setPointer( TSBK_BEGIN );
}
dispatch( tsbkMessage2 );
}
else
{
setDUID( DataUnitID.TSBK3 );
mMessage.setPointer( TSBK_BEGIN );
mComplete = true;
}
dispatch( tsbkMessage2 );
break;
case TSBK3:
/* Remove interleaving */
P25Interleave.deinterleave( mMessage, TSBK_BEGIN, TSBK_END );
/* Remove trellis encoding */
mHalfRate.decode( mMessage, TSBK_BEGIN, TSBK_END );
/* Remove trellis encoding - abort processing if we have an
* unsuccessful decode due to excessive errors */
if( mHalfRate.decode( mMessage, TSBK_BEGIN, TSBK_END ) )
{
BitSetBuffer tsbkBuffer3 = mMessage.copy();
tsbkBuffer3.setSize( TSBK_DECODED_END );
TSBKMessage tsbkMessage3 = TSBKMessageFactory.getMessage(
tsbkBuffer3, DataUnitID.TSBK3, mAliasList );
BitSetBuffer tsbkBuffer3 = mMessage.copy();
tsbkBuffer3.setSize( TSBK_DECODED_END );
TSBKMessage tsbkMessage3 = TSBKMessageFactory.getMessage(
tsbkBuffer3, DataUnitID.TSBK3, mAliasList );
dispatch( tsbkMessage3 );
}
mComplete = true;
dispatch( tsbkMessage3 );
break;
case UNKN:
mComplete = true;