Initial code check in continued

This commit is contained in:
dennis.sheirer
2014-04-26 19:48:24 +00:00
parent 960a6f9952
commit 3dfedcf8d1
474 changed files with 76613 additions and 0 deletions

View File

@@ -0,0 +1,233 @@
/*******************************************************************************
* 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 decode.passport;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.TreeSet;
import message.Message;
import sample.Listener;
import alias.Alias;
import alias.AliasList;
import controller.activity.ActivitySummaryProvider;
public class PassportActivitySummary implements ActivitySummaryProvider,
Listener<Message>
{
private static final int sINT_NULL_VALUE = -1;
private DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000");
private AliasList mAliasList;
private HashSet<String> mTalkgroupsFirstHeard = new HashSet<String>();
private TreeSet<String> mTalkgroups = new TreeSet<String>();
private TreeSet<String> mMobileIDs = new TreeSet<String>();
private HashMap<Integer,String> mSiteLCNs = new HashMap<Integer,String>();
private HashMap<Integer,String> mNeighborLCNs = new HashMap<Integer,String>();
/**
* Compiles a summary of active talkgroups, unique radio ids encountered
* in the decoded messages. Talkgroups have to be heard a minimum of twice
* to be considered valid ... in order to weed out the invalid ones.
*
* Returns a textual summary of the activity
*/
public PassportActivitySummary( AliasList list )
{
mAliasList = list;
}
@Override
public void receive( Message message )
{
if( message instanceof PassportMessage )
{
PassportMessage passport = ((PassportMessage)message);
if( passport.isValid() )
{
switch( passport.getMessageType() )
{
case CA_STRT:
mSiteLCNs.put( passport.getLCN(),
passport.getLCNFrequencyFormatted() );
case CA_ENDD:
String talkgroup =
String.valueOf( passport.getTalkgroupID() );
if( mTalkgroupsFirstHeard.contains( talkgroup ) )
{
mTalkgroups.add( talkgroup );
}
else
{
mTalkgroupsFirstHeard.add( talkgroup );
}
break;
case ID_RDIO:
String min = passport.getMobileID();
if( min != null )
{
mMobileIDs.add( min );
}
break;
case SY_IDLE:
if( passport.getFree() != 0 )
{
mNeighborLCNs.put( passport.getFree(),
passport.getFreeFrequencyFormatted() );
}
break;
default:
break;
}
}
}
}
@Override
public String getSummary()
{
StringBuilder sb = new StringBuilder();
sb.append( "Activity Summary\n" );
sb.append( "Decoder:\tPassport\n\n" );
sb.append( "Site Channels\n" );
if( mSiteLCNs.isEmpty() )
{
sb.append( " None\n" );
}
else
{
ArrayList<Integer> channels = new ArrayList( mSiteLCNs.keySet() );
Collections.sort( channels );
for( Integer channel: channels )
{
sb.append( " " + channel );
sb.append( "\t" + mSiteLCNs.get( channel ) );
sb.append( "\n" );
}
}
sb.append( "\nNeighbor Channels\n" );
if( mNeighborLCNs.isEmpty() )
{
sb.append( " None\n" );
}
else
{
ArrayList<Integer> channels = new ArrayList( mNeighborLCNs.keySet() );
Collections.sort( channels );
for( Integer channel: channels )
{
sb.append( " " + channel );
sb.append( "\t" + mNeighborLCNs.get( channel ) );
sb.append( "\n" );
}
}
sb.append( "\nTalkgroups\n" );
if( mTalkgroups.isEmpty() )
{
sb.append( " None\n" );
}
else
{
Iterator<String> it = mTalkgroups.iterator();
while( it.hasNext() )
{
String tgid = it.next();
sb.append( " " );
sb.append( tgid );
sb.append( " " );
Alias alias = mAliasList.getTalkgroupAlias( tgid );
if( alias != null )
{
sb.append( alias.getName() );
}
sb.append( "\n" );
}
}
sb.append( "\nMobile ID Numbers\n" );
if( mMobileIDs.isEmpty() )
{
sb.append( " None\n" );
}
else
{
Iterator<String> it = mMobileIDs.iterator();
while( it.hasNext() )
{
String min = it.next();
sb.append( " " );
sb.append( min );
sb.append( " " );
Alias alias = mAliasList.getMobileIDNumberAlias( min );
if( alias != null )
{
sb.append( alias.getName() );
}
sb.append( "\n" );
}
}
return sb.toString();
}
public static String formatTalkgroup( String talkgroup )
{
StringBuilder sb = new StringBuilder();
if( talkgroup.length() == 6 )
{
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();
}
else
{
return talkgroup;
}
}
}

