/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014,2015 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.tait;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone;
import map.Plottable;
import message.Message;
import org.jdesktop.swingx.mapviewer.GeoPosition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import alias.Alias;
import alias.AliasList;
import bits.BinaryMessage;
import edac.CRC;
public class Tait1200GPSMessage extends Message
{
private final static Logger mLog = LoggerFactory.getLogger( Tait1200GPSMessage.class );
public static int[] REVS_1 = { 0,1,2,3 };
public static int[] SYNC = { 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 };
public static int[] SIZE = { 20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35 };
public static int[] FROM_DIGIT_1 = { 36,37,38,39,40,41,42,43 };
public static int[] FROM_DIGIT_2 = { 44,45,46,47,48,49,50,51 };
public static int[] FROM_DIGIT_3 = { 52,53,54,55,56,57,58,59 };
public static int[] FROM_DIGIT_4 = { 60,61,62,63,64,65,66,67 };
public static int[] FROM_DIGIT_5 = { 68,69,70,71,72,73,74,75 };
public static int[] FROM_DIGIT_6 = { 76,77,78,79,80,81,82,83 };
public static int[] FROM_DIGIT_7 = { 84,85,86,87,88,89,90,91 };
public static int[] FROM_DIGIT_8 = { 92,93,94,95,96,97,98,99 };
public static int[] CHECKSUM_1 = { 100,101,102,103,104,105,106,107,108,109,
110,111,112,113,114,115 };
public static int[] REVS_2 = { 116,117,118,119,120,121,122,123,124,125,126,127,
128,129,130,131 };
public static int[] SIZE_2 = { 188,189,190,191,192,193,194,195,196,197,198,
199,200,201,202,203 };
public static int[] TO_DIGIT_1 = { 204,205,206,207,208,209,210,211 };
public static int[] TO_DIGIT_2 = { 212,213,214,215,216,217,218,219 };
public static int[] TO_DIGIT_3 = { 220,221,222,223,224,225,226,227 };
public static int[] TO_DIGIT_4 = { 228,229,230,231,232,233,234,235 };
public static int[] TO_DIGIT_5 = { 236,237,238,239,240,241,242,243 };
public static int[] TO_DIGIT_6 = { 244,245,246,247,248,249,250,251 };
public static int[] TO_DIGIT_7 = { 252,253,254,255,256,257,258,259 };
public static int[] TO_DIGIT_8 = { 260,261,262,263,264,265,266,267 };
public static int[] UNKNOWN_1 = { 268,269,270,271,272,273,274,275 };
public static int[] CHECKSUM_2 = { 276,277,278,279,280,281,282,283,284,285,
286,287,288,289,290,291 };
public static int DIVIDER_1 = 292;
public static int[] HOUR_TENS = { 293,294,295 };
public static int[] HOUR_ONES = { 296,297,298,299 };
public static int DIVIDER_2 = 300;
public static int[] MINUTES_TENS = { 301,302,303 };
public static int[] MINUTES_ONES = { 304,305,306,307 };
public static int DIVIDER_3 = 308;
public static int[] SECONDS_TENS = { 309,310,311 };
public static int[] SECONDS_ONES = { 312,313,314,315 };
public static int DIVIDER_4 = 316;
public static int[] LATITUDE_SIGN = { 317,318 };
public static int DIVIDER_5 = 319;
public static int[] LATITUDE_DEGREES_TENS = { 320,321,322,323 };
public static int[] LATITUDE_DEGREES_ONES = { 324,325,326,327 };
public static int DIVIDER_6 = 328;
public static int[] LATITUDE_MINUTES_TENS = { 329,330,331 };
public static int[] LATITUDE_MINUTES_ONES = { 332,333,334,335 };
public static int[] LATITUDE_SECONDS_HUND = { 336,337,338,339 };
public static int[] LATITUDE_SECONDS_TENS = { 340,341,342,344 };
public static int[] LATITUDE_SECONDS_ONES = { 344,345,346,347 };
public static int DIVIDER_7 = 348;
public static int[] LONGITUDE_SIGN = { 349,350 };
public static int LONGITUDE_DEGREES_HUNDREDS = 351;
public static int[] LONGITUDE_DEGREES_TENS = { 352,353,354,355 };
public static int[] LONGITUDE_DEGREES_ONES = { 356,357,358,359 };
public static int DIVIDER_9 = 360;
public static int[] LONGITUDE_MINUTES_TENS = { 361,362,363 };
public static int[] LONGITUDE_MINUTES_ONES = { 364,365,366,367 };
public static int[] LONGITUDE_SECONDS_HUND = { 368,369,370,371 };
public static int[] LONGITUDE_SECONDS_TENS = { 372,373,374,375 };
public static int[] LONGITUDE_SECONDS_ONES = { 376,377,378,379 };
public static int DIVIDER_10 = 380;
public static int[] DATE_DAY_TENS = { 381,382,383 };
public static int[] DATE_DAY_ONES = { 384,385,386,387 };
//TODO: not sure if this field is the month field or the speed field
public static int[] DATE_MONTH = { 388,389,390,391 };
public static int[] SPEED_HUNDREDS = { 388,389,390,391 };
public static int[] SPEED_TENS = { 392,393,394,395 };
public static int[] SPEED_ONES = { 396,397,398,399 };
public static int[] SPEED_TENTHS = { 400,401,402,403 };
private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" );
private BinaryMessage mMessage;
private AliasList mAliasList;
private CRC mCRC;
public Tait1200GPSMessage( BinaryMessage message, AliasList list )
{
mMessage = message;
mAliasList = list;
mLog.debug( toString() );
}
public boolean isValid()
{
//TODO: Override until we figure out the CRC
return true;
}
public int getMessage1Size()
{
return mMessage.getInt( SIZE );
}
public int getMessage2Size()
{
return mMessage.getInt( SIZE_2 );
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( "GPS FROM:" );
sb.append( getFromID() );
sb.append( " TO:" );
sb.append( getToID() );
sb.append( " LOCATION:" );
GeoPosition location = getGPSLocation();
sb.append( location.getLatitude() );
sb.append( " " );
sb.append( location.getLongitude() );
sb.append( " SPEED:" );
sb.append( getSpeed() );
sb.append( "KPH" );
sb.append( " GPS TIME:" );
sb.append( mSDF.format( new Date( getGPSTime() ) ) );
sb.append( " " );
sb.append( mMessage.toString() );
return sb.toString();
}
public GeoPosition getGPSLocation()
{
double latitude = mMessage.getInt( LATITUDE_DEGREES_TENS ) * 10.0d;
latitude += mMessage.getInt( LATITUDE_DEGREES_ONES );
latitude += (double)mMessage.getInt( LATITUDE_MINUTES_TENS ) / 6.0d;
latitude += (double)mMessage.getInt( LATITUDE_MINUTES_ONES ) / 60.0d;
latitude += (double)mMessage.getInt( LATITUDE_SECONDS_HUND ) / 600.0d;
latitude += (double)mMessage.getInt( LATITUDE_SECONDS_TENS ) / 6000.0d;
latitude += (double)mMessage.getInt( LATITUDE_SECONDS_ONES ) / 60000.0d;
if( mMessage.getInt( LATITUDE_SIGN ) == 0 )
{
latitude *= -1;
}
double longitude = mMessage.get( LONGITUDE_DEGREES_HUNDREDS ) ? 100.0d : 0.0d;
longitude += mMessage.getInt( LONGITUDE_DEGREES_TENS ) * 10.0d;
longitude += mMessage.getInt( LONGITUDE_DEGREES_ONES );
longitude += (double)mMessage.getInt( LONGITUDE_MINUTES_TENS ) / 6.0d;
longitude += (double)mMessage.getInt( LONGITUDE_MINUTES_ONES ) / 60.0d;
longitude += (double)mMessage.getInt( LONGITUDE_SECONDS_HUND ) / 600.0d;
longitude += (double)mMessage.getInt( LONGITUDE_SECONDS_TENS ) / 6000.0d;
longitude += (double)mMessage.getInt( LONGITUDE_SECONDS_ONES ) / 60000.0d;
if( mMessage.getInt( LONGITUDE_SIGN ) == 0 )
{
longitude = -1;
}
return new GeoPosition( latitude, longitude );
}
public long getGPSTime()
{
Calendar cal = new GregorianCalendar( TimeZone.getTimeZone( "UTC" ) );
cal.clear();
/* Use current time to get current year */
cal.setTimeInMillis( System.currentTimeMillis() );
//TODO: this field is either the month field, or the speed hundredths field
cal.set( Calendar.MONTH, mMessage.getInt( DATE_MONTH ) );
int day = mMessage.getInt( DATE_DAY_TENS ) * 10 +
mMessage.getInt( DATE_DAY_ONES );
cal.set( Calendar.DAY_OF_MONTH, day );
cal.set( Calendar.HOUR_OF_DAY, ( mMessage.getInt( HOUR_TENS ) * 10 ) +
mMessage.getInt( HOUR_ONES ) );
cal.set( Calendar.MINUTE, ( mMessage.getInt( MINUTES_TENS ) * 10 ) +
mMessage.getInt( MINUTES_ONES ) );
cal.set( Calendar.SECOND, ( mMessage.getInt( SECONDS_TENS ) * 10 ) +
mMessage.getInt( SECONDS_ONES ) );
cal.set( Calendar.MILLISECOND, 0 );
return cal.getTimeInMillis();
}
public String getSpeed()
{
StringBuilder sb = new StringBuilder();
sb.append( getDigit( SPEED_HUNDREDS ) );
sb.append( getDigit( SPEED_TENS ) );
sb.append( getDigit( SPEED_ONES ) );
sb.append( "." );
sb.append( getDigit( SPEED_TENTHS ) );
return sb.toString();
}
private String getDigit( int[] field )
{
int value = mMessage.getInt( field );
return ( 0 <= value && value <= 9 ) ? String.valueOf( value ) : "?";
}
@Override
public String getFromID()
{
StringBuilder sb = new StringBuilder();
sb.append( getCharacter( FROM_DIGIT_1 ) );
sb.append( getCharacter( FROM_DIGIT_2 ) );
sb.append( getCharacter( FROM_DIGIT_3 ) );
sb.append( getCharacter( FROM_DIGIT_4 ) );
sb.append( getCharacter( FROM_DIGIT_5 ) );
sb.append( getCharacter( FROM_DIGIT_6 ) );
sb.append( getCharacter( FROM_DIGIT_7 ) );
sb.append( getCharacter( FROM_DIGIT_8 ) );
return sb.toString();
}
@Override
public Alias getFromIDAlias()
{
if( mAliasList != null )
{
return mAliasList.getTalkgroupAlias( getFromID() );
}
return null;
}
@Override
public String getToID()
{
StringBuilder sb = new StringBuilder();
sb.append( getCharacter( TO_DIGIT_1 ) );
sb.append( getCharacter( TO_DIGIT_2 ) );
sb.append( getCharacter( TO_DIGIT_3 ) );
sb.append( getCharacter( TO_DIGIT_4 ) );
sb.append( getCharacter( TO_DIGIT_5 ) );
sb.append( getCharacter( TO_DIGIT_6 ) );
sb.append( getCharacter( TO_DIGIT_7 ) );
sb.append( getCharacter( TO_DIGIT_8 ) );
return sb.toString();
}
@Override
public Alias getToIDAlias()
{
if( mAliasList != null )
{
return mAliasList.getTalkgroupAlias( getToID() );
}
return null;
}
public char getCharacter( int[] bits )
{
int value = mMessage.getInt( bits );
return (char)value;
}
/**
* Pads spaces onto the end of the value to make it 'places' long
*/
public String pad( String value, int places, String padCharacter )
{
StringBuilder sb = new StringBuilder();
sb.append( value );
while( sb.length() < places )
{
sb.append( padCharacter );
}
return sb.toString();
}
/**
* 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();
}
@Override
public String getBinaryMessage()
{
return mMessage.toString();
}
@Override
public String getProtocol()
{
return "Tait-1200";
}
@Override
public String getEventType()
{
return "GPS";
}
@Override
public String getMessage()
{
return toString();
}
@Override
public String getErrorStatus()
{
return "UNKNOWN";
// return mCRC.getDisplayText();
}
@Override
public Plottable getPlottable()
{
return new Plottable( getGPSTime(), getGPSLocation(), getFromID(),
getFromIDAlias() );
}
/**
* 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;
}
}