/*******************************************************************************
* 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.ltrnet;
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 LTRNetOSWMessage extends LTRNetMessage
{
private static final int CHANNEL_FREQUENCY_MESSAGE_TYPE_BIT = 20;
private static final int CHANNEL_MAP_MESSAGE_TYPE_BIT = 17;
private static final int CHIU_UNIQUE_ID = 17;
private static final int CHIU_SITE_ID = 18;
private static final int CHIU_TRANSMIT_FREQUENCY = 24;
private static final int CHIU_RECEIVE_FREQUENCY = 25;
private static final int CHIU_NEIGHBOR = 26;
private static final int CHIU_MAP = 28;
private static final int CHIU_CALL_END = 31;
private static final int HOME_DIRECTED_GROUP_CALL = 29;
private static final int HOME_CHANNEL_IN_USE_MUTE_AUDIO = 30;
private static final int GROUP_DO_NOTHING = 253;
private static final int GROUP_CWID = 254;
private static final int GROUP_CHANNEL_IDLE = 255;
private static final int FREE_ALL_CHANNELS_BUSY = 0;
private static final int FREE_LTR_GROUP_CALL = 30;
private static final int FREE_LTRNET_GROUP_CALL = 31;
private LTRNetOSWMessage mAuxMessage;
public LTRNetOSWMessage( BinaryMessage message, AliasList list )
{
super( message, MessageDirection.OSW, list );
mMessageType = getType();
}
public void setAuxiliaryMessage( LTRNetOSWMessage message )
{
if( message != null )
{
mAuxMessage = message;
}
}
public String getRadioUniqueIDText()
{
StringBuilder sb = new StringBuilder();
int radioId = getRadioUniqueID();
if( radioId != INT_NULL_VALUE )
{
sb.append( " RADIO UNIQUE ID: " );
sb.append( radioId );
}
return sb.toString();
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( mDatestampFormatter.format(
new Date( System.currentTimeMillis() ) ) );
sb.append( " LTRNet 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;
case MA_CHNL:
sb.append( "CHAN MAP LOW LCN [" );
sb.append( getChannelMapAsString( getChannelMapLow() ) );
sb.append( "]" );
break;
case MA_CHNH:
sb.append( "CHAN MAP HIGH LCN [" );
sb.append( getChannelMapAsString( getChannelMapHigh() ) );
sb.append( "]" );
break;
case FQ_RXLO:
sb.append( "CHAN RX LOW LCN:" );
sb.append( format( getHomeRepeater(), 2 ) );
sb.append( " = " );
long rxFreqLo = getFrequency();
if( rxFreqLo > 0 )
{
sb.append( mDecimalFormatter.format( (double)rxFreqLo / 1E6d ) );
}
else
{
sb.append( "000.00000" );
}
sb.append( " MHz" );
break;
case FQ_RXHI:
sb.append( "CHAN RX HIGH LCN:" );
sb.append( format( getHomeRepeater(), 2 ) );
sb.append( " = " );
long rxfreqhi = getFrequency();
if( rxfreqhi > 0 )
{
sb.append( mDecimalFormatter.format( (double)rxfreqhi / 1E6d ) );
}
else
{
sb.append( "000.00000" );
}
sb.append( " MHz" );
break;
case FQ_TXHI:
sb.append( "CHAN TX LOW LCN:" );
sb.append( format( getHomeRepeater(), 2 ) );
sb.append( " = " );
long txfreq = getFrequency();
if( txfreq > 0 )
{
sb.append( mDecimalFormatter.format( (double)txfreq / 1E6d ) );
}
else
{
sb.append( "000.00000" );
}
sb.append( " MHz" );
break;
case FQ_TXLO:
sb.append( "CHAN TX HIGH LCN:" );
sb.append( format( getHomeRepeater(), 2 ) );
sb.append( " = " );
long txFreqLo = getFrequency();
if( txFreqLo > 0 )
{
sb.append( mDecimalFormatter.format( (double)txFreqLo / 1E6d ) );
}
else
{
sb.append( "000.00000" );
}
sb.append( " MHz" );
break;
case ID_SITE:
String siteId = getSiteID();
if( siteId != null )
{
sb.append( "SITE ID: " );
sb.append( siteId );
sb.append( " " );
sb.append( getSiteIDAlias() );
}
break;
case ID_NBOR:
int neighborRank = getNeighborRank();
String neighborId = getNeighborID();
if( neighborRank != INT_NULL_VALUE &&
neighborId != null )
{
sb.append( "NEIGHBOR SITE " );
sb.append( neighborRank );
sb.append( ": " );
sb.append( neighborId );
sb.append( " " );
sb.append( getNeighborIDAlias() );
}
break;
case ID_UNIQ:
sb.append( "REGISTER RADIO UNIQUE ID:" );
sb.append( getRadioUniqueID() );
break;
default:
sb.append( "UNKNOWN " );
sb.append( getArea() );
sb.append( " " + format( getChannel(), 2 ) );
sb.append( " " + format( getHomeRepeater(), 2 ) );
sb.append( " " + format( getGroup(), 3 ) );
sb.append( " " + format( getFree(), 2 ) );
sb.append( " 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()
{
StringBuilder sb = new StringBuilder();
sb.append( getArea() );
sb.append( "-" );
sb.append( format( getHomeRepeater(), 2 ) );
sb.append( "-" );
sb.append( format( getGroup(), 3 ) );
return sb.toString();
}
public Alias getTalkgroupIDAlias()
{
if( mAliasList != null )
{
return mAliasList.getTalkgroupAlias( getTalkgroupID() );
}
return null;
}
public ArrayList<Integer> getChannelMapLow()
{
ArrayList<Integer> retVal = new ArrayList<Integer>();
if( mMessageType == MessageType.MA_CHNL )
{
for( int x = 27; x >= 18; x-- )
{
if( mMessage.get( x ) )
{
retVal.add( 28 - x );
}
}
}
return retVal;
}
public ArrayList<Integer> getChannelMapHigh()
{
ArrayList<Integer> retVal = new ArrayList<Integer>();
if( mMessageType == MessageType.MA_CHNH )
{
for( int x = 27; x >= 18; x-- )
{
if( mMessage.get( x ) )
{
retVal.add( 38 - x );
}
}
}
return retVal;
}
public String getChannelMapAsString( ArrayList<Integer> channels )
{
StringBuilder sb = new StringBuilder();
for( Integer channel: channels )
{
sb.append( channel );
sb.append( " " );
}
return sb.toString();
}
public int getRadioUniqueID()
{
int retVal = INT_NULL_VALUE;
if( mMessageType == MessageType.ID_UNIQ )
{
retVal = mMessage.getInt( 17, 32 );
}
return retVal;
}
public Alias getRadioUniqueIDAlias()
{
if( mAliasList != null )
{
return mAliasList.getUniqueID( getRadioUniqueID() );
}
return null;
}
public String getSiteID()
{
if( mMessageType == MessageType.ID_SITE )
{
return String.valueOf( mMessage.getInt( 23, 32 ) );
}
return null;
}
public Alias getSiteIDAlias()
{
if( mAliasList != null )
{
return mAliasList.getSiteID( getSiteID() );
}
return null;
}
public String getNeighborID()
{
if( mMessageType == MessageType.ID_NBOR )
{
int id = mMessage.getInt( 23, 32 );
if( id > 0 )
{
return String.valueOf( id );
}
}
return null;
}
private Alias getNeighborIDAlias()
{
if( mAliasList != null )
{
return mAliasList.getSiteID( getNeighborID() );
}
return null;
}
public int getNeighborRank()
{
int retVal = INT_NULL_VALUE;
if( mMessageType == MessageType.ID_NBOR )
{
retVal = mMessage.getInt( 15, 18 ) + 1;
}
return retVal;
}
public long getFrequency()
{
long retVal = 0;
if( mAuxMessage != null )
{
int high;
int low;
MessageType type = getMessageType();
if( type == MessageType.FQ_RXHI || type == MessageType.FQ_TXHI )
{
high = getHighChannelUnits();
low = mAuxMessage.getLowChannelUnits();
}
else
{
high = mAuxMessage.getHighChannelUnits();
low = getLowChannelUnits();
}
if( high != INT_NULL_VALUE && low != INT_NULL_VALUE )
{
retVal = ( high + low ) * 1250 + 150000000;
}
}
return retVal;
}
/**
* Returns the value of the high order (15 - 12) frequency bits which are
* the lower 4 bits of the Free field, left shifted by 12, representing the
* frequency value in units of .00125 MHz
*/
public int getHighChannelUnits()
{
int retVal = INT_NULL_VALUE;
if( mAuxMessage != null &&
( mMessageType == MessageType.FQ_RXHI ||
mMessageType == MessageType.FQ_TXHI ) )
{
retVal = mMessage.getInt( 29, 32 );
retVal = Integer.rotateLeft( retVal, 12 );
}
return retVal;
}
/**
* Returns the value of the low order (11 - 0) frequency bits which are
* contained in the Group and Free fields representing the frequency value
* units of .00125 MHz
*/
public int getLowChannelUnits()
{
int retVal = INT_NULL_VALUE;
if( mMessageType == MessageType.FQ_RXLO ||
mMessageType == MessageType.FQ_TXLO )
{
retVal = mMessage.getInt( 21, 32 );
}
return retVal;
}
public MessageType getType()
{
MessageType retVal = MessageType.UN_KNWN;
int channel = getChannel();
int home = getHomeRepeater();
//LTR-Net messages
if( home != 31 && ( channel > 20 || home > 20 ) )
{
switch( channel )
{
case CHIU_UNIQUE_ID:
retVal = MessageType.ID_UNIQ;
break;
case CHIU_SITE_ID:
retVal = MessageType.ID_SITE;
break;
case CHIU_TRANSMIT_FREQUENCY:
if( getHomeRepeater() == 24 )
{
retVal = MessageType.ID_UNIQ;
}
else
{
if( mMessage.get( CHANNEL_FREQUENCY_MESSAGE_TYPE_BIT ) )
{
retVal = MessageType.FQ_TXHI;
}
else
{
retVal = MessageType.FQ_TXLO;
}
}
break;
case CHIU_RECEIVE_FREQUENCY:
if( mMessage.get( CHANNEL_FREQUENCY_MESSAGE_TYPE_BIT ) )
{
retVal = MessageType.FQ_RXHI;
}
else
{
retVal = MessageType.FQ_RXLO;
}
break;
case CHIU_NEIGHBOR:
retVal = MessageType.ID_NBOR;
break;
case CHIU_MAP:
if( mMessage.get( CHANNEL_MAP_MESSAGE_TYPE_BIT ) )
{
retVal = MessageType.MA_CHNH;
}
else
{
retVal = MessageType.MA_CHNL;
}
break;
case CHIU_CALL_END:
retVal = MessageType.CA_ENDD;
}
}
else
{
int group = getGroup();
if( group == GROUP_CHANNEL_IDLE )
{
retVal = MessageType.SY_IDLE;
}
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 );
}
Alias neighbor = getNeighborIDAlias();
if( neighbor != null )
{
aliases.add( neighbor );
}
Alias uid = getRadioUniqueIDAlias();
if( uid != null )
{
aliases.add( uid );
}
Alias site = getSiteIDAlias();
if( site != null )
{
aliases.add( site );
}
return aliases;
}
}