View File

@@ -0,0 +1,71 @@
/*******************************************************************************
* 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 decode.passport;
public enum PassportBand
{
BAND_800( "800", 800000000 ),
BAND_900( "900", 900000000 ),
BAND_400( "400", 400000000 ),
BAND_420( "420", 420000000 ),
BAND_440( "440", 440000000 ),
BAND_450( "450", 450000000 ),
BAND_460( "460", 460000000 ),
BAND_470( "470", 470000000 ),
BAND_480( "480", 480000000 ),
BAND_490( "490", 490000000 ),
BAND_CANADA( "CAN", 0 ),
BAND_NTIA( "NTIA", 0 ),
BAND_RESERVED12( "RESVD", 0 ),
BAND_216( "216", 216000000 ),
BAND_RESERVED14( "RESVD", 0 ),
BAND_700( "700", 700000000 ),
BAND_VHF( "VHF", 0 ),
BAND_UNKNOWN( "UNK", 0 );
private String mDescription;
private int mBase;
private PassportBand( String description, int base )
{
mDescription = description;
mBase = base;
}
public static PassportBand lookup( int band )
{
if( band >= 0 && band < 16 )
{
return PassportBand.values()[ band ];
}
else
{
return PassportBand.BAND_UNKNOWN;
}
}
public String getDescription()
{
return mDescription;
}
public int getBase()
{
return mBase;
}
}

View File

@@ -0,0 +1,164 @@
/*******************************************************************************
* 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 decode.passport;
import alias.Alias;
import alias.AliasList;
import controller.activity.CallEvent;
import decode.DecoderType;
public class PassportCallEvent extends CallEvent
{
private int mChannel;
private int mFrequency;
public PassportCallEvent( CallEventType callEventType,
AliasList aliasList,
String fromID,
String toID,
int channel,
int frequency,
String details )
{
super( DecoderType.PASSPORT, callEventType,
aliasList, fromID, toID, details );
mChannel = channel;
mFrequency = frequency;
}
private Alias getAlias( String talkgroup )
{
if( hasAliasList() )
{
return getAliasList().getTalkgroupAlias( talkgroup );
}
return null;
}
private Alias getMobileIDAlias( String mobileID )
{
if( hasAliasList() )
{
return getAliasList().getMobileIDNumberAlias( mobileID );
}
return null;
}
@Override
public Alias getFromIDAlias()
{
return getMobileIDAlias( getFromID() );
}
@Override
public Alias getToIDAlias()
{
return getAlias( getToID() );
}
@Override
public int getChannel()
{
return mChannel;
}
@Override
public int getFrequency()
{
return mFrequency;
}
public static class Builder
{
/* Required parameters */
private CallEventType mCallEventType;
/* Optional parameters */
private AliasList mAliasList;
private String mFromID;
private String mToID;
private String mDetails;
private int mChannel;
private int mFrequency;
public Builder( CallEventType callEventType )
{
mCallEventType = callEventType;
}
public Builder aliasList( AliasList aliasList )
{
mAliasList = aliasList;
return this;
}
public Builder from( String val )
{
mFromID = val;
return this;
}
public Builder channel( int channel )
{
mChannel = channel;
return this;
}
public Builder details( String details )
{
mDetails = details;
return this;
}
public Builder frequency( int frequency )
{
mFrequency = frequency;
return this;
}
public Builder to( String toID )
{
mToID = toID;
return this;
}
public PassportCallEvent build()
{
return new PassportCallEvent( this );
}
}
/**
* Private constructor for the builder
*/
private PassportCallEvent( Builder builder )
{
this( builder.mCallEventType,
builder.mAliasList,
builder.mFromID,
builder.mToID,
builder.mChannel,
builder.mFrequency,
builder.mDetails );
}
}

