/*******************************************************************************
* 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 decode.passport;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import map.Plottable;
import message.Message;
import message.MessageType;
import alias.Alias;
import alias.AliasList;
import bits.BitSetBuffer;
import crc.CRC;
import crc.CRCPassport;
import decode.DecoderType;
public class PassportMessage extends Message
{
private static final String sUNKNOWN = "**UNKNOWN**";
private SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" );
private DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000");
@SuppressWarnings( "unused" )
private static final int[] sSYNC = { 8,7,6,5,4,3,2,1,0 };
private static final int[] sDCC = { 10,9 }; //Digital Color Code
private static final int[] sLCN = { 21,20,19,18,17,16,15,14,13,12,11 };
private static final int[] sSITE = { 28,27,26,25,24,23,22 };
private static final int[] sGROUP = { 44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29 };
private static final int[] sRADIO_ID = { 44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22 };
// private static final int[] sNEIGHBOR_OFFSET = { 32,31,30 };
private static final int[] sNEIGHBOR_BAND = { 36,35,34,33 };
private static final int[] sNEIGHBOR_REGISTRATION = { 29 };
private static final int[] sSITE_OFFSET = { 40,39,38 };
private static final int[] sSITE_BAND = { 44,43,42,41 };
private static final int[] sSITE_REGISTRATION = { 37 };
private static final int[] sTYPE = { 48,47,46,45 };
private static final int[] sFREE = { 59,58,57,56,55,54,53,52,51,50,49 };
private static final int[] sCHECKSUM = { 67,66,65,64,63,62,61,60 };
private BitSetBuffer mMessage;
private CRC mCRC;
private AliasList mAliasList;
private PassportMessage mIdleMessage;
public PassportMessage( BitSetBuffer message,
PassportMessage idleMessage,
AliasList list )
{
mMessage = CRCPassport.correct( message );
mIdleMessage = idleMessage;
mCRC = CRCPassport.check( mMessage );
mAliasList = list;
}
public PassportMessage( BitSetBuffer message, AliasList list )
{
this( message, null, list );
}
public BitSetBuffer getBitSetBuffer()
{
return mMessage;
}
public boolean isValid()
{
return mCRC != CRC.FAILED_CRC &&
mCRC != CRC.FAILED_PARITY &&
mCRC != CRC.UNKNOWN;
}
public CRC getCRC()
{
return mCRC;
}
public MessageType getMessageType()
{
MessageType retVal = MessageType.UN_KNWN;
int type = getMessageTypeNumber();
int lcn = getLCN();
switch( type )
{
case 0: //Group Call
retVal = MessageType.CA_STRT;
break;
case 1:
if( getFree() == 2042 )
{
retVal = MessageType.ID_TGAS;
}
else if( lcn < 1792 )
{
retVal = MessageType.CA_STRT;
}
else if( lcn == 1792 || lcn == 1793 )
{
retVal = MessageType.SY_IDLE;
}
else if( lcn == 2047 )
{
retVal = MessageType.CA_ENDD;
}
break;
case 2:
retVal = MessageType.CA_STRT;
break;
case 5:
retVal = MessageType.CA_PAGE;
break;
case 6:
retVal = MessageType.ID_RDIO;
break;
case 9:
retVal = MessageType.DA_STRT;
break;
case 11:
retVal = MessageType.RA_REGI;
break;
default:
break;
}
return retVal;
}
public int getDCC()
{
return getInt( sDCC );
}
public int getSite()
{
return getInt( sSITE );
}
public int getMessageTypeNumber()
{
return getInt( sTYPE );
}
public int getTalkgroupID()
{
if( getMessageType() == MessageType.SY_IDLE )
{
return 0;
}
else
{
return getInt( sGROUP );
}
}
public Alias getTalkgroupIDAlias()
{
int tg = getTalkgroupID();
if( mAliasList != null )
{
return mAliasList.getTalkgroupAlias( String.valueOf( tg ) );
}
return null;
}
public int getLCN()
{
return getInt( sLCN );
}
public long getLCNFrequency()
{
return getSiteFrequency( getLCN() );
}
public String getLCNFrequencyFormatted()
{
return mDecimalFormatter.format( (double)getLCNFrequency() / 1000000.0d );
}
public PassportBand getSiteBand()
{
return PassportBand.lookup( getInt( sSITE_BAND ) );
}
public PassportBand getNeighborBand()
{
return PassportBand.lookup( getInt( sNEIGHBOR_BAND ) );
}
public int getFree()
{
return getInt( sFREE );
}
public long getFreeFrequency()
{
return getSiteFrequency( getFree() );
}
public String getFreeFrequencyFormatted()
{
return mDecimalFormatter.format( (double)getFreeFrequency() / 1000000.0d );
}
public long getNeighborFrequency()
{
if( getMessageType() == MessageType.SY_IDLE )
{
PassportBand band = getNeighborBand();
return band.getFrequency( getFree() );
}
return 0;
}
/**
* Returns the radio id in hex format, or null if not the correct message
* type
*
* @return - radio id or null
*/
public String getMobileID()
{
if( getMessageType() == MessageType.ID_RDIO )
{
int radioId = getInt( sRADIO_ID );
return String.format("%06X", radioId & 0xFFFFFF );
}
else
{
return null;
}
}
public Alias getMobileIDAlias()
{
String min = getMobileID();
if( mAliasList != null && min != null )
{
return mAliasList.getMobileIDNumberAlias( min );
}
return null;
}
/**
* Appends spaces to the end of the stringbuilder to make it length long
*/
private 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
*/
private static String getHex( int value, int digits )
{
return String.format( "%0" + digits + "X", value );
}
private int getInt( int[] bits )
{
int retVal = 0;
for( int x = 0; x < bits.length; x++ )
{
if( mMessage.get( bits[ x ] ) )
{
retVal += 1< 0 && free < 896 )
{
sb.append( "/" );
sb.append( getFreeFrequencyFormatted() );
}
sb.append( " TYP:" );
sb.append( format( getMessageTypeNumber(), 2 ) );
sb.append( " TG:" );
sb.append( format( getTalkgroupID(), 5 ) );
break;
}
return sb.toString();
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( mSDF.format( mTimeReceived ) );
sb.append( " " );
sb.append( getMessage() );
pad( sb, 100 );
sb.append( mCRC.toString() );
pad( sb, 110 );
sb.append( " [" );
sb.append( mMessage );
sb.append( "]" );
return sb.toString();
}
@Override
public String getErrorStatus()
{
return mCRC.getDisplayText();
}
public boolean matches( PassportMessage otherMessage )
{
return this.getBitSetBuffer().equals( otherMessage.getBitSetBuffer() );
}
@Override
public Plottable getPlottable()
{
// TODO Auto-generated method stub
return null;
}
}