Closes #42 - ltr standard call state was not accurately tracking call

state. 

Note: a related issue described in ticket #42 involves issues with the
audio output module.  A new ticket will be opened with that issue.
This commit is contained in:
denny
2016-01-17 19:42:13 -05:00
parent 0c5692cb1e
commit 810bb7384e
16 changed files with 925 additions and 1075 deletions

View File

@@ -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.
*/

View File

@@ -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:

View File

@@ -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 <http://www.gnu.org/licenses/>
******************************************************************************/
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();
}

View File

@@ -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() )

View File

@@ -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<Integer,String> mActiveCalls = new HashMap<Integer,String>();
private HashSet<String> mTalkgroupsFirstHeard = new HashSet<String>();
private TreeSet<String> mTalkgroups = new TreeSet<String>();
private TreeSet<Integer> mActiveLCNs = new TreeSet<Integer>();
private final static Logger mLog =
LoggerFactory.getLogger( LTRStandardDecoderState.class );
private Map<Integer,LTRCallEvent> mActiveCalls = new HashMap<>();
private Set<String> mTalkgroupsFirstHeard = new HashSet<>();
private Set<String> 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<Integer> 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<String> 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<Integer> getActiveLCNs()
{
List<Integer> 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;
}
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
******************************************************************************/
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<Alias> getAliases()
{
List<Alias> aliases = new ArrayList<Alias>();
Alias from = getFromIDAlias();
if( from != null )
{
aliases.add( from );
}
Alias to = getToIDAlias();
if( to != null )
{
aliases.add( to );
}
return aliases;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
******************************************************************************/
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<<x;
}
}
return retVal;
}
/**
* Pads an integer value with additional zeroes to make it decimalPlaces long
*/
public String format( int number, int decimalPlaces )
{
StringBuilder sb = new StringBuilder();
int paddingRequired = decimalPlaces - ( String.valueOf( number ).length() );
for( int x = 0; x < paddingRequired; x++)
{
sb.append( "0" );
}
sb.append( number );
return sb.toString();
}
public String format( String val, int places )
{
StringBuilder sb = new StringBuilder();
sb.append( val );
while( sb.length() < places )
{
sb.append( " " );
}
return sb.toString();
}
public CRC getCRC()
{
return mCRC;
}
public int getArea()
{
return getInt( sAREA );
}
public int getChannel()
{
return getInt( sCHANNEL );
}
public int getHomeRepeater()
{
return getInt( sHOME_REPEATER );
}
public int getGroup()
{
return getInt( sGROUP );
}
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 int getFree()
{
return getInt( sFREE );
}
public int getCRCChecksum()
{
return getInt( sCRC );
}
@Override
public String getProtocol()
{
return DecoderType.LTR_STANDARD.getDisplayString();
}
@Override
public String getEventType()
{
if( mMessageType != null )
{
return mMessageType.getDisplayText();
}
else
{
return MessageType.UN_KNWN.getDisplayText();
}
}
@Override
public String getToID()
{
return null;
}
@Override
public Alias getToIDAlias()
{
return null;
}
@Override
public String getErrorStatus()
{
return mCRC.getDisplayText();
}
}

View File

@@ -6,6 +6,7 @@ import java.util.List;
import message.Message;
import message.MessageType;
import module.decode.ltrstandard.message.LTRStandardMessage;
import filter.Filter;
import filter.FilterElement;

View File

@@ -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
@@ -19,9 +19,16 @@ package module.decode.ltrstandard;
import message.Message;
import message.MessageDirection;
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.ltrstandard.message.UnknownMessage;
import sample.Listener;
import alias.AliasList;
import bits.BinaryMessage;
import edac.CRC;
import edac.CRCLTR;
public class LTRStandardMessageProcessor implements Listener<BinaryMessage>
{
@@ -29,6 +36,12 @@ public class LTRStandardMessageProcessor implements Listener<BinaryMessage>
private AliasList mAliasList;
private Listener<Message> 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<BinaryMessage>
}
@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<Message> listener )
{
mMessageListener = listener;

View File

@@ -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 <http://www.gnu.org/licenses/>
******************************************************************************/
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<Alias> getAliases()
{
List<Alias> aliases = new ArrayList<Alias>();
Alias from = getFromIDAlias();
if( from != null )
{
aliases.add( from );
}
Alias to = getToIDAlias();
if( to != null )
{
aliases.add( to );
}
return aliases;
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
******************************************************************************/
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();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
******************************************************************************/
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();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
******************************************************************************/
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();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
******************************************************************************/
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<Alias> getAliases()
{
List<Alias> 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();
}
}

View File

@@ -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 <http://www.gnu.org/licenses/>
******************************************************************************/
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();
}
}

View File

@@ -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 );
}
}
}