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