diff --git a/src/message/Message.java b/src/message/Message.java
index ac1ab775..9b6b7a17 100644
--- a/src/message/Message.java
+++ b/src/message/Message.java
@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
+ * Copyright (C) 2014-2016 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,52 +17,94 @@
******************************************************************************/
package message;
-
import java.util.Collections;
-import java.util.Date;
import java.util.List;
import map.Plottable;
-import module.decode.event.MessageDetailsProvider;
import alias.Alias;
-public abstract class Message implements MessageDetailsProvider
+public abstract class Message
{
protected long mTimeReceived;
- protected MessageType mType;
public Message()
- {
- this( MessageType.UN_KNWN );
- }
-
- public Message( MessageType type )
{
mTimeReceived = System.currentTimeMillis();
- mType = type;
}
-
- public abstract Plottable getPlottable();
- public abstract String toString();
-
- public abstract boolean isValid();
-
public long getTimeReceived()
{
return mTimeReceived;
}
-
- public Date getDateReceived()
+
+ /**
+ * Map plottable
+ */
+ public Plottable getPlottable()
{
- return new Date( mTimeReceived );
- }
-
- public MessageType getType()
- {
- return mType;
+//TODO: move this to an IPlottable interface that only gets implemented as needed
+ return null;
}
+ /**
+ * Decoded textual representation of the message
+ */
+ public abstract String toString();
+
+ /**
+ * Indicates if the message is valid and has passed crc/integrity checks
+ */
+ public abstract boolean isValid();
+
+ /**
+ * Status of the CRC check of the message
+ */
+ public abstract String getErrorStatus();
+
+ /**
+ * Parsed Message
+ * @return
+ */
+ public abstract String getMessage();
+
+ /**
+ * Raw ( 0 & 1 ) message bits
+ */
+ public abstract String getBinaryMessage();
+
+
+ /**
+ * Decoded protocol
+ */
+ public abstract String getProtocol();
+
+ /**
+ * Event - call, data, idle, etc.
+ */
+ public abstract String getEventType();
+
+ /**
+ * Formatted from identifier
+ */
+ public abstract String getFromID();
+
+ /**
+ * From identifier alias (from AliasManager)
+ */
+ public abstract Alias getFromIDAlias();
+
+ /**
+ * Formatted to identifier
+ */
+ public abstract String getToID();
+
+ /**
+ * To identifier alias (from AliasManager)
+ * @return
+ */
+ public abstract Alias getToIDAlias();
+
+
/**
* Provides a listing of aliases contained in the message.
*/
diff --git a/src/module/decode/event/MessageActivityModel.java b/src/module/decode/event/MessageActivityModel.java
index 65850196..390c4c59 100644
--- a/src/module/decode/event/MessageActivityModel.java
+++ b/src/module/decode/event/MessageActivityModel.java
@@ -19,6 +19,7 @@ package module.decode.event;
import java.awt.EventQueue;
import java.text.SimpleDateFormat;
+import java.util.Date;
import java.util.LinkedList;
import javax.swing.table.AbstractTableModel;
@@ -174,7 +175,7 @@ public class MessageActivityModel extends AbstractTableModel
switch( columnIndex )
{
case sTIME:
- return mSDFTime.format( message.getDateReceived() );
+ return mSDFTime.format( new Date( message.getTimeReceived() ) );
case sPROTOCOL:
return message.getProtocol();
case sERROR_STATUS:
diff --git a/src/module/decode/event/MessageDetailsProvider.java b/src/module/decode/event/MessageDetailsProvider.java
deleted file mode 100644
index bfce825e..00000000
--- a/src/module/decode/event/MessageDetailsProvider.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- ******************************************************************************/
-package module.decode.event;
-
-import alias.Alias;
-
-/**
- * Getter methods to support the Message Activity Table/Model
- */
-public interface MessageDetailsProvider
-{
- /**
- * Indicates if the message is valid and has passed crc/integrity checks
- */
- public abstract boolean isValid();
-
- /**
- * Status of the CRC check of the message
- */
- public abstract String getErrorStatus();
-
- /**
- * Parsed Message
- * @return
- */
- public abstract String getMessage();
-
- /**
- * Raw ( 0 & 1 ) message bits
- */
- public abstract String getBinaryMessage();
-
-
- /**
- * Decoded protocol
- */
- public abstract String getProtocol();
-
- /**
- * Event - call, data, idle, etc.
- */
- public abstract String getEventType();
-
- /**
- * Formatted from identifier
- */
- public abstract String getFromID();
-
- /**
- * From identifier alias (from AliasManager)
- */
- public abstract Alias getFromIDAlias();
-
- /**
- * Formatted to identifier
- */
- public abstract String getToID();
-
- /**
- * To identifier alias (from AliasManager)
- * @return
- */
- public abstract Alias getToIDAlias();
-
-}
diff --git a/src/module/decode/ltrnet/LTRCallEvent.java b/src/module/decode/ltrnet/LTRCallEvent.java
index 21a46afb..3bb553ca 100644
--- a/src/module/decode/ltrnet/LTRCallEvent.java
+++ b/src/module/decode/ltrnet/LTRCallEvent.java
@@ -51,17 +51,16 @@ public class LTRCallEvent extends CallEvent
*/
public boolean isMatchingTalkgroup( LTRNetMessage message )
{
- String talkgroup = message.getTalkgroupID();
-
- if( talkgroup != null && getToID() != null &&
- talkgroup.contentEquals( getToID() ) )
- {
- return true;
- }
-
- return false;
+ return isMatchingTalkgroup( message.getTalkgroupID() );
}
+ public boolean isMatchingTalkgroup( String talkgroup )
+ {
+ return talkgroup != null &&
+ getToID() != null &&
+ talkgroup.contentEquals( getToID() );
+ }
+
private Alias getAlias( String talkgroup )
{
if( hasAliasList() )
diff --git a/src/module/decode/ltrstandard/LTRStandardDecoderState.java b/src/module/decode/ltrstandard/LTRStandardDecoderState.java
index 94cd32b9..8664f38e 100644
--- a/src/module/decode/ltrstandard/LTRStandardDecoderState.java
+++ b/src/module/decode/ltrstandard/LTRStandardDecoderState.java
@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
- * Copyright (C) 2014,2015 Dennis Sheirer
+ * Copyright (C) 2014-2016 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,21 +17,31 @@
******************************************************************************/
package module.decode.ltrstandard;
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.TreeSet;
import message.Message;
import module.decode.DecoderType;
import module.decode.event.CallEvent.CallEventType;
import module.decode.ltrnet.LTRCallEvent;
+import module.decode.ltrstandard.message.CallEndMessage;
+import module.decode.ltrstandard.message.CallMessage;
+import module.decode.ltrstandard.message.IdleMessage;
+import module.decode.ltrstandard.message.LTRStandardMessage;
import module.decode.state.ChangedAttribute;
import module.decode.state.DecoderState;
import module.decode.state.DecoderStateEvent;
import module.decode.state.DecoderStateEvent.Event;
import module.decode.state.State;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import alias.Alias;
import alias.AliasList;
import audio.metadata.Metadata;
@@ -39,15 +49,17 @@ import audio.metadata.MetadataType;
public class LTRStandardDecoderState extends DecoderState
{
- private HashMap mActiveCalls = new HashMap();
- private HashSet mTalkgroupsFirstHeard = new HashSet();
- private TreeSet mTalkgroups = new TreeSet();
- private TreeSet mActiveLCNs = new TreeSet();
+ private final static Logger mLog =
+ LoggerFactory.getLogger( LTRStandardDecoderState.class );
+
+ private Map mActiveCalls = new HashMap<>();
+ private Set mTalkgroupsFirstHeard = new HashSet<>();
+ private Set mTalkgroups = new TreeSet<>();
private String mTalkgroup;
private Alias mTalkgroupAlias;
- private int mChannelNumber;
private long mFrequency;
+ private LCNTracker mLCNTracker = new LCNTracker();
public LTRStandardDecoderState( AliasList aliasList )
{
@@ -70,171 +82,126 @@ public class LTRStandardDecoderState extends DecoderState
{
}
- private LTRCallEvent getCurrentCallEvent()
- {
- return (LTRCallEvent)mCurrentCallEvent;
- }
-
@Override
public void receive( Message message )
{
- if( message.isValid() )
+ if( message.isValid() && message instanceof LTRStandardMessage )
{
- if( message instanceof LTRStandardOSWMessage )
+ switch( ((LTRStandardMessage)message).getMessageType() )
{
- LTRStandardOSWMessage ltr = ((LTRStandardOSWMessage)message);
-
- switch( ltr.getMessageType() )
- {
- case CA_STRT:
- if( 0 < ltr.getChannel() && ltr.getChannel() <=20 )
- {
- /* If we haven't captured the LCN yet ... */
- if( mChannelNumber == 0 )
- {
- mChannelNumber = ltr.getChannel();
- broadcast( ChangedAttribute.CHANNEL_NUMBER );
- }
+ case CA_STRT:
+ CallMessage start = (CallMessage)message;
+
+ int channel = start.getChannel();
+
+ mLCNTracker.processCallChannel( channel );
+
+ mLCNTracker.processFreeChannel( start.getFree() );
- /* If this call event is for this channel, then update
- * talkgroup and alias */
- if( ltr.getChannel() == mChannelNumber )
- {
- mTalkgroup = ltr.getTalkgroupID();
- broadcast( ChangedAttribute.TO_TALKGROUP );
-
- mTalkgroupAlias = ltr.getTalkgroupIDAlias();
- broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
-
- LTRCallEvent current = getCurrentCallEvent();
+ //Only process calls on this LCN, or call detects for
+ //talkgroups that are homed on this LCN
+ if( mLCNTracker.isValidChannel( channel ) &&
+ ( mLCNTracker.isCurrentChannel( channel ) ||
+ mLCNTracker.isCurrentChannel( start.getHomeRepeater() ) ) )
+ {
+ LTRCallEvent event = mActiveCalls.get( channel );
+
+ if( event == null ||
+ !event.isMatchingTalkgroup( start.getToID() ) )
+ {
+ //Check for different talkgroup
+ if( event != null )
+ {
+ event.end();
+ mActiveCalls.remove( channel );
+ }
- /* Detect when we haven't reset from the previous
- * call and now have a new call coming in with
- * a different talkgroup */
- if( current != null && isDifferentTalkgroup( mTalkgroup ) )
- {
- mCurrentCallEvent.end();
- broadcast( mCurrentCallEvent );
- mCurrentCallEvent = null;
- }
-
- if( current == null )
- {
- mCurrentCallEvent =
- new LTRCallEvent.Builder(
- DecoderType.LTR_STANDARD, CallEventType.CALL )
- .to( ltr.getTalkgroupID() )
- .aliasList( getAliasList() )
- .channel( String.valueOf( ltr.getChannel() ) )
- .frequency( mFrequency )
- .build();
+ boolean current = mLCNTracker.isCurrentChannel( channel );
+
+ event = new LTRCallEvent.Builder(
+ DecoderType.LTR_STANDARD,
+ current ? CallEventType.CALL : CallEventType.CALL_DETECT )
+ .to( start.getToID() )
+ .aliasList( getAliasList() )
+ .channel( start.getChannelFormatted() )
+ .frequency( current ? mFrequency : 0 )
+ .build();
+
+ mActiveCalls.put( channel, event );
+
+ broadcast( event );
+
+ if( mLCNTracker.isCurrentChannel( channel ) )
+ {
+ mTalkgroup = start.getToID();
+ broadcast( ChangedAttribute.TO_TALKGROUP );
+
+ processTalkgroup( mTalkgroup );
+
+ mTalkgroupAlias = start.getToIDAlias();
+ broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
- broadcast( mCurrentCallEvent );
-
- broadcast( new Metadata( MetadataType.TO,
- mTalkgroup, mTalkgroupAlias, true ) );
+ broadcast( new Metadata( MetadataType.TO,
+ mTalkgroup, mTalkgroupAlias, true ) );
+ }
+ }
+
+ broadcast( new DecoderStateEvent( this,
+ Event.CONTINUATION, State.CALL ) );
+ }
+ break;
+ case CA_ENDD:
+ CallEndMessage end = (CallEndMessage)message;
- broadcast( new DecoderStateEvent( this,
- Event.START, State.CALL ) );
- }
- else
- {
- broadcast( new DecoderStateEvent( this,
- Event.CONTINUATION, State.CALL ) );
- }
- }
- }
- break;
- case CA_ENDD:
- mTalkgroup = ltr.getTalkgroupID();
- broadcast( ChangedAttribute.TO_TALKGROUP );
+ int repeater = end.getHomeRepeater();
+
+ mLCNTracker.processCallChannel( repeater );
+
+ if( mLCNTracker.isCurrentChannel( repeater ) )
+ {
+ mTalkgroup = end.getToID();
+ broadcast( ChangedAttribute.TO_TALKGROUP );
+
+ processTalkgroup( mTalkgroup );
+
+ mTalkgroupAlias = end.getToIDAlias();
+ broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
- mTalkgroupAlias = ltr.getTalkgroupIDAlias();
- broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS );
+ LTRCallEvent event = mActiveCalls.remove( repeater );
+
+ if( event != null )
+ {
+ event.end();
+ broadcast( event );
+ broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) );
+ }
+ }
+ break;
+ case SY_IDLE:
+ IdleMessage idle = (IdleMessage)message;
- if( mCurrentCallEvent != null )
- {
- mCurrentCallEvent.end();
- broadcast( mCurrentCallEvent );
- broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) );
- mCurrentCallEvent = null;
- }
-
- int home = ltr.getHomeRepeater();
-
- if( 0 < home && home <= 20 )
- {
- String talkgroup = ltr.getTalkgroupID( false );
-
- if( mTalkgroupsFirstHeard.contains( talkgroup ) )
- {
- mTalkgroups.add( talkgroup );
- }
- else
- {
- mTalkgroupsFirstHeard.add( talkgroup );
- }
- }
+ int lcn = idle.getChannel();
- int channel = ltr.getChannel();
-
- if( 0 < channel && channel <= 20 )
- {
- if( channel == home )
- {
- if( mChannelNumber != channel )
- {
- mChannelNumber = channel;
- broadcast( ChangedAttribute.CHANNEL_NUMBER );
- }
-
- mActiveLCNs.add( channel );
- }
-
- int free = ltr.getFree();
-
- if( 0 < free && free <= 20 )
- {
- mActiveLCNs.add( free );
- }
- }
-
- break;
- case SY_IDLE:
- int idleChannel = ltr.getChannel();
-
- if( idleChannel == ltr.getHomeRepeater() &&
- idleChannel == ltr.getFree() )
- {
- if( mChannelNumber != idleChannel )
- {
- mChannelNumber = idleChannel;
- broadcast( ChangedAttribute.CHANNEL_NUMBER );
- }
-
- mActiveLCNs.add( idleChannel );
- }
- broadcast( new DecoderStateEvent( this,
- Event.CONTINUATION, State.IDLE ) );
- break;
- case UN_KNWN:
- default:
- break;
- }
+ mLCNTracker.processCallChannel( lcn );
+
+ break;
+ case UN_KNWN:
+ default:
+ break;
}
}
}
-
- /**
- * Indicates if the talkgroup is different than the talkgroup specified in
- * the current call event
- */
- private boolean isDifferentTalkgroup( String talkgroup )
+
+ private void processTalkgroup( String talkgroup )
{
- return talkgroup != null &&
- mCurrentCallEvent != null &&
- mCurrentCallEvent.getToID() != null &&
- !mCurrentCallEvent.getToID().contentEquals( talkgroup );
+ if( mTalkgroupsFirstHeard.contains( talkgroup ) )
+ {
+ mTalkgroups.add( talkgroup );
+ }
+ else
+ {
+ mTalkgroupsFirstHeard.add( talkgroup );
+ }
}
/**
@@ -245,8 +212,7 @@ public class LTRStandardDecoderState extends DecoderState
mActiveCalls.clear();
mTalkgroupsFirstHeard.clear();
mTalkgroups.clear();
- mActiveLCNs.clear();
- mChannelNumber = 0;
+ mLCNTracker.reset();
resetState();
}
@@ -256,13 +222,20 @@ public class LTRStandardDecoderState extends DecoderState
*/
private void resetState()
{
- if( mCurrentCallEvent != null )
+ for( Integer key: mActiveCalls.keySet() )
{
- mCurrentCallEvent.setEnd( System.currentTimeMillis() );
- broadcast( mCurrentCallEvent );
- mCurrentCallEvent = null;
+ LTRCallEvent event = mActiveCalls.get( key );
+
+ if( event != null )
+ {
+ event.end();
+
+ broadcast( event );
+ }
}
-
+
+ mActiveCalls.clear();
+
mTalkgroup = null;
broadcast( ChangedAttribute.TO_TALKGROUP );
@@ -282,12 +255,12 @@ public class LTRStandardDecoderState extends DecoderState
public boolean hasChannelNumber()
{
- return mChannelNumber != 0;
+ return mLCNTracker.getCurrentChannel() != 0;
}
public int getChannelNumber()
{
- return mChannelNumber;
+ return mLCNTracker.getCurrentChannel();
}
@Override
@@ -322,28 +295,28 @@ public class LTRStandardDecoderState extends DecoderState
sb.append( "Monitored LCN: " );
- if( mChannelNumber > 0 )
+ if( hasChannelNumber() )
{
- sb.append( mChannelNumber );
+ sb.append( getChannelNumber() );
}
else
{
- sb.append( "unknown" );
+ sb.append( "*Insufficient Data*" );
}
sb.append( "\n" );
- Integer[] lcns = mActiveLCNs.toArray( new Integer[0] );
-
sb.append( "Active LCNs:\t" );
+
+ List lcns = mLCNTracker.getActiveLCNs();
- if( lcns.length > 0 )
+ if( lcns.size() > 0 )
{
- sb.append( Arrays.toString( lcns ) );
+ sb.append( mLCNTracker.getActiveLCNs() );
}
else
{
- sb.append( "none" );
+ sb.append( "*Insufficient Data*" );
}
sb.append( "\n\n" );
@@ -355,19 +328,15 @@ public class LTRStandardDecoderState extends DecoderState
}
else
{
- Iterator it = mTalkgroups.iterator();
-
- while( it.hasNext() )
+ for( String talkgroup: mTalkgroups )
{
- String tgid = it.next();
-
sb.append( " " );
- sb.append( formatTalkgroup( tgid ) );
+ sb.append( talkgroup );
sb.append( " " );
if( hasAliasList() )
{
- Alias alias = getAliasList().getTalkgroupAlias( tgid );
+ Alias alias = getAliasList().getTalkgroupAlias( talkgroup );
if( alias != null )
{
@@ -376,30 +345,147 @@ public class LTRStandardDecoderState extends DecoderState
}
sb.append( "\n" );
+
}
}
return sb.toString();
}
- public static String formatTalkgroup( String talkgroup )
+ /**
+ * Tracks the set of call and free channels for a system in order to
+ * dynamically determine the current LCN and minimize false triggers from
+ * bogus decoded LTR messages. Tracks the number of occurances of each
+ * LCN and uses a dynamic threshold to determine LCN validity.
+ */
+ public class LCNTracker
{
- StringBuilder sb = new StringBuilder();
+ private static final int DEFAULT_COUNT = 10;
+ private int[] mCallLCNCounts;
+ private int[] mFreeLCNCounts;
+ private int mCallHighestCount;
+ private int mFreeHighestCount;
+ private int mCurrentLCN;
- if( talkgroup.length() == 6 )
+ public LCNTracker()
{
- sb.append( talkgroup.substring( 0, 1 ) );
- sb.append( "-" );
- sb.append( talkgroup.substring( 1, 3 ) );
- sb.append( "-" );
- sb.append( talkgroup.substring( 3, 6 ) );
-
- return sb.toString();
+ reset();
}
- else
+
+ public void logStatistics()
{
- return talkgroup;
+ for( int x = 1; x <= 20; x++ )
+ {
+ mLog.debug( "Call " + x + ": " + mCallLCNCounts[ x ] );
+ }
+ mLog.debug( "Call Highest Count: " + mCallHighestCount );
+ for( int x = 1; x <= 20; x++ )
+ {
+ mLog.debug( "Free " + x + ": " + mFreeLCNCounts[ x ] );
+ }
+ mLog.debug( "Free Highest Count: " + mFreeHighestCount );
+
+ mLog.debug( "Current LCN: " + mCurrentLCN );
+ }
+
+ public void reset()
+ {
+ //Dim channel arrays to 21 -- ignore the 0 index
+ mCallLCNCounts = new int[ 21 ];
+ mFreeLCNCounts = new int[ 21 ];
+ mCallHighestCount = 3;
+ mFreeHighestCount = DEFAULT_COUNT;
+ mCurrentLCN = 0;
+ }
+
+ public boolean isCurrentChannel( int channel )
+ {
+ if( mCurrentLCN == 0 )
+ {
+ return true;
+ }
+ else
+ {
+ return channel == mCurrentLCN;
+ }
+ }
+
+ public int getCurrentChannel()
+ {
+ return mCurrentLCN;
+ }
+
+ /**
+ * Indicates if the channel is valid based on the observed current and
+ * free channels reported by the system. Tracks the count of each
+ * reported free channel the highest free channel count is used as a
+ * threshold where a channel is valid when its count exceeds 20% of the
+ * highest free channel count. A channel is also valid when it is
+ * the currently monitored channel.
+ */
+ public boolean isValidChannel( int channel )
+ {
+ if( isCurrentChannel( channel ) )
+ {
+ return true;
+ }
+
+ int count = mFreeLCNCounts[ channel ];
+
+ int threshold = (int)( (double)mFreeHighestCount * 0.2 );
+
+ return count >= threshold;
+ }
+
+ public void processCallChannel( int channel )
+ {
+ if( 1 <= channel && channel <= 20 )
+ {
+ mCallLCNCounts[ channel ]++;
+
+ if( mCallLCNCounts[ channel ] > mCallHighestCount )
+ {
+ mCallHighestCount = mCallLCNCounts[ channel ];
+ mCurrentLCN = channel;
+
+ broadcast( ChangedAttribute.CHANNEL_NUMBER );
+ }
+ }
+ }
+
+ public void processFreeChannel( int channel )
+ {
+ if( 1 <= channel && channel <= 20 )
+ {
+ mFreeLCNCounts[ channel ]++;
+
+ if( mFreeLCNCounts[ channel ] > mFreeHighestCount )
+ {
+ mFreeHighestCount = mFreeLCNCounts[ channel ];
+ }
+ }
+ }
+
+ public List getActiveLCNs()
+ {
+ List active = new ArrayList<>();
+
+ if( mFreeHighestCount > DEFAULT_COUNT )
+ {
+ for( int x = 1; x <= 20; x++ )
+ {
+ if( mCurrentLCN != 0 && isCurrentChannel( x ) )
+ {
+ active.add( x );
+ }
+ else if( isValidChannel( x ) )
+ {
+ active.add( x );
+ }
+ }
+ }
+
+ return active;
}
}
-
}
diff --git a/src/module/decode/ltrstandard/LTRStandardISWMessage.java b/src/module/decode/ltrstandard/LTRStandardISWMessage.java
deleted file mode 100644
index 8f16ac1f..00000000
--- a/src/module/decode/ltrstandard/LTRStandardISWMessage.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- ******************************************************************************/
-package module.decode.ltrstandard;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import map.Plottable;
-import message.MessageDirection;
-import message.MessageType;
-import alias.Alias;
-import alias.AliasList;
-import bits.BinaryMessage;
-
-public class LTRStandardISWMessage extends LTRStandardMessage
-{
- private static final int sCALL_END = 31;
- private static final int sIDLE = 255;
-
- public LTRStandardISWMessage( BinaryMessage message, AliasList list )
- {
- super( message, MessageDirection.OSW, list );
- mMessageType = getType();
- }
-
- @Override
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
- sb.append( mDatestampFormatter.format(
- new Date( System.currentTimeMillis() ) ) );
- sb.append( " LTR OSW [" );
- sb.append( mCRC.getAbbreviation() );
- sb.append( "] " );
- sb.append( getMessage() );
- pad( sb, 100 );
- sb.append( mMessage.toString() );
-
- return sb.toString();
- }
-
- @Override
- public String getMessage()
- {
- StringBuilder sb = new StringBuilder();
-
- switch( mMessageType )
- {
- case SY_IDLE:
- sb.append( "IDLE AREA:" );
- sb.append( getArea() );
- sb.append( " LCN:" );
- sb.append( format( getChannel(), 2 ) );
- sb.append( " HOME:" );
- sb.append( format( getHomeRepeater(), 2 ) );
- sb.append( " GRP:" );
- sb.append( format( getGroup(), 3 ) );
- sb.append( " FREE:" );
- sb.append( format( getFree(), 2 ) );
- break;
- case CA_STRT:
- sb.append( "CALL LCN:" );
- sb.append( format( getChannel(), 2 ) );
- sb.append( " TG [ " );
- sb.append( getTalkgroupID() );
- sb.append( "/" );
- sb.append( getTalkgroupIDAlias() );
- sb.append( " ] FREE:" );
- sb.append( format( getFree(), 2 ) );
- break;
- case CA_ENDD:
- sb.append( "END* AREA:" );
- sb.append( getArea() );
- sb.append( " LCN:" );
- sb.append( format( getChannel(), 2 ) );
- sb.append( " TG [" );
- sb.append( getTalkgroupID() );
- sb.append( "/" );
- sb.append( getTalkgroupIDAlias() );
- sb.append( "] FREE:" );
- sb.append( format( getFree(), 2 ) );
- break;
- default:
- sb.append( "UNKNOWN A:" );
- sb.append( getArea() );
- sb.append( " C:" );
- sb.append( format( getChannel(), 2 ) );
- sb.append( " H:" );
- sb.append( format( getHomeRepeater(), 2 ) );
- sb.append( " G:" );
- sb.append( format( getGroup(), 3 ) );
- sb.append( " F:" );
- sb.append( format( getFree(), 2 ) );
- }
-
- return sb.toString();
- }
-
- public String getTalkgroupID()
- {
- return getTalkgroupID( true );
- }
-
- public String getTalkgroupID( boolean format )
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( getArea() );
-
- if( format )
- {
- sb.append( "-" );
- }
-
- sb.append( format( getHomeRepeater(), 2 ) );
-
- if( format )
- {
- sb.append( "-" );
- }
-
- sb.append( format( getGroup(), 3 ) );
-
- return sb.toString();
- }
-
- public Alias getTalkgroupIDAlias()
- {
- StringBuilder sb = new StringBuilder();
- sb.append( getArea() );
- sb.append( format( getHomeRepeater(), 2 ) );
- sb.append( format( getGroup(), 3 ) );
-
- return mAliasList.getTalkgroupAlias( sb.toString() );
- }
-
- public MessageType getType()
- {
- MessageType retVal = MessageType.UN_KNWN;
-
- int group = getGroup();
-
- if( group == sIDLE )
- {
- retVal = MessageType.SY_IDLE;
- }
- else
- {
- int channel = getChannel();
-
- if( channel == sCALL_END )
- {
- retVal = MessageType.CA_ENDD;
- }
- else
- {
- retVal = MessageType.CA_STRT;
- }
- }
-
- return retVal;
- }
-
- @Override
- public String getFromID()
- {
- if( mMessageType == MessageType.CA_STRT ||
- mMessageType == MessageType.CA_ENDD )
- {
- return getTalkgroupID();
- }
- else
- {
- return null;
- }
- }
-
- @Override
- public Alias getFromIDAlias()
- {
- if( mMessageType == MessageType.CA_STRT ||
- mMessageType == MessageType.CA_ENDD )
- {
- return getTalkgroupIDAlias();
- }
- else
- {
- return null;
- }
- }
-
- @Override
- public Plottable getPlottable()
- {
- // TODO Auto-generated method stub
- return null;
- }
-
- /**
- * Provides a listing of aliases contained in the message.
- */
- public List getAliases()
- {
- List aliases = new ArrayList();
-
- Alias from = getFromIDAlias();
-
- if( from != null )
- {
- aliases.add( from );
- }
-
- Alias to = getToIDAlias();
-
- if( to != null )
- {
- aliases.add( to );
- }
-
- return aliases;
- }
-}
diff --git a/src/module/decode/ltrstandard/LTRStandardMessage.java b/src/module/decode/ltrstandard/LTRStandardMessage.java
deleted file mode 100644
index 408c7bd9..00000000
--- a/src/module/decode/ltrstandard/LTRStandardMessage.java
+++ /dev/null
@@ -1,254 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- ******************************************************************************/
-package module.decode.ltrstandard;
-
-import java.text.DecimalFormat;
-import java.text.SimpleDateFormat;
-
-import message.Message;
-import message.MessageDirection;
-import message.MessageType;
-import module.decode.DecoderType;
-import alias.Alias;
-import alias.AliasList;
-import bits.BinaryMessage;
-import edac.CRC;
-import edac.CRCLTR;
-
-public abstract class LTRStandardMessage extends Message
-{
- protected static final String sUNKNOWN = "**UNKNOWN**";
- protected static final int sINT_NULL_VALUE = -1;
- protected static final double sDOUBLE_NULL_VALUE = -1.0D;
- protected SimpleDateFormat mDatestampFormatter =
- new SimpleDateFormat( "yyyyMMdd HHmmss" );
- protected DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000");
-
- protected static final int[] sSYNC = { 8,7,6,5,4,3,2,1,0 };
- protected static final int[] sAREA = { 9 };
- protected static final int[] sCHANNEL = { 14,13,12,11,10 };
- protected static final int[] sHOME_REPEATER = { 19,18,17,16,15 };
- protected static final int[] sGROUP = { 27,26,25,24,23,22,21,20 };
- protected static final int[] sFREE = { 32,31,30,29,28 };
- protected static final int[] sCRC = { 39,38,37,36,35,34,33 };
-
- protected BinaryMessage mMessage;
- protected MessageType mMessageType;
- protected CRC mCRC;
- protected AliasList mAliasList;
-
- public LTRStandardMessage( BinaryMessage message,
- MessageDirection direction,
- AliasList list )
- {
- mMessage = message;
- mCRC = CRCLTR.check( message, direction );
- mAliasList = list;
- }
-
- public MessageType getMessageType()
- {
- return mMessageType;
- }
-
- public boolean isValid()
- {
- return mCRC != CRC.FAILED_CRC && mCRC != CRC.FAILED_PARITY;
- }
-
- /**
- * Appends spaces to the end of the stringbuilder to make it length long
- */
- protected void pad( StringBuilder sb, int length )
- {
- while( sb.length() < length )
- {
- sb.append( " " );
- }
- }
-
- @Override
- public String getBinaryMessage()
- {
- return mMessage.toString();
- }
-
- /**
- * Returns a value formatted in hex notation.
- *
- * @param value - integer value
- * @return - hex digits returned
- */
- protected static String getHex( int value, int digits )
- {
- return String.format( "%0" + digits + "X", value );
- }
-
- protected int getInt( int[] bits )
- {
- int retVal = 0;
-
- for( int x = 0; x < bits.length; x++ )
- {
- if( mMessage.get( bits[ x ] ) )
- {
- retVal += 1<
{
@@ -29,6 +36,12 @@ public class LTRStandardMessageProcessor implements Listener
private AliasList mAliasList;
private Listener mMessageListener;
+ /**
+ * Processes raw binary messages and converts them to the correct message class
+ *
+ * @param direction - inbound (ISW) or outbound (OSW)
+ * @param list - alias list
+ */
public LTRStandardMessageProcessor( MessageDirection direction, AliasList list )
{
mDirection = direction;
@@ -36,27 +49,66 @@ public class LTRStandardMessageProcessor implements Listener
}
@Override
- public void receive( BinaryMessage buffer )
+ public void receive( BinaryMessage binaryMessage )
{
if( mMessageListener != null )
{
- LTRStandardMessage message;
-
- if( mDirection == MessageDirection.OSW )
+ //Inbound Status Word (ISW) is a bit-flipped version of the Outbound
+ //(OSW), so flip the bits and process it as an OSW
+ if( mDirection == MessageDirection.ISW )
{
- message = new LTRStandardOSWMessage( buffer, mAliasList );
+ binaryMessage.flip( 0, 40 );
}
- else
+
+ CRC crc = CRCLTR.check( binaryMessage, mDirection );
+
+ if( crc != CRC.FAILED_CRC &&
+ crc != CRC.FAILED_PARITY )
{
- buffer.flip( 0, 40 );
+ LTRStandardMessage message;
- message = new LTRStandardISWMessage( buffer, mAliasList );
+ int channel = binaryMessage.getInt( LTRStandardMessage.CHANNEL );
+ int home = binaryMessage.getInt( LTRStandardMessage.HOME_REPEATER );
+ int free = binaryMessage.getInt( LTRStandardMessage.FREE );
+ int group = binaryMessage.getInt( LTRStandardMessage.GROUP );
+
+ if( isValidChannel( channel ) &&
+ isValidChannel( home ) &&
+ isValidChannel( free ) )
+ {
+ if( channel == free && group == 255 )
+ {
+ message = new IdleMessage( binaryMessage, mDirection, mAliasList, crc );
+ }
+ else
+ {
+ message = new CallMessage( binaryMessage, mDirection, mAliasList, crc );
+ }
+ }
+ else if( channel == 31 &&
+ isValidChannel( home ) &&
+ isValidChannel( free ) )
+ {
+ message = new CallEndMessage( binaryMessage, mDirection, mAliasList, crc );
+ }
+ else
+ {
+ message = new UnknownMessage( binaryMessage, mDirection, mAliasList, crc );
+ }
+
+ mMessageListener.receive( message );
}
-
- mMessageListener.receive( message );
}
}
+ /**
+ * Checks the channel (LCN) number to ensure it is in the range 1 -20
+ */
+ private boolean isValidChannel( int channel )
+ {
+ return ( 1 <= channel && channel <= 20 );
+ }
+
public void setMessageListener( Listener listener )
{
mMessageListener = listener;
diff --git a/src/module/decode/ltrstandard/LTRStandardOSWMessage.java b/src/module/decode/ltrstandard/LTRStandardOSWMessage.java
deleted file mode 100644
index df3bde82..00000000
--- a/src/module/decode/ltrstandard/LTRStandardOSWMessage.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- ******************************************************************************/
-package module.decode.ltrstandard;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import map.Plottable;
-import message.MessageDirection;
-import message.MessageType;
-import alias.Alias;
-import alias.AliasList;
-import bits.BinaryMessage;
-
-public class LTRStandardOSWMessage extends LTRStandardMessage
-{
- private static final int sCALL_END = 31;
- private static final int sIDLE = 255;
-
- public LTRStandardOSWMessage( BinaryMessage message, AliasList list )
- {
- super( message, MessageDirection.OSW, list );
- mMessageType = getType();
- }
-
- @Override
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
- sb.append( mDatestampFormatter.format(
- new Date( System.currentTimeMillis() ) ) );
- sb.append( " LTR OSW [" );
- sb.append( mCRC.getAbbreviation() );
- sb.append( "] " );
- sb.append( getMessage() );
- pad( sb, 100 );
- sb.append( mMessage.toString() );
-
- return sb.toString();
- }
-
- @Override
- public String getMessage()
- {
- StringBuilder sb = new StringBuilder();
-
- switch( mMessageType )
- {
- case SY_IDLE:
- sb.append( "IDLE AREA:" );
- sb.append( getArea() );
- sb.append( " LCN:" );
- sb.append( format( getChannel(), 2 ) );
- sb.append( " HOME:" );
- sb.append( format( getHomeRepeater(), 2 ) );
- sb.append( " GRP:" );
- sb.append( format( getGroup(), 3 ) );
- sb.append( " FREE:" );
- sb.append( format( getFree(), 2 ) );
- break;
- case CA_STRT:
- sb.append( "CALL LCN:" );
- sb.append( format( getChannel(), 2 ) );
- sb.append( " TG [ " );
- sb.append( getTalkgroupID() );
- sb.append( "/" );
- Alias alias = getTalkgroupIDAlias();
- if( alias != null )
- {
- sb.append( getTalkgroupIDAlias().getName() );
- }
- else
- {
- sb.append( "UNKNOWN" );
- }
- sb.append( " ] FREE:" );
- sb.append( format( getFree(), 2 ) );
- break;
- case CA_ENDD:
- sb.append( "END* AREA:" );
- sb.append( getArea() );
- sb.append( " LCN:" );
- sb.append( format( getChannel(), 2 ) );
- sb.append( " TG [" );
- sb.append( getTalkgroupID() );
- sb.append( "/" );
- Alias endAlias = getTalkgroupIDAlias();
- if( endAlias != null )
- {
- sb.append( getTalkgroupIDAlias().getName() );
- }
- else
- {
- sb.append( "UNKNOWN" );
- }
- sb.append( "] FREE:" );
- sb.append( format( getFree(), 2 ) );
- break;
- default:
- sb.append( "UNKNOWN A:" );
- sb.append( getArea() );
- sb.append( " C:" );
- sb.append( format( getChannel(), 2 ) );
- sb.append( " H:" );
- sb.append( format( getHomeRepeater(), 2 ) );
- sb.append( " G:" );
- sb.append( format( getGroup(), 3 ) );
- sb.append( " F:" );
- sb.append( format( getFree(), 2 ) );
- }
-
- return sb.toString();
- }
-
- public String getTalkgroupID()
- {
- return getTalkgroupID( true );
- }
-
- public String getTalkgroupID( boolean format )
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( getArea() );
-
- if( format )
- {
- sb.append( "-" );
- }
-
- sb.append( format( getHomeRepeater(), 2 ) );
-
- if( format )
- {
- sb.append( "-" );
- }
-
- sb.append( format( getGroup(), 3 ) );
-
- return sb.toString();
- }
-
- public Alias getTalkgroupIDAlias()
- {
- return mAliasList.getTalkgroupAlias( getTalkgroupID() );
- }
-
- public MessageType getType()
- {
- MessageType retVal = MessageType.UN_KNWN;
-
- /* Group 255 when the channel equals the free is the IDLE message */
- if( getGroup() == sIDLE && getChannel() == getFree() )
- {
- retVal = MessageType.SY_IDLE;
- }
- else
- {
- int channel = getChannel();
-
- if( channel == sCALL_END )
- {
- retVal = MessageType.CA_ENDD;
- }
- else
- {
- retVal = MessageType.CA_STRT;
- }
- }
-
- return retVal;
- }
-
- @Override
- public String getFromID()
- {
- if( mMessageType == MessageType.CA_STRT ||
- mMessageType == MessageType.CA_ENDD )
- {
- return getTalkgroupID();
- }
- else
- {
- return null;
- }
- }
-
- @Override
- public Alias getFromIDAlias()
- {
- if( mMessageType == MessageType.CA_STRT ||
- mMessageType == MessageType.CA_ENDD )
- {
- return getTalkgroupIDAlias();
- }
- else
- {
- return null;
- }
- }
-
- @Override
- public Plottable getPlottable()
- {
- // TODO Auto-generated method stub
- return null;
- }
-
- /**
- * Provides a listing of aliases contained in the message.
- */
- public List getAliases()
- {
- List aliases = new ArrayList();
-
- Alias from = getFromIDAlias();
-
- if( from != null )
- {
- aliases.add( from );
- }
-
- Alias to = getToIDAlias();
-
- if( to != null )
- {
- aliases.add( to );
- }
-
- return aliases;
- }
-}
diff --git a/src/module/decode/ltrstandard/message/CallEndMessage.java b/src/module/decode/ltrstandard/message/CallEndMessage.java
new file mode 100644
index 00000000..e5023139
--- /dev/null
+++ b/src/module/decode/ltrstandard/message/CallEndMessage.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * SDR Trunk
+ * Copyright (C) 2014-2016 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ ******************************************************************************/
+package module.decode.ltrstandard.message;
+
+import message.MessageDirection;
+import message.MessageType;
+import alias.Alias;
+import alias.AliasList;
+import bits.BinaryMessage;
+import edac.CRC;
+
+public class CallEndMessage extends LTRStandardMessage
+{
+ public CallEndMessage( BinaryMessage message, MessageDirection direction,
+ AliasList list, CRC crc )
+ {
+ super( message, direction, list, crc );
+ }
+
+ @Override
+ public MessageType getMessageType()
+ {
+ return MessageType.CA_ENDD;
+ }
+
+ @Override
+ public String getMessage()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( "END* AREA:" );
+ sb.append( getArea() );
+ sb.append( " LCN:" );
+ sb.append( getChannelFormatted() );
+ sb.append( " TG [" );
+ sb.append( getToID() );
+ sb.append( "/" );
+ Alias endAlias = getToIDAlias();
+ if( endAlias != null )
+ {
+ sb.append( getToIDAlias().getName() );
+ }
+ else
+ {
+ sb.append( "UNKNOWN" );
+ }
+ sb.append( "] FREE:" );
+ sb.append( getFreeFormatted() );
+
+ return sb.toString();
+ }
+}
diff --git a/src/module/decode/ltrstandard/message/CallMessage.java b/src/module/decode/ltrstandard/message/CallMessage.java
new file mode 100644
index 00000000..f7d44ca6
--- /dev/null
+++ b/src/module/decode/ltrstandard/message/CallMessage.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * SDR Trunk
+ * Copyright (C) 2014-2016 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ ******************************************************************************/
+package module.decode.ltrstandard.message;
+
+import message.MessageDirection;
+import message.MessageType;
+import alias.Alias;
+import alias.AliasList;
+import bits.BinaryMessage;
+import edac.CRC;
+
+public class CallMessage extends LTRStandardMessage
+{
+ public CallMessage( BinaryMessage message, MessageDirection direction,
+ AliasList list, CRC crc )
+ {
+ super( message, direction, list, crc );
+ }
+
+ @Override
+ public MessageType getMessageType()
+ {
+ return MessageType.CA_STRT;
+ }
+
+ @Override
+ public String getMessage()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( "CALL LCN:" );
+ sb.append( getChannelFormatted() );
+ sb.append( " TG [ " );
+ sb.append( getToID() );
+ sb.append( "/" );
+
+ Alias alias = getToIDAlias();
+
+ if( alias != null )
+ {
+ sb.append( getToIDAlias().getName() );
+ }
+ else
+ {
+ sb.append( "UNKNOWN" );
+ }
+
+ sb.append( " ] FREE:" );
+ sb.append( getFreeFormatted() );
+
+ return sb.toString();
+ }
+}
diff --git a/src/module/decode/ltrstandard/message/IdleMessage.java b/src/module/decode/ltrstandard/message/IdleMessage.java
new file mode 100644
index 00000000..ba8e4d97
--- /dev/null
+++ b/src/module/decode/ltrstandard/message/IdleMessage.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * SDR Trunk
+ * Copyright (C) 2014-2016 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ ******************************************************************************/
+package module.decode.ltrstandard.message;
+
+import message.MessageDirection;
+import message.MessageType;
+import alias.AliasList;
+import bits.BinaryMessage;
+import edac.CRC;
+
+public class IdleMessage extends LTRStandardMessage
+{
+ public IdleMessage( BinaryMessage message, MessageDirection direction,
+ AliasList list, CRC crc )
+ {
+ super( message, direction, list, crc );
+ }
+
+ @Override
+ public MessageType getMessageType()
+ {
+ return MessageType.SY_IDLE;
+ }
+
+ @Override
+ public String getMessage()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( "IDLE AREA:" );
+ sb.append( getArea() );
+ sb.append( " LCN:" );
+ sb.append( getChannelFormatted() );
+ sb.append( " HOME:" );
+ sb.append( getHomeRepeaterFormatted() );
+ sb.append( " GRP:" );
+ sb.append( getGroupFormatted() );
+ sb.append( " FREE:" );
+ sb.append( getFreeFormatted() );
+
+ return sb.toString();
+ }
+}
diff --git a/src/module/decode/ltrstandard/message/LTRStandardMessage.java b/src/module/decode/ltrstandard/message/LTRStandardMessage.java
new file mode 100644
index 00000000..87f381b2
--- /dev/null
+++ b/src/module/decode/ltrstandard/message/LTRStandardMessage.java
@@ -0,0 +1,213 @@
+/*******************************************************************************
+ * SDR Trunk
+ * Copyright (C) 2014-2016 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ ******************************************************************************/
+package module.decode.ltrstandard.message;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import message.Message;
+import message.MessageDirection;
+import message.MessageType;
+import module.decode.DecoderType;
+
+import org.apache.commons.lang3.StringUtils;
+
+import alias.Alias;
+import alias.AliasList;
+import bits.BinaryMessage;
+import edac.CRC;
+
+public abstract class LTRStandardMessage extends Message
+{
+ protected SimpleDateFormat mDatestampFormatter =
+ new SimpleDateFormat( "yyyyMMdd HHmmss" );
+
+ public static final int[] SYNC = { 0,1,2,3,4,5,6,7,8 };
+ public static final int[] AREA = { 9 };
+ public static final int[] CHANNEL = { 10,11,12,13,14 };
+ public static final int[] HOME_REPEATER = { 15,16,17,18,19 };
+ public static final int[] GROUP = { 20,21,22,23,24,25,26,27 };
+ public static final int[] FREE = { 28,29,30,31,32 };
+ public static final int[] CHECKSUM = { 33,34,35,36,37,38,39 };
+
+ protected BinaryMessage mMessage;
+ protected MessageDirection mMessageDirection;
+ protected AliasList mAliasList;
+ protected CRC mCRC;
+
+ public LTRStandardMessage( BinaryMessage message,
+ MessageDirection direction,
+ AliasList list,
+ CRC crc )
+ {
+ mMessage = message;
+ mMessageDirection = direction;
+ mAliasList = list;
+ mCRC = crc;
+ }
+
+ public abstract MessageType getMessageType();
+
+ public boolean isValid()
+ {
+ return mCRC != CRC.FAILED_CRC && mCRC != CRC.FAILED_PARITY;
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append( mDatestampFormatter.format(
+ new Date( System.currentTimeMillis() ) ) );
+ sb.append( " LTR " );
+ sb.append( mMessageDirection.name() );
+ sb.append( " [" );
+ sb.append( mCRC.getAbbreviation() );
+ sb.append( "] " );
+ sb.append( getMessage() );
+
+ return sb.toString();
+ }
+
+ @Override
+ public String getBinaryMessage()
+ {
+ return mMessage.toString();
+ }
+
+ public CRC getCRC()
+ {
+ return mCRC;
+ }
+
+ public int getArea()
+ {
+ return mMessage.getInt( AREA );
+ }
+
+ public int getChannel()
+ {
+ return mMessage.getInt( CHANNEL );
+ }
+
+ public String getChannelFormatted()
+ {
+ return StringUtils.leftPad( String.valueOf( getChannel() ), 2, "0" );
+ }
+
+ public int getHomeRepeater()
+ {
+ return mMessage.getInt( HOME_REPEATER );
+ }
+
+ public String getHomeRepeaterFormatted()
+ {
+ return StringUtils.leftPad( String.valueOf( getHomeRepeater() ), 2, "0" );
+ }
+
+ public int getGroup()
+ {
+ return mMessage.getInt( GROUP );
+ }
+
+ public String getGroupFormatted()
+ {
+ return StringUtils.leftPad( String.valueOf( getGroup() ), 3, "0" );
+ }
+
+ public int getFree()
+ {
+ return mMessage.getInt( FREE );
+ }
+
+ public String getFreeFormatted()
+ {
+ return StringUtils.leftPad( String.valueOf( getFree() ), 2, "0" );
+ }
+
+ @Override
+ public String getFromID()
+ {
+ return null;
+ }
+
+ @Override
+ public Alias getFromIDAlias()
+ {
+ return null;
+ }
+
+ @Override
+ public String getToID()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( getArea() );
+ sb.append( "-" );
+ sb.append( getHomeRepeaterFormatted() );
+ sb.append( "-" );
+ sb.append( getGroupFormatted() );
+
+ return sb.toString();
+ }
+
+ @Override
+ public Alias getToIDAlias()
+ {
+ return mAliasList.getTalkgroupAlias( getToID() );
+ }
+
+ public List getAliases()
+ {
+ List aliases = new ArrayList<>();
+
+ Alias talkgroupAlias = getToIDAlias();
+
+ if( talkgroupAlias != null )
+ {
+ aliases.add( talkgroupAlias );
+ }
+
+ return aliases;
+ }
+
+ public int getCRCChecksum()
+ {
+ return mMessage.getInt( CHECKSUM );
+ }
+
+ @Override
+ public String getProtocol()
+ {
+ return DecoderType.LTR_STANDARD.getDisplayString();
+ }
+
+ @Override
+ public String getEventType()
+ {
+ return getMessageType().getDisplayText();
+ }
+
+ @Override
+ public String getErrorStatus()
+ {
+ return mCRC.getDisplayText();
+ }
+}
diff --git a/src/module/decode/ltrstandard/message/UnknownMessage.java b/src/module/decode/ltrstandard/message/UnknownMessage.java
new file mode 100644
index 00000000..b975c667
--- /dev/null
+++ b/src/module/decode/ltrstandard/message/UnknownMessage.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * SDR Trunk
+ * Copyright (C) 2014-2016 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ ******************************************************************************/
+package module.decode.ltrstandard.message;
+
+import message.MessageDirection;
+import message.MessageType;
+import alias.AliasList;
+import bits.BinaryMessage;
+import edac.CRC;
+
+public class UnknownMessage extends LTRStandardMessage
+{
+ public UnknownMessage( BinaryMessage message, MessageDirection direction,
+ AliasList list, CRC crc )
+ {
+ super( message, direction, list, crc );
+ }
+
+ @Override
+ public MessageType getMessageType()
+ {
+ return MessageType.UN_KNWN;
+ }
+
+ @Override
+ public String getMessage()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append( "UNKNOWN AREA:" );
+ sb.append( getArea() );
+ sb.append( " LCN:" );
+ sb.append( getChannelFormatted() );
+ sb.append( " HOME:" );
+ sb.append( getHomeRepeaterFormatted() );
+ sb.append( " GRP:" );
+ sb.append( getGroupFormatted() );
+ sb.append( " FREE:" );
+ sb.append( getFreeFormatted() );
+
+ return sb.toString();
+ }
+}
diff --git a/src/module/decode/state/ChannelState.java b/src/module/decode/state/ChannelState.java
index 3b538349..8a76047f 100644
--- a/src/module/decode/state/ChannelState.java
+++ b/src/module/decode/state/ChannelState.java
@@ -17,6 +17,7 @@
******************************************************************************/
package module.decode.state;
+import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -182,8 +183,16 @@ public class ChannelState extends Module implements ICallEventProvider,
{
if( mRunningMonitor == null && mStateMonitor != null )
{
- mRunningMonitor = mThreadPoolManager.scheduleFixedRate(
- ThreadType.DECODER, mStateMonitor, 20, TimeUnit.MILLISECONDS );
+ try
+ {
+ mRunningMonitor = mThreadPoolManager.scheduleFixedRate(
+ ThreadType.DECODER, mStateMonitor, 20, TimeUnit.MILLISECONDS );
+ }
+ catch( RejectedExecutionException ree )
+ {
+ mLog.error( "state monitor scheduled task rejected", ree );
+ mMonitoring.set( false );
+ }
}
else
{
@@ -372,6 +381,10 @@ public class ChannelState extends Module implements ICallEventProvider,
break;
}
}
+ else
+ {
+ mLog.debug( "Can't change from [" + mState + "] to [" + state + "]" );
+ }
}
public class StateMonitor implements Runnable
@@ -379,23 +392,35 @@ public class ChannelState extends Module implements ICallEventProvider,
@Override
public void run()
{
- if( getState() == State.FADE )
+ try
{
- if( mResetTimeout <= System.currentTimeMillis() )
+ if( getState() == State.FADE )
{
- setState( State.END );
+ if( mResetTimeout <= System.currentTimeMillis() )
+ {
+ setState( State.END );
+ }
+ }
+ else if( getState() == State.END )
+ {
+ setState( State.IDLE );
+ }
+ else
+ {
+ if( mFadeTimeout <= System.currentTimeMillis() )
+ {
+ setState( State.FADE );
+ }
}
}
- else if( getState() == State.END )
+ catch( Exception e )
{
- setState( State.IDLE );
- }
- else
- {
- if( mFadeTimeout <= System.currentTimeMillis() )
- {
- setState( State.FADE );
- }
+ mLog.error( "Exception thrown while state monitor is running " +
+ "- state [" + getState() +
+ "] current [" + System.currentTimeMillis() +
+ "] mResetTimeout [" + mResetTimeout +
+ "] mFadeTimeout [" + mFadeTimeout +
+ "]", e );
}
}
}