View File

@@ -0,0 +1,236 @@
/*******************************************************************************
* 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 decode.passport;
import message.Message;
import alias.Alias;
import alias.AliasList;
import audio.AudioType;
import audio.SquelchListener;
import controller.activity.CallEvent.CallEventType;
import controller.channel.ProcessingChain;
import controller.state.AuxChannelState;
import controller.state.ChannelState;
public class PassportChannelState extends ChannelState
{
private String mTalkgroup;
private Alias mTalkgroupAlias;
private String mMobileID;
private Alias mMobileIDAlias;
private int mChannelNumber;
private int mFrequency;
private PassportCallEvent mCurrentCallEvent;
private PassportActivitySummary mActivitySummary;
public PassportChannelState( ProcessingChain channel, AliasList aliasList )
{
super( channel, aliasList );
mActivitySummary = new PassportActivitySummary( aliasList );
}
@Override
public String getActivitySummary()
{
StringBuilder sb = new StringBuilder();
sb.append( mActivitySummary.getSummary() );
for( AuxChannelState state: mAuxChannelStates )
{
sb.append( state.getActivitySummary() );
sb.append( "\n\n" );
}
return sb.toString();
}
@Override
@SuppressWarnings( "incomplete-switch" )
public void receive( Message message )
{
super.receive( message );
if( message instanceof PassportMessage )
{
mActivitySummary.receive( message );
PassportMessage passport = (PassportMessage)message;
if( passport.isValid() )
{
switch( passport.getMessageType() )
{
case CA_STRT:
setTalkgroup( String.valueOf( passport.getTalkgroupID() ) );
setTalkgroupAlias( passport.getTalkgroupIDAlias() );
if( mTalkgroupAlias != null )
{
try
{
if( mTalkgroupAlias.hasAudioType() )
{
mAudioTypeListener.setAudioType(
mTalkgroupAlias.getAudioType() );
}
}
catch( Exception e )
{
e.printStackTrace();
}
}
mChannelNumber = passport.getLCN();
mFrequency = passport.getLCNFrequency();
if( getState() != State.CALL )
{
mCurrentCallEvent = new PassportCallEvent
.Builder( CallEventType.CALL_START )
.aliasList( mAliasList )
.channel( mChannelNumber )
.frequency( mFrequency )
.to( String.valueOf( passport.getTalkgroupID() ) )
.build();
mCallEventModel.add( mCurrentCallEvent );
}
setState( State.CALL );
break;
case DA_STRT:
setTalkgroup( String.valueOf( passport.getTalkgroupID() ) );
setTalkgroupAlias( passport.getTalkgroupIDAlias() );
setState( State.DATA );
break;
case CA_ENDD:
setTalkgroup( String.valueOf( passport.getTalkgroupID() ) );
setTalkgroupAlias( passport.getTalkgroupIDAlias() );
fade( CallEventType.CALL_END );
break;
case ID_RDIO:
setMobileID( passport.getMobileID() );
setMobileIDAlias( passport.getMobileIDAlias() );
if( mCurrentCallEvent != null )
{
mCurrentCallEvent.setFromID( mMobileID );
}
setState( State.CALL );
break;
}
}
}
}
/**
* Intercept the fade event so that we can generate a call end event
*/
@Override
public void fade( final CallEventType type )
{
/*
* We can receive multiple call tear-down messages -- only post a call
* end event for the one that can change the state to fade
*/
if( getState().canChangeTo( State.FADE ) )
{
mCallEventModel.add(
new PassportCallEvent.Builder( type )
.aliasList( getAliasList() )
.channel( mChannelNumber )
.frequency( mProcessingChain.getChannel()
.getTunerChannel().getFrequency() )
.to( mTalkgroup )
.build() );
}
super.fade( type );
}
/**
* Make the ConventionalChannelState always unsquelched
*/
public void addListener( SquelchListener listener )
{
super.addListener( listener );
}
public void reset()
{
mAudioTypeListener.setAudioType( AudioType.NORMAL );
mCurrentCallEvent = null;
setTalkgroup( null );
setTalkgroupAlias( null );
setMobileID( null );
setMobileIDAlias( null );
super.reset();
}
public String getTalkgroup()
{
return mTalkgroup;
}
public void setTalkgroup( String talkgroup )
{
mTalkgroup = talkgroup;
broadcastChange( ChangedAttribute.TO_TALKGROUP );
}
public Alias getTalkgroupAlias()
{
return mTalkgroupAlias;
}
public void setTalkgroupAlias( Alias alias )
{
mTalkgroupAlias = alias;
broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS );
}
public String getMobileID()
{
return mMobileID;
}
public void setMobileID( String id )
{
mMobileID = id;
broadcastChange( ChangedAttribute.FROM_TALKGROUP );
}
public Alias getMobileIDAlias()
{
return mMobileIDAlias;
}
public void setMobileIDAlias( Alias alias )
{
mMobileIDAlias = alias;
broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS );
}
}

View File

@@ -0,0 +1,108 @@
/*******************************************************************************
* 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 decode.passport;
import source.Source.SampleType;
import alias.AliasList;
import bits.MessageFramer;
import bits.SyncPattern;
import decode.Decoder;
import decode.DecoderType;
import decode.ltrstandard.LTRFSKDemodulator;
import dsp.filter.DCRemovalFilter2;
import dsp.nbfm.FilteringNBFMDemodulator;
public class PassportDecoder extends Decoder
{
/**
* This value determines how quickly the DC remove filter responds to
* changes in frequency.
*/
private static final double sDC_REMOVAL_RATIO = 0.000003;
public static final int sPASSPORT_MESSAGE_LENGTH = 68;
public static final int sPASSPORT_SYNC_LENGTH = 9;
private FilteringNBFMDemodulator mNBFMDemodulator;
private DCRemovalFilter2 mDCRemovalFilter;
private LTRFSKDemodulator mPassportFSKDemodulator;
private MessageFramer mPassportMessageFramer;
private PassportMessageProcessor mPassportMessageProcessor;
public PassportDecoder( SampleType sampleType, AliasList aliasList )
{
super( sampleType );
/**
* Only setup a demod chain if we're receiving complex samples. If
* we're receiving demodulated samples, they'll be handled the same
* was as we handle the output of the demodulator.
*/
if( mSourceSampleType == SampleType.COMPLEX )
{
/**
* The Decoder super class is both a Complex listener and a Short
* listener. So, we add the demod to listen to the incoming
* quadrature samples, and we wire the output of the demod right
* back to this class, so we can receive the demodulated output
* to process
*/
mNBFMDemodulator = new FilteringNBFMDemodulator();
this.addComplexListener( mNBFMDemodulator );
/**
* Remove the DC component that is present when we're mistuned
*/
mDCRemovalFilter = new DCRemovalFilter2( sDC_REMOVAL_RATIO );
mNBFMDemodulator.addListener( mDCRemovalFilter );
mDCRemovalFilter.setListener( this.getFloatReceiver() );
}
mPassportFSKDemodulator =
new LTRFSKDemodulator();
/**
* The DC removal filter will impact performance of the LTRFSKDemod,
* so we get the samples straight from the NBFM demod
*/
if( mSourceSampleType == SampleType.COMPLEX )
{
mNBFMDemodulator.addListener( mPassportFSKDemodulator );
}
else
{
addFloatListener( mPassportFSKDemodulator );
}
mPassportMessageFramer =
new MessageFramer( SyncPattern.PASSPORT.getPattern(),
sPASSPORT_MESSAGE_LENGTH );
mPassportFSKDemodulator.addListener( mPassportMessageFramer );
mPassportMessageProcessor = new PassportMessageProcessor( aliasList );
mPassportMessageFramer.addMessageListener( mPassportMessageProcessor );
mPassportMessageProcessor.addMessageListener( this );
}
@Override
public DecoderType getType()
{
return DecoderType.PASSPORT;
}
}

View File

@@ -0,0 +1,31 @@
/*******************************************************************************
* 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 decode.passport;
import decode.DecodeEditor;
import decode.config.DecodeConfiguration;
public class PassportEditor extends DecodeEditor
{
private static final long serialVersionUID = 1L;
public PassportEditor( DecodeConfiguration config )
{
super( config );
}
}

View File

@@ -0,0 +1,634 @@
/*******************************************************************************
* 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 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 static final int sINT_NULL_VALUE = -1;
private static final double sDOUBLE_NULL_VALUE = -1.0D;
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();
return mAliasList.getTalkgroupAlias( String.valueOf( tg ) );
}
public int getLCN()
{
return getInt( sLCN );
}
public int 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 getFrequency( int base, int channel )
{
return ( base + ( channel * 12500 ) );
}
public int getFree()
{
return getInt( sFREE );
}
public int getFreeFrequency()
{
return getSiteFrequency( getFree() );
}
public String getFreeFrequencyFormatted()
{
return mDecimalFormatter.format( (double)getFreeFrequency() / 1000000.0d );
}
public int getNeighborFrequency()
{
int retVal = 0;
if( getMessageType() == MessageType.SY_IDLE )
{
PassportBand band = getNeighborBand();
retVal = getFrequency( band.getBase(), getFree() );
}
return retVal;
}
/**
* 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( 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<<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();
}
@Override
public String getProtocol()
{
return DecoderType.PASSPORT.getDisplayString();
}
@Override
public String getEventType()
{
// TODO Auto-generated method stub
return null;
}
@Override
public String getFromID()
{
// TODO Auto-generated method stub
return null;
}
@Override
public Alias getFromIDAlias()
{
// TODO Auto-generated method stub
return null;
}
@Override
public String getToID()
{
// TODO Auto-generated method stub
return null;
}
@Override
public Alias getToIDAlias()
{
// TODO Auto-generated method stub
return null;
}
public int getSiteFrequency( int channel )
{
int freq = 0;
if( mIdleMessage != null && 0 < channel && channel < 1792 )
{
PassportBand band = mIdleMessage.getSiteBand();
if( band != PassportBand.BAND_UNKNOWN )
{
int base = band.getBase();
freq = getFrequency( base, channel );
}
}
return freq;
}
@Override
public String getMessage()
{
StringBuilder sb = new StringBuilder();
switch( getMessageType() )
{
case SY_IDLE:
sb.append( "IDLE SITE:" );
sb.append( format( getSite(), 3 ) );
sb.append( " NEIGHBOR:" );
sb.append( format( getFree(), 3 ) );
sb.append( "/" );
sb.append( getFreeFrequencyFormatted() );
break;
case CA_PAGE:
sb.append( "PAGING TG:" );
sb.append( format( getTalkgroupID(), 5 ) );
sb.append( "/" );
Alias pageAlias = getTalkgroupIDAlias();
if( pageAlias != null )
{
sb.append( pageAlias.getName() );
}
else
{
sb.append( sUNKNOWN );
}
sb.append( " SITE:" );
sb.append( format( getSite(), 3 ) );
sb.append( " CHAN:" );
sb.append( format( getLCN(), 4 ) );
sb.append( "/" );
sb.append( getLCNFrequencyFormatted() );
sb.append( " FREE:" );
sb.append( format( getFree(), 3 ) );
sb.append( "/" );
sb.append( getFreeFrequencyFormatted() );
break;
case CA_STRT:
sb.append( "CALL TG:" );
sb.append( format( getTalkgroupID(), 5 ) );
sb.append( "/" );
Alias startAlias = getTalkgroupIDAlias();
if( startAlias != null )
{
sb.append( startAlias.getName() );
}
else
{
sb.append( sUNKNOWN );
}
sb.append( " SITE:" );
sb.append( format( getSite(), 3 ) );
sb.append( " CHAN:" );
sb.append( format( getLCN(), 4 ) );
sb.append( "/" );
sb.append( getLCNFrequencyFormatted() );
sb.append( " FREE:" );
sb.append( format( getFree(), 3 ) );
sb.append( "/" );
sb.append( getFreeFrequencyFormatted() );
break;
case DA_STRT:
sb.append( "** DATA TG:" );
sb.append( format( getTalkgroupID(), 5 ) );
sb.append( "/" );
Alias dataStartAlias = getTalkgroupIDAlias();
if( dataStartAlias != null )
{
sb.append( dataStartAlias.getName() );
}
else
{
sb.append( sUNKNOWN );
}
sb.append( " SITE:" );
sb.append( format( getSite(), 3 ) );
sb.append( " CHAN:" );
sb.append( format( getLCN(), 4 ) );
sb.append( "/" );
sb.append( getLCNFrequencyFormatted() );
sb.append( " FREE:" );
sb.append( format( getFree(), 3 ) );
sb.append( "/" );
sb.append( getFreeFrequencyFormatted() );
break;
case CA_ENDD:
sb.append( "END TG:" );
sb.append( format( getTalkgroupID(), 5 ) );
sb.append( "/" );
Alias endAlias = getTalkgroupIDAlias();
if( endAlias != null )
{
sb.append( endAlias.getName() );
}
else
{
sb.append( sUNKNOWN );
}
sb.append( " SITE:" );
sb.append( format( getSite(), 3 ) );
sb.append( " CHAN:" );
sb.append( format( getLCN(), 4 ) );
sb.append( "/" );
sb.append( getLCNFrequencyFormatted() );
sb.append( " FREE:" );
sb.append( format( getFree(), 3 ) );
sb.append( "/" );
sb.append( getFreeFrequencyFormatted() );
break;
case ID_RDIO:
sb.append( "MOBILE ID MIN:" );
sb.append( getMobileID() );
sb.append( "/" );
Alias mobileIDAlias = getMobileIDAlias();
if( mobileIDAlias != null )
{
sb.append( mobileIDAlias.getName() );
}
else
{
sb.append( "UNKNOWN" );
}
sb.append( " FREE:" );
sb.append( format( getFree(), 3 ) );
sb.append( "/" );
sb.append( getFreeFrequencyFormatted() );
break;
case ID_TGAS:
sb.append( "ASSIGN TALKGROUP:" );
sb.append( format( getTalkgroupID(), 5 ) );
sb.append( "/" );
Alias assignAlias = getTalkgroupIDAlias();
if( assignAlias != null )
{
sb.append( assignAlias.getName() );
}
else
{
sb.append( sUNKNOWN );
}
sb.append( " SITE:" );
sb.append( format( getSite(), 3 ) );
sb.append( " CHAN:" );
sb.append( format( getLCN(), 4 ) );
sb.append( "/" );
sb.append( getLCNFrequencyFormatted() );
break;
case RA_REGI:
sb.append( "RADIO REGISTER TG: " );
sb.append( format( getTalkgroupID(), 5 ) );
sb.append( "/" );
Alias regAlias = getTalkgroupIDAlias();
if( regAlias != null )
{
sb.append( regAlias.getName() );
}
else
{
sb.append( sUNKNOWN );
}
break;
default:
sb.append( "UNKNOWN SITE:" );
sb.append( format( getSite(), 3 ) );
sb.append( " CHAN:" );
sb.append( format( getLCN(), 4 ) );
sb.append( "/" );
sb.append( getLCNFrequencyFormatted() );
sb.append( " FREE:" );
int free = getFree();
sb.append( format( free, 3 ) );
if( free > 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;
}
}

View File

@@ -0,0 +1,109 @@
/*******************************************************************************
* 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 decode.passport;
import java.util.HashMap;
import message.Message;
import message.MessageType;
import sample.Broadcaster;
import sample.Listener;
import alias.AliasList;
import bits.BitSetBuffer;
public class PassportMessageProcessor implements Listener<BitSetBuffer>
{
private Broadcaster<Message> mBroadcaster = new Broadcaster<Message>();
private IdleMessageFinder mIdleFinder = new IdleMessageFinder();
private AliasList mAliasList;
private PassportMessage mIdleMessage;
public PassportMessageProcessor( AliasList list )
{
mAliasList = list;
}
@Override
public void receive( BitSetBuffer buffer )
{
PassportMessage message;
if( mIdleMessage != null )
{
message = new PassportMessage( buffer, mIdleMessage, mAliasList );
}
else
{
message = new PassportMessage( buffer, mAliasList );
mIdleFinder.receive( message );
}
mBroadcaster.receive( message );
}
public void addMessageListener( Listener<Message> listener )
{
mBroadcaster.addListener( listener );
}
public void removeMessageListener( Listener<Message> listener )
{
mBroadcaster.removeListener( listener );
}
public class IdleMessageFinder
{
public HashMap<String,Integer> mMessageCounts =
new HashMap<String,Integer>();
public boolean mIdleMessageFound = false;
public IdleMessageFinder()
{
}
public void receive( PassportMessage message )
{
if( !mIdleMessageFound &&
message.isValid() &&
message.getMessageType() == MessageType.SY_IDLE )
{
if( mMessageCounts.containsKey( message.getBinaryMessage() ) )
{
int count = mMessageCounts.get( message.getBinaryMessage() );
if( count >= 3 )
{
mIdleMessageFound = true;
mIdleMessage = message;
mMessageCounts = null;
}
else
{
count++;
mMessageCounts.put( message.getBinaryMessage(), count );
}
}
else
{
mMessageCounts.put( message.getBinaryMessage(), 1 );
}
}
}
}
}

View File

@@ -0,0 +1,255 @@
/*******************************************************************************
* 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 decode.passport;
import java.awt.EventQueue;
import javax.swing.JLabel;
import settings.ColorSetting;
import settings.Setting;
import settings.SettingsManager;
import alias.Alias;
import controller.channel.Channel;
import controller.state.ChannelState.ChangedAttribute;
import controller.state.ChannelStatePanel;
public class PassportPanel extends ChannelStatePanel
{
private static final long serialVersionUID = 1L;
private JLabel mStateLabel;
private JLabel mSourceLabel;
private JLabel mChannelLabel;
private JLabel mProtocol = new JLabel( "Passport" );
private JLabel mToTalkgroup = new JLabel();
private JLabel mToTalkgroupAlias = new JLabel();
private JLabel mFromTalkgroupType = new JLabel( " " );
private JLabel mFromTalkgroup = new JLabel();
private JLabel mFromTalkgroupAlias = new JLabel();
private SettingsManager mSettingsManager;
public PassportPanel( SettingsManager settingsManager, Channel channel )
{
super( channel );
mSettingsManager = settingsManager;
init();
}
public void dispose()
{
super.dispose();
mSettingsManager = null;
}
public void init()
{
mStateLabel = new JLabel( mChannel.getProcessingChain().
getChannelState().getState().getDisplayValue() );
mStateLabel.setFont( mFontDecoder );
mStateLabel.setForeground( mColorLabelDecoder );
mSourceLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() );
mSourceLabel.setFont( mFontDetails );
mSourceLabel.setForeground( mColorLabelDetails );
mChannelLabel = new JLabel( mChannel.getChannelDisplayName() );
mChannelLabel.setFont( mFontDetails );
mChannelLabel.setForeground( mColorLabelDetails );
mProtocol.setFont( mFontDecoder );
mProtocol.setForeground( mColorLabelDecoder );
mToTalkgroup.setFont( mFontDecoder );
mToTalkgroup.setForeground( mColorLabelDecoder );
mToTalkgroupAlias.setFont( mFontDecoder );
mToTalkgroupAlias.setForeground( mColorLabelDecoder );
mFromTalkgroupType.setFont( mFontDecoder );
mFromTalkgroupType.setForeground( mColorLabelDecoder );
mFromTalkgroup.setFont( mFontDecoder );
mFromTalkgroup.setForeground( mColorLabelDecoder );
mFromTalkgroupAlias.setFont( mFontDecoder );
mFromTalkgroupAlias.setForeground( mColorLabelDecoder );
add( mStateLabel );
add( mSourceLabel );
add( mChannelLabel, "wrap" );
add( mProtocol );
add( mToTalkgroup );
add( mToTalkgroupAlias, "wrap" );
add( mFromTalkgroupType );
add( mFromTalkgroup );
add( mFromTalkgroupAlias, "wrap" );
}
@Override
public void receive( final ChangedAttribute changedAttribute )
{
EventQueue.invokeLater( new Runnable()
{
@Override
public void run()
{
PassportChannelState state = (PassportChannelState)getChannel()
.getProcessingChain().getChannelState();
switch( changedAttribute )
{
case CHANNEL_STATE:
mStateLabel.setText( state.getState().getDisplayValue() );
break;
case SOURCE:
mSourceLabel.setText( mChannel.getSourceConfiguration()
.getDescription() );
break;
case CHANNEL_NAME:
case SITE_NAME:
case SYSTEM_NAME:
mChannelLabel.setText( mChannel.getChannelDisplayName() );
break;
case TO_TALKGROUP:
mToTalkgroup.setText( state.getTalkgroup() );
break;
case TO_TALKGROUP_ALIAS:
Alias toAlias = state.getTalkgroupAlias();
if( toAlias != null )
{
mToTalkgroupAlias.setText( toAlias.getName() );
mToTalkgroupAlias.setIcon( mSettingsManager
.getImageIcon( toAlias.getIconName(), 12 ) );
}
else
{
mToTalkgroupAlias.setText( null );
mToTalkgroupAlias.setIcon( null );
}
break;
case FROM_TALKGROUP:
String mid = state.getMobileID();
mFromTalkgroup.setText( state.getMobileID() );
if( mid != null )
{
mFromTalkgroupType.setText( "Mobile ID" );
}
else
{
mFromTalkgroupType.setText( " " );
}
break;
case FROM_TALKGROUP_ALIAS:
Alias fromAlias = state.getMobileIDAlias();
if( fromAlias != null )
{
mFromTalkgroupAlias.setText( fromAlias.getName() );
mFromTalkgroupAlias.setIcon( mSettingsManager
.getImageIcon( fromAlias.getIconName(), 12 ) );
}
else
{
mFromTalkgroupAlias.setText( null );
mFromTalkgroupAlias.setIcon( null );
}
break;
default:
break;
}
repaint();
}
});
}
@Override
public void settingChanged( Setting setting )
{
super.settingChanged( setting );
if( setting instanceof ColorSetting )
{
ColorSetting colorSetting = (ColorSetting)setting;
switch( colorSetting.getColorSettingName() )
{
case CHANNEL_STATE_LABEL_DECODER:
if( mColorLabelDecoder != null )
{
if( mStateLabel != null )
{
mStateLabel.setForeground( mColorLabelDecoder );
}
if( mProtocol != null )
{
mProtocol.setForeground( mColorLabelDecoder );
}
if( mToTalkgroup != null )
{
mToTalkgroup.setForeground( mColorLabelDecoder );
}
if( mToTalkgroupAlias != null )
{
mToTalkgroupAlias.setForeground( mColorLabelDecoder );
}
if( mFromTalkgroupType != null )
{
mFromTalkgroupType.setForeground( mColorLabelDecoder );
}
if( mFromTalkgroup != null )
{
mFromTalkgroup.setForeground( mColorLabelDecoder );
}
if( mFromTalkgroupAlias != null )
{
mFromTalkgroupAlias.setForeground( mColorLabelDecoder );
}
}
break;
case CHANNEL_STATE_LABEL_DETAILS:
if( mColorLabelDetails != null )
{
if( mSourceLabel != null )
{
mSourceLabel.setForeground( mColorLabelDetails );
}
if( mChannelLabel != null )
{
mChannelLabel.setForeground( mColorLabelDetails );
}
}
break;
}
}
}
}