mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
Initial code check in continued
This commit is contained in:
110
src/decode/AuxDecodeComponentEditor.java
Normal file
110
src/decode/AuxDecodeComponentEditor.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import controller.channel.AbstractChannelEditor;
|
||||
import controller.channel.ChannelNode;
|
||||
import decode.config.AuxDecodeConfiguration;
|
||||
|
||||
public class AuxDecodeComponentEditor extends AbstractChannelEditor
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private HashMap<DecoderType,AuxDecoderCheckBox> mControls =
|
||||
new HashMap<DecoderType,AuxDecoderCheckBox>();
|
||||
|
||||
public AuxDecodeComponentEditor( ChannelNode channelNode )
|
||||
{
|
||||
super( channelNode );
|
||||
|
||||
List<DecoderType> decoders = DecoderType.getAuxDecoders();
|
||||
|
||||
Collections.sort( decoders );
|
||||
|
||||
for( DecoderType decoder: decoders )
|
||||
{
|
||||
AuxDecoderCheckBox control = new AuxDecoderCheckBox( decoder );
|
||||
|
||||
if( getConfig().getAuxDecoders().contains( decoder ) )
|
||||
{
|
||||
control.setSelected( true );
|
||||
}
|
||||
|
||||
add( control, "wrap" );
|
||||
|
||||
mControls.put( decoder, control );
|
||||
}
|
||||
}
|
||||
|
||||
public AuxDecodeConfiguration getConfig()
|
||||
{
|
||||
return getChannelNode().getChannel().getAuxDecodeConfiguration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
getConfig().clearAuxDecoders();
|
||||
|
||||
for( DecoderType decoder: mControls.keySet() )
|
||||
{
|
||||
if( mControls.get( decoder ) .isSelected() )
|
||||
{
|
||||
getConfig().addAuxDecoder( decoder );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
SwingUtilities.invokeLater(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
for( DecoderType decoder: mControls.keySet() )
|
||||
{
|
||||
mControls.get( decoder ).setSelected(
|
||||
getConfig().getAuxDecoders().contains( decoder ) );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public class AuxDecoderCheckBox extends JCheckBox
|
||||
{
|
||||
private DecoderType mDecoderType;
|
||||
|
||||
public AuxDecoderCheckBox( DecoderType decoder )
|
||||
{
|
||||
super( decoder.getDisplayString() );
|
||||
|
||||
mDecoderType = decoder;
|
||||
}
|
||||
}
|
||||
}
|
||||
134
src/decode/DecodeComponentEditor.java
Normal file
134
src/decode/DecodeComponentEditor.java
Normal file
@@ -0,0 +1,134 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import controller.channel.AbstractChannelEditor;
|
||||
import controller.channel.ChannelNode;
|
||||
import decode.config.DecodeConfigFactory;
|
||||
import decode.config.DecodeConfiguration;
|
||||
|
||||
public class DecodeComponentEditor extends AbstractChannelEditor
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private JComboBox<DecoderType> mComboDecoders;
|
||||
|
||||
private DecodeEditor mEditor;
|
||||
|
||||
public DecodeComponentEditor( ChannelNode channelNode )
|
||||
{
|
||||
super( channelNode );
|
||||
|
||||
/**
|
||||
* ComboBox: Decoders
|
||||
*/
|
||||
mComboDecoders = new JComboBox<DecoderType>();
|
||||
|
||||
DefaultComboBoxModel<DecoderType> model =
|
||||
new DefaultComboBoxModel<DecoderType>();
|
||||
|
||||
for( DecoderType type: DecoderType.getAvailableDecoders() )
|
||||
{
|
||||
model.addElement( type );
|
||||
}
|
||||
|
||||
mComboDecoders.setModel( model );
|
||||
|
||||
mComboDecoders.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
DecoderType selected = mComboDecoders
|
||||
.getItemAt( mComboDecoders.getSelectedIndex() );
|
||||
|
||||
if( selected != null )
|
||||
{
|
||||
DecodeConfiguration config;
|
||||
|
||||
if( mChannelNode.getChannel()
|
||||
.getDecodeConfiguration().getDecoderType() ==
|
||||
selected )
|
||||
{
|
||||
config = mChannelNode.getChannel()
|
||||
.getDecodeConfiguration();
|
||||
}
|
||||
else
|
||||
{
|
||||
config = DecodeConfigFactory
|
||||
.getDecodeConfiguration( selected );
|
||||
}
|
||||
|
||||
//Remove the existing one
|
||||
if( mEditor != null )
|
||||
{
|
||||
remove( mEditor );
|
||||
}
|
||||
|
||||
//Change to the new one
|
||||
mEditor = DecodeEditorFactory.getPanel( config, mChannelNode );
|
||||
|
||||
add( mEditor, "span 2" );
|
||||
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
});
|
||||
add( mComboDecoders, "wrap" );
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
final DecoderType decoder = mChannelNode.getChannel()
|
||||
.getDecodeConfiguration().getDecoderType();
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run() {
|
||||
mComboDecoders.setSelectedItem( decoder );
|
||||
|
||||
mComboDecoders.requestFocus();
|
||||
|
||||
mComboDecoders.requestFocusInWindow();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
mEditor.save();
|
||||
|
||||
//The component that calls save will invoke the change broadcast
|
||||
mChannelNode.getChannel()
|
||||
.setDecodeConfiguration( mEditor.getConfig(), false );
|
||||
}
|
||||
}
|
||||
52
src/decode/DecodeEditor.java
Normal file
52
src/decode/DecodeEditor.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import controller.Editor;
|
||||
import decode.config.DecodeConfiguration;
|
||||
|
||||
public class DecodeEditor extends Editor
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected DecodeConfiguration mConfig;
|
||||
|
||||
public DecodeEditor( DecodeConfiguration config )
|
||||
{
|
||||
mConfig = config;
|
||||
}
|
||||
|
||||
public DecodeConfiguration getConfig()
|
||||
{
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
}
|
||||
59
src/decode/DecodeEditorFactory.java
Normal file
59
src/decode/DecodeEditorFactory.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import controller.channel.ChannelNode;
|
||||
import decode.config.DecodeConfiguration;
|
||||
import decode.ltrnet.LTRNetEditor;
|
||||
import decode.ltrstandard.LTRStandardEditor;
|
||||
import decode.mpt1327.MPT1327Editor;
|
||||
import decode.nbfm.NBFMEditor;
|
||||
import decode.passport.PassportEditor;
|
||||
|
||||
public class DecodeEditorFactory
|
||||
{
|
||||
public static DecodeEditor getPanel( DecodeConfiguration config,
|
||||
ChannelNode channelNode )
|
||||
{
|
||||
DecodeEditor configuredPanel;
|
||||
|
||||
switch( config.getDecoderType() )
|
||||
{
|
||||
case NBFM:
|
||||
configuredPanel = new NBFMEditor( config );
|
||||
break;
|
||||
case LTR_STANDARD:
|
||||
configuredPanel = new LTRStandardEditor( config );
|
||||
break;
|
||||
case LTR_NET:
|
||||
configuredPanel = new LTRNetEditor( config );
|
||||
break;
|
||||
case MPT1327:
|
||||
configuredPanel = new MPT1327Editor( config, channelNode );
|
||||
break;
|
||||
case PASSPORT:
|
||||
configuredPanel = new PassportEditor( config );
|
||||
break;
|
||||
default:
|
||||
configuredPanel = new DecodeEditor( config );
|
||||
break;
|
||||
}
|
||||
|
||||
return configuredPanel;
|
||||
}
|
||||
}
|
||||
148
src/decode/Decoder.java
Normal file
148
src/decode/Decoder.java
Normal file
@@ -0,0 +1,148 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import message.Message;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import sample.complex.ComplexSample;
|
||||
import source.Source.SampleType;
|
||||
import eventlog.MessageEventLogger;
|
||||
|
||||
public abstract class Decoder implements Listener<Message>
|
||||
{
|
||||
private MessageEventLogger mEventLogger;
|
||||
private Broadcaster<Message> mMessageBroadcaster = new Broadcaster<Message>();
|
||||
private Broadcaster<Float> mFloatBroadcaster = new Broadcaster<Float>();
|
||||
private Broadcaster<ComplexSample> mComplexBroadcaster =
|
||||
new Broadcaster<ComplexSample>();
|
||||
|
||||
protected SampleType mSourceSampleType;
|
||||
protected ArrayList<Decoder> mAuxiliaryDecoders = new ArrayList<Decoder>();
|
||||
|
||||
public Decoder( SampleType sampleType )
|
||||
{
|
||||
mSourceSampleType = sampleType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a float listener interface for connecting this decoder to a
|
||||
* float stream provider
|
||||
*/
|
||||
public Listener<Float> getFloatReceiver()
|
||||
{
|
||||
return (Listener<Float>)mFloatBroadcaster;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a complex listener interface for connecting this decoder to a
|
||||
* float stream provider
|
||||
*/
|
||||
public Listener<ComplexSample> getComplexReceiver()
|
||||
{
|
||||
return (Listener<ComplexSample>)mComplexBroadcaster;
|
||||
}
|
||||
|
||||
public abstract DecoderType getType();
|
||||
|
||||
public void addAuxiliaryDecoder( Decoder decoder )
|
||||
{
|
||||
mAuxiliaryDecoders.add( decoder );
|
||||
mFloatBroadcaster.addListener( decoder.getFloatReceiver() );
|
||||
decoder.addMessageListener( this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup method. Invoke this method after stop and before delete.
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
for( Decoder auxiliaryDecoder: mAuxiliaryDecoders )
|
||||
{
|
||||
auxiliaryDecoder.dispose();
|
||||
}
|
||||
|
||||
mAuxiliaryDecoders.clear();
|
||||
|
||||
mMessageBroadcaster.clear();
|
||||
mComplexBroadcaster.clear();
|
||||
mFloatBroadcaster.clear();
|
||||
}
|
||||
|
||||
public List<Decoder> getAuxiliaryDecoders()
|
||||
{
|
||||
return mAuxiliaryDecoders;
|
||||
}
|
||||
|
||||
public void setEventLogger( MessageEventLogger eventLogger )
|
||||
{
|
||||
mEventLogger = eventLogger;
|
||||
}
|
||||
|
||||
public MessageEventLogger getEventLogger()
|
||||
{
|
||||
return mEventLogger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Message message )
|
||||
{
|
||||
send( message );
|
||||
}
|
||||
|
||||
public void send( Message message )
|
||||
{
|
||||
if( mMessageBroadcaster != null )
|
||||
{
|
||||
mMessageBroadcaster.receive( message );
|
||||
}
|
||||
}
|
||||
|
||||
public void addMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mMessageBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
public void removeMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mMessageBroadcaster.removeListener( listener );
|
||||
}
|
||||
|
||||
public void addFloatListener( Listener<Float> listener )
|
||||
{
|
||||
mFloatBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
public void removeFloatListener( Listener<Float> listener )
|
||||
{
|
||||
mFloatBroadcaster.removeListener( listener );
|
||||
}
|
||||
|
||||
public void addComplexListener( Listener<ComplexSample> listener )
|
||||
{
|
||||
mComplexBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
public void removeComplexListener( Listener<ComplexSample> listener )
|
||||
{
|
||||
mComplexBroadcaster.removeListener( listener );
|
||||
}
|
||||
}
|
||||
181
src/decode/DecoderFactory.java
Normal file
181
src/decode/DecoderFactory.java
Normal file
@@ -0,0 +1,181 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import source.Source.SampleType;
|
||||
import alias.AliasList;
|
||||
import controller.channel.ChannelMap;
|
||||
import controller.channel.ProcessingChain;
|
||||
import controller.state.ChannelState;
|
||||
import decode.config.AuxDecodeConfiguration;
|
||||
import decode.config.DecodeConfigLTRNet;
|
||||
import decode.config.DecodeConfigLTRStandard;
|
||||
import decode.config.DecodeConfigMPT1327;
|
||||
import decode.config.DecodeConfigNBFM;
|
||||
import decode.config.DecodeConfiguration;
|
||||
import decode.fleetsync2.Fleetsync2Decoder;
|
||||
import decode.fleetsync2.FleetsyncChannelState;
|
||||
import decode.ltrnet.LTRNetChannelState;
|
||||
import decode.ltrnet.LTRNetDecoder;
|
||||
import decode.ltrstandard.LTRChannelState;
|
||||
import decode.ltrstandard.LTRStandardDecoder;
|
||||
import decode.mdc1200.MDCChannelState;
|
||||
import decode.mdc1200.MDCDecoder;
|
||||
import decode.mpt1327.MPT1327ChannelState;
|
||||
import decode.mpt1327.MPT1327Decoder;
|
||||
import decode.nbfm.NBFMChannelState;
|
||||
import decode.nbfm.NBFMConventionalDecoder;
|
||||
import decode.passport.PassportChannelState;
|
||||
import decode.passport.PassportDecoder;
|
||||
|
||||
public class DecoderFactory
|
||||
{
|
||||
/**
|
||||
* Returns a fully configured decoder with embedded auxiliary decoders
|
||||
* @param channelConfig
|
||||
* @return
|
||||
*/
|
||||
public static Decoder getDecoder( ProcessingChain chain,
|
||||
SampleType sampleType,
|
||||
AliasList aliasList )
|
||||
{
|
||||
Decoder retVal = null;
|
||||
|
||||
DecodeConfiguration config =
|
||||
chain.getChannel().getDecodeConfiguration();
|
||||
|
||||
|
||||
switch( config.getDecoderType() )
|
||||
{
|
||||
case NBFM:
|
||||
retVal = new NBFMConventionalDecoder( sampleType );
|
||||
break;
|
||||
case LTR_STANDARD:
|
||||
DecodeConfigLTRStandard ltrStandardConfig =
|
||||
(DecodeConfigLTRStandard)config;
|
||||
|
||||
retVal = new LTRStandardDecoder( sampleType, aliasList,
|
||||
ltrStandardConfig.getMessageDirection() );
|
||||
break;
|
||||
case LTR_NET:
|
||||
DecodeConfigLTRNet ltrNetConfig = (DecodeConfigLTRNet)config;
|
||||
|
||||
retVal = new LTRNetDecoder( sampleType, aliasList,
|
||||
ltrNetConfig.getMessageDirection() );
|
||||
break;
|
||||
case MPT1327:
|
||||
retVal = new MPT1327Decoder( sampleType, aliasList );
|
||||
break;
|
||||
case PASSPORT:
|
||||
retVal = new PassportDecoder( sampleType, aliasList );
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown decoder type [" +
|
||||
config.getDecoderType().toString() + "]" );
|
||||
}
|
||||
|
||||
if( retVal != null )
|
||||
{
|
||||
AuxDecodeConfiguration auxConfig =
|
||||
chain.getChannel().getAuxDecodeConfiguration();
|
||||
|
||||
for( DecoderType auxDecoder: auxConfig.getAuxDecoders() )
|
||||
{
|
||||
switch( auxDecoder )
|
||||
{
|
||||
case FLEETSYNC2:
|
||||
retVal.addAuxiliaryDecoder(
|
||||
new Fleetsync2Decoder( aliasList ) );
|
||||
break;
|
||||
case MDC1200:
|
||||
retVal.addAuxiliaryDecoder(
|
||||
new MDCDecoder( aliasList ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static ChannelState getChannelStateNew( ProcessingChain chain,
|
||||
AliasList aliasList )
|
||||
{
|
||||
ChannelState retVal = null;
|
||||
|
||||
DecodeConfiguration decodeConfig =
|
||||
chain.getChannel().getDecodeConfiguration();
|
||||
|
||||
switch( decodeConfig.getDecoderType() )
|
||||
{
|
||||
case NBFM:
|
||||
retVal = new NBFMChannelState( chain, aliasList );
|
||||
|
||||
DecodeConfigNBFM conv =
|
||||
(DecodeConfigNBFM)decodeConfig;
|
||||
|
||||
break;
|
||||
case LTR_STANDARD:
|
||||
retVal = new LTRChannelState( chain, aliasList );
|
||||
break;
|
||||
case LTR_NET:
|
||||
retVal = new LTRNetChannelState( chain, aliasList );
|
||||
break;
|
||||
case MPT1327:
|
||||
DecodeConfigMPT1327 mpt = (DecodeConfigMPT1327)decodeConfig;
|
||||
|
||||
String channelMapName = mpt.getChannelMapName();
|
||||
|
||||
ChannelMap map = chain.getResourceManager().getPlaylistManager()
|
||||
.getPlayist().getChannelMapList()
|
||||
.getChannelMap( channelMapName );
|
||||
|
||||
retVal = new MPT1327ChannelState( chain, aliasList, map );
|
||||
break;
|
||||
case PASSPORT:
|
||||
retVal = new PassportChannelState( chain, aliasList );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if( retVal != null )
|
||||
{
|
||||
AuxDecodeConfiguration auxConfig =
|
||||
chain.getChannel().getAuxDecodeConfiguration();
|
||||
|
||||
for( DecoderType auxDecoder: auxConfig.getAuxDecoders() )
|
||||
{
|
||||
switch( auxDecoder )
|
||||
{
|
||||
case FLEETSYNC2:
|
||||
retVal.addAuxChannelState(
|
||||
new FleetsyncChannelState( retVal ) );
|
||||
break;
|
||||
case MDC1200:
|
||||
retVal.addAuxChannelState(
|
||||
new MDCChannelState( retVal ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
110
src/decode/DecoderType.java
Normal file
110
src/decode/DecoderType.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Enumeration of decoder types
|
||||
*/
|
||||
public enum DecoderType
|
||||
{
|
||||
FLEETSYNC2( "Fleetsync II", "Fsync2", "images/fm", 12500 ),
|
||||
LTR_STANDARD( "LTR-Standard", "LTR", "images/ltr_standard", 10000 ),
|
||||
LTR_NET( "LTR-Net", "LTR-Net", "images/ltr_net", 10000 ),
|
||||
MDC1200( "MDC1200", "MDC1200", "images/fm", 12500 ),
|
||||
MPT1327( "MPT1327", "MPT1327", "images/mpt1327", 12500 ),
|
||||
NBFM( "NBFM", "NBFM", "images/fm", 12500 ),
|
||||
PASSPORT( "Passport", "Passport", "images/passport", 12500 );
|
||||
|
||||
private String mDisplayString;
|
||||
private String mShortDisplayString;
|
||||
private String mIconPrefix;
|
||||
private int mChannelBandwidth;
|
||||
|
||||
DecoderType( String displayString, String shortDisplayString,
|
||||
String iconPrefix, int bandwidth )
|
||||
{
|
||||
mDisplayString = displayString;
|
||||
mShortDisplayString = shortDisplayString;
|
||||
mIconPrefix = iconPrefix;
|
||||
mChannelBandwidth = bandwidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Primary decoders
|
||||
*/
|
||||
public static EnumSet<DecoderType> getAvailableDecoders()
|
||||
{
|
||||
return EnumSet.of( DecoderType.LTR_NET,
|
||||
DecoderType.LTR_STANDARD,
|
||||
DecoderType.MPT1327,
|
||||
DecoderType.NBFM,
|
||||
DecoderType.PASSPORT );
|
||||
}
|
||||
|
||||
/**
|
||||
* Decoders that can be used in the viewer application
|
||||
*/
|
||||
public static EnumSet<DecoderType> getInstrumentableDecoders()
|
||||
{
|
||||
return EnumSet.of( DecoderType.FLEETSYNC2,
|
||||
DecoderType.MDC1200,
|
||||
DecoderType.MPT1327 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns listing of auxiliary decoders.
|
||||
*/
|
||||
public static ArrayList<DecoderType> getAuxDecoders()
|
||||
{
|
||||
ArrayList<DecoderType> decoders = new ArrayList<DecoderType>();
|
||||
|
||||
decoders.add( DecoderType.FLEETSYNC2 );
|
||||
decoders.add( DecoderType.MDC1200 );
|
||||
|
||||
return decoders;
|
||||
}
|
||||
|
||||
public String getDisplayString()
|
||||
{
|
||||
return mDisplayString;
|
||||
}
|
||||
|
||||
public String getShortDisplayString()
|
||||
{
|
||||
return mShortDisplayString;
|
||||
}
|
||||
|
||||
public String getIconPrefix()
|
||||
{
|
||||
return mIconPrefix;
|
||||
}
|
||||
|
||||
public int getChannelBandwidth()
|
||||
{
|
||||
return mChannelBandwidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return mDisplayString;
|
||||
}
|
||||
}
|
||||
60
src/decode/config/AuxDecodeConfiguration.java
Normal file
60
src/decode/config/AuxDecodeConfiguration.java
Normal file
@@ -0,0 +1,60 @@
|
||||
/*******************************************************************************
|
||||
* 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.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
|
||||
import controller.config.Configuration;
|
||||
import decode.DecoderType;
|
||||
|
||||
public class AuxDecodeConfiguration extends Configuration
|
||||
{
|
||||
ArrayList<DecoderType> mAuxDecoders = new ArrayList<DecoderType>();
|
||||
|
||||
public AuxDecodeConfiguration()
|
||||
{
|
||||
}
|
||||
|
||||
@XmlElement( name="aux_decoder" )
|
||||
public ArrayList<DecoderType> getAuxDecoders()
|
||||
{
|
||||
return mAuxDecoders;
|
||||
}
|
||||
|
||||
public void setAuxDecoders( ArrayList<DecoderType> decoders )
|
||||
{
|
||||
mAuxDecoders = decoders;
|
||||
}
|
||||
|
||||
public void addAuxDecoder( DecoderType decoder )
|
||||
{
|
||||
mAuxDecoders.add( decoder );
|
||||
}
|
||||
|
||||
public void removeAuxDecoder( DecoderType decoder )
|
||||
{
|
||||
mAuxDecoders.remove( decoder );
|
||||
}
|
||||
|
||||
public void clearAuxDecoders()
|
||||
{
|
||||
mAuxDecoders.clear();
|
||||
}
|
||||
}
|
||||
58
src/decode/config/DecodeConfigFactory.java
Normal file
58
src/decode/config/DecodeConfigFactory.java
Normal file
@@ -0,0 +1,58 @@
|
||||
/*******************************************************************************
|
||||
* 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.config;
|
||||
|
||||
import decode.DecoderType;
|
||||
|
||||
public class DecodeConfigFactory
|
||||
{
|
||||
public static DecodeConfiguration getDefaultDecodeConfiguration()
|
||||
{
|
||||
return getDecodeConfiguration( DecoderType.NBFM );
|
||||
}
|
||||
|
||||
public static DecodeConfiguration
|
||||
getDecodeConfiguration( DecoderType decoder )
|
||||
{
|
||||
DecodeConfiguration retVal;
|
||||
|
||||
switch( decoder )
|
||||
{
|
||||
case NBFM:
|
||||
retVal = new DecodeConfigNBFM();
|
||||
break;
|
||||
case LTR_STANDARD:
|
||||
retVal = new DecodeConfigLTRStandard();
|
||||
break;
|
||||
case LTR_NET:
|
||||
retVal = new DecodeConfigLTRNet();
|
||||
break;
|
||||
case MPT1327:
|
||||
retVal = new DecodeConfigMPT1327();
|
||||
break;
|
||||
case PASSPORT:
|
||||
retVal = new DecodeConfigPassport();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException( "DecodeConfigFactory - "
|
||||
+ "unknown decoder type [" + decoder.toString() + "]" );
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
44
src/decode/config/DecodeConfigLTRNet.java
Normal file
44
src/decode/config/DecodeConfigLTRNet.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*******************************************************************************
|
||||
* 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.config;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
|
||||
import message.MessageDirection;
|
||||
import decode.DecoderType;
|
||||
|
||||
public class DecodeConfigLTRNet extends DecodeConfiguration
|
||||
{
|
||||
private MessageDirection mMessageDirection = MessageDirection.OSW;
|
||||
|
||||
public DecodeConfigLTRNet()
|
||||
{
|
||||
super( DecoderType.LTR_NET );
|
||||
}
|
||||
|
||||
@XmlAttribute( name = "direction" )
|
||||
public MessageDirection getMessageDirection()
|
||||
{
|
||||
return mMessageDirection;
|
||||
}
|
||||
|
||||
public void setMessageDirection( MessageDirection direction )
|
||||
{
|
||||
mMessageDirection = direction;
|
||||
}
|
||||
}
|
||||
44
src/decode/config/DecodeConfigLTRStandard.java
Normal file
44
src/decode/config/DecodeConfigLTRStandard.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*******************************************************************************
|
||||
* 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.config;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
|
||||
import message.MessageDirection;
|
||||
import decode.DecoderType;
|
||||
|
||||
public class DecodeConfigLTRStandard extends DecodeConfiguration
|
||||
{
|
||||
private MessageDirection mMessageDirection = MessageDirection.OSW;
|
||||
|
||||
public DecodeConfigLTRStandard()
|
||||
{
|
||||
super( DecoderType.LTR_STANDARD );
|
||||
}
|
||||
|
||||
@XmlAttribute( name = "direction" )
|
||||
public MessageDirection getMessageDirection()
|
||||
{
|
||||
return mMessageDirection;
|
||||
}
|
||||
|
||||
public void setMessageDirection( MessageDirection direction )
|
||||
{
|
||||
mMessageDirection = direction;
|
||||
}
|
||||
}
|
||||
43
src/decode/config/DecodeConfigMPT1327.java
Normal file
43
src/decode/config/DecodeConfigMPT1327.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*******************************************************************************
|
||||
* 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.config;
|
||||
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
|
||||
import decode.DecoderType;
|
||||
|
||||
public class DecodeConfigMPT1327 extends DecodeConfiguration
|
||||
{
|
||||
private String mChannelMapName;
|
||||
|
||||
public DecodeConfigMPT1327()
|
||||
{
|
||||
super( DecoderType.MPT1327 );
|
||||
}
|
||||
|
||||
@XmlElement
|
||||
public String getChannelMapName()
|
||||
{
|
||||
return mChannelMapName;
|
||||
}
|
||||
|
||||
public void setChannelMapName( String name )
|
||||
{
|
||||
mChannelMapName = name;
|
||||
}
|
||||
}
|
||||
28
src/decode/config/DecodeConfigNBFM.java
Normal file
28
src/decode/config/DecodeConfigNBFM.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*******************************************************************************
|
||||
* 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.config;
|
||||
|
||||
import decode.DecoderType;
|
||||
|
||||
public class DecodeConfigNBFM extends DecodeConfiguration
|
||||
{
|
||||
public DecodeConfigNBFM()
|
||||
{
|
||||
super( DecoderType.NBFM );
|
||||
}
|
||||
}
|
||||
28
src/decode/config/DecodeConfigPassport.java
Normal file
28
src/decode/config/DecodeConfigPassport.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*******************************************************************************
|
||||
* 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.config;
|
||||
|
||||
import decode.DecoderType;
|
||||
|
||||
public class DecodeConfigPassport extends DecodeConfiguration
|
||||
{
|
||||
public DecodeConfigPassport()
|
||||
{
|
||||
super( DecoderType.PASSPORT );
|
||||
}
|
||||
}
|
||||
50
src/decode/config/DecodeConfiguration.java
Normal file
50
src/decode/config/DecodeConfiguration.java
Normal file
@@ -0,0 +1,50 @@
|
||||
/*******************************************************************************
|
||||
* 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.config;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlSeeAlso;
|
||||
|
||||
import controller.config.Configuration;
|
||||
import decode.DecoderType;
|
||||
|
||||
@XmlSeeAlso( { DecodeConfigNBFM.class,
|
||||
DecodeConfigLTRNet.class,
|
||||
DecodeConfigLTRStandard.class,
|
||||
DecodeConfigMPT1327.class,
|
||||
DecodeConfigPassport.class } )
|
||||
@XmlRootElement( name = "decode_configuration" )
|
||||
public abstract class DecodeConfiguration extends Configuration
|
||||
{
|
||||
private DecoderType mDecoderType = DecoderType.NBFM;
|
||||
|
||||
public DecodeConfiguration()
|
||||
{
|
||||
this( DecoderType.NBFM );
|
||||
}
|
||||
|
||||
public DecodeConfiguration( DecoderType type )
|
||||
{
|
||||
mDecoderType = type;
|
||||
}
|
||||
|
||||
public DecoderType getDecoderType()
|
||||
{
|
||||
return mDecoderType;
|
||||
}
|
||||
}
|
||||
175
src/decode/fleetsync1/Fleetsync1Message.java
Normal file
175
src/decode/fleetsync1/Fleetsync1Message.java
Normal file
@@ -0,0 +1,175 @@
|
||||
/*******************************************************************************
|
||||
* 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.fleetsync1;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import map.Plottable;
|
||||
import message.Message;
|
||||
import alias.Alias;
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class Fleetsync1Message extends Message
|
||||
{
|
||||
private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
|
||||
|
||||
private BitSetBuffer mMessage;
|
||||
|
||||
public Fleetsync1Message( BitSetBuffer message )
|
||||
{
|
||||
mMessage = message;
|
||||
}
|
||||
|
||||
public boolean isValid()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( mSDF.format( new Date( System.currentTimeMillis() ) ) );
|
||||
sb.append( " " );
|
||||
sb.append( "FS1 [" + mMessage.toString() + "]" );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
@SuppressWarnings( "unused" )
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBinaryMessage()
|
||||
{
|
||||
return mMessage.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorStatus()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plottable getPlottable()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
49
src/decode/fleetsync1/Fleetsync1MessageProcessor.java
Normal file
49
src/decode/fleetsync1/Fleetsync1MessageProcessor.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*******************************************************************************
|
||||
* 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.fleetsync1;
|
||||
|
||||
import log.Log;
|
||||
import message.Message;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class Fleetsync1MessageProcessor implements Listener<BitSetBuffer>
|
||||
{
|
||||
private Broadcaster<Message> mBroadcaster = new Broadcaster<Message>();
|
||||
|
||||
@Override
|
||||
public void receive( BitSetBuffer buffer )
|
||||
{
|
||||
Fleetsync1Message message = new Fleetsync1Message( buffer );
|
||||
|
||||
Log.info( message.toString() );
|
||||
|
||||
mBroadcaster.receive( message );
|
||||
}
|
||||
|
||||
public void addMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
public void removeMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.removeListener( listener );
|
||||
}
|
||||
}
|
||||
184
src/decode/fleetsync2/Fleetsync2Decoder.java
Normal file
184
src/decode/fleetsync2/Fleetsync2Decoder.java
Normal file
@@ -0,0 +1,184 @@
|
||||
/*******************************************************************************
|
||||
* 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.fleetsync2;
|
||||
|
||||
import instrument.Instrumentable;
|
||||
import instrument.tap.Tap;
|
||||
import instrument.tap.stream.BinaryTap;
|
||||
import instrument.tap.stream.FloatTap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import sample.Listener;
|
||||
import source.Source.SampleType;
|
||||
import alias.AliasList;
|
||||
import bits.MessageFramer;
|
||||
import bits.SyncPattern;
|
||||
import decode.Decoder;
|
||||
import decode.DecoderType;
|
||||
import dsp.filter.Filters;
|
||||
import dsp.filter.FloatFIRFilter;
|
||||
import dsp.filter.FloatHalfBandFilter;
|
||||
import dsp.fsk.FSK2Decoder;
|
||||
import dsp.fsk.FSK2Decoder.Output;
|
||||
|
||||
/**
|
||||
* Fleetsync II Decoder - 1200 baud 2FSK decoder that can process 48k sample rate
|
||||
* complex or floating point samples and output fully framed Fleetsync II
|
||||
* messages
|
||||
*/
|
||||
public class Fleetsync2Decoder extends Decoder implements Instrumentable
|
||||
{
|
||||
/* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */
|
||||
private static final int sDECIMATED_SAMPLE_RATE = 24000;
|
||||
|
||||
/* Baud or Symbol Rate */
|
||||
private static final int sSYMBOL_RATE = 1200;
|
||||
|
||||
/* Message length - 5 x REVS + 16 x SYNC + 8 x 64Bit Blocks */
|
||||
private static final int sMESSAGE_LENGTH = 537;
|
||||
|
||||
/* Instrumentation Taps */
|
||||
private ArrayList<Tap> mAvailableTaps;
|
||||
private static final String INSTRUMENT_INPUT =
|
||||
"Tap Point: Float Input";
|
||||
private static final String INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD =
|
||||
"Tap Point: Bandpass Filter > < FSK2 Decoder";
|
||||
private static final String INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER =
|
||||
"Tap Point: FSK2 Decoder > < Message Framer";
|
||||
|
||||
private FSK2Decoder mFSKDecoder;
|
||||
private FloatHalfBandFilter mDecimationFilter;
|
||||
private FloatFIRFilter mBandPassFilter;
|
||||
private MessageFramer mMessageFramer;
|
||||
private Fleetsync2MessageProcessor mMessageProcessor;
|
||||
|
||||
public Fleetsync2Decoder( AliasList aliasList )
|
||||
{
|
||||
super( SampleType.FLOAT );
|
||||
|
||||
mDecimationFilter = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
addFloatListener( mDecimationFilter );
|
||||
|
||||
mBandPassFilter = new FloatFIRFilter(
|
||||
Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02 );
|
||||
mDecimationFilter.setListener( mBandPassFilter );
|
||||
|
||||
mFSKDecoder = new FSK2Decoder( sDECIMATED_SAMPLE_RATE,
|
||||
sSYMBOL_RATE, Output.INVERTED );
|
||||
mBandPassFilter.setListener( mFSKDecoder );
|
||||
|
||||
mMessageFramer = new MessageFramer(
|
||||
SyncPattern.FLEETSYNC2.getPattern(), sMESSAGE_LENGTH );
|
||||
mFSKDecoder.setListener( mMessageFramer );
|
||||
|
||||
mMessageProcessor = new Fleetsync2MessageProcessor( aliasList );
|
||||
mMessageFramer.addMessageListener( mMessageProcessor );
|
||||
mMessageProcessor.addMessageListener( this );
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
super.dispose();
|
||||
|
||||
mDecimationFilter.dispose();
|
||||
mBandPassFilter.dispose();
|
||||
mFSKDecoder.dispose();
|
||||
mMessageFramer.dispose();
|
||||
mMessageProcessor.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecoderType getType()
|
||||
{
|
||||
return DecoderType.FLEETSYNC2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a float listener interface for connecting this decoder to a
|
||||
* float stream provider
|
||||
*/
|
||||
public Listener<Float> getFloatReceiver()
|
||||
{
|
||||
return (Listener<Float>)mDecimationFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Tap> getTaps()
|
||||
{
|
||||
if( mAvailableTaps == null )
|
||||
{
|
||||
mAvailableTaps = new ArrayList<Tap>();
|
||||
|
||||
mAvailableTaps.add( new FloatTap( INSTRUMENT_INPUT, 0, 1.0f ) );
|
||||
mAvailableTaps.add( new FloatTap(
|
||||
INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD, 0, 0.5f ) );
|
||||
mAvailableTaps.addAll( mFSKDecoder.getTaps() );
|
||||
mAvailableTaps.add( new BinaryTap(
|
||||
INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER, 0, 0.025f ) );
|
||||
}
|
||||
|
||||
return mAvailableTaps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTap( Tap tap )
|
||||
{
|
||||
mFSKDecoder.addTap( tap );
|
||||
|
||||
switch( tap.getName() )
|
||||
{
|
||||
case INSTRUMENT_INPUT:
|
||||
FloatTap inputTap = (FloatTap)tap;
|
||||
addFloatListener( inputTap );
|
||||
break;
|
||||
case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD:
|
||||
FloatTap bpTap = (FloatTap)tap;
|
||||
mBandPassFilter.setListener( bpTap );
|
||||
bpTap.setListener( mFSKDecoder );
|
||||
break;
|
||||
case INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER:
|
||||
BinaryTap decoderTap = (BinaryTap)tap;
|
||||
mFSKDecoder.setListener( decoderTap );
|
||||
decoderTap.setListener( mMessageFramer );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTap( Tap tap )
|
||||
{
|
||||
mFSKDecoder.removeTap( tap );
|
||||
|
||||
switch( tap.getName() )
|
||||
{
|
||||
case INSTRUMENT_INPUT:
|
||||
FloatTap inputTap = (FloatTap)tap;
|
||||
removeFloatListener( inputTap );
|
||||
break;
|
||||
case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD:
|
||||
mBandPassFilter.setListener( mFSKDecoder );
|
||||
break;
|
||||
case INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER:
|
||||
mFSKDecoder.setListener( mMessageFramer );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
src/decode/fleetsync2/Fleetsync2MessageProcessor.java
Normal file
59
src/decode/fleetsync2/Fleetsync2MessageProcessor.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*******************************************************************************
|
||||
* 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.fleetsync2;
|
||||
|
||||
import message.Message;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class Fleetsync2MessageProcessor implements Listener<BitSetBuffer>
|
||||
{
|
||||
private Broadcaster<Message> mBroadcaster = new Broadcaster<Message>();
|
||||
|
||||
private AliasList mAliasList;
|
||||
|
||||
public Fleetsync2MessageProcessor( AliasList list )
|
||||
{
|
||||
mAliasList = list;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mBroadcaster.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( BitSetBuffer buffer )
|
||||
{
|
||||
FleetsyncMessage message = new FleetsyncMessage( buffer, mAliasList );
|
||||
|
||||
mBroadcaster.receive( message );
|
||||
}
|
||||
|
||||
public void addMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
public void removeMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.removeListener( listener );
|
||||
}
|
||||
}
|
||||
171
src/decode/fleetsync2/FleetsyncActivitySummary.java
Normal file
171
src/decode/fleetsync2/FleetsyncActivitySummary.java
Normal file
@@ -0,0 +1,171 @@
|
||||
/*******************************************************************************
|
||||
* 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.fleetsync2;
|
||||
|
||||
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 FleetsyncActivitySummary implements ActivitySummaryProvider,
|
||||
Listener<Message>
|
||||
{
|
||||
private AliasList mAliasList;
|
||||
private TreeSet<String> mIdents = new TreeSet<String>();
|
||||
private TreeSet<String> mEmergencyIdents = new TreeSet<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 FleetsyncActivitySummary( AliasList list )
|
||||
{
|
||||
mAliasList = list;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mIdents.clear();
|
||||
mEmergencyIdents.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Message message )
|
||||
{
|
||||
if( message instanceof FleetsyncMessage )
|
||||
{
|
||||
FleetsyncMessage fs = ((FleetsyncMessage)message);
|
||||
|
||||
if( fs.isValid() )
|
||||
{
|
||||
String from = fs.getFromID();
|
||||
mIdents.add( from );
|
||||
|
||||
String to = fs.getToID();
|
||||
|
||||
if( !to.startsWith( "xxx" ))
|
||||
{
|
||||
mIdents.add( to );
|
||||
}
|
||||
}
|
||||
|
||||
FleetsyncMessageType type = fs.getMessageType();
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case EMERGENCY:
|
||||
case LONE_WORKER_EMERGENCY:
|
||||
mEmergencyIdents.add( fs.getFromID() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSummary()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "=============================\n" );
|
||||
sb.append( "Decoder:\tFleetsync II\n\n" );
|
||||
sb.append( "Fleetsync Idents\n" );
|
||||
|
||||
if( mIdents.isEmpty() )
|
||||
{
|
||||
sb.append( " None\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Iterator<String> it = mIdents.iterator();
|
||||
|
||||
while( it.hasNext() )
|
||||
{
|
||||
String ident = it.next();
|
||||
|
||||
sb.append( " " );
|
||||
sb.append( formatIdent( ident ) );
|
||||
sb.append( " " );
|
||||
|
||||
Alias alias = mAliasList.getFleetsyncAlias( ident );
|
||||
|
||||
if( alias != null )
|
||||
{
|
||||
sb.append( alias.getName() );
|
||||
}
|
||||
|
||||
sb.append( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
sb.append( "\nFleetsync Emergency Activations\n" );
|
||||
|
||||
if( mEmergencyIdents.isEmpty() )
|
||||
{
|
||||
sb.append( " None\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Iterator<String> it = mEmergencyIdents.iterator();
|
||||
|
||||
while( it.hasNext() )
|
||||
{
|
||||
String ident = it.next();
|
||||
|
||||
sb.append( " " );
|
||||
sb.append( formatIdent( ident ) );
|
||||
sb.append( " " );
|
||||
|
||||
Alias alias = mAliasList.getFleetsyncAlias( ident );
|
||||
|
||||
if( alias != null )
|
||||
{
|
||||
sb.append( alias.getName() );
|
||||
}
|
||||
|
||||
sb.append( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String formatIdent( String ident )
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if( ident.length() == 7 )
|
||||
{
|
||||
sb.append( ident.substring( 0, 3 ) );
|
||||
sb.append( "-" );
|
||||
sb.append( ident.substring( 3, 7 ) );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return ident;
|
||||
}
|
||||
}
|
||||
}
|
||||
188
src/decode/fleetsync2/FleetsyncCallEvent.java
Normal file
188
src/decode/fleetsync2/FleetsyncCallEvent.java
Normal file
@@ -0,0 +1,188 @@
|
||||
/*******************************************************************************
|
||||
* 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.fleetsync2;
|
||||
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import controller.activity.CallEvent;
|
||||
import decode.DecoderType;
|
||||
|
||||
public class FleetsyncCallEvent extends CallEvent
|
||||
{
|
||||
public FleetsyncCallEvent( CallEventType callEventType,
|
||||
AliasList aliasList,
|
||||
String fromID,
|
||||
String toID,
|
||||
String details )
|
||||
{
|
||||
super( DecoderType.FLEETSYNC2, callEventType,
|
||||
aliasList, fromID, toID, details );
|
||||
}
|
||||
|
||||
private Alias getAlias( String ident )
|
||||
{
|
||||
if( hasAliasList() )
|
||||
{
|
||||
return getAliasList().getFleetsyncAlias( ident );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getFromIDAlias()
|
||||
{
|
||||
return getAlias( getFromID() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getToIDAlias()
|
||||
{
|
||||
return getAlias( getToID() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChannel()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFrequency()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static class Builder
|
||||
{
|
||||
/* Required parameters */
|
||||
private CallEventType mCallEventType;
|
||||
|
||||
/* Optional parameters */
|
||||
private AliasList mAliasList;
|
||||
private String mFromID;
|
||||
private String mToID;
|
||||
private String mDetails;
|
||||
|
||||
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 details( String details )
|
||||
{
|
||||
mDetails = details;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder to( String toID )
|
||||
{
|
||||
mToID = toID;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FleetsyncCallEvent build()
|
||||
{
|
||||
return new FleetsyncCallEvent( this );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor for the builder
|
||||
*/
|
||||
private FleetsyncCallEvent( Builder builder )
|
||||
{
|
||||
this( builder.mCallEventType,
|
||||
builder.mAliasList,
|
||||
builder.mFromID,
|
||||
builder.mToID,
|
||||
builder.mDetails );
|
||||
}
|
||||
|
||||
public static FleetsyncCallEvent
|
||||
getFleetsync2Event( FleetsyncMessage message )
|
||||
{
|
||||
CallEventType type = CallEventType.UNKNOWN;
|
||||
StringBuilder sbDetails = new StringBuilder();
|
||||
sbDetails.append( "Fleetsync " );
|
||||
|
||||
switch( message.getMessageType() )
|
||||
{
|
||||
case ACKNOWLEDGE:
|
||||
type = CallEventType.ACKNOWLEDGE;
|
||||
break;
|
||||
case ANI:
|
||||
type = CallEventType.ID_ANI;
|
||||
break;
|
||||
case EMERGENCY:
|
||||
type = CallEventType.EMERGENCY;
|
||||
break;
|
||||
case GPS:
|
||||
type = CallEventType.GPS;
|
||||
sbDetails.append( message.getGPSLocation() );
|
||||
break;
|
||||
case LONE_WORKER_EMERGENCY:
|
||||
type = CallEventType.EMERGENCY;
|
||||
sbDetails.append( "LONE WORKER EMERGENCY ALERT" );
|
||||
break;
|
||||
case PAGING:
|
||||
type = CallEventType.PAGE;
|
||||
break;
|
||||
case STATUS:
|
||||
type = CallEventType.STATUS;
|
||||
sbDetails.append( "Status: " );
|
||||
sbDetails.append( message.getStatus() );
|
||||
|
||||
if( message.getStatusAlias() != null )
|
||||
{
|
||||
sbDetails.append( "/" );
|
||||
sbDetails.append( message.getStatusAlias().getName() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if( type == CallEventType.ID_ANI ||
|
||||
type == CallEventType.EMERGENCY )
|
||||
{
|
||||
return new FleetsyncCallEvent.Builder( type )
|
||||
.details( sbDetails.toString() )
|
||||
.from( message.getFromID() )
|
||||
.build();
|
||||
}
|
||||
else
|
||||
{
|
||||
return new FleetsyncCallEvent.Builder( type )
|
||||
.details( sbDetails.toString() )
|
||||
.from( message.getFromID() )
|
||||
.to( message.getToID() )
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
195
src/decode/fleetsync2/FleetsyncChannelState.java
Normal file
195
src/decode/fleetsync2/FleetsyncChannelState.java
Normal file
@@ -0,0 +1,195 @@
|
||||
/*******************************************************************************
|
||||
* 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.fleetsync2;
|
||||
|
||||
import message.Message;
|
||||
import alias.Alias;
|
||||
import controller.state.AuxChannelState;
|
||||
import controller.state.ChannelState;
|
||||
import controller.state.ChannelState.ChangedAttribute;
|
||||
import controller.state.ChannelState.State;
|
||||
|
||||
public class FleetsyncChannelState extends AuxChannelState
|
||||
{
|
||||
private static final String PROTOCOL = "FSync II";
|
||||
|
||||
private String mFleetFrom;
|
||||
private Alias mFleetFromAlias;
|
||||
|
||||
private String mFleetTo;
|
||||
private Alias mFleetToAlias;
|
||||
|
||||
private String mMessage;
|
||||
private String mMessageType;
|
||||
|
||||
private FleetsyncActivitySummary mActivitySummary;
|
||||
|
||||
public FleetsyncChannelState( ChannelState parentState )
|
||||
{
|
||||
super( parentState );
|
||||
|
||||
mActivitySummary =
|
||||
new FleetsyncActivitySummary( parentState.getAliasList() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
setFleetIDFrom( null );
|
||||
setFleetIDFromAlias( null );
|
||||
setFleetIDTo( null );
|
||||
setFleetIDToAlias( null );
|
||||
setMessage( null );
|
||||
setMessageType( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fade()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Message message )
|
||||
{
|
||||
if( message instanceof FleetsyncMessage )
|
||||
{
|
||||
mActivitySummary.receive( message );
|
||||
|
||||
FleetsyncMessage fleetsync = (FleetsyncMessage)message;
|
||||
|
||||
setFleetIDFrom( fleetsync.getFromID() );
|
||||
setFleetIDFromAlias( fleetsync.getFromIDAlias() );
|
||||
|
||||
FleetsyncMessageType type = fleetsync.getMessageType();
|
||||
|
||||
if( type != FleetsyncMessageType.ANI )
|
||||
{
|
||||
setFleetIDTo( fleetsync.getToID() );
|
||||
setFleetIDToAlias( fleetsync.getToIDAlias() );
|
||||
}
|
||||
|
||||
setMessageType( type.getLabel() );
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case GPS:
|
||||
setMessage( fleetsync.getGPSLocation() );
|
||||
break;
|
||||
case STATUS:
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append( fleetsync.getStatus() );
|
||||
|
||||
Alias status = fleetsync.getStatusAlias();
|
||||
|
||||
if( status != null )
|
||||
{
|
||||
sb.append( "/" );
|
||||
sb.append( status.getName() );
|
||||
}
|
||||
|
||||
setMessage( sb.toString() );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Set the state to CALL on the parent channel state, so that the
|
||||
* parent state timer can track turning off any updates we apply */
|
||||
mParentChannelState.setState( State.CALL );
|
||||
|
||||
FleetsyncCallEvent fsCallEvent =
|
||||
FleetsyncCallEvent.getFleetsync2Event( fleetsync );
|
||||
|
||||
fsCallEvent.setAliasList( mParentChannelState.getAliasList() );
|
||||
|
||||
mParentChannelState.receiveCallEvent( fsCallEvent );
|
||||
}
|
||||
}
|
||||
|
||||
public String getFleetIDFrom()
|
||||
{
|
||||
return mFleetFrom;
|
||||
}
|
||||
|
||||
public void setFleetIDFrom( String from )
|
||||
{
|
||||
mFleetFrom = from;
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP );
|
||||
}
|
||||
|
||||
public Alias getFleetIDFromAlias()
|
||||
{
|
||||
return mFleetFromAlias;
|
||||
}
|
||||
|
||||
public void setFleetIDFromAlias( Alias alias )
|
||||
{
|
||||
mFleetFromAlias = alias;
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS );
|
||||
}
|
||||
|
||||
public String getFleetIDTo()
|
||||
{
|
||||
return mFleetTo;
|
||||
}
|
||||
|
||||
public void setFleetIDTo( String to )
|
||||
{
|
||||
mFleetTo = to;
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP );
|
||||
}
|
||||
|
||||
public Alias getFleetIDToAlias()
|
||||
{
|
||||
return mFleetToAlias;
|
||||
}
|
||||
|
||||
public void setFleetIDToAlias( Alias alias )
|
||||
{
|
||||
mFleetToAlias = alias;
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS );
|
||||
}
|
||||
|
||||
public String getMessage()
|
||||
{
|
||||
return mMessage;
|
||||
}
|
||||
|
||||
public void setMessage( String message )
|
||||
{
|
||||
mMessage = message;
|
||||
broadcastChange( ChangedAttribute.MESSAGE );
|
||||
}
|
||||
|
||||
public String getMessageType()
|
||||
{
|
||||
return mMessageType;
|
||||
}
|
||||
|
||||
public void setMessageType( String type )
|
||||
{
|
||||
mMessageType = type;
|
||||
broadcastChange( ChangedAttribute.MESSAGE_TYPE );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivitySummary()
|
||||
{
|
||||
return mActivitySummary.getSummary();
|
||||
}
|
||||
}
|
||||
863
src/decode/fleetsync2/FleetsyncMessage.java
Normal file
863
src/decode/fleetsync2/FleetsyncMessage.java
Normal file
@@ -0,0 +1,863 @@
|
||||
/*******************************************************************************
|
||||
* 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.fleetsync2;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.BitSet;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import map.Plottable;
|
||||
import message.Message;
|
||||
|
||||
import org.jdesktop.swingx.mapviewer.GeoPosition;
|
||||
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
import crc.CRC;
|
||||
import crc.CRCFleetsync;
|
||||
|
||||
public class FleetsyncMessage extends Message
|
||||
{
|
||||
private static DecimalFormat mDecimalFormatter = new DecimalFormat("#.#####");
|
||||
private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" );
|
||||
|
||||
//Calendar to use in calculating time hacks
|
||||
Calendar mCalendar = new GregorianCalendar();
|
||||
|
||||
//Message parts are identified in big-endian order for correct translation
|
||||
|
||||
//Message Header
|
||||
private static int[] sRevs = { 4,3,2,1,0 };
|
||||
private static int[] sSync = { 20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5 };
|
||||
|
||||
//Message Block 1
|
||||
private static int[] sStatusMessage = { 27,26,25,24,23,22,21 };
|
||||
private static int[] sMessageType = { 33,32,31,30,29 };
|
||||
|
||||
private static int sEmergencyFlag = 22;
|
||||
private static int sLoneWorkerFlag = 24;
|
||||
private static int sPagingFlag = 26;
|
||||
private static int sEndOfTransmissionFlag = 27;
|
||||
private static int sManualFlag = 28;
|
||||
private static int sANIFlag = 29;
|
||||
private static int sStatusFlag = 30;
|
||||
private static int sAcknowledgeFlag = 31;
|
||||
//32 - unknown - always 0
|
||||
//33 - unknown - always 0
|
||||
//34 - unknown - set for ACKNOWLEDGE
|
||||
private static int sGPSExtensionFlag = 35;
|
||||
private static int sFleetExtensionFlag = 36;
|
||||
private static int[] sFleetFrom = { 44,43,42,41,40,39,38,37 };
|
||||
private static int[] sIdentFrom = { 56,55,54,53,52,51,50,49,48,47,46,45 };
|
||||
private static int[] sIdentTo = { 68,67,66,65,64,63,62,61,60,59,58,57 };
|
||||
private static int[] sCRC1 = { 84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69 };
|
||||
|
||||
//Message Block 2
|
||||
private static int[] sFleetTo = { 92,91,90,89,88,87,86,85 };
|
||||
private static int[] sCRC2 = { 148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,132 };
|
||||
|
||||
//Message Block 3
|
||||
private static int[] sGPSHours = { 176,175,174,173,172 };
|
||||
private static int[] sGPSMinutes = { 182,181,180,179,178,177 };
|
||||
private static int[] sGPSSeconds = { 188,187,186,185,184,183 };
|
||||
private static int[] sCRC3 = { 212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197 };
|
||||
|
||||
//Message Block 4
|
||||
private static int[] sGPSChecksum = { 220,219,218,217,216,215,214,213 };
|
||||
private static int[] sLatitudeDegreesMinutes = { 236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221 };
|
||||
private static int[] sLatitudeDecimalMinutes = { 251,250,249,248,247,246,245,244,243,242,241,240,239,238 };
|
||||
private static int[] sSpeed = { 276,275,274,273,272,271,270,269,268,267,266,265,264,263,262,261,260,259,258,257,256,255,254,253,252 };
|
||||
private static int[] sCRC4 = { 276,275,274,273,272,271,270,269,268,267,266,265,264,263,262,261 };
|
||||
|
||||
//Message Block 5
|
||||
private static int[] sGPSCentury = { 284,283,282,281,280,279,278,277 };
|
||||
private static int[] sGPSYear = { 291,290,289,288,287,286,285 };
|
||||
private static int[] sGPSMonth = { 295,294,293,292 };
|
||||
private static int[] sGPSDay = { 300,299,298,297,296 };
|
||||
private static int[] sLongitudeDegreesMinutes = { 316,315,314,313,312,311,310,309,308,307,306,305,304,303,302,301 };
|
||||
private static int[] sLongitudeDecimalMinutes = { 331,330,329,328,327,326,325,324,323,322,321,320,319,318 };
|
||||
private static int[] sCRC5 = { 340,339,338,337,336,335,334,333,332,331,330,329,328,327,326,325 };
|
||||
|
||||
//Message Block 6
|
||||
private static int[] sGPSUnknown1 = { 352,351,350,349 };
|
||||
private static int[] sGPSHeading = { 365,363,362,361,360,359,358,357,356,355,354,353 };
|
||||
private static int[] sCRC6 = { 404,403,402,401,400,399,398,397,396,395,394,393,392,391,390,389 };
|
||||
|
||||
//Message Block 7
|
||||
private static int[] sBLK7WhatIsIt = { 444,443,442,441,440,439,438,437,436,435,434,433,432,431,430,429 };
|
||||
private static int[] sCRC7 = { 468,467,466,465,464,463,462,461,460,459,458,457,456,455,454,453 };
|
||||
|
||||
//Message Block 8
|
||||
private static int[] sGPSSpeed = { 491,490,489,488,487,486,485,484 };
|
||||
private static int[] sGPSSpeedFractional = { 499,498,497,496,495,494,493,492 };
|
||||
private static int[] sCRC8 = { 532,531,530,529,528,527,526,525,524,523,522,521,520,519,518,517 };
|
||||
|
||||
private BitSetBuffer mMessage;
|
||||
private CRC[] mCRC = new CRC[ 8 ];
|
||||
private AliasList mAliasList;
|
||||
|
||||
public FleetsyncMessage( BitSetBuffer message, AliasList list )
|
||||
{
|
||||
mMessage = message;
|
||||
mAliasList = list;
|
||||
checkParity();
|
||||
}
|
||||
|
||||
private void checkParity()
|
||||
{
|
||||
//Check message block 1
|
||||
mCRC[ 0 ] = detectAndCorrect( 21, 85 );
|
||||
|
||||
//Only check subsequent blocks if we know block 1 is correct
|
||||
if( mCRC[ 0 ] == CRC.PASSED || mCRC[ 0 ] == CRC.CORRECTED )
|
||||
{
|
||||
if( hasFleetExtensionFlag() )
|
||||
{
|
||||
//Check message block 2
|
||||
mCRC[ 1 ] = detectAndCorrect( 85, 149 );
|
||||
}
|
||||
|
||||
if( hasGPSFlag() )
|
||||
{
|
||||
//Check message block 3
|
||||
mCRC[ 2 ] = detectAndCorrect( 149, 213 );
|
||||
//Check message block 4
|
||||
mCRC[ 3 ] = detectAndCorrect( 213, 277 );
|
||||
//Check message block 5
|
||||
mCRC[ 4 ] = detectAndCorrect( 277, 341 );
|
||||
//Check message block 6
|
||||
mCRC[ 5 ] = detectAndCorrect( 341, 405 );
|
||||
//Check message block 7
|
||||
mCRC[ 6 ] = detectAndCorrect( 405, 469 );
|
||||
//Check message block 8
|
||||
mCRC[ 7 ] = detectAndCorrect( 469, 533 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CRC detectAndCorrect( int start, int end )
|
||||
{
|
||||
BitSet original = mMessage.get( start, end );
|
||||
|
||||
CRC retVal = CRCFleetsync.check( original );
|
||||
|
||||
//Attempt to correct single-bit errors
|
||||
if( retVal == CRC.FAILED_PARITY )
|
||||
{
|
||||
int[] errorBitPositions = CRCFleetsync.findBitErrors( original );
|
||||
|
||||
if( errorBitPositions != null )
|
||||
{
|
||||
for( int errorBitPosition: errorBitPositions )
|
||||
{
|
||||
mMessage.flip( start + errorBitPosition );
|
||||
}
|
||||
|
||||
retVal = CRC.CORRECTED;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public boolean isValid()
|
||||
{
|
||||
boolean valid = true;
|
||||
|
||||
for( int x = 0; x < mCRC.length; x++ )
|
||||
{
|
||||
CRC crc = mCRC[ x ];
|
||||
|
||||
if( crc != null )
|
||||
{
|
||||
if( crc == CRC.FAILED_CRC ||
|
||||
crc == CRC.FAILED_PARITY ||
|
||||
crc == CRC.UNKNOWN )
|
||||
{
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( mSDF.format( new Date( System.currentTimeMillis() ) ) );
|
||||
|
||||
sb.append( " FSync2 " + getParity() );
|
||||
|
||||
sb.append( getMessage() );
|
||||
|
||||
sb.append( " [" + mMessage.toString() + "]" );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
public FleetsyncMessageType getMessageType()
|
||||
{
|
||||
if( hasStatusFlag() )
|
||||
{
|
||||
if( hasAcknowledgeFlag() )
|
||||
{
|
||||
return FleetsyncMessageType.ACKNOWLEDGE;
|
||||
}
|
||||
|
||||
if( hasGPSFlag() )
|
||||
{
|
||||
return FleetsyncMessageType.GPS;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FleetsyncMessageType.STATUS;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( hasAcknowledgeFlag() )
|
||||
{
|
||||
return FleetsyncMessageType.ACKNOWLEDGE;
|
||||
}
|
||||
|
||||
if( hasANIFlag() )
|
||||
{
|
||||
return FleetsyncMessageType.ANI;
|
||||
}
|
||||
|
||||
if( hasGPSFlag() )
|
||||
{
|
||||
return FleetsyncMessageType.GPS;
|
||||
}
|
||||
|
||||
if( hasPagingFlag() )
|
||||
{
|
||||
return FleetsyncMessageType.PAGING;
|
||||
}
|
||||
|
||||
if( hasEmergencyFlag() )
|
||||
{
|
||||
if( hasLoneWorkerFlag() )
|
||||
{
|
||||
return FleetsyncMessageType.LONE_WORKER_EMERGENCY;
|
||||
}
|
||||
else
|
||||
return FleetsyncMessageType.EMERGENCY;
|
||||
}
|
||||
}
|
||||
|
||||
return FleetsyncMessageType.UNKNOWN;
|
||||
}
|
||||
|
||||
public int getStatus()
|
||||
{
|
||||
return getStatusNumber() + 9;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the RAW status number. The actual status number should be
|
||||
* accessed via the getStatus() method.
|
||||
* @return
|
||||
*/
|
||||
public int getStatusNumber()
|
||||
{
|
||||
return getInt( sStatusMessage );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public Alias getStatusAlias()
|
||||
{
|
||||
if( mAliasList != null )
|
||||
{
|
||||
return mAliasList.getStatus( getStatus() );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getFleetFrom()
|
||||
{
|
||||
return getInt( sFleetFrom ) + 99;
|
||||
}
|
||||
|
||||
public int getIdentifierFrom()
|
||||
{
|
||||
return getInt( sIdentFrom ) + 999;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverted Flags - 0 = flag is true
|
||||
* @return
|
||||
*/
|
||||
public boolean hasEndOfTransmissionFlag()
|
||||
{
|
||||
return !mMessage.get( sEndOfTransmissionFlag );
|
||||
}
|
||||
|
||||
public boolean hasEmergencyFlag()
|
||||
{
|
||||
return !mMessage.get( sEmergencyFlag );
|
||||
}
|
||||
|
||||
public boolean hasLoneWorkerFlag()
|
||||
{
|
||||
return !mMessage.get( sLoneWorkerFlag );
|
||||
}
|
||||
|
||||
public boolean hasPagingFlag()
|
||||
{
|
||||
return !mMessage.get( sPagingFlag );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Normal Flags - 1 = flag is true
|
||||
* @return
|
||||
*/
|
||||
public boolean hasANIFlag()
|
||||
{
|
||||
return mMessage.get( sANIFlag );
|
||||
}
|
||||
|
||||
public boolean hasAcknowledgeFlag()
|
||||
{
|
||||
return mMessage.get( sAcknowledgeFlag );
|
||||
}
|
||||
|
||||
public boolean hasFleetExtensionFlag()
|
||||
{
|
||||
return mMessage.get( sFleetExtensionFlag );
|
||||
}
|
||||
|
||||
public boolean hasGPSFlag()
|
||||
{
|
||||
return mMessage.get( sGPSExtensionFlag );
|
||||
}
|
||||
|
||||
public boolean hasStatusFlag()
|
||||
{
|
||||
return mMessage.get( sStatusFlag );
|
||||
}
|
||||
|
||||
|
||||
public int getFleetTo()
|
||||
{
|
||||
if( hasFleetExtensionFlag() )
|
||||
{
|
||||
return getInt( sFleetTo ) + 99;
|
||||
}
|
||||
else
|
||||
{
|
||||
return getInt( sFleetFrom ) + 99;
|
||||
}
|
||||
}
|
||||
|
||||
public int getIdentifierTo()
|
||||
{
|
||||
return getInt( sIdentTo ) + 999;
|
||||
}
|
||||
|
||||
/**
|
||||
* GPS Heading
|
||||
* @return
|
||||
*/
|
||||
public double getHeading()
|
||||
{
|
||||
double retVal = 0.0;
|
||||
|
||||
int heading = getInt( sGPSHeading );
|
||||
|
||||
if( heading != 4095 )
|
||||
{
|
||||
retVal = (double)(heading / 10.0 );
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public int getBlock7WhatIsIt()
|
||||
{
|
||||
int value = getInt( sBLK7WhatIsIt );
|
||||
|
||||
if( value == 65535 )
|
||||
{
|
||||
value = -1;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Speed in Kph - whole numbers and three decimal digits of precision
|
||||
* @return
|
||||
*/
|
||||
public double getSpeed()
|
||||
{
|
||||
double retVal = 0.0;
|
||||
|
||||
int temp = getInt( sSpeed );
|
||||
|
||||
if( temp != 0 )
|
||||
{
|
||||
retVal = (double)temp / 1000.0D;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public double getLatitude()
|
||||
{
|
||||
//TODO: determine the correct hemisphere indicator and replace this
|
||||
//hardcoded "0" with the correct hemisphere value
|
||||
|
||||
return convertDDMToDD( 0,
|
||||
getInt( sLatitudeDegreesMinutes ),
|
||||
getInt( sLatitudeDecimalMinutes ) );
|
||||
}
|
||||
|
||||
public double getLongitude()
|
||||
{
|
||||
//TODO: determine the correct hemisphere indicator and replace this
|
||||
//hardcoded "1" with the correct hemisphere value
|
||||
|
||||
return convertDDMToDD( 1,
|
||||
getInt( sLongitudeDegreesMinutes ),
|
||||
getInt( sLongitudeDecimalMinutes ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts Degrees Decimal Minutes to Decimal Degrees
|
||||
*
|
||||
* Latitude and Longitude values are represented by:
|
||||
*
|
||||
* @param hemisphere - 0=North & East, 1=South & West
|
||||
* @param degreesMinutes - an integer value with the first 2-3 digits representing
|
||||
* the degrees and the last two digits representing the minutes
|
||||
*
|
||||
* @param decimalDegrees - an integer value representing the fractional
|
||||
* minutes
|
||||
*
|
||||
* @return - decimal degrees formatted value
|
||||
*/
|
||||
public double convertDDMToDD( int hemisphere, int degreesMinutes, int decimalDegrees )
|
||||
{
|
||||
double retVal = 0.0;
|
||||
|
||||
if( degreesMinutes != 0 )
|
||||
{
|
||||
//Degrees - divide value by 100 and retain the whole number value (ie degrees)
|
||||
retVal += (double)( degreesMinutes / 100 );
|
||||
|
||||
//Minutes - modulus by 100 to get the whole minutes value
|
||||
int wholeMinutes = degreesMinutes % 100;
|
||||
|
||||
if( wholeMinutes != 0 )
|
||||
{
|
||||
retVal += (double)( wholeMinutes / 60.0D );
|
||||
}
|
||||
}
|
||||
|
||||
if( decimalDegrees != 0 )
|
||||
{
|
||||
//Fractional Minutes - divide by 10,000 to get the decimal place correct
|
||||
//then divide by 60 (minutes) to get the decimal value
|
||||
//10,000 * 60 = 600,000
|
||||
retVal += (double)( decimalDegrees / 600000.0D );
|
||||
}
|
||||
|
||||
//Adjust the value +/- for the hemisphere
|
||||
if( hemisphere == 1 ) //South and West values
|
||||
{
|
||||
retVal = -retVal;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public String getGPSTime()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append( format( getInt( sGPSHours ), 2 ) );
|
||||
sb.append( ":" );
|
||||
sb.append( format( getInt( sGPSMinutes ), 2 ) );
|
||||
sb.append( ":" );
|
||||
sb.append( format( getInt( sGPSSeconds ), 2 ) );
|
||||
sb.append( "z" );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 7 bit checksum that is part of the GPGGA message, I think
|
||||
* @return
|
||||
*/
|
||||
public int getGPSChecksum()
|
||||
{
|
||||
return getInt( sGPSChecksum );
|
||||
}
|
||||
|
||||
public String getGPSChecksumString()
|
||||
{
|
||||
String sum = Integer.toHexString( getGPSChecksum() );
|
||||
|
||||
if( sum.length() == 1 )
|
||||
{
|
||||
return "*0" + sum;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "*" + sum;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 1-based calendar day of month
|
||||
* 1 = 1st day of month
|
||||
*/
|
||||
public int getGPSDay()
|
||||
{
|
||||
return getInt( sGPSDay ) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 0-based GPS Month
|
||||
* 0 = January
|
||||
*
|
||||
* Note: actual day of month value is 1-based, so we subtract 1 to get
|
||||
* the actual day of month value
|
||||
*/
|
||||
public int getGPSMonth()
|
||||
{
|
||||
return getInt( sGPSMonth );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns year, which is a combination of the century bit field + 1, times
|
||||
* 100, plus the year bit field
|
||||
*/
|
||||
public int getGPSYear()
|
||||
{
|
||||
// return ( ( getInt( sGPSCentury ) + 1 ) * 100 ) + getInt( sGPSYear );
|
||||
return ( 2000 ) + getInt( sGPSYear );
|
||||
}
|
||||
|
||||
public String getGPSLocation()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append( pad( String.valueOf( mDecimalFormatter.format( getLatitude() ) ), 8, "0" ) + " " );
|
||||
sb.append( pad( String.valueOf( mDecimalFormatter.format( getLongitude() ) ), 10, "0" ) );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
public String getGPSDate()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( getGPSYear() + "-" );
|
||||
|
||||
int month = getGPSMonth();
|
||||
|
||||
if( month < 10 )
|
||||
{
|
||||
sb.append( "0" );
|
||||
}
|
||||
|
||||
sb.append( month + "-" );
|
||||
|
||||
int day = getGPSDay();
|
||||
|
||||
if( day < 10 )
|
||||
{
|
||||
sb.append( "0" );
|
||||
}
|
||||
|
||||
sb.append( day );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBinaryMessage()
|
||||
{
|
||||
return mMessage.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* String representing results of the parity check
|
||||
*
|
||||
* [P] = passes parity check
|
||||
* [f] = fails parity check
|
||||
* [C] = corrected message
|
||||
* [-] = message section not present
|
||||
*/
|
||||
public String getParity()
|
||||
{
|
||||
return "[" + CRC.format( mCRC ) + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol()
|
||||
{
|
||||
return "Fleetsync II";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType()
|
||||
{
|
||||
return getMessageType().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFromID()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( getFleetFrom() );
|
||||
sb.append( "-" );
|
||||
sb.append( getIdentifierFrom() );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getFromIDAlias()
|
||||
{
|
||||
if( mAliasList != null )
|
||||
{
|
||||
return mAliasList.getFleetsyncAlias( getFromID() );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToID()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( getFleetTo() );
|
||||
sb.append( "-" );
|
||||
sb.append( getIdentifierTo() );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getToIDAlias()
|
||||
{
|
||||
if( mAliasList != null )
|
||||
{
|
||||
return mAliasList.getFleetsyncAlias( getToID() );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getFromTo( boolean includeFrom, boolean includeTo )
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if( includeFrom )
|
||||
{
|
||||
sb.append( "FM:" );
|
||||
sb.append( getFromID() );
|
||||
|
||||
Alias from = getFromIDAlias();
|
||||
|
||||
if( from != null )
|
||||
{
|
||||
sb.append( " " );
|
||||
sb.append( from.getName() );
|
||||
}
|
||||
}
|
||||
|
||||
if( includeFrom && includeTo )
|
||||
{
|
||||
sb.append( " " );
|
||||
}
|
||||
|
||||
if( includeTo )
|
||||
{
|
||||
sb.append( "TO:" );
|
||||
sb.append( getToID() );
|
||||
|
||||
Alias to = getToIDAlias();
|
||||
|
||||
if( to != null )
|
||||
{
|
||||
sb.append( " " );
|
||||
sb.append( to.getName() );
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
FleetsyncMessageType type = getMessageType();
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case ACKNOWLEDGE:
|
||||
sb.append( "ACKNOWLEDGE " );
|
||||
sb.append( getFromTo( true, true ) );
|
||||
break;
|
||||
case ANI:
|
||||
sb.append( "ANI " );
|
||||
sb.append( getFromTo( true, false ) );
|
||||
break;
|
||||
case EMERGENCY:
|
||||
sb.append( "**EMERGENCY** " );
|
||||
sb.append( getFromTo( true, true ) );
|
||||
break;
|
||||
case GPS:
|
||||
sb.append( "GPS [" );
|
||||
sb.append( getGPSLocation() );
|
||||
sb.append( "] " );
|
||||
|
||||
sb.append( getFromTo( true, true ) );
|
||||
|
||||
sb.append( " HDG:" );
|
||||
sb.append( pad( String.valueOf( getHeading() ), 5, " " ) );
|
||||
|
||||
sb.append( " GPSDate[" );
|
||||
sb.append( getGPSDate() );
|
||||
sb.append( " " );
|
||||
sb.append( getGPSTime() );
|
||||
sb.append( "]" );
|
||||
break;
|
||||
case LONE_WORKER_EMERGENCY:
|
||||
sb.append( "**LONE WORKER EMERGENCY** " );
|
||||
sb.append( getFromTo( true, true ) );
|
||||
break;
|
||||
case PAGING:
|
||||
sb.append( "PAGING " );
|
||||
sb.append( getFromTo( true, true ) );
|
||||
break;
|
||||
case STATUS:
|
||||
sb.append( "STATUS: " + format( getStatus(), 2 ) );
|
||||
|
||||
Alias status = getStatusAlias();
|
||||
if( status != null )
|
||||
{
|
||||
sb.append( "/" );
|
||||
sb.append( status.getName() );
|
||||
}
|
||||
|
||||
sb.append( getFromTo( true, true ) );
|
||||
break;
|
||||
case UNKNOWN:
|
||||
default:
|
||||
sb.append( "*** UNKNOWN *** " );
|
||||
sb.append( getFromTo( true, true ) );
|
||||
break;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorStatus()
|
||||
{
|
||||
return CRC.format( mCRC );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plottable getPlottable()
|
||||
{
|
||||
if( isValid() && getMessageType() == FleetsyncMessageType.GPS )
|
||||
{
|
||||
GeoPosition position =
|
||||
new GeoPosition( getLatitude(), getLongitude() );
|
||||
|
||||
Alias alias = mAliasList.getFleetsyncAlias( getFromID() );
|
||||
|
||||
return new Plottable( mTimeReceived, position, getFromID(), alias );
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
42
src/decode/fleetsync2/FleetsyncMessageType.java
Normal file
42
src/decode/fleetsync2/FleetsyncMessageType.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*******************************************************************************
|
||||
* 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.fleetsync2;
|
||||
|
||||
public enum FleetsyncMessageType
|
||||
{
|
||||
ACKNOWLEDGE( "ACK" ),
|
||||
ANI( "ANI" ),
|
||||
EMERGENCY( "EMERG" ),
|
||||
GPS( "GPS" ),
|
||||
LONE_WORKER_EMERGENCY( "LONE WORKER" ),
|
||||
PAGING( "PAGE" ),
|
||||
STATUS( "STATUS" ),
|
||||
UNKNOWN( "UNK" );
|
||||
|
||||
private String mLabel;
|
||||
|
||||
private FleetsyncMessageType( String label )
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
}
|
||||
167
src/decode/fleetsync2/FleetsyncPanel.java
Normal file
167
src/decode/fleetsync2/FleetsyncPanel.java
Normal file
@@ -0,0 +1,167 @@
|
||||
/*******************************************************************************
|
||||
* 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.fleetsync2;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import settings.ColorSetting;
|
||||
import settings.ColorSetting.ColorSettingName;
|
||||
import settings.Setting;
|
||||
import settings.SettingsManager;
|
||||
import controller.state.AuxStatePanel;
|
||||
import controller.state.ChannelState.ChangedAttribute;
|
||||
|
||||
public class FleetsyncPanel extends AuxStatePanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String PROTOCOL = "FSync II";
|
||||
|
||||
private JLabel mFromLabel = new JLabel( " " );
|
||||
private JLabel mFrom = new JLabel();
|
||||
private JLabel mFromAlias = new JLabel();
|
||||
|
||||
private JLabel mToLabel = new JLabel();
|
||||
private JLabel mTo = new JLabel( " " );
|
||||
private JLabel mToAlias = new JLabel();
|
||||
|
||||
private JLabel mProtocol = new JLabel( PROTOCOL );
|
||||
private JLabel mMessage = new JLabel();
|
||||
private JLabel mMessageType = new JLabel();
|
||||
|
||||
public FleetsyncPanel( SettingsManager settingsManager,
|
||||
FleetsyncChannelState state )
|
||||
{
|
||||
super( settingsManager, state );
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public FleetsyncChannelState getState()
|
||||
{
|
||||
return (FleetsyncChannelState)mAuxChannelState;
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setOpaque( false ); //Use the parent panel's background color
|
||||
|
||||
setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") );
|
||||
|
||||
addLabel( mFromLabel, false );
|
||||
addLabel( mFrom, false );
|
||||
addLabel( mFromAlias, true );
|
||||
|
||||
addLabel( mToLabel, false );
|
||||
addLabel( mTo, false );
|
||||
addLabel( mToAlias, true );
|
||||
|
||||
addLabel( mProtocol, false );
|
||||
addLabel( mMessageType, false );
|
||||
addLabel( mMessage, true );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( final ChangedAttribute changedAttribute )
|
||||
{
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
switch( changedAttribute )
|
||||
{
|
||||
case FROM_TALKGROUP:
|
||||
String from = getState().getFleetIDFrom();
|
||||
|
||||
if( from != null )
|
||||
{
|
||||
mFromLabel.setText( "FM:" );
|
||||
}
|
||||
else
|
||||
{
|
||||
mFromLabel.setText( " " );
|
||||
}
|
||||
mFrom.setText( from );
|
||||
break;
|
||||
case FROM_TALKGROUP_ALIAS:
|
||||
updateAliasLabel( mFromAlias,
|
||||
getState().getFleetIDFromAlias() );
|
||||
break;
|
||||
case TO_TALKGROUP:
|
||||
String to = getState().getFleetIDTo();
|
||||
|
||||
if( to != null )
|
||||
{
|
||||
mToLabel.setText( "TO:" );
|
||||
}
|
||||
else
|
||||
{
|
||||
mToLabel.setText( " " );
|
||||
}
|
||||
mTo.setText( to );
|
||||
break;
|
||||
case TO_TALKGROUP_ALIAS:
|
||||
updateAliasLabel( mToAlias,
|
||||
getState().getFleetIDToAlias() );
|
||||
break;
|
||||
case MESSAGE:
|
||||
mMessage.setText( getState().getMessage() );
|
||||
break;
|
||||
case MESSAGE_TYPE:
|
||||
mMessageType.setText( getState().getMessageType() );
|
||||
break;
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settingChanged( Setting setting )
|
||||
{
|
||||
super.settingChanged( setting );
|
||||
|
||||
if( setting instanceof ColorSetting )
|
||||
{
|
||||
ColorSetting color = (ColorSetting)setting;
|
||||
|
||||
if( color.getColorSettingName() ==
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER )
|
||||
{
|
||||
mFromLabel.setForeground( color.getColor() );
|
||||
mFrom.setForeground( color.getColor() );
|
||||
mFromAlias.setForeground( color.getColor() );
|
||||
mToLabel.setForeground( color.getColor() );
|
||||
mTo.setForeground( color.getColor() );
|
||||
mToAlias.setForeground( color.getColor() );
|
||||
mProtocol.setForeground( color.getColor() );
|
||||
mMessageType.setForeground( color.getColor() );
|
||||
mMessage.setForeground( color.getColor() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
184
src/decode/ltrnet/LTRCallEvent.java
Normal file
184
src/decode/ltrnet/LTRCallEvent.java
Normal file
@@ -0,0 +1,184 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrnet;
|
||||
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import controller.activity.CallEvent;
|
||||
import decode.DecoderType;
|
||||
|
||||
public class LTRCallEvent extends CallEvent
|
||||
{
|
||||
private int mChannel;
|
||||
private int mFrequency;
|
||||
|
||||
public LTRCallEvent( CallEventType callEventType,
|
||||
AliasList aliasList,
|
||||
String fromID,
|
||||
String toID,
|
||||
int channel,
|
||||
int frequency,
|
||||
String details )
|
||||
{
|
||||
super( DecoderType.LTR_STANDARD, callEventType,
|
||||
aliasList, fromID, toID, details );
|
||||
|
||||
mChannel = channel;
|
||||
mFrequency = frequency;
|
||||
}
|
||||
|
||||
private Alias getAlias( String talkgroup )
|
||||
{
|
||||
if( hasAliasList() )
|
||||
{
|
||||
return getAliasList().getTalkgroupAlias( talkgroup );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getFromIDAlias()
|
||||
{
|
||||
if( mCallEventType == CallEventType.CALL_START ||
|
||||
mCallEventType == CallEventType.REGISTER ||
|
||||
mCallEventType == CallEventType.ID_UNIQUE )
|
||||
{
|
||||
if( hasAliasList() )
|
||||
{
|
||||
try
|
||||
{
|
||||
int radioID = Integer.parseInt( getFromID() );
|
||||
|
||||
return mAliasList.getUniqueID( radioID );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
//Do nothing, we couldn't parse the int radio id value
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( mCallEventType == CallEventType.REGISTER_ESN )
|
||||
{
|
||||
if( hasAliasList() )
|
||||
{
|
||||
return mAliasList.getESNAlias( getFromID() );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return getAlias( getFromID() );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@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 LTRCallEvent build()
|
||||
{
|
||||
return new LTRCallEvent( this );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor for the builder
|
||||
*/
|
||||
private LTRCallEvent( Builder builder )
|
||||
{
|
||||
this( builder.mCallEventType,
|
||||
builder.mAliasList,
|
||||
builder.mFromID,
|
||||
builder.mToID,
|
||||
builder.mChannel,
|
||||
builder.mFrequency,
|
||||
builder.mDetails );
|
||||
}
|
||||
|
||||
}
|
||||
424
src/decode/ltrnet/LTRNetActivitySummary.java
Normal file
424
src/decode/ltrnet/LTRNetActivitySummary.java
Normal file
@@ -0,0 +1,424 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrnet;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import message.Message;
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import controller.activity.ActivitySummaryProvider;
|
||||
|
||||
public class LTRNetActivitySummary implements ActivitySummaryProvider
|
||||
{
|
||||
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> mESNs = new TreeSet<String>();
|
||||
private TreeSet<Integer> mUniqueIDs = new TreeSet<Integer>();
|
||||
private TreeSet<Integer> mNeighborIDs = new TreeSet<Integer>();
|
||||
private TreeSet<Integer> mSiteIDs = new TreeSet<Integer>();
|
||||
private HashMap<Integer,Double> mReceiveFrequencies =
|
||||
new HashMap<Integer,Double>();
|
||||
private HashMap<Integer,Double> mTransmitFrequencies =
|
||||
new HashMap<Integer,Double>();
|
||||
private int mMonitoredLCN;
|
||||
|
||||
/**
|
||||
* 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 LTRNetActivitySummary( AliasList list )
|
||||
{
|
||||
mAliasList = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Message message )
|
||||
{
|
||||
if( message instanceof LTRNetOSWMessage )
|
||||
{
|
||||
LTRNetOSWMessage ltr = ((LTRNetOSWMessage)message);
|
||||
|
||||
if( ltr.isValid() )
|
||||
{
|
||||
switch( ltr.getMessageType() )
|
||||
{
|
||||
case CA_STRT:
|
||||
case CA_ENDD:
|
||||
String talkgroup = ltr.getTalkgroupID( false );
|
||||
|
||||
if( mTalkgroupsFirstHeard.contains( talkgroup ) )
|
||||
{
|
||||
mTalkgroups.add( talkgroup );
|
||||
}
|
||||
else
|
||||
{
|
||||
mTalkgroupsFirstHeard.add( talkgroup );
|
||||
}
|
||||
break;
|
||||
case SY_IDLE:
|
||||
mMonitoredLCN = ltr.getChannel();
|
||||
break;
|
||||
case MA_CHNH:
|
||||
break;
|
||||
case MA_CHNL:
|
||||
break;
|
||||
case FQ_RXHI:
|
||||
mReceiveFrequencies.put( ltr.getHomeRepeater(),
|
||||
ltr.getFrequency() );
|
||||
break;
|
||||
case FQ_RXLO:
|
||||
break;
|
||||
case FQ_TXHI:
|
||||
mTransmitFrequencies.put( ltr.getHomeRepeater(),
|
||||
ltr.getFrequency() );
|
||||
break;
|
||||
case FQ_TXLO:
|
||||
break;
|
||||
case ID_ESNH:
|
||||
break;
|
||||
case ID_ESNL:
|
||||
break;
|
||||
case ID_NBOR:
|
||||
int neighborID = ltr.getNeighborID();
|
||||
|
||||
if( neighborID != sINT_NULL_VALUE )
|
||||
{
|
||||
mNeighborIDs.add( neighborID );
|
||||
}
|
||||
break;
|
||||
case ID_UNIQ:
|
||||
int uniqueid = ltr.getRadioUniqueID();
|
||||
|
||||
if( uniqueid != sINT_NULL_VALUE )
|
||||
{
|
||||
mUniqueIDs.add( uniqueid );
|
||||
}
|
||||
|
||||
break;
|
||||
case ID_SITE:
|
||||
int siteID = ltr.getSiteID();
|
||||
|
||||
if( siteID != sINT_NULL_VALUE )
|
||||
{
|
||||
mSiteIDs.add( siteID );
|
||||
}
|
||||
break;
|
||||
case UN_KNWN:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( message instanceof LTRNetISWMessage )
|
||||
{
|
||||
LTRNetISWMessage ltr = ((LTRNetISWMessage)message);
|
||||
|
||||
if( ltr.isValid() )
|
||||
{
|
||||
switch( ltr.getMessageType() )
|
||||
{
|
||||
case CA_STRT:
|
||||
case CA_ENDD:
|
||||
String talkgroup = ltr.getTalkgroupID();
|
||||
|
||||
if( mTalkgroupsFirstHeard.contains( talkgroup ) )
|
||||
{
|
||||
mTalkgroups.add( talkgroup );
|
||||
}
|
||||
else
|
||||
{
|
||||
mTalkgroupsFirstHeard.add( talkgroup );
|
||||
}
|
||||
break;
|
||||
case ID_ESNH:
|
||||
case ID_ESNL:
|
||||
String esn = ltr.getESN();
|
||||
|
||||
if( !esn.contains( "xxxx" ) )
|
||||
{
|
||||
mESNs.add( ltr.getESN() );
|
||||
}
|
||||
break;
|
||||
case ID_UNIQ:
|
||||
int uniqueid = ltr.getRadioUniqueID();
|
||||
|
||||
if( uniqueid != sINT_NULL_VALUE )
|
||||
{
|
||||
mUniqueIDs.add( uniqueid );
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSummary()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "Activity Summary\n" );
|
||||
sb.append( "Decoder:\tLTR-Net\n\n" );
|
||||
|
||||
if( mSiteIDs.isEmpty() )
|
||||
{
|
||||
sb.append( "Site:\tUnknown\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Iterator<Integer> it = mSiteIDs.iterator();
|
||||
|
||||
while( it.hasNext() )
|
||||
{
|
||||
sb.append( "Site:\t" );
|
||||
|
||||
int siteID = it.next();
|
||||
|
||||
sb.append( siteID );
|
||||
|
||||
Alias siteAlias = mAliasList.getSiteID( siteID );
|
||||
|
||||
if( siteAlias != null )
|
||||
{
|
||||
sb.append( " " );
|
||||
sb.append( siteAlias.getName() );
|
||||
}
|
||||
|
||||
sb.append( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
sb.append( "\nLCNs (transmit | receive)\n" );
|
||||
|
||||
if( mReceiveFrequencies.isEmpty() && mTransmitFrequencies.isEmpty() )
|
||||
{
|
||||
sb.append( " None\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int x = 1; x < 21; x++ )
|
||||
{
|
||||
double rcv = 0.0d;
|
||||
|
||||
if( mReceiveFrequencies.containsKey( x ) )
|
||||
{
|
||||
rcv = mReceiveFrequencies.get( x );
|
||||
}
|
||||
|
||||
double xmt = 0.0d;
|
||||
|
||||
if( mTransmitFrequencies.containsKey( x ) )
|
||||
{
|
||||
xmt = mTransmitFrequencies.get( x );
|
||||
}
|
||||
|
||||
if( rcv > 0.0d || xmt > 0.0d )
|
||||
{
|
||||
if( x < 10 )
|
||||
{
|
||||
sb.append( " " );
|
||||
}
|
||||
|
||||
sb.append( x );
|
||||
sb.append( ": " );
|
||||
|
||||
if( xmt == -1.0d )
|
||||
{
|
||||
sb.append( "---.-----" );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( mDecimalFormatter.format( xmt ) );
|
||||
}
|
||||
sb.append( " | " );
|
||||
|
||||
if( rcv == -1.0d )
|
||||
{
|
||||
sb.append( "---.-----" );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( mDecimalFormatter.format( rcv ) );
|
||||
}
|
||||
|
||||
if( x == mMonitoredLCN )
|
||||
{
|
||||
sb.append( " **" );
|
||||
}
|
||||
|
||||
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( formatTalkgroup( tgid ) );
|
||||
sb.append( " " );
|
||||
|
||||
Alias alias = mAliasList.getTalkgroupAlias( tgid );
|
||||
|
||||
if( alias != null )
|
||||
{
|
||||
sb.append( alias.getName() );
|
||||
}
|
||||
|
||||
sb.append( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
sb.append( "\nRadio Unique IDs\n" );
|
||||
|
||||
if( mUniqueIDs.isEmpty() )
|
||||
{
|
||||
sb.append( " None\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Iterator<Integer> it = mUniqueIDs.iterator();
|
||||
|
||||
while( it.hasNext() )
|
||||
{
|
||||
int uid = it.next();
|
||||
|
||||
sb.append( " " );
|
||||
sb.append( uid );
|
||||
sb.append( " " );
|
||||
|
||||
Alias alias = mAliasList.getUniqueID( uid );
|
||||
|
||||
if( alias != null )
|
||||
{
|
||||
sb.append( alias.getName() );
|
||||
}
|
||||
|
||||
sb.append( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
sb.append( "\nESNs\n" );
|
||||
|
||||
if( mESNs.isEmpty() )
|
||||
{
|
||||
sb.append( " None\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Iterator<String> it = mESNs.iterator();
|
||||
|
||||
while( it.hasNext() )
|
||||
{
|
||||
String esn = it.next();
|
||||
|
||||
sb.append( " " );
|
||||
sb.append( esn );
|
||||
sb.append( " " );
|
||||
|
||||
Alias alias = mAliasList.getESNAlias( esn );
|
||||
|
||||
if( alias != null )
|
||||
{
|
||||
sb.append( alias.getName() );
|
||||
}
|
||||
|
||||
sb.append( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
sb.append( "\nNeighbor Sites\n" );
|
||||
|
||||
if( mNeighborIDs.isEmpty() )
|
||||
{
|
||||
sb.append( " None\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Iterator<Integer> it = mNeighborIDs.iterator();
|
||||
|
||||
while( it.hasNext() )
|
||||
{
|
||||
int neighbor = it.next();
|
||||
|
||||
sb.append( " " );
|
||||
sb.append( neighbor );
|
||||
sb.append( " " );
|
||||
|
||||
Alias alias = mAliasList.getSiteID( neighbor );
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
331
src/decode/ltrnet/LTRNetChannelState.java
Normal file
331
src/decode/ltrnet/LTRNetChannelState.java
Normal file
@@ -0,0 +1,331 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrnet;
|
||||
|
||||
import message.Message;
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import controller.activity.CallEvent.CallEventType;
|
||||
import controller.channel.ProcessingChain;
|
||||
import controller.state.AuxChannelState;
|
||||
import controller.state.ChannelState;
|
||||
|
||||
public class LTRNetChannelState extends ChannelState
|
||||
{
|
||||
private String mToTalkgroup;
|
||||
private Alias mToTalkgroupAlias;
|
||||
private String mFromTalkgroup;
|
||||
private Alias mFromTalkgroupAlias;
|
||||
private String mFromTalkgroupType;
|
||||
private int mChannelNumber;
|
||||
private String mUniqueID;
|
||||
private LTRCallEvent mCurrentCallEvent;
|
||||
private LTRNetActivitySummary mActivitySummary;
|
||||
|
||||
public LTRNetChannelState( ProcessingChain channel, AliasList aliasList )
|
||||
{
|
||||
super( channel, aliasList );
|
||||
|
||||
mActivitySummary = new LTRNetActivitySummary( aliasList );
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 LTRCallEvent.Builder( type )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( mChannelNumber )
|
||||
.frequency( mProcessingChain.getChannel()
|
||||
.getTunerChannel().getFrequency() )
|
||||
.to( mToTalkgroup )
|
||||
.build() );
|
||||
|
||||
}
|
||||
|
||||
mCurrentCallEvent = null;
|
||||
|
||||
super.fade( type );
|
||||
}
|
||||
|
||||
@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
|
||||
public void receive( Message message )
|
||||
{
|
||||
super.receive( message );
|
||||
|
||||
if( message instanceof LTRNetOSWMessage )
|
||||
{
|
||||
mActivitySummary.receive( message );
|
||||
|
||||
LTRNetOSWMessage ltr = (LTRNetOSWMessage)message;
|
||||
|
||||
Alias talkgroupAlias = ltr.getTalkgroupIDAlias();
|
||||
|
||||
switch( ltr.getMessageType() )
|
||||
{
|
||||
case CA_STRT:
|
||||
mToTalkgroup = ltr.getTalkgroupID();
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP );
|
||||
|
||||
if( talkgroupAlias != null )
|
||||
{
|
||||
mToTalkgroupAlias = talkgroupAlias;
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS );
|
||||
}
|
||||
|
||||
mChannelNumber = ltr.getChannel();
|
||||
broadcastChange( ChangedAttribute.CHANNEL_NUMBER );
|
||||
|
||||
if( getState() != ChannelState.State.CALL )
|
||||
{
|
||||
mCallEventModel.add(
|
||||
new LTRCallEvent.Builder( CallEventType.CALL_START )
|
||||
.aliasList( mAliasList )
|
||||
.channel( ltr.getChannel() )
|
||||
.frequency( mProcessingChain.getChannel()
|
||||
.getTunerChannel().getFrequency() )
|
||||
.to( mToTalkgroup )
|
||||
.build() );
|
||||
}
|
||||
|
||||
setState( State.CALL );
|
||||
break;
|
||||
case CA_ENDD:
|
||||
mToTalkgroup = ltr.getTalkgroupID();
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP );
|
||||
|
||||
if( talkgroupAlias != null )
|
||||
{
|
||||
mToTalkgroupAlias = talkgroupAlias;
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS );
|
||||
}
|
||||
|
||||
fade( CallEventType.CALL_END );
|
||||
break;
|
||||
case ID_UNIQ:
|
||||
mFromTalkgroupType = "UID";
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP_TYPE );
|
||||
|
||||
mFromTalkgroup = String.valueOf( ltr.getRadioUniqueID() );
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP );
|
||||
|
||||
mFromTalkgroupAlias = ltr.getRadioUniqueIDAlias();
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS );
|
||||
|
||||
mCallEventModel.add(
|
||||
new LTRCallEvent.Builder( CallEventType.REGISTER )
|
||||
.aliasList( mAliasList )
|
||||
.details( "Radio Unique ID" )
|
||||
.frequency( mProcessingChain.getChannel()
|
||||
.getTunerChannel().getFrequency() )
|
||||
.from( String.valueOf( ltr.getRadioUniqueID() ) )
|
||||
.build() );
|
||||
|
||||
setState( State.CALL );
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else if( message instanceof LTRNetISWMessage )
|
||||
{
|
||||
mActivitySummary.receive( message );
|
||||
|
||||
LTRNetISWMessage ltr = (LTRNetISWMessage)message;
|
||||
|
||||
Alias talkgroupAlias = ltr.getTalkgroupIDAlias();
|
||||
|
||||
switch( ltr.getMessageType() )
|
||||
{
|
||||
case CA_STRT:
|
||||
mToTalkgroup = ltr.getTalkgroupID();
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP );
|
||||
|
||||
if( talkgroupAlias != null )
|
||||
{
|
||||
mToTalkgroupAlias = talkgroupAlias;
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS );
|
||||
}
|
||||
|
||||
if( getState() != State.CALL )
|
||||
{
|
||||
mCurrentCallEvent =
|
||||
new LTRCallEvent.Builder( CallEventType.CALL_START )
|
||||
.aliasList( mAliasList )
|
||||
.channel( ltr.getChannel() )
|
||||
.frequency( mProcessingChain.getChannel()
|
||||
.getTunerChannel().getFrequency() )
|
||||
.to( mToTalkgroup )
|
||||
.build();
|
||||
|
||||
mCallEventModel.add( mCurrentCallEvent );
|
||||
}
|
||||
setState( State.CALL );
|
||||
break;
|
||||
case CA_ENDD:
|
||||
mToTalkgroup = ltr.getTalkgroupID();
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP );
|
||||
|
||||
if( talkgroupAlias != null )
|
||||
{
|
||||
mToTalkgroupAlias = talkgroupAlias;
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS );
|
||||
}
|
||||
fade( CallEventType.CALL_END );
|
||||
break;
|
||||
case ID_UNIQ:
|
||||
mFromTalkgroupType = "UID";
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP_TYPE );
|
||||
|
||||
mFromTalkgroup = String.valueOf( ltr.getRadioUniqueID() );
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP );
|
||||
|
||||
mFromTalkgroupAlias = ltr.getRadioUniqueIDAlias();
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS );
|
||||
|
||||
if( getState() != State.CALL )
|
||||
{
|
||||
mCallEventModel.add( new LTRCallEvent.Builder(
|
||||
CallEventType.ID_UNIQUE )
|
||||
.aliasList( mAliasList )
|
||||
.details( "Unique ID" )
|
||||
.frequency( mProcessingChain.getChannel()
|
||||
.getTunerChannel().getFrequency() )
|
||||
.from( String.valueOf( ltr.getRadioUniqueID() ) )
|
||||
.build() );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( mCurrentCallEvent != null )
|
||||
{
|
||||
mCurrentCallEvent.setFromID(
|
||||
String.valueOf( ltr.getRadioUniqueID() ) );
|
||||
mCurrentCallEvent.setDetails( "Unique ID" );
|
||||
}
|
||||
}
|
||||
setState( State.CALL );
|
||||
break;
|
||||
case ID_ESNH:
|
||||
case ID_ESNL:
|
||||
mFromTalkgroupType = "ESN";
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP_TYPE );
|
||||
|
||||
mFromTalkgroup = ltr.getESN();
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP );
|
||||
|
||||
mFromTalkgroupAlias = ltr.getESNAlias();
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS );
|
||||
|
||||
setState( State.CALL );
|
||||
|
||||
mCallEventModel.add(
|
||||
new LTRCallEvent.Builder( CallEventType.REGISTER_ESN )
|
||||
.aliasList( mAliasList )
|
||||
.details( "ESN:" + ltr.getESN() )
|
||||
.frequency( mProcessingChain.getChannel()
|
||||
.getTunerChannel().getFrequency() )
|
||||
.from( ltr.getESN() )
|
||||
.build() );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
mToTalkgroup = null;
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP );
|
||||
|
||||
mToTalkgroupAlias = null;
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS );
|
||||
|
||||
mFromTalkgroup = null;
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP );
|
||||
|
||||
mFromTalkgroupAlias = null;
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS );
|
||||
|
||||
mFromTalkgroupType = " ";
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP_TYPE );
|
||||
|
||||
super.reset();
|
||||
}
|
||||
|
||||
public String getToTalkgroup()
|
||||
{
|
||||
return mToTalkgroup;
|
||||
}
|
||||
|
||||
public Alias getToTalkgroupAlias()
|
||||
{
|
||||
return mToTalkgroupAlias;
|
||||
}
|
||||
|
||||
public String getFromTalkgroup()
|
||||
{
|
||||
return mFromTalkgroup;
|
||||
}
|
||||
|
||||
public Alias getFromTalkgroupAlias()
|
||||
{
|
||||
return mFromTalkgroupAlias;
|
||||
}
|
||||
|
||||
public String getFromTalkgroupType()
|
||||
{
|
||||
return mFromTalkgroupType;
|
||||
}
|
||||
|
||||
public boolean hasChannelNumber()
|
||||
{
|
||||
return mChannelNumber != 0;
|
||||
}
|
||||
|
||||
public int getChannelNumber()
|
||||
{
|
||||
return mChannelNumber;
|
||||
}
|
||||
}
|
||||
118
src/decode/ltrnet/LTRNetDecoder.java
Normal file
118
src/decode/ltrnet/LTRNetDecoder.java
Normal file
@@ -0,0 +1,118 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrnet;
|
||||
|
||||
import message.MessageDirection;
|
||||
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 LTRNetDecoder 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 sLTR_STANDARD_MESSAGE_LENGTH = 40;
|
||||
private FilteringNBFMDemodulator mDemodulator;
|
||||
private DCRemovalFilter2 mDCRemovalFilter;
|
||||
private LTRFSKDemodulator mLTRFSKDemodulator;
|
||||
private MessageFramer mLTRMessageFramer;
|
||||
private LTRNetMessageProcessor mLTRMessageProcessor;
|
||||
|
||||
public LTRNetDecoder( SampleType sampleType,
|
||||
AliasList aliasList,
|
||||
MessageDirection direction )
|
||||
{
|
||||
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 Float
|
||||
* 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
|
||||
*/
|
||||
mDemodulator = new FilteringNBFMDemodulator();
|
||||
this.addComplexListener( mDemodulator );
|
||||
|
||||
/**
|
||||
* Remove the DC component that is present when we're mistuned
|
||||
*/
|
||||
mDCRemovalFilter = new DCRemovalFilter2( sDC_REMOVAL_RATIO );
|
||||
mDemodulator.addListener( mDCRemovalFilter );
|
||||
|
||||
mDCRemovalFilter.setListener( this.getFloatReceiver() );
|
||||
}
|
||||
|
||||
mLTRFSKDemodulator = 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 )
|
||||
{
|
||||
mDemodulator.addListener( mLTRFSKDemodulator );
|
||||
}
|
||||
else
|
||||
{
|
||||
mDCRemovalFilter.setListener( mLTRFSKDemodulator );
|
||||
}
|
||||
|
||||
if( direction == MessageDirection.OSW )
|
||||
{
|
||||
mLTRMessageFramer =
|
||||
new MessageFramer( SyncPattern.LTR_STANDARD_OSW.getPattern(),
|
||||
sLTR_STANDARD_MESSAGE_LENGTH );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLTRMessageFramer =
|
||||
new MessageFramer( SyncPattern.LTR_STANDARD_ISW.getPattern(),
|
||||
sLTR_STANDARD_MESSAGE_LENGTH );
|
||||
}
|
||||
|
||||
mLTRFSKDemodulator.addListener( mLTRMessageFramer );
|
||||
|
||||
mLTRMessageProcessor = new LTRNetMessageProcessor( direction, aliasList );
|
||||
mLTRMessageFramer.addMessageListener( mLTRMessageProcessor );
|
||||
mLTRMessageProcessor.addMessageListener( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecoderType getType()
|
||||
{
|
||||
return DecoderType.LTR_NET;
|
||||
}
|
||||
}
|
||||
70
src/decode/ltrnet/LTRNetEditor.java
Normal file
70
src/decode/ltrnet/LTRNetEditor.java
Normal file
@@ -0,0 +1,70 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrnet;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JComboBox;
|
||||
|
||||
import message.MessageDirection;
|
||||
import decode.DecodeEditor;
|
||||
import decode.config.DecodeConfigLTRNet;
|
||||
import decode.config.DecodeConfiguration;
|
||||
|
||||
public class LTRNetEditor extends DecodeEditor
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private JComboBox<MessageDirection> mComboDirection;
|
||||
|
||||
public LTRNetEditor( DecodeConfiguration config )
|
||||
{
|
||||
super( config );
|
||||
|
||||
initGUI();
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
mComboDirection = new JComboBox<MessageDirection>();
|
||||
|
||||
mComboDirection.setModel(
|
||||
new DefaultComboBoxModel<MessageDirection>( MessageDirection.values() ) );
|
||||
|
||||
mComboDirection.setSelectedItem( ((DecodeConfigLTRNet)mConfig).getMessageDirection() );
|
||||
|
||||
mComboDirection.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
MessageDirection selected = mComboDirection
|
||||
.getItemAt( mComboDirection.getSelectedIndex() );
|
||||
|
||||
if( selected != null )
|
||||
{
|
||||
((DecodeConfigLTRNet)mConfig).setMessageDirection( selected );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
add( mComboDirection, "span 2,wrap" );
|
||||
}
|
||||
}
|
||||
312
src/decode/ltrnet/LTRNetISWMessage.java
Normal file
312
src/decode/ltrnet/LTRNetISWMessage.java
Normal file
@@ -0,0 +1,312 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrnet;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import map.Plottable;
|
||||
import message.MessageDirection;
|
||||
import message.MessageType;
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
import crc.CRC;
|
||||
import crc.CRCLTR;
|
||||
|
||||
public class LTRNetISWMessage extends LTRNetMessage
|
||||
{
|
||||
private static final int sCHIU_UNIQUE_ID = 24;
|
||||
private static final int sCHIU_ESN_LOW = 27;
|
||||
private static final int sCHIU_ESN_HIGH = 29;
|
||||
|
||||
private static final int sFREE_CALL = 21;
|
||||
private static final int sFREE_END_CALL = 23;
|
||||
private static final int sFREE_REQUEST_ACCESS = 31;
|
||||
|
||||
|
||||
private LTRNetISWMessage mAuxMessage;
|
||||
|
||||
public LTRNetISWMessage( BitSetBuffer message, AliasList list )
|
||||
{
|
||||
super( message, MessageDirection.ISW, list );
|
||||
|
||||
/**
|
||||
* If the CRC fails, test for a transmitted CRC of 127 and then check
|
||||
* the Free field for special messages
|
||||
*/
|
||||
if( mCRC == CRC.FAILED_CRC && CRCLTR.getTransmittedChecksum( mMessage ) == 127 )
|
||||
{
|
||||
if( getFree() == sFREE_REQUEST_ACCESS ||
|
||||
getFree() == sFREE_END_CALL )
|
||||
{
|
||||
mCRC = CRC.PASSED;
|
||||
}
|
||||
}
|
||||
|
||||
mMessageType = getMessageType();
|
||||
}
|
||||
|
||||
public void setAuxiliaryMessage( LTRNetISWMessage message )
|
||||
{
|
||||
if( message != null )
|
||||
{
|
||||
mAuxMessage = message;
|
||||
}
|
||||
}
|
||||
|
||||
public String getESN()
|
||||
{
|
||||
if( mMessageType == MessageType.ID_ESNH )
|
||||
{
|
||||
if( mAuxMessage != null &&
|
||||
mAuxMessage.getMessageType() == MessageType.ID_ESNL )
|
||||
{
|
||||
return getESNHigh() + mAuxMessage.getESNLow();
|
||||
}
|
||||
else
|
||||
{
|
||||
return getESNHigh() + "xxxx";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( mAuxMessage != null &&
|
||||
mAuxMessage.getMessageType() == MessageType.ID_ESNH )
|
||||
{
|
||||
return mAuxMessage.getESNHigh() + getESNLow();
|
||||
}
|
||||
else
|
||||
{
|
||||
return "xxxx" + getESNLow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Alias getESNAlias()
|
||||
{
|
||||
if( mAliasList != null )
|
||||
{
|
||||
return mAliasList.getESNAlias( getESN() );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getESNHigh()
|
||||
{
|
||||
int esnHigh = getInt( sSIXTEEN_BITS );
|
||||
|
||||
return String.format("%04X", esnHigh & 0xFFFF );
|
||||
}
|
||||
|
||||
public String getESNLow()
|
||||
{
|
||||
int esnLow = getInt( sSIXTEEN_BITS );
|
||||
|
||||
return String.format("%04X", esnLow & 0xFFFF );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append( mDatestampFormatter.format( new Date( System.currentTimeMillis() ) ) );
|
||||
sb.append( " LTRNet ISW [" );
|
||||
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 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 RQ_ACCE:
|
||||
sb.append( "REQUEST ACCESS 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 ID_ESNL:
|
||||
sb.append( "ESN LOW " );
|
||||
sb.append( getESN() );
|
||||
sb.append( "/" );
|
||||
sb.append( getESNAlias() );
|
||||
break;
|
||||
case ID_ESNH:
|
||||
sb.append( "ESN HIGH " );
|
||||
sb.append( getESN() );
|
||||
sb.append( "/" );
|
||||
sb.append( getESNAlias() );
|
||||
break;
|
||||
case ID_UNIQ:
|
||||
sb.append( "RADIO UNIQUE ID " );
|
||||
sb.append( getRadioUniqueID() );
|
||||
|
||||
if( getRadioUniqueIDAlias() != null )
|
||||
{
|
||||
sb.append( "/" );
|
||||
sb.append( getRadioUniqueIDAlias().getName() );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sb.append( "UNKNOWN " );
|
||||
sb.append( getArea() );
|
||||
sb.append( " " );
|
||||
sb.append( format( getChannel(), 2 ) );
|
||||
sb.append( " " );
|
||||
sb.append( format( getHomeRepeater(), 2 ) );
|
||||
sb.append( " " );
|
||||
sb.append( format( getGroup(), 3 ) );
|
||||
sb.append( " " );
|
||||
sb.append( format( getFree(), 2 ) );
|
||||
sb.append( " CRC:" );
|
||||
sb.append( CRCLTR.getTransmittedChecksum( mMessage ) );
|
||||
sb.append( " CALC:" );
|
||||
sb.append( CRCLTR.getCalculatedChecksum( mMessage ) );
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public int getRadioUniqueID()
|
||||
{
|
||||
int retVal = sINT_NULL_VALUE;
|
||||
|
||||
if( mMessageType == MessageType.ID_UNIQ )
|
||||
{
|
||||
retVal = getInt( sSIXTEEN_BITS );
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public Alias getRadioUniqueIDAlias()
|
||||
{
|
||||
if( mAliasList != null )
|
||||
{
|
||||
return mAliasList.getUniqueID( getRadioUniqueID() );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public MessageType getMessageType()
|
||||
{
|
||||
MessageType retVal = MessageType.UN_KNWN;
|
||||
|
||||
int channel = getChannel();
|
||||
|
||||
if( channel == 31 )
|
||||
{
|
||||
retVal = MessageType.CA_ENDD;
|
||||
}
|
||||
else if( channel > 20 )
|
||||
{
|
||||
switch( channel )
|
||||
{
|
||||
case sCHIU_UNIQUE_ID:
|
||||
retVal = MessageType.ID_UNIQ;
|
||||
break;
|
||||
case sCHIU_ESN_LOW:
|
||||
retVal = MessageType.ID_ESNH;
|
||||
break;
|
||||
case sCHIU_ESN_HIGH:
|
||||
retVal = MessageType.ID_ESNL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if( channel > 0 )
|
||||
{
|
||||
int free = getFree();
|
||||
|
||||
switch( free )
|
||||
{
|
||||
case sFREE_CALL:
|
||||
retVal = MessageType.CA_STRT;
|
||||
break;
|
||||
case sFREE_END_CALL:
|
||||
retVal = MessageType.CA_ENDD;
|
||||
break;
|
||||
case sFREE_REQUEST_ACCESS:
|
||||
retVal = MessageType.RQ_ACCE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
@Override
|
||||
public String getFromID()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getFromIDAlias()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAuxiliaryMessage( LTRNetOSWMessage message )
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plottable getPlottable()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
}
|
||||
243
src/decode/ltrnet/LTRNetMessage.java
Normal file
243
src/decode/ltrnet/LTRNetMessage.java
Normal file
@@ -0,0 +1,243 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrnet;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
import message.Message;
|
||||
import message.MessageDirection;
|
||||
import message.MessageType;
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
import crc.CRC;
|
||||
import crc.CRCLTR;
|
||||
import decode.DecoderType;
|
||||
|
||||
public abstract class LTRNetMessage 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 static final int[] sSIXTEEN_BITS = { 32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17 };
|
||||
protected static final int[] sMANUFACTURER_ID = { 23,22,21,20,19,18,17 };
|
||||
|
||||
protected BitSetBuffer mMessage;
|
||||
protected MessageType mMessageType;
|
||||
protected CRC mCRC;
|
||||
protected AliasList mAliasList;
|
||||
|
||||
public LTRNetMessage( BitSetBuffer message,
|
||||
MessageDirection direction,
|
||||
AliasList list )
|
||||
{
|
||||
mMessage = message;
|
||||
mCRC = CRCLTR.check( message, direction );
|
||||
mAliasList = list;
|
||||
}
|
||||
|
||||
public MessageType getMessageType()
|
||||
{
|
||||
return mMessageType;
|
||||
}
|
||||
|
||||
public abstract void setAuxiliaryMessage( LTRNetOSWMessage message );
|
||||
|
||||
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<<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();
|
||||
}
|
||||
|
||||
public CRC getCRC()
|
||||
{
|
||||
return mCRC;
|
||||
}
|
||||
|
||||
public int getArea()
|
||||
{
|
||||
return getInt( sAREA );
|
||||
}
|
||||
|
||||
public int getChannel()
|
||||
{
|
||||
return getInt( sCHANNEL );
|
||||
}
|
||||
|
||||
public int getHomeRepeater()
|
||||
{
|
||||
return getInt( sHOME_REPEATER );
|
||||
}
|
||||
|
||||
public int getGroup()
|
||||
{
|
||||
return getInt( sGROUP );
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
return mAliasList.getTalkgroupAlias( getTalkgroupID() );
|
||||
}
|
||||
|
||||
public int getFree()
|
||||
{
|
||||
return getInt( sFREE );
|
||||
}
|
||||
|
||||
public int getCRCChecksum()
|
||||
{
|
||||
return getInt( sCRC );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol()
|
||||
{
|
||||
return DecoderType.LTR_NET.getDisplayString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType()
|
||||
{
|
||||
if( mMessageType != null )
|
||||
{
|
||||
return mMessageType.getDisplayText();
|
||||
}
|
||||
else
|
||||
{
|
||||
return MessageType.UN_KNWN.getDisplayText();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToID()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getToIDAlias()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorStatus()
|
||||
{
|
||||
return mCRC.getDisplayText();
|
||||
}
|
||||
}
|
||||
147
src/decode/ltrnet/LTRNetMessageProcessor.java
Normal file
147
src/decode/ltrnet/LTRNetMessageProcessor.java
Normal file
@@ -0,0 +1,147 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrnet;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import message.Message;
|
||||
import message.MessageDirection;
|
||||
import message.MessageType;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class LTRNetMessageProcessor implements Listener<BitSetBuffer>
|
||||
{
|
||||
private Broadcaster<Message> mBroadcaster = new Broadcaster<Message>();
|
||||
private HashMap<Integer,LTRNetOSWMessage> mReceiveAuxMessages =
|
||||
new HashMap<Integer,LTRNetOSWMessage>();
|
||||
private HashMap<Integer,LTRNetOSWMessage> mTransmitAuxMessages =
|
||||
new HashMap<Integer,LTRNetOSWMessage>();
|
||||
private LTRNetISWMessage mESNHighMessage;
|
||||
private LTRNetISWMessage mESNLowMessage;
|
||||
|
||||
private MessageDirection mDirection;
|
||||
private AliasList mAliasList;
|
||||
|
||||
public LTRNetMessageProcessor( MessageDirection direction,
|
||||
AliasList list )
|
||||
{
|
||||
mDirection = direction;
|
||||
mAliasList = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( BitSetBuffer buffer )
|
||||
{
|
||||
LTRNetMessage message;
|
||||
|
||||
if( mDirection == MessageDirection.OSW )
|
||||
{
|
||||
message = new LTRNetOSWMessage( buffer, mAliasList );
|
||||
|
||||
if( message.isValid() )
|
||||
{
|
||||
if( message.getMessageType() == MessageType.FQ_TXHI )
|
||||
{
|
||||
message.setAuxiliaryMessage(
|
||||
mTransmitAuxMessages.get( message.getHomeRepeater() ) );
|
||||
}
|
||||
else if( message.getMessageType() == MessageType.FQ_TXLO )
|
||||
{
|
||||
mTransmitAuxMessages.put( message.getHomeRepeater(),
|
||||
(LTRNetOSWMessage)message );
|
||||
}
|
||||
else if( message.getMessageType() == MessageType.FQ_RXHI )
|
||||
{
|
||||
message.setAuxiliaryMessage(
|
||||
mReceiveAuxMessages.get( message.getHomeRepeater() ) );
|
||||
}
|
||||
else if( message.getMessageType() == MessageType.FQ_RXLO )
|
||||
{
|
||||
mReceiveAuxMessages.put( message.getHomeRepeater(),
|
||||
(LTRNetOSWMessage)message );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.flip( 0, 40 );
|
||||
|
||||
message = new LTRNetISWMessage( buffer, mAliasList );
|
||||
|
||||
if( message.isValid() )
|
||||
{
|
||||
/**
|
||||
* Catch ESN High messages to marry up with ESN Low messages. Purge
|
||||
* the ESN high message after 2 messages, so that it doesn't get
|
||||
* paired with the wrong esn low message
|
||||
*/
|
||||
if( message.getMessageType() == MessageType.ID_ESNH )
|
||||
{
|
||||
mESNHighMessage = (LTRNetISWMessage)message;
|
||||
|
||||
if( mESNLowMessage != null )
|
||||
{
|
||||
/* Check that the stored message is not older than
|
||||
* 5 seconds */
|
||||
if( System.currentTimeMillis() -
|
||||
mESNLowMessage.getTimeReceived() < 5000 )
|
||||
{
|
||||
((LTRNetISWMessage)message).setAuxiliaryMessage( mESNLowMessage );
|
||||
}
|
||||
else
|
||||
{
|
||||
mESNLowMessage = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( message.getMessageType() == MessageType.ID_ESNL )
|
||||
{
|
||||
mESNLowMessage = (LTRNetISWMessage)message;
|
||||
|
||||
if( mESNHighMessage != null )
|
||||
{
|
||||
/* Check that the stored message is not older than 5 seconds */
|
||||
if( System.currentTimeMillis() - mESNHighMessage.getTimeReceived() < 5000 )
|
||||
{
|
||||
((LTRNetISWMessage)message).setAuxiliaryMessage( mESNHighMessage );
|
||||
}
|
||||
else
|
||||
{
|
||||
mESNHighMessage = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mBroadcaster.receive( message );
|
||||
}
|
||||
|
||||
public void addMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
public void removeMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.removeListener( listener );
|
||||
}
|
||||
}
|
||||
579
src/decode/ltrnet/LTRNetOSWMessage.java
Normal file
579
src/decode/ltrnet/LTRNetOSWMessage.java
Normal file
@@ -0,0 +1,579 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrnet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
|
||||
import map.Plottable;
|
||||
import message.MessageDirection;
|
||||
import message.MessageType;
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class LTRNetOSWMessage extends LTRNetMessage
|
||||
{
|
||||
private static final int sCHANNEL_FREQUENCY_MESSAGE_TYPE_BIT = 20;
|
||||
private static final int sCHANNEL_MAP_MESSAGE_TYPE_BIT = 17;
|
||||
|
||||
private static final int sCHIU_UNIQUE_ID = 17;
|
||||
private static final int sCHIU_SITE_ID = 18;
|
||||
private static final int sCHIU_TRANSMIT_FREQUENCY = 24;
|
||||
private static final int sCHIU_RECEIVE_FREQUENCY = 25;
|
||||
private static final int sCHIU_NEIGHBOR = 26;
|
||||
private static final int sCHIU_MAP = 28;
|
||||
private static final int sCHIU_CALL_END = 31;
|
||||
|
||||
private static final int sHOME_DIRECTED_GROUP_CALL = 29;
|
||||
private static final int sHOME_CHANNEL_IN_USE_MUTE_AUDIO = 30;
|
||||
|
||||
private static final int sGROUP_DO_NOTHING = 253;
|
||||
private static final int sGROUP_CWID = 254;
|
||||
private static final int sGROUP_CHANNEL_IDLE = 255;
|
||||
|
||||
private static final int sFREE_ALL_CHANNELS_BUSY = 0;
|
||||
private static final int sFREE_LTR_GROUP_CALL = 30;
|
||||
private static final int sFREE_LTRNET_GROUP_CALL = 31;
|
||||
|
||||
private LTRNetOSWMessage mAuxMessage;
|
||||
|
||||
public LTRNetOSWMessage( BitSetBuffer 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 != sINT_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( " = " );
|
||||
double rxfreqhi = getFrequency();
|
||||
if( rxfreqhi != sDOUBLE_NULL_VALUE )
|
||||
{
|
||||
sb.append( mDecimalFormatter.format( rxfreqhi ) );
|
||||
}
|
||||
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( " MSG 1 OF 2" );
|
||||
break;
|
||||
case FQ_TXHI:
|
||||
sb.append( "CHAN TX LOW LCN:" );
|
||||
sb.append( format( getHomeRepeater(), 2 ) );
|
||||
sb.append( " = " );
|
||||
double txfreq = getFrequency();
|
||||
if( txfreq != sDOUBLE_NULL_VALUE )
|
||||
{
|
||||
sb.append( mDecimalFormatter.format( txfreq ) );
|
||||
}
|
||||
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( " MSG 1 OF 2" );
|
||||
break;
|
||||
case ID_SITE:
|
||||
int siteId = getSiteID();
|
||||
if( siteId != sINT_NULL_VALUE )
|
||||
{
|
||||
sb.append( "SITE ID: " );
|
||||
sb.append( siteId );
|
||||
sb.append( " " );
|
||||
sb.append( getSiteIDAlias() );
|
||||
}
|
||||
break;
|
||||
case ID_NBOR:
|
||||
int neighborRank = getNeighborRank();
|
||||
int neighborId = getNeighborID();
|
||||
|
||||
if( neighborRank != sINT_NULL_VALUE &&
|
||||
neighborId != sINT_NULL_VALUE )
|
||||
{
|
||||
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()
|
||||
{
|
||||
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()
|
||||
{
|
||||
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 = sINT_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 int getSiteID()
|
||||
{
|
||||
int retVal = sINT_NULL_VALUE;
|
||||
|
||||
if( mMessageType == MessageType.ID_SITE )
|
||||
{
|
||||
retVal = mMessage.getInt( 23, 32 );
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public Alias getSiteIDAlias()
|
||||
{
|
||||
if( mAliasList != null )
|
||||
{
|
||||
return mAliasList.getSiteID( getSiteID() );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getNeighborID()
|
||||
{
|
||||
int retVal = sINT_NULL_VALUE;
|
||||
|
||||
if( mMessageType == MessageType.ID_NBOR )
|
||||
{
|
||||
retVal = mMessage.getInt( 23, 32 );
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private Alias getNeighborIDAlias()
|
||||
{
|
||||
if( mAliasList != null )
|
||||
{
|
||||
return mAliasList.getSiteID( getNeighborID() );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getNeighborRank()
|
||||
{
|
||||
int retVal = sINT_NULL_VALUE;
|
||||
|
||||
if( mMessageType == MessageType.ID_NBOR )
|
||||
{
|
||||
retVal = mMessage.getInt( 15, 18 ) + 1;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public double getFrequency()
|
||||
{
|
||||
double retVal = sDOUBLE_NULL_VALUE;
|
||||
|
||||
if( mAuxMessage != null )
|
||||
{
|
||||
int high = getHighChannelUnits();
|
||||
int low = mAuxMessage.getLowChannelUnits();
|
||||
|
||||
if( high != sINT_NULL_VALUE && low != sINT_NULL_VALUE )
|
||||
{
|
||||
retVal = ( high + low ) * .00125 + 150.0;
|
||||
}
|
||||
}
|
||||
|
||||
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 = sINT_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 = sINT_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 sCHIU_UNIQUE_ID:
|
||||
retVal = MessageType.ID_UNIQ;
|
||||
break;
|
||||
case sCHIU_SITE_ID:
|
||||
retVal = MessageType.ID_SITE;
|
||||
break;
|
||||
case sCHIU_TRANSMIT_FREQUENCY:
|
||||
if( getHomeRepeater() == 24 )
|
||||
{
|
||||
retVal = MessageType.ID_UNIQ;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( mMessage.get( sCHANNEL_FREQUENCY_MESSAGE_TYPE_BIT ) )
|
||||
{
|
||||
retVal = MessageType.FQ_TXHI;
|
||||
}
|
||||
else
|
||||
{
|
||||
retVal = MessageType.FQ_TXLO;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case sCHIU_RECEIVE_FREQUENCY:
|
||||
if( mMessage.get( sCHANNEL_FREQUENCY_MESSAGE_TYPE_BIT ) )
|
||||
{
|
||||
retVal = MessageType.FQ_RXHI;
|
||||
}
|
||||
else
|
||||
{
|
||||
retVal = MessageType.FQ_RXLO;
|
||||
}
|
||||
break;
|
||||
case sCHIU_NEIGHBOR:
|
||||
retVal = MessageType.ID_NBOR;
|
||||
break;
|
||||
case sCHIU_MAP:
|
||||
if( mMessage.get( sCHANNEL_MAP_MESSAGE_TYPE_BIT ) )
|
||||
{
|
||||
retVal = MessageType.MA_CHNH;
|
||||
}
|
||||
else
|
||||
{
|
||||
retVal = MessageType.MA_CHNL;
|
||||
}
|
||||
break;
|
||||
case sCHIU_CALL_END:
|
||||
retVal = MessageType.CA_ENDD;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int group = getGroup();
|
||||
|
||||
if( group == sGROUP_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;
|
||||
}
|
||||
}
|
||||
229
src/decode/ltrnet/LTRNetPanel.java
Normal file
229
src/decode/ltrnet/LTRNetPanel.java
Normal file
@@ -0,0 +1,229 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrnet;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import settings.ColorSetting;
|
||||
import settings.Setting;
|
||||
import alias.Alias;
|
||||
import controller.channel.Channel;
|
||||
import controller.state.ChannelState.ChangedAttribute;
|
||||
import controller.state.ChannelStatePanel;
|
||||
|
||||
public class LTRNetPanel extends ChannelStatePanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private JLabel mStateLabel;
|
||||
private JLabel mSourceLabel;
|
||||
private JLabel mChannelLabel;
|
||||
|
||||
private JLabel mProtocol = new JLabel( "LTR-Net" );
|
||||
private JLabel mToTalkgroup = new JLabel();
|
||||
private JLabel mToTalkgroupAlias = new JLabel();
|
||||
|
||||
private JLabel mFromType = new JLabel( " " );
|
||||
private JLabel mFromTalkgroup = new JLabel();
|
||||
private JLabel mFromTalkgroupAlias = new JLabel();
|
||||
|
||||
public LTRNetPanel( Channel channel )
|
||||
{
|
||||
super( channel );
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
mFromType.setFont( mFontDecoder );
|
||||
mFromType.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( mFromType );
|
||||
add( mFromTalkgroup );
|
||||
add( mFromTalkgroupAlias, "wrap" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( final ChangedAttribute changedAttribute )
|
||||
{
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
LTRNetChannelState state = (LTRNetChannelState)mChannel
|
||||
.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 FROM_TALKGROUP:
|
||||
mFromTalkgroup.setText( state.getFromTalkgroup() );
|
||||
break;
|
||||
case FROM_TALKGROUP_ALIAS:
|
||||
Alias fromAlias = state.getFromTalkgroupAlias();
|
||||
|
||||
if( fromAlias != null )
|
||||
{
|
||||
mFromTalkgroupAlias.setText( fromAlias.getName() );
|
||||
String iconNameString = fromAlias.getIconName();
|
||||
mFromTalkgroupAlias.setIcon( getSettingsManager()
|
||||
.getImageIcon( iconNameString, 12 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
mFromTalkgroupAlias.setText( null );
|
||||
mFromTalkgroupAlias.setIcon( null );
|
||||
}
|
||||
break;
|
||||
case FROM_TALKGROUP_TYPE:
|
||||
mFromType.setText( state.getFromTalkgroupType() );
|
||||
break;
|
||||
case TO_TALKGROUP:
|
||||
mToTalkgroup.setText( state.getToTalkgroup() );
|
||||
break;
|
||||
case TO_TALKGROUP_ALIAS:
|
||||
Alias tgAlias = state.getToTalkgroupAlias();
|
||||
|
||||
if( tgAlias != null )
|
||||
{
|
||||
mToTalkgroupAlias.setText( tgAlias.getName() );
|
||||
String iconNameString = tgAlias.getIconName();
|
||||
mToTalkgroupAlias.setIcon( getSettingsManager()
|
||||
.getImageIcon( iconNameString, 12 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
mToTalkgroupAlias.setText( null );
|
||||
mToTalkgroupAlias.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( mStateLabel != null )
|
||||
{
|
||||
mStateLabel.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mProtocol != null )
|
||||
{
|
||||
mProtocol.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mToTalkgroup != null )
|
||||
{
|
||||
mToTalkgroup.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mToTalkgroupAlias != null )
|
||||
{
|
||||
mToTalkgroupAlias.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mFromType != null )
|
||||
{
|
||||
mFromType.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mFromTalkgroup != null )
|
||||
{
|
||||
mFromTalkgroup.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mFromTalkgroupAlias != null )
|
||||
{
|
||||
mFromTalkgroupAlias.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
break;
|
||||
case CHANNEL_STATE_LABEL_DETAILS:
|
||||
if( mSourceLabel != null )
|
||||
{
|
||||
mSourceLabel.setForeground( mColorLabelDetails );
|
||||
}
|
||||
if( mChannelLabel != null )
|
||||
{
|
||||
mChannelLabel.setForeground( mColorLabelDetails );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
208
src/decode/ltrstandard/LTRChannelState.java
Normal file
208
src/decode/ltrstandard/LTRChannelState.java
Normal file
@@ -0,0 +1,208 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrstandard;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import message.Message;
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import controller.activity.CallEvent.CallEventType;
|
||||
import controller.channel.ProcessingChain;
|
||||
import controller.state.AuxChannelState;
|
||||
import controller.state.ChannelState;
|
||||
import decode.ltrnet.LTRCallEvent;
|
||||
|
||||
public class LTRChannelState extends ChannelState
|
||||
{
|
||||
private String mTalkgroup;
|
||||
private Alias mTalkgroupAlias;
|
||||
private int mChannelNumber;
|
||||
private HashMap<Integer,String> mActiveCalls = new HashMap<Integer,String>();
|
||||
private LTRStandardActivitySummary mActivitySummary;
|
||||
|
||||
public LTRChannelState( ProcessingChain channel, AliasList aliasList )
|
||||
{
|
||||
super( channel, aliasList );
|
||||
|
||||
mActivitySummary = new LTRStandardActivitySummary( 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
|
||||
public void receive( Message message )
|
||||
{
|
||||
super.receive( message );
|
||||
|
||||
if( message instanceof LTRStandardOSWMessage )
|
||||
{
|
||||
mActivitySummary.receive( message );
|
||||
|
||||
LTRStandardOSWMessage ltr = (LTRStandardOSWMessage)message;
|
||||
|
||||
switch( ltr.getMessageType() )
|
||||
{
|
||||
case CA_STRT:
|
||||
if( 0 < ltr.getChannel() && ltr.getChannel() <=20 )
|
||||
{
|
||||
/* If this channel is the same as the talkgroup's home
|
||||
* repeater, then the channel number refers to the
|
||||
* currently tuned frequency and we capture the channel
|
||||
* number ... otherwise, it might be reflecting a call
|
||||
* on another LCN */
|
||||
if( ltr.getChannel() == ltr.getHomeRepeater() )
|
||||
{
|
||||
mChannelNumber = ltr.getChannel();
|
||||
broadcastChange( ChangedAttribute.CHANNEL_NUMBER );
|
||||
}
|
||||
|
||||
/* If this call event is for this channel, then update
|
||||
* talkgroup and alias */
|
||||
if( ltr.getChannel() == mChannelNumber )
|
||||
{
|
||||
mTalkgroup = ltr.getTalkgroupID();
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP );
|
||||
|
||||
mTalkgroupAlias = ltr.getTalkgroupIDAlias();
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS );
|
||||
}
|
||||
|
||||
/* If this is a new call, log a call event */
|
||||
if( !mActiveCalls.containsKey( ltr.getChannel() ) ||
|
||||
!mActiveCalls.get( ltr.getChannel() )
|
||||
.contentEquals( ltr.getTalkgroupID() ) )
|
||||
{
|
||||
mActiveCalls.put( ltr.getChannel(), ltr.getTalkgroupID() );
|
||||
|
||||
CallEventType callEventType =
|
||||
( ltr.getChannel() == mChannelNumber ) ?
|
||||
CallEventType.CALL_START :
|
||||
CallEventType.CALL_DETECT;
|
||||
|
||||
mCallEventModel.add(
|
||||
new LTRCallEvent.Builder( callEventType )
|
||||
.to( ltr.getTalkgroupID() )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( ltr.getChannel() )
|
||||
.frequency( ( ltr.getChannel() == mChannelNumber ) ?
|
||||
mProcessingChain.getChannel()
|
||||
.getTunerChannel().getFrequency() : 0 )
|
||||
.build() );
|
||||
}
|
||||
|
||||
setState( State.CALL );
|
||||
}
|
||||
break;
|
||||
case CA_ENDD:
|
||||
mTalkgroup = ltr.getTalkgroupID();
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP );
|
||||
|
||||
mTalkgroupAlias = ltr.getTalkgroupIDAlias();
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS );
|
||||
|
||||
fade( CallEventType.CALL_END );
|
||||
break;
|
||||
case SY_IDLE:
|
||||
int channel = ltr.getChannel();
|
||||
|
||||
if( 0 < channel && channel <= 20 &&
|
||||
channel == ltr.getHomeRepeater() &&
|
||||
channel == ltr.getFree() )
|
||||
{
|
||||
mChannelNumber = channel;
|
||||
broadcastChange( ChangedAttribute.CHANNEL_NUMBER );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
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 ) )
|
||||
{
|
||||
mActiveCalls.clear();
|
||||
|
||||
mCallEventModel.add(
|
||||
new LTRCallEvent.Builder( type )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( mChannelNumber )
|
||||
.frequency( mProcessingChain.getChannel().getTunerChannel().getFrequency() )
|
||||
.to( mTalkgroup )
|
||||
.build() );
|
||||
}
|
||||
|
||||
super.fade( type );
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
mTalkgroup = null;
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP );
|
||||
|
||||
mTalkgroupAlias = null;
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS );
|
||||
|
||||
super.reset();
|
||||
}
|
||||
|
||||
public String getTalkgroup()
|
||||
{
|
||||
return mTalkgroup;
|
||||
}
|
||||
|
||||
public Alias getTalkgroupAlias()
|
||||
{
|
||||
return mTalkgroupAlias;
|
||||
}
|
||||
|
||||
public boolean hasChannelNumber()
|
||||
{
|
||||
return mChannelNumber != 0;
|
||||
}
|
||||
|
||||
public int getChannelNumber()
|
||||
{
|
||||
return mChannelNumber;
|
||||
}
|
||||
}
|
||||
132
src/decode/ltrstandard/LTRFSKDemodulator.java
Normal file
132
src/decode/ltrstandard/LTRFSKDemodulator.java
Normal file
@@ -0,0 +1,132 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrstandard;
|
||||
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import dsp.BooleanZeroCrossingDetector;
|
||||
import dsp.FloatToBinarySlicer;
|
||||
import dsp.filter.BooleanAveragingFilter;
|
||||
import dsp.filter.Filters;
|
||||
import dsp.filter.FloatFIRFilter;
|
||||
import dsp.filter.FloatHalfBandFilter;
|
||||
import dsp.filter.FloatHalfBandNoDecimateFilter;
|
||||
import dsp.filter.SquaringFilter;
|
||||
|
||||
public class LTRFSKDemodulator implements Listener<Float>
|
||||
{
|
||||
FloatHalfBandFilter mHBFilter1;
|
||||
FloatHalfBandFilter mHBFilter2;
|
||||
FloatHalfBandFilter mHBFilter3;
|
||||
FloatHalfBandFilter mHBFilter4;
|
||||
FloatHalfBandFilter mHBFilter5;
|
||||
FloatHalfBandNoDecimateFilter mHBFilter6;
|
||||
FloatFIRFilter mDCBlocker;
|
||||
|
||||
SquaringFilter mSquaringFilter;
|
||||
BooleanAveragingFilter mAveragingFilter;
|
||||
BooleanZeroCrossingDetector mZeroCrossingDetector;
|
||||
FloatToBinarySlicer mSlicer;
|
||||
Broadcaster<Boolean> mBroadcaster = new Broadcaster<Boolean>();
|
||||
|
||||
//debug
|
||||
// FloatWaveRecorder mRecorder;
|
||||
/**
|
||||
* Implements a Logic Trunked Radio sub-audible 300 baud FSK signaling
|
||||
* decoder. Expects a 48k short-valued sample rate, which is decimated
|
||||
* to a final 1.5k sample rate via five cascaded decimating half-band
|
||||
* filters, and applies a sixth, non-decimating half-band filter to bring
|
||||
* the overall bandwidth down to .75k. Each half band filter requires
|
||||
* 8 multiply/accumulate (MAC) cycles per input sample, totaling 2560
|
||||
* MAC filtering cycles per output bit, to get from 48k input to 300-baud
|
||||
* bit stream output.
|
||||
*
|
||||
* Applies a balancer to the demodulated fsk waveform to normalize the
|
||||
* values about the zero-axis and to offset fluctuations induced by the
|
||||
* DC correction filter.
|
||||
*
|
||||
* Converts filtered, decimated samples to squared bit values and then
|
||||
* applies a zero crossing detector to make a baud decision every 5 samples.
|
||||
*
|
||||
* Outputs the decoded bit stream.
|
||||
*/
|
||||
public LTRFSKDemodulator()
|
||||
{
|
||||
mHBFilter1 = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
|
||||
mHBFilter2 = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
mHBFilter1.setListener( mHBFilter2 );
|
||||
|
||||
mHBFilter3 = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
mHBFilter2.setListener( mHBFilter3 );
|
||||
|
||||
mHBFilter4 = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
mHBFilter3.setListener( mHBFilter4 );
|
||||
|
||||
mHBFilter5 = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
mHBFilter4.setListener( mHBFilter5 );
|
||||
|
||||
mHBFilter6 = new FloatHalfBandNoDecimateFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
mHBFilter5.setListener( mHBFilter6 );
|
||||
|
||||
// mDCBlocker = new FloatFIRFilter(
|
||||
// Filters.FIRHP_LTR_DCBLOCK_750FS_50FC.getCoefficients(), 1.0f );
|
||||
// mHBFilter6.setListener( mDCBlocker );
|
||||
|
||||
/**
|
||||
* Set the slicer's long buffer to a sample length of 14 baud periods
|
||||
* ( 5 * 14 )since 12 bauds should be the longest string of ones we'll
|
||||
* encounter
|
||||
*
|
||||
* Set the short buffer to two baud periods (5 * 2)
|
||||
*/
|
||||
//Set the buffer to 5 samples per baud times 12 bauds. Set the sync
|
||||
//lock period to 5 samples per baud times 31 bits (155 samples)
|
||||
mSlicer = new FloatToBinarySlicer( 70, 5 );
|
||||
mHBFilter6.setListener( mSlicer );
|
||||
// mDCBlocker.setListener( mSlicer );
|
||||
|
||||
mAveragingFilter = new BooleanAveragingFilter( 5 );
|
||||
mSlicer.setListener( mAveragingFilter );
|
||||
|
||||
mZeroCrossingDetector = new BooleanZeroCrossingDetector( 5 );
|
||||
mAveragingFilter.setListener( mZeroCrossingDetector );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
{
|
||||
mHBFilter1.receive( sample );
|
||||
}
|
||||
|
||||
public void addListener( Listener<Boolean> listener )
|
||||
{
|
||||
mZeroCrossingDetector.setListener( listener );
|
||||
}
|
||||
|
||||
public void removeListener( Listener<Boolean> listener )
|
||||
{
|
||||
mZeroCrossingDetector.removeListener( listener );
|
||||
}
|
||||
}
|
||||
122
src/decode/ltrstandard/LTRFSKDemodulator2.java
Normal file
122
src/decode/ltrstandard/LTRFSKDemodulator2.java
Normal file
@@ -0,0 +1,122 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrstandard;
|
||||
|
||||
import sample.Listener;
|
||||
import dsp.BooleanZeroCrossingDetector;
|
||||
import dsp.FloatToBinarySlicerLTR;
|
||||
import dsp.SyncDetectListener;
|
||||
import dsp.filter.BooleanAveragingFilter;
|
||||
import dsp.filter.Filters;
|
||||
import dsp.filter.FloatHalfBandFilter;
|
||||
import dsp.filter.FloatHalfBandNoDecimateFilter;
|
||||
import dsp.filter.SquaringFilter;
|
||||
|
||||
public class LTRFSKDemodulator2 implements Listener<Float>, SyncDetectListener
|
||||
{
|
||||
FloatHalfBandFilter mHBFilter1;
|
||||
FloatHalfBandFilter mHBFilter2;
|
||||
FloatHalfBandFilter mHBFilter3;
|
||||
FloatHalfBandFilter mHBFilter4;
|
||||
FloatHalfBandFilter mHBFilter5;
|
||||
FloatHalfBandNoDecimateFilter mHBFilter6;
|
||||
|
||||
SquaringFilter mSquaringFilter;
|
||||
BooleanAveragingFilter mAveragingFilter;
|
||||
BooleanZeroCrossingDetector mZeroCrossingDetector;
|
||||
FloatToBinarySlicerLTR mSlicer;
|
||||
|
||||
/**
|
||||
* Implements a Logic Trunked Radio sub-audible 300 baud FSK signaling
|
||||
* decoder. Expects a 48k short-valued sample rate, which is decimated
|
||||
* to a final 1.5k sample rate via five cascaded decimating half-band
|
||||
* filters, and applies a sixth, non-decimating half-band filter to bring
|
||||
* the overall bandwidth down to .75k. Each half band filter requires
|
||||
* 8 multiply/accumulate (MAC) cycles per input sample, totaling 2560
|
||||
* MAC filtering cycles per output bit, to get from 48k input to 300-baud
|
||||
* bit stream output.
|
||||
*
|
||||
* Applies a balancer to the demodulated fsk waveform to normalize the
|
||||
* values about the zero-axis and to offset fluctuations induced by the
|
||||
* DC correction filter.
|
||||
*
|
||||
* Converts filtered, decimated samples to squared bit values and then
|
||||
* applies a zero crossing detector to make a baud decision every 5 samples.
|
||||
*
|
||||
* Outputs the decoded bit stream.
|
||||
*/
|
||||
public LTRFSKDemodulator2( int syncLength, int messageLength )
|
||||
{
|
||||
mHBFilter1 = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
|
||||
mHBFilter2 = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
mHBFilter1.setListener( mHBFilter2 );
|
||||
|
||||
mHBFilter3 = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
mHBFilter2.setListener( mHBFilter3 );
|
||||
|
||||
mHBFilter4 = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
mHBFilter3.setListener( mHBFilter4 );
|
||||
|
||||
mHBFilter5 = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
mHBFilter4.setListener( mHBFilter5 );
|
||||
|
||||
mHBFilter6 = new FloatHalfBandNoDecimateFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
mHBFilter5.setListener( mHBFilter6 );
|
||||
|
||||
mSlicer = new FloatToBinarySlicerLTR( syncLength, messageLength, 5 );
|
||||
mHBFilter6.setListener( mSlicer );
|
||||
|
||||
mAveragingFilter = new BooleanAveragingFilter( 5 );
|
||||
mSlicer.setListener( mAveragingFilter );
|
||||
|
||||
mZeroCrossingDetector = new BooleanZeroCrossingDetector( 5 );
|
||||
mAveragingFilter.setListener( mZeroCrossingDetector );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
{
|
||||
mHBFilter1.receive( sample );
|
||||
}
|
||||
|
||||
public void setListener( Listener<Boolean> listener )
|
||||
{
|
||||
mZeroCrossingDetector.setListener( listener );
|
||||
}
|
||||
|
||||
public void removeListener( Listener<Boolean> listener )
|
||||
{
|
||||
mZeroCrossingDetector.removeListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass the sync detection down to the slicer
|
||||
*/
|
||||
@Override
|
||||
public void syncDetected()
|
||||
{
|
||||
mSlicer.syncDetected();
|
||||
}
|
||||
}
|
||||
219
src/decode/ltrstandard/LTRPanel.java
Normal file
219
src/decode/ltrstandard/LTRPanel.java
Normal file
@@ -0,0 +1,219 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrstandard;
|
||||
|
||||
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 LTRPanel extends ChannelStatePanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private JLabel mStateLabel;
|
||||
private JLabel mSourceLabel;
|
||||
private JLabel mChannelLabel;
|
||||
|
||||
private JLabel mProtocol = new JLabel( "LTR Standard" );
|
||||
private JLabel mTalkgroup = new JLabel();
|
||||
private JLabel mTalkgroupAlias = new JLabel();
|
||||
|
||||
private JLabel mLCNLabel = new JLabel( "LCN" );
|
||||
private JLabel mLCN = new JLabel();
|
||||
|
||||
private SettingsManager mSettingsManager;
|
||||
|
||||
public LTRPanel( SettingsManager settingsManager, Channel channel )
|
||||
{
|
||||
super( channel );
|
||||
|
||||
channel.getProcessingChain().getChannelState().addListener( this );
|
||||
|
||||
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 );
|
||||
|
||||
mTalkgroup.setFont( mFontDecoder );
|
||||
mTalkgroup.setForeground( mColorLabelDecoder );
|
||||
|
||||
mTalkgroupAlias.setFont( mFontDecoder );
|
||||
mTalkgroupAlias.setForeground( mColorLabelDecoder );
|
||||
|
||||
mLCNLabel.setFont( mFontDecoder );
|
||||
mLCNLabel.setForeground( mColorLabelDetails );
|
||||
|
||||
mLCN.setFont( mFontDecoder );
|
||||
mLCN.setForeground( mColorLabelDecoder );
|
||||
|
||||
add( mStateLabel );
|
||||
add( mSourceLabel );
|
||||
add( mChannelLabel, "wrap" );
|
||||
|
||||
add( mProtocol );
|
||||
add( mTalkgroup );
|
||||
add( mTalkgroupAlias, "wrap" );
|
||||
|
||||
add( mLCNLabel );
|
||||
add( mLCN, "wrap" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( final ChangedAttribute attribute )
|
||||
{
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
LTRChannelState state = (LTRChannelState)mChannel
|
||||
.getProcessingChain().getChannelState();
|
||||
|
||||
switch( attribute )
|
||||
{
|
||||
case CHANNEL_NUMBER:
|
||||
if( state.getChannelNumber() != 0 )
|
||||
{
|
||||
mLCN.setText(
|
||||
String.valueOf( state.getChannelNumber() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLCN.setText( "" );
|
||||
}
|
||||
break;
|
||||
case FROM_TALKGROUP:
|
||||
mTalkgroup.setText( state.getTalkgroup() );
|
||||
/* fall-through */
|
||||
case FROM_TALKGROUP_ALIAS:
|
||||
Alias alias = state.getTalkgroupAlias();
|
||||
|
||||
if( alias != null )
|
||||
{
|
||||
mTalkgroupAlias.setText( alias.getName() );
|
||||
mTalkgroupAlias.setIcon( mSettingsManager
|
||||
.getImageIcon( alias.getIconName(), 12 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
mTalkgroupAlias.setText( null );
|
||||
mTalkgroupAlias.setIcon( null );
|
||||
}
|
||||
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;
|
||||
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( mStateLabel != null )
|
||||
{
|
||||
mStateLabel.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mProtocol != null )
|
||||
{
|
||||
mProtocol.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mTalkgroup != null )
|
||||
{
|
||||
mTalkgroup.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mTalkgroupAlias != null )
|
||||
{
|
||||
mTalkgroupAlias.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mLCN != null )
|
||||
{
|
||||
mLCN.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
break;
|
||||
case CHANNEL_STATE_LABEL_DETAILS:
|
||||
if( mSourceLabel != null )
|
||||
{
|
||||
mSourceLabel.setForeground( mColorLabelDetails );
|
||||
}
|
||||
if( mChannelLabel != null )
|
||||
{
|
||||
mChannelLabel.setForeground( mColorLabelDetails );
|
||||
}
|
||||
if( mLCNLabel != null )
|
||||
{
|
||||
mLCNLabel.setForeground( mColorLabelDetails );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
209
src/decode/ltrstandard/LTRStandardActivitySummary.java
Normal file
209
src/decode/ltrstandard/LTRStandardActivitySummary.java
Normal file
@@ -0,0 +1,209 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrstandard;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Arrays;
|
||||
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 LTRStandardActivitySummary 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<Integer> mActiveLCNs = new TreeSet<Integer>();
|
||||
private int mMonitoredLCN;
|
||||
|
||||
/**
|
||||
* 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 LTRStandardActivitySummary( AliasList list )
|
||||
{
|
||||
mAliasList = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Message message )
|
||||
{
|
||||
if( message instanceof LTRStandardOSWMessage )
|
||||
{
|
||||
LTRStandardOSWMessage ltr = ((LTRStandardOSWMessage)message);
|
||||
|
||||
if( ltr.isValid() )
|
||||
{
|
||||
switch( ltr.getMessageType() )
|
||||
{
|
||||
case CA_STRT:
|
||||
case CA_ENDD:
|
||||
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 channel = ltr.getChannel();
|
||||
|
||||
if( 0 < channel && channel <= 20 )
|
||||
{
|
||||
if( channel == home )
|
||||
{
|
||||
mMonitoredLCN = channel;
|
||||
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() )
|
||||
{
|
||||
mMonitoredLCN = idleChannel;
|
||||
mActiveLCNs.add( idleChannel );
|
||||
}
|
||||
break;
|
||||
case UN_KNWN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSummary()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "Activity Summary\n\n" );
|
||||
|
||||
sb.append( "Decoder:\tLTR-Standard\n" );
|
||||
|
||||
sb.append( "Monitored LCN: " );
|
||||
|
||||
if( mMonitoredLCN > 0 )
|
||||
{
|
||||
sb.append( mMonitoredLCN );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( "unknown" );
|
||||
}
|
||||
|
||||
sb.append( "\n" );
|
||||
|
||||
Integer[] lcns = mActiveLCNs.toArray( new Integer[0] );
|
||||
|
||||
sb.append( "Active LCNs:\t" );
|
||||
|
||||
if( lcns.length > 0 )
|
||||
{
|
||||
sb.append( Arrays.toString( lcns ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( "none" );
|
||||
}
|
||||
sb.append( "\n\n" );
|
||||
|
||||
sb.append( "Talkgroups\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( formatTalkgroup( tgid ) );
|
||||
sb.append( " " );
|
||||
|
||||
Alias alias = mAliasList.getTalkgroupAlias( tgid );
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
117
src/decode/ltrstandard/LTRStandardDecoder.java
Normal file
117
src/decode/ltrstandard/LTRStandardDecoder.java
Normal file
@@ -0,0 +1,117 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrstandard;
|
||||
|
||||
import message.MessageDirection;
|
||||
import source.Source.SampleType;
|
||||
import alias.AliasList;
|
||||
import bits.MessageFramer;
|
||||
import bits.SyncPattern;
|
||||
import decode.Decoder;
|
||||
import decode.DecoderType;
|
||||
import dsp.filter.DCRemovalFilter2;
|
||||
import dsp.nbfm.FilteringNBFMDemodulator;
|
||||
|
||||
public class LTRStandardDecoder 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 sLTR_STANDARD_MESSAGE_LENGTH = 40;
|
||||
private FilteringNBFMDemodulator mDemodulator;
|
||||
private DCRemovalFilter2 mDCRemovalFilter;
|
||||
private LTRFSKDemodulator mLTRFSKDemodulator;
|
||||
private MessageFramer mLTRMessageFramer;
|
||||
private LTRStandardMessageProcessor mLTRMessageProcessor;
|
||||
|
||||
public LTRStandardDecoder( SampleType sampleType,
|
||||
AliasList aliasList,
|
||||
MessageDirection direction )
|
||||
{
|
||||
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
|
||||
*/
|
||||
mDemodulator = new FilteringNBFMDemodulator();
|
||||
this.addComplexListener( mDemodulator );
|
||||
|
||||
/**
|
||||
* Remove the DC component that is present when we're mistuned
|
||||
*/
|
||||
mDCRemovalFilter = new DCRemovalFilter2( sDC_REMOVAL_RATIO );
|
||||
mDemodulator.addListener( mDCRemovalFilter );
|
||||
|
||||
mDCRemovalFilter.setListener( this.getFloatReceiver() );
|
||||
}
|
||||
|
||||
mLTRFSKDemodulator = 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 )
|
||||
{
|
||||
mDemodulator.addListener( mLTRFSKDemodulator );
|
||||
}
|
||||
else
|
||||
{
|
||||
mDCRemovalFilter.setListener( mLTRFSKDemodulator );
|
||||
}
|
||||
|
||||
if( direction == MessageDirection.OSW )
|
||||
{
|
||||
mLTRMessageFramer =
|
||||
new MessageFramer( SyncPattern.LTR_STANDARD_OSW.getPattern(),
|
||||
sLTR_STANDARD_MESSAGE_LENGTH );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLTRMessageFramer =
|
||||
new MessageFramer( SyncPattern.LTR_STANDARD_ISW.getPattern(),
|
||||
sLTR_STANDARD_MESSAGE_LENGTH );
|
||||
}
|
||||
|
||||
mLTRFSKDemodulator.addListener( mLTRMessageFramer );
|
||||
|
||||
mLTRMessageProcessor = new LTRStandardMessageProcessor( direction, aliasList );
|
||||
mLTRMessageFramer.addMessageListener( mLTRMessageProcessor );
|
||||
mLTRMessageProcessor.addMessageListener( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecoderType getType()
|
||||
{
|
||||
return DecoderType.LTR_STANDARD;
|
||||
}
|
||||
}
|
||||
70
src/decode/ltrstandard/LTRStandardEditor.java
Normal file
70
src/decode/ltrstandard/LTRStandardEditor.java
Normal file
@@ -0,0 +1,70 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrstandard;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JComboBox;
|
||||
|
||||
import message.MessageDirection;
|
||||
import decode.DecodeEditor;
|
||||
import decode.config.DecodeConfigLTRStandard;
|
||||
import decode.config.DecodeConfiguration;
|
||||
|
||||
public class LTRStandardEditor extends DecodeEditor
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private JComboBox<MessageDirection> mComboDirection;
|
||||
|
||||
public LTRStandardEditor( DecodeConfiguration config )
|
||||
{
|
||||
super( config );
|
||||
|
||||
initGUI();
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
mComboDirection = new JComboBox<MessageDirection>();
|
||||
|
||||
mComboDirection.setModel(
|
||||
new DefaultComboBoxModel<MessageDirection>( MessageDirection.values() ) );
|
||||
|
||||
mComboDirection.setSelectedItem( ((DecodeConfigLTRStandard)mConfig).getMessageDirection() );
|
||||
|
||||
mComboDirection.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
MessageDirection selected = mComboDirection
|
||||
.getItemAt( mComboDirection.getSelectedIndex() );
|
||||
|
||||
if( selected != null )
|
||||
{
|
||||
((DecodeConfigLTRStandard)mConfig).setMessageDirection( selected );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
add( mComboDirection, "span 2" );
|
||||
}
|
||||
}
|
||||
212
src/decode/ltrstandard/LTRStandardISWMessage.java
Normal file
212
src/decode/ltrstandard/LTRStandardISWMessage.java
Normal file
@@ -0,0 +1,212 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrstandard;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import map.Plottable;
|
||||
import message.MessageDirection;
|
||||
import message.MessageType;
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class LTRStandardISWMessage extends LTRStandardMessage
|
||||
{
|
||||
private static final int sCALL_END = 31;
|
||||
private static final int sIDLE = 255;
|
||||
|
||||
public LTRStandardISWMessage( BitSetBuffer 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;
|
||||
}
|
||||
}
|
||||
256
src/decode/ltrstandard/LTRStandardMessage.java
Normal file
256
src/decode/ltrstandard/LTRStandardMessage.java
Normal file
@@ -0,0 +1,256 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrstandard;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
|
||||
import log.Log;
|
||||
import message.Message;
|
||||
import message.MessageDirection;
|
||||
import message.MessageType;
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
import crc.CRC;
|
||||
import crc.CRCLTR;
|
||||
import decode.DecoderType;
|
||||
|
||||
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 BitSetBuffer mMessage;
|
||||
protected MessageType mMessageType;
|
||||
protected CRC mCRC;
|
||||
protected AliasList mAliasList;
|
||||
|
||||
public LTRStandardMessage( BitSetBuffer 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<<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();
|
||||
}
|
||||
|
||||
public CRC getCRC()
|
||||
{
|
||||
return mCRC;
|
||||
}
|
||||
|
||||
public int getArea()
|
||||
{
|
||||
return getInt( sAREA );
|
||||
}
|
||||
|
||||
public int getChannel()
|
||||
{
|
||||
return getInt( sCHANNEL );
|
||||
}
|
||||
|
||||
public int getHomeRepeater()
|
||||
{
|
||||
return getInt( sHOME_REPEATER );
|
||||
}
|
||||
|
||||
public int getGroup()
|
||||
{
|
||||
return getInt( sGROUP );
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
Log.debug( "LTRStandardMessage - getting alias for:" + getTalkgroupID() );
|
||||
return mAliasList.getTalkgroupAlias( getTalkgroupID() );
|
||||
}
|
||||
|
||||
public int getFree()
|
||||
{
|
||||
return getInt( sFREE );
|
||||
}
|
||||
|
||||
public int getCRCChecksum()
|
||||
{
|
||||
return getInt( sCRC );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol()
|
||||
{
|
||||
return DecoderType.LTR_STANDARD.getDisplayString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType()
|
||||
{
|
||||
if( mMessageType != null )
|
||||
{
|
||||
return mMessageType.getDisplayText();
|
||||
}
|
||||
else
|
||||
{
|
||||
return MessageType.UN_KNWN.getDisplayText();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToID()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getToIDAlias()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorStatus()
|
||||
{
|
||||
return mCRC.getDisplayText();
|
||||
}
|
||||
}
|
||||
67
src/decode/ltrstandard/LTRStandardMessageProcessor.java
Normal file
67
src/decode/ltrstandard/LTRStandardMessageProcessor.java
Normal file
@@ -0,0 +1,67 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrstandard;
|
||||
|
||||
import message.Message;
|
||||
import message.MessageDirection;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class LTRStandardMessageProcessor implements Listener<BitSetBuffer>
|
||||
{
|
||||
private MessageDirection mDirection;
|
||||
private Broadcaster<Message> mBroadcaster = new Broadcaster<Message>();
|
||||
private AliasList mAliasList;
|
||||
|
||||
public LTRStandardMessageProcessor( MessageDirection direction, AliasList list )
|
||||
{
|
||||
mDirection = direction;
|
||||
mAliasList = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( BitSetBuffer buffer )
|
||||
{
|
||||
LTRStandardMessage message;
|
||||
|
||||
if( mDirection == MessageDirection.OSW )
|
||||
{
|
||||
message = new LTRStandardOSWMessage( buffer, mAliasList );
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.flip( 0, 40 );
|
||||
|
||||
message = new LTRStandardISWMessage( buffer, mAliasList );
|
||||
}
|
||||
|
||||
mBroadcaster.receive( message );
|
||||
}
|
||||
|
||||
public void addMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
public void removeMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.removeListener( listener );
|
||||
}
|
||||
}
|
||||
222
src/decode/ltrstandard/LTRStandardOSWMessage.java
Normal file
222
src/decode/ltrstandard/LTRStandardOSWMessage.java
Normal file
@@ -0,0 +1,222 @@
|
||||
/*******************************************************************************
|
||||
* 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.ltrstandard;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import map.Plottable;
|
||||
import message.MessageDirection;
|
||||
import message.MessageType;
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class LTRStandardOSWMessage extends LTRStandardMessage
|
||||
{
|
||||
private static final int sCALL_END = 31;
|
||||
private static final int sIDLE = 255;
|
||||
|
||||
public LTRStandardOSWMessage( BitSetBuffer 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;
|
||||
}
|
||||
}
|
||||
131
src/decode/mdc1200/MDCActivitySummary.java
Normal file
131
src/decode/mdc1200/MDCActivitySummary.java
Normal file
@@ -0,0 +1,131 @@
|
||||
/*******************************************************************************
|
||||
* 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.mdc1200;
|
||||
|
||||
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 MDCActivitySummary implements ActivitySummaryProvider,
|
||||
Listener<Message>
|
||||
{
|
||||
private AliasList mAliasList;
|
||||
private TreeSet<String> mIdents = new TreeSet<String>();
|
||||
private TreeSet<String> mEmergencyIdents = new TreeSet<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 MDCActivitySummary( AliasList list )
|
||||
{
|
||||
mAliasList = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Message message )
|
||||
{
|
||||
if( message instanceof MDCMessage )
|
||||
{
|
||||
MDCMessage mdc = ((MDCMessage)message);
|
||||
|
||||
mIdents.add( mdc.getUnitID() );
|
||||
|
||||
if( mdc.isEmergency() )
|
||||
{
|
||||
mEmergencyIdents.add( mdc.getUnitID() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSummary()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "=============================\n" );
|
||||
sb.append( "Decoder:\tMDC-1200\n\n" );
|
||||
sb.append( "MDC-1200 Idents\n" );
|
||||
|
||||
if( mIdents.isEmpty() )
|
||||
{
|
||||
sb.append( " None\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Iterator<String> it = mIdents.iterator();
|
||||
|
||||
while( it.hasNext() )
|
||||
{
|
||||
String ident = it.next();
|
||||
|
||||
sb.append( " " );
|
||||
sb.append( ident );
|
||||
sb.append( " " );
|
||||
|
||||
Alias alias = mAliasList.getMDC1200Alias( ident );
|
||||
|
||||
if( alias != null )
|
||||
{
|
||||
sb.append( alias.getName() );
|
||||
}
|
||||
|
||||
sb.append( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
sb.append( "MDC-1200 Emergency Idents\n" );
|
||||
|
||||
if( mEmergencyIdents.isEmpty() )
|
||||
{
|
||||
sb.append( " None\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Iterator<String> it = mEmergencyIdents.iterator();
|
||||
|
||||
while( it.hasNext() )
|
||||
{
|
||||
String ident = it.next();
|
||||
|
||||
sb.append( " " );
|
||||
sb.append( ident );
|
||||
sb.append( " " );
|
||||
|
||||
Alias alias = mAliasList.getMDC1200Alias( ident );
|
||||
|
||||
if( alias != null )
|
||||
{
|
||||
sb.append( alias.getName() );
|
||||
}
|
||||
|
||||
sb.append( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
159
src/decode/mdc1200/MDCCallEvent.java
Normal file
159
src/decode/mdc1200/MDCCallEvent.java
Normal file
@@ -0,0 +1,159 @@
|
||||
/*******************************************************************************
|
||||
* 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.mdc1200;
|
||||
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import controller.activity.CallEvent;
|
||||
import decode.DecoderType;
|
||||
|
||||
public class MDCCallEvent extends CallEvent
|
||||
{
|
||||
public MDCCallEvent( CallEventType callEventType,
|
||||
AliasList aliasList,
|
||||
String fromID,
|
||||
String toID,
|
||||
String details )
|
||||
{
|
||||
super( DecoderType.MDC1200, callEventType,
|
||||
aliasList, fromID, toID, details );
|
||||
}
|
||||
|
||||
private Alias getAlias( String ident )
|
||||
{
|
||||
if( hasAliasList() )
|
||||
{
|
||||
return getAliasList().getMDC1200Alias( ident );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getFromIDAlias()
|
||||
{
|
||||
return getAlias( getFromID() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getToIDAlias()
|
||||
{
|
||||
return getAlias( getToID() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChannel()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFrequency()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static class Builder
|
||||
{
|
||||
/* Required parameters */
|
||||
private CallEventType mCallEventType;
|
||||
|
||||
/* Optional parameters */
|
||||
private AliasList mAliasList;
|
||||
private String mFromID;
|
||||
private String mToID;
|
||||
private String mDetails;
|
||||
|
||||
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 details( String details )
|
||||
{
|
||||
mDetails = details;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder to( String toID )
|
||||
{
|
||||
mToID = toID;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MDCCallEvent build()
|
||||
{
|
||||
return new MDCCallEvent( this );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor for the builder
|
||||
*/
|
||||
private MDCCallEvent( Builder builder )
|
||||
{
|
||||
this( builder.mCallEventType,
|
||||
builder.mAliasList,
|
||||
builder.mFromID,
|
||||
builder.mToID,
|
||||
builder.mDetails );
|
||||
}
|
||||
|
||||
public static MDCCallEvent
|
||||
getMDCCallEvent( MDCMessage message )
|
||||
{
|
||||
CallEventType type = CallEventType.UNKNOWN;
|
||||
StringBuilder sbDetails = new StringBuilder();
|
||||
sbDetails.append( "MDC-1200 OPCODE:" );
|
||||
sbDetails.append( String.valueOf( message.getOpcode() ) );
|
||||
|
||||
switch( message.getMessageType() )
|
||||
{
|
||||
case ACKNOWLEDGE:
|
||||
type = CallEventType.ACKNOWLEDGE;
|
||||
break;
|
||||
case ANI:
|
||||
type = CallEventType.ID_ANI;
|
||||
break;
|
||||
case EMERGENCY:
|
||||
type = CallEventType.EMERGENCY;
|
||||
break;
|
||||
case PAGING:
|
||||
type = CallEventType.PAGE;
|
||||
break;
|
||||
}
|
||||
|
||||
return new MDCCallEvent.Builder( type )
|
||||
.details( sbDetails.toString() )
|
||||
.from( message.getFromID() )
|
||||
.to( message.getToID() )
|
||||
.build();
|
||||
}
|
||||
}
|
||||
183
src/decode/mdc1200/MDCChannelState.java
Normal file
183
src/decode/mdc1200/MDCChannelState.java
Normal file
@@ -0,0 +1,183 @@
|
||||
/*******************************************************************************
|
||||
* 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.mdc1200;
|
||||
|
||||
import message.Message;
|
||||
import alias.Alias;
|
||||
import controller.state.AuxChannelState;
|
||||
import controller.state.ChannelState;
|
||||
import controller.state.ChannelState.ChangedAttribute;
|
||||
import controller.state.ChannelState.State;
|
||||
|
||||
public class MDCChannelState extends AuxChannelState
|
||||
{
|
||||
private String mFrom;
|
||||
private Alias mFromAlias;
|
||||
private String mTo;
|
||||
private Alias mToAlias;
|
||||
private String mMessage;
|
||||
private String mMessageType;
|
||||
|
||||
private MDCActivitySummary mActivitySummary;
|
||||
|
||||
public MDCChannelState( ChannelState parentState )
|
||||
{
|
||||
super( parentState );
|
||||
|
||||
mActivitySummary = new MDCActivitySummary( parentState.getAliasList() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
setFrom( null );
|
||||
setFromAlias( null );
|
||||
setTo( null);
|
||||
setToAlias( null );
|
||||
setMessage( null );
|
||||
setMessageType( null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fade() {}
|
||||
|
||||
@Override
|
||||
public void receive( Message message )
|
||||
{
|
||||
if( message instanceof MDCMessage )
|
||||
{
|
||||
mActivitySummary.receive( message );
|
||||
|
||||
MDCMessage mdc = (MDCMessage)message;
|
||||
|
||||
setFrom( mdc.getFromID() );
|
||||
setFromAlias( mdc.getFromIDAlias() );
|
||||
|
||||
MDCMessageType type = mdc.getMessageType();
|
||||
|
||||
setMessageType( type.getLabel() );
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
switch( type )
|
||||
{
|
||||
case ACKNOWLEDGE:
|
||||
case ANI:
|
||||
case EMERGENCY:
|
||||
case PAGING:
|
||||
case STATUS:
|
||||
default:
|
||||
sb.append( "OPCODE " );
|
||||
sb.append( String.valueOf( mdc.getOpcode() ) );
|
||||
|
||||
if( mdc.isBOT() )
|
||||
{
|
||||
sb.append( " TYPE:BOT" );
|
||||
}
|
||||
|
||||
if( mdc.isEOT() )
|
||||
{
|
||||
sb.append( " TYPE:EOT" );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
setMessage( sb.toString() );
|
||||
|
||||
//Set the state to CALL on the parent channel state, so that the
|
||||
//timer can track turning off any updates we apply
|
||||
mParentChannelState.setState( State.CALL );
|
||||
|
||||
MDCCallEvent event = MDCCallEvent.getMDCCallEvent( mdc );
|
||||
event.setAliasList( mParentChannelState.getAliasList() );
|
||||
mParentChannelState.receiveCallEvent( event );
|
||||
}
|
||||
}
|
||||
|
||||
public String getFrom()
|
||||
{
|
||||
return mFrom;
|
||||
}
|
||||
|
||||
public void setFrom( String from )
|
||||
{
|
||||
mFrom = from;
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP );
|
||||
}
|
||||
|
||||
public Alias getFromAlias()
|
||||
{
|
||||
return mFromAlias;
|
||||
}
|
||||
|
||||
public void setFromAlias( Alias alias )
|
||||
{
|
||||
mFromAlias = alias;
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP_ALIAS );
|
||||
}
|
||||
|
||||
public String getTo()
|
||||
{
|
||||
return mTo;
|
||||
}
|
||||
|
||||
public void setTo( String to )
|
||||
{
|
||||
mTo = to;
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP );
|
||||
}
|
||||
|
||||
public Alias getToAlias()
|
||||
{
|
||||
return mToAlias;
|
||||
}
|
||||
|
||||
public void setToAlias( Alias alias )
|
||||
{
|
||||
mToAlias = alias;
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP_ALIAS );
|
||||
}
|
||||
|
||||
public String getMessage()
|
||||
{
|
||||
return mMessage;
|
||||
}
|
||||
|
||||
public void setMessage( String message )
|
||||
{
|
||||
mMessage = message;
|
||||
broadcastChange( ChangedAttribute.MESSAGE );
|
||||
}
|
||||
|
||||
public String getMessageType()
|
||||
{
|
||||
return mMessageType;
|
||||
}
|
||||
|
||||
public void setMessageType( String type )
|
||||
{
|
||||
mMessageType = type;
|
||||
broadcastChange( ChangedAttribute.MESSAGE_TYPE );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActivitySummary()
|
||||
{
|
||||
return mActivitySummary.getSummary();
|
||||
}
|
||||
}
|
||||
202
src/decode/mdc1200/MDCDecoder.java
Normal file
202
src/decode/mdc1200/MDCDecoder.java
Normal file
@@ -0,0 +1,202 @@
|
||||
/*******************************************************************************
|
||||
* 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.mdc1200;
|
||||
|
||||
import instrument.Instrumentable;
|
||||
import instrument.tap.Tap;
|
||||
import instrument.tap.stream.BinaryTap;
|
||||
import instrument.tap.stream.FloatTap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import source.Source.SampleType;
|
||||
import alias.AliasList;
|
||||
import bits.MessageFramer;
|
||||
import bits.SyncPattern;
|
||||
import decode.Decoder;
|
||||
import decode.DecoderType;
|
||||
import dsp.NRZDecoder;
|
||||
import dsp.filter.Filters;
|
||||
import dsp.filter.FloatFIRFilter;
|
||||
import dsp.filter.FloatHalfBandFilter;
|
||||
import dsp.fsk.FSK2Decoder;
|
||||
import dsp.fsk.FSK2Decoder.Output;
|
||||
|
||||
/**
|
||||
* MDC1200 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate
|
||||
* complex or floating point samples and output fully framed MDC1200 messages
|
||||
*/
|
||||
public class MDCDecoder extends Decoder implements Instrumentable
|
||||
{
|
||||
/* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */
|
||||
private static final int sDECIMATED_SAMPLE_RATE = 24000;
|
||||
|
||||
/* Baud or Symbol Rate */
|
||||
private static final int sSYMBOL_RATE = 1200;
|
||||
|
||||
/* Message length */
|
||||
private static final int sMESSAGE_LENGTH = 304;
|
||||
|
||||
/* Instrumentation Taps */
|
||||
private ArrayList<Tap> mAvailableTaps;
|
||||
private static final String INSTRUMENT_INPUT =
|
||||
"Tap Point: Float Input";
|
||||
private static final String INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD =
|
||||
"Tap Point: Bandpass Filter > < FSK2 Decoder";
|
||||
private static final String INSTRUMENT_FSK2_DECODER_TO_NRZ_DECODER =
|
||||
"Tap Point: FSK2 Decoder > < NRZI Decoder";
|
||||
private static final String INSTRUMENT_NRZI_DECODER_TO_MESSAGE_FRAMER =
|
||||
"Tap Point: NRZ Decoder > < Message Framer";
|
||||
|
||||
private FSK2Decoder mFSKDecoder;
|
||||
private FloatHalfBandFilter mDecimationFilter;
|
||||
private FloatFIRFilter mBandPassFilter;
|
||||
private NRZDecoder mNRZDecoder;
|
||||
private MessageFramer mMessageFramer;
|
||||
private MDCMessageProcessor mMessageProcessor;
|
||||
|
||||
public MDCDecoder( AliasList aliasList )
|
||||
{
|
||||
super( SampleType.FLOAT );
|
||||
|
||||
/* Decimation filter: 48000 / 2 = 24000 */
|
||||
mDecimationFilter = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
addFloatListener( mDecimationFilter );
|
||||
|
||||
/* Bandpass filter - predefined */
|
||||
mBandPassFilter = new FloatFIRFilter(
|
||||
Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02 );
|
||||
mDecimationFilter.setListener( mBandPassFilter );
|
||||
|
||||
/* 2FSK Decoder */
|
||||
mFSKDecoder = new FSK2Decoder( sDECIMATED_SAMPLE_RATE,
|
||||
sSYMBOL_RATE, Output.NORMAL );
|
||||
mBandPassFilter.setListener( mFSKDecoder );
|
||||
|
||||
/* NRZ Decoder */
|
||||
mNRZDecoder = new NRZDecoder( NRZDecoder.MODE_INVERTED );
|
||||
mFSKDecoder.setListener( mNRZDecoder );
|
||||
|
||||
/* Message Framer */
|
||||
mMessageFramer = new MessageFramer( SyncPattern.MDC1200.getPattern(),
|
||||
sMESSAGE_LENGTH );
|
||||
mNRZDecoder.setListener( mMessageFramer );
|
||||
|
||||
/* Message Processor */
|
||||
mMessageProcessor = new MDCMessageProcessor( aliasList );
|
||||
mMessageProcessor.addMessageListener( this );
|
||||
mMessageFramer.addMessageListener( mMessageProcessor );
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
super.dispose();
|
||||
|
||||
mDecimationFilter.dispose();
|
||||
mBandPassFilter.dispose();
|
||||
mFSKDecoder.dispose();
|
||||
mNRZDecoder.dispose();
|
||||
mMessageFramer.dispose();
|
||||
mMessageProcessor.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecoderType getType()
|
||||
{
|
||||
return DecoderType.MDC1200;
|
||||
}
|
||||
|
||||
/* Instrumentation */
|
||||
@Override
|
||||
public List<Tap> getTaps()
|
||||
{
|
||||
if( mAvailableTaps == null )
|
||||
{
|
||||
mAvailableTaps = new ArrayList<Tap>();
|
||||
|
||||
mAvailableTaps.add(
|
||||
new FloatTap( INSTRUMENT_INPUT, 0, 1.0f ) );
|
||||
mAvailableTaps.add(
|
||||
new FloatTap( INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD, 0, 0.5f ) );
|
||||
mAvailableTaps.addAll( mFSKDecoder.getTaps() );
|
||||
mAvailableTaps.add(
|
||||
new BinaryTap( INSTRUMENT_FSK2_DECODER_TO_NRZ_DECODER, 0, 0.025f ) );
|
||||
mAvailableTaps.add(
|
||||
new BinaryTap( INSTRUMENT_NRZI_DECODER_TO_MESSAGE_FRAMER, 0, 0.025f ) );
|
||||
}
|
||||
|
||||
return mAvailableTaps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTap( Tap tap )
|
||||
{
|
||||
mFSKDecoder.addTap( tap );
|
||||
|
||||
switch( tap.getName() )
|
||||
{
|
||||
case INSTRUMENT_INPUT:
|
||||
addFloatListener( (FloatTap)tap );
|
||||
break;
|
||||
case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD:
|
||||
/* wire the tap between the bandpass filter and the demod */
|
||||
FloatTap floatTap = (FloatTap)tap;
|
||||
mBandPassFilter.setListener( floatTap );
|
||||
floatTap.setListener( mFSKDecoder );
|
||||
break;
|
||||
case INSTRUMENT_FSK2_DECODER_TO_NRZ_DECODER:
|
||||
/* wire between FSK2 decoder and NRZ decoder */
|
||||
BinaryTap binTap = (BinaryTap)tap;
|
||||
mFSKDecoder.setListener( binTap );
|
||||
binTap.setListener( mNRZDecoder );
|
||||
break;
|
||||
case INSTRUMENT_NRZI_DECODER_TO_MESSAGE_FRAMER:
|
||||
/* wire between demod and framer */
|
||||
BinaryTap binInvTap = (BinaryTap)tap;
|
||||
mNRZDecoder.setListener( binInvTap );
|
||||
binInvTap.setListener( mMessageFramer );
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTap( Tap tap )
|
||||
{
|
||||
mFSKDecoder.removeTap( tap );
|
||||
|
||||
switch( tap.getName() )
|
||||
{
|
||||
case INSTRUMENT_INPUT:
|
||||
removeFloatListener( (FloatTap)tap );
|
||||
break;
|
||||
case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD:
|
||||
mBandPassFilter.setListener( mFSKDecoder );
|
||||
break;
|
||||
case INSTRUMENT_FSK2_DECODER_TO_NRZ_DECODER:
|
||||
mFSKDecoder.setListener( mNRZDecoder );
|
||||
break;
|
||||
case INSTRUMENT_NRZI_DECODER_TO_MESSAGE_FRAMER:
|
||||
mNRZDecoder.setListener( mMessageFramer );
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
319
src/decode/mdc1200/MDCMessage.java
Normal file
319
src/decode/mdc1200/MDCMessage.java
Normal file
@@ -0,0 +1,319 @@
|
||||
/*******************************************************************************
|
||||
* 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.mdc1200;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import map.Plottable;
|
||||
import message.Message;
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class MDCMessage extends Message
|
||||
{
|
||||
private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" );
|
||||
|
||||
private static int[] sSYNC1 = { 0,1,2,3,4,5,6,7,8,9,
|
||||
10,11,12,13,14,15,16,17,18,19,
|
||||
20,21,22,23,24,25,26,27,28,29,
|
||||
30,31,32,33,34,35,36,37,38,39 };
|
||||
|
||||
private static int[] sOPCODE = { 47,46,45,44,43,42,41,40 };
|
||||
private static int sANI_FLAG = 40;
|
||||
private static int sDIRECTION_FLAG = 45;
|
||||
private static int sACKNOWLEDGE_REQUIRED_FLAG = 46;
|
||||
private static int sPACKET_TYPE_FLAG = 47;
|
||||
private static int sEMERGENCY_FLAG = 48;
|
||||
private static int[] sARGUMENT = { 49,50,51,52,53,54 };
|
||||
private static int sBOT_EOT_FLAG = 55;
|
||||
private static int[] sDIGIT_2 = { 59,58,57,56 };
|
||||
private static int[] sDIGIT_1 = { 63,62,61,60 };
|
||||
private static int[] sDIGIT_4 = { 67,66,65,64 };
|
||||
private static int[] sDIGIT_3 = { 71,70,69,68 };
|
||||
|
||||
private BitSetBuffer mMessage;
|
||||
private AliasList mAliasList;
|
||||
|
||||
public MDCMessage( BitSetBuffer message, AliasList list )
|
||||
{
|
||||
mMessage = message;
|
||||
mAliasList = list;
|
||||
}
|
||||
|
||||
public boolean isValid()
|
||||
{
|
||||
//TODO: add CRC and/or convolution decoding/repair
|
||||
return true;
|
||||
}
|
||||
|
||||
public PacketType getPacketType()
|
||||
{
|
||||
if( mMessage.get( sPACKET_TYPE_FLAG ) )
|
||||
{
|
||||
return PacketType.DATA;
|
||||
}
|
||||
else
|
||||
{
|
||||
return PacketType.CMND;
|
||||
}
|
||||
}
|
||||
|
||||
public Acknowledge getResponse()
|
||||
{
|
||||
if( mMessage.get( sACKNOWLEDGE_REQUIRED_FLAG ) )
|
||||
{
|
||||
return Acknowledge.YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Acknowledge.NO;
|
||||
}
|
||||
}
|
||||
|
||||
public Direction getDirection()
|
||||
{
|
||||
if( mMessage.get( sDIRECTION_FLAG ) )
|
||||
{
|
||||
return Direction.OUT;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Direction.IN;
|
||||
}
|
||||
}
|
||||
|
||||
public int getOpcode()
|
||||
{
|
||||
return mMessage.getInt( sOPCODE );
|
||||
}
|
||||
|
||||
public int getArgument()
|
||||
{
|
||||
return mMessage.getInt( sARGUMENT );
|
||||
}
|
||||
|
||||
public boolean isEmergency()
|
||||
{
|
||||
return mMessage.get( sEMERGENCY_FLAG );
|
||||
}
|
||||
|
||||
|
||||
public boolean isANI()
|
||||
{
|
||||
return mMessage.get( sANI_FLAG );
|
||||
}
|
||||
|
||||
public boolean isBOT()
|
||||
{
|
||||
return mMessage.get( sBOT_EOT_FLAG );
|
||||
}
|
||||
|
||||
public boolean isEOT()
|
||||
{
|
||||
return !mMessage.get( sBOT_EOT_FLAG );
|
||||
}
|
||||
|
||||
public String getUnitID()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( Integer.toHexString( mMessage.getInt( sDIGIT_1 ) ).toUpperCase() );
|
||||
sb.append( Integer.toHexString( mMessage.getInt( sDIGIT_2 ) ).toUpperCase() );
|
||||
sb.append( Integer.toHexString( mMessage.getInt( sDIGIT_3 ) ).toUpperCase() );
|
||||
sb.append( Integer.toHexString( mMessage.getInt( sDIGIT_4 ) ).toUpperCase() );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public MDCMessageType getMessageType()
|
||||
{
|
||||
switch( getOpcode() )
|
||||
{
|
||||
case 0:
|
||||
if( isEmergency() )
|
||||
{
|
||||
return MDCMessageType.EMERGENCY;
|
||||
}
|
||||
case 1:
|
||||
return MDCMessageType.ANI;
|
||||
default:
|
||||
return MDCMessageType.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( mSDF.format( new Date( System.currentTimeMillis() ) ) );
|
||||
|
||||
sb.append( getMessage() );
|
||||
|
||||
sb.append( " [" + mMessage.toString() + "]" );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
@SuppressWarnings( "unused" )
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBinaryMessage()
|
||||
{
|
||||
return mMessage.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol()
|
||||
{
|
||||
return "MDC-1200";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFromID()
|
||||
{
|
||||
return getUnitID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getFromIDAlias()
|
||||
{
|
||||
return mAliasList.getMDC1200Alias( getFromID() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToID()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getToIDAlias()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "MDC1200 UNIT:" + getUnitID() );
|
||||
if( isEmergency() )
|
||||
{
|
||||
sb.append( " **EMERGENCY**" );
|
||||
}
|
||||
|
||||
if( isBOT() )
|
||||
{
|
||||
sb.append( " BOT" );
|
||||
}
|
||||
|
||||
if( isEOT() )
|
||||
{
|
||||
sb.append( " EOT" );
|
||||
}
|
||||
|
||||
sb.append( " OPCODE:" + format( getOpcode(), 2 ) );
|
||||
sb.append( " ARG:" + format( getArgument(), 3 ) );
|
||||
sb.append( " TYPE:" + getPacketType().toString() );
|
||||
sb.append( " ACK:" + getResponse().toString() );
|
||||
sb.append( " DIR:" + pad( getDirection().toString(), 3, " " ) );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorStatus()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plottable getPlottable()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
private enum PacketType { CMND, DATA };
|
||||
private enum Acknowledge { YES, NO };
|
||||
private enum Direction { IN, OUT };
|
||||
}
|
||||
155
src/decode/mdc1200/MDCMessageProcessor.java
Normal file
155
src/decode/mdc1200/MDCMessageProcessor.java
Normal file
@@ -0,0 +1,155 @@
|
||||
/*******************************************************************************
|
||||
* 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.mdc1200;
|
||||
|
||||
import message.Message;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class MDCMessageProcessor implements Listener<BitSetBuffer>
|
||||
{
|
||||
private static int sMESSAGE_LENGTH = 112;
|
||||
|
||||
private Broadcaster<Message> mBroadcaster = new Broadcaster<Message>();
|
||||
private AliasList mAliasList;
|
||||
|
||||
public MDCMessageProcessor( AliasList aliasList )
|
||||
{
|
||||
mAliasList = aliasList;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mBroadcaster.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( BitSetBuffer buffer )
|
||||
{
|
||||
/**
|
||||
* De-interleave the 112 bits of message one, starting after the sync
|
||||
* pattern
|
||||
*/
|
||||
deinterleave( buffer, 40 );
|
||||
|
||||
/**
|
||||
* For simplicity, we also deinterleave message 2, which may or may not
|
||||
* be there, starting after the initial sync ( 0 - 39 ) and the initial
|
||||
* message ( 40 - 151 ) and the second sync ( 152 - 191 ).
|
||||
*/
|
||||
deinterleave( buffer, 192 );
|
||||
|
||||
/**
|
||||
* Wrap the buffer in a message along with the designated alias list
|
||||
* and send it on its merry way
|
||||
*/
|
||||
MDCMessage message = new MDCMessage( buffer, mAliasList );
|
||||
|
||||
mBroadcaster.receive( message );
|
||||
}
|
||||
|
||||
public void addMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
public void removeMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.removeListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes NRZ-I encoding from the entire buffer. Assumes that the initial
|
||||
* bit will be a 0 after decode, despite what is currently in the buffer,
|
||||
* in order to maintain correct polarity of the decoded message.
|
||||
*/
|
||||
private void removeNRZIEncoding( BitSetBuffer buffer )
|
||||
{
|
||||
/**
|
||||
* Clear bit position 0, the first bit of the sync pattern, to start
|
||||
* the decode with the correct polarity
|
||||
*/
|
||||
buffer.clear( 0 );
|
||||
|
||||
/**
|
||||
* Set each bit according to the state of the previous bit using XOR
|
||||
* as follows:
|
||||
*
|
||||
* 0 = same as previous state
|
||||
* 1 = inverse of previous state
|
||||
*/
|
||||
for( int x = 1; x < buffer.size(); x++ )
|
||||
{
|
||||
if( buffer.get( x - 1 ) ^ buffer.get( x ) )
|
||||
{
|
||||
buffer.set( x );
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.clear( x );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deinterleaves a 112-bit packet, starting at the offset into the buffer
|
||||
*/
|
||||
private void deinterleave( BitSetBuffer buffer, int offset )
|
||||
{
|
||||
if( buffer.size() < sMESSAGE_LENGTH + offset )
|
||||
{
|
||||
throw new IllegalArgumentException( "MDCMessageProcessor - "
|
||||
+ "cannot deinterleave message - message buffer too short" );
|
||||
}
|
||||
|
||||
BitSetBuffer deinterleaved = new BitSetBuffer( 112 );
|
||||
|
||||
int deinterleavedPointer = 0;
|
||||
|
||||
for( int column = 0; column < 16; column++ )
|
||||
{
|
||||
for( int row = 0; row < 7; row++ )
|
||||
{
|
||||
if( buffer.get( ( row * 16 ) + column + offset ) )
|
||||
{
|
||||
deinterleaved.set( deinterleavedPointer );
|
||||
}
|
||||
|
||||
deinterleavedPointer++;
|
||||
}
|
||||
}
|
||||
|
||||
//Overlay the deinterleaved bits back onto the source message and
|
||||
//return it
|
||||
buffer.clear( offset, offset + sMESSAGE_LENGTH );
|
||||
|
||||
for( int x = 0; x < sMESSAGE_LENGTH; x++ )
|
||||
{
|
||||
if( deinterleaved.get( x ) )
|
||||
{
|
||||
buffer.set( x + offset );
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.clear( x + offset );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/decode/mdc1200/MDCMessageType.java
Normal file
40
src/decode/mdc1200/MDCMessageType.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/*******************************************************************************
|
||||
* 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.mdc1200;
|
||||
|
||||
public enum MDCMessageType
|
||||
{
|
||||
ACKNOWLEDGE( "Acknowledge" ),
|
||||
ANI( "ANI" ),
|
||||
EMERGENCY( "Emergency" ),
|
||||
PAGING( "Paging" ),
|
||||
STATUS( "Status" ),
|
||||
UNKNOWN( "Unk" );
|
||||
|
||||
private String mLabel;
|
||||
|
||||
private MDCMessageType( String label )
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
}
|
||||
161
src/decode/mdc1200/MDCPanel.java
Normal file
161
src/decode/mdc1200/MDCPanel.java
Normal file
@@ -0,0 +1,161 @@
|
||||
/*******************************************************************************
|
||||
* 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.mdc1200;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import settings.ColorSetting;
|
||||
import settings.ColorSetting.ColorSettingName;
|
||||
import settings.Setting;
|
||||
import settings.SettingsManager;
|
||||
import controller.state.AuxStatePanel;
|
||||
import controller.state.ChannelState.ChangedAttribute;
|
||||
|
||||
public class MDCPanel extends AuxStatePanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String sPROTOCOL = "MDC-1200";
|
||||
|
||||
private JLabel mFromLabel = new JLabel( " " );
|
||||
private JLabel mFrom = new JLabel();
|
||||
private JLabel mFromAlias = new JLabel();
|
||||
|
||||
private JLabel mToLabel = new JLabel( " " );
|
||||
private JLabel mTo = new JLabel();
|
||||
private JLabel mToAlias = new JLabel();
|
||||
|
||||
private JLabel mProtocol = new JLabel( sPROTOCOL );
|
||||
private JLabel mMessage = new JLabel();
|
||||
private JLabel mMessageType = new JLabel();
|
||||
|
||||
public MDCPanel( SettingsManager settingsManager, MDCChannelState state )
|
||||
{
|
||||
super( settingsManager, state );
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
public MDCChannelState getState()
|
||||
{
|
||||
return (MDCChannelState)mAuxChannelState;
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setOpaque( false ); //Use the parent panel's background color
|
||||
|
||||
setLayout( new MigLayout( "insets 1 0 0 0", "[grow,fill]", "[]0[]0[]") );
|
||||
|
||||
addLabel( mFromLabel, false );
|
||||
addLabel( mFrom, false );
|
||||
addLabel( mFromAlias, true );
|
||||
|
||||
addLabel( mToLabel, false );
|
||||
addLabel( mTo, false );
|
||||
addLabel( mToAlias, true );
|
||||
|
||||
addLabel( mProtocol, false );
|
||||
addLabel( mMessageType, false );
|
||||
addLabel( mMessage, true );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( final ChangedAttribute changedAttribute )
|
||||
{
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
switch( changedAttribute )
|
||||
{
|
||||
case FROM_TALKGROUP:
|
||||
String from = getState().getFrom();
|
||||
|
||||
if( from != null )
|
||||
{
|
||||
mFromLabel.setText( "FM:" );
|
||||
}
|
||||
else
|
||||
{
|
||||
mFromLabel.setText( " " );
|
||||
}
|
||||
mFrom.setText( from );
|
||||
break;
|
||||
case FROM_TALKGROUP_ALIAS:
|
||||
updateAliasLabel( mFromAlias,
|
||||
getState().getFromAlias() );
|
||||
break;
|
||||
case TO_TALKGROUP:
|
||||
String to = getState().getTo();
|
||||
|
||||
if( to != null )
|
||||
{
|
||||
mToLabel.setText( "TO:" );
|
||||
}
|
||||
else
|
||||
{
|
||||
mToLabel.setText( " " );
|
||||
}
|
||||
mTo.setText( to );
|
||||
break;
|
||||
case TO_TALKGROUP_ALIAS:
|
||||
updateAliasLabel( mToAlias,
|
||||
getState().getToAlias() );
|
||||
break;
|
||||
case MESSAGE:
|
||||
mMessage.setText( getState().getMessage() );
|
||||
break;
|
||||
case MESSAGE_TYPE:
|
||||
mMessageType.setText( getState().getMessageType() );
|
||||
break;
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settingChanged( Setting setting )
|
||||
{
|
||||
super.settingChanged( setting );
|
||||
|
||||
if( setting instanceof ColorSetting )
|
||||
{
|
||||
ColorSetting color = (ColorSetting)setting;
|
||||
|
||||
if( color.getColorSettingName() ==
|
||||
ColorSettingName.CHANNEL_STATE_LABEL_AUX_DECODER )
|
||||
{
|
||||
mFromLabel.setForeground( color.getColor() );
|
||||
mFrom.setForeground( color.getColor() );
|
||||
mFromAlias.setForeground( color.getColor() );
|
||||
mToLabel.setForeground( color.getColor() );
|
||||
mTo.setForeground( color.getColor() );
|
||||
mToAlias.setForeground( color.getColor() );
|
||||
mProtocol.setForeground( color.getColor() );
|
||||
mMessageType.setForeground( color.getColor() );
|
||||
mMessage.setForeground( color.getColor() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
171
src/decode/mpt1327/MPT1327ActivitySummary.java
Normal file
171
src/decode/mpt1327/MPT1327ActivitySummary.java
Normal file
@@ -0,0 +1,171 @@
|
||||
/*******************************************************************************
|
||||
* 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.mpt1327;
|
||||
|
||||
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 MPT1327ActivitySummary implements ActivitySummaryProvider,
|
||||
Listener<Message>
|
||||
{
|
||||
private static final int sINT_NULL_VALUE = -1;
|
||||
|
||||
private AliasList mAliasList;
|
||||
private TreeSet<String> mIdents = new TreeSet<String>();
|
||||
private int mSite;
|
||||
|
||||
/**
|
||||
* 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 MPT1327ActivitySummary( AliasList list )
|
||||
{
|
||||
mAliasList = list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup method
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
mIdents.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Message message )
|
||||
{
|
||||
if( message instanceof MPT1327Message )
|
||||
{
|
||||
MPT1327Message mpt = ((MPT1327Message)message);
|
||||
|
||||
if( mpt.isValid() )
|
||||
{
|
||||
switch( mpt.getMessageType() )
|
||||
{
|
||||
case ACK:
|
||||
mIdents.add( mpt.getFromID() );
|
||||
break;
|
||||
case ACKI:
|
||||
mIdents.add( mpt.getFromID() );
|
||||
mIdents.add( mpt.getToID() );
|
||||
break;
|
||||
case ALH:
|
||||
mSite = mpt.getSiteID();
|
||||
break;
|
||||
case GTC:
|
||||
if( mpt.hasFromID() )
|
||||
{
|
||||
mIdents.add( mpt.getFromID() );
|
||||
}
|
||||
if( mpt.hasToID() )
|
||||
{
|
||||
mIdents.add( mpt.getToID() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSummary()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "Activity Summary\n" );
|
||||
sb.append( "Decoder:\tMPT-1327\n" );
|
||||
sb.append( "Site:\t" );
|
||||
|
||||
if( mSite != 0 )
|
||||
{
|
||||
sb.append( mSite );
|
||||
|
||||
if( mAliasList != null )
|
||||
{
|
||||
Alias siteAlias = mAliasList.getSiteID( mSite );
|
||||
|
||||
if( siteAlias != null )
|
||||
{
|
||||
sb.append( " " );
|
||||
sb.append( siteAlias.getName() );
|
||||
sb.append( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
sb.append( "Network:\t" );
|
||||
|
||||
if( ( mSite & 0x4000 ) == 0x4000 )
|
||||
{
|
||||
sb.append( "National #" );
|
||||
int net = (int)Long.rotateRight( ( mSite & 0x3000 ) , 12 );
|
||||
sb.append( net );
|
||||
sb.append( "\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( "Regional\n" );
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( "Unknown\n" );
|
||||
}
|
||||
|
||||
sb.append( "\n" );
|
||||
|
||||
sb.append( "Talkgroups\n" );
|
||||
if( mIdents.isEmpty() )
|
||||
{
|
||||
sb.append( " None\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
Iterator<String> it = mIdents.iterator();
|
||||
|
||||
while( it.hasNext() )
|
||||
{
|
||||
String ident = it.next();
|
||||
|
||||
sb.append( " " );
|
||||
sb.append( ident );
|
||||
sb.append( " " );
|
||||
|
||||
Alias alias = mAliasList.getMPT1327Alias( ident );
|
||||
|
||||
if( alias != null )
|
||||
{
|
||||
sb.append( alias.getName() );
|
||||
}
|
||||
|
||||
sb.append( "\n" );
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
154
src/decode/mpt1327/MPT1327CallEvent.java
Normal file
154
src/decode/mpt1327/MPT1327CallEvent.java
Normal file
@@ -0,0 +1,154 @@
|
||||
/*******************************************************************************
|
||||
* 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.mpt1327;
|
||||
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import controller.activity.CallEvent;
|
||||
import decode.DecoderType;
|
||||
|
||||
public class MPT1327CallEvent extends CallEvent
|
||||
{
|
||||
private int mChannel;
|
||||
private int mFrequency;
|
||||
|
||||
public MPT1327CallEvent( CallEventType callEventType,
|
||||
AliasList aliasList,
|
||||
String fromID,
|
||||
String toID,
|
||||
int channel,
|
||||
int frequency,
|
||||
String details )
|
||||
{
|
||||
super( DecoderType.MPT1327, callEventType,
|
||||
aliasList, fromID, toID, details );
|
||||
|
||||
mChannel = channel;
|
||||
mFrequency = frequency;
|
||||
}
|
||||
|
||||
private Alias getAlias( String mpt1327Talkgroup )
|
||||
{
|
||||
if( hasAliasList() )
|
||||
{
|
||||
return getAliasList().getMPT1327Alias( mpt1327Talkgroup );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getFromIDAlias()
|
||||
{
|
||||
return getAlias( 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 MPT1327CallEvent build()
|
||||
{
|
||||
return new MPT1327CallEvent( this );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor for the builder
|
||||
*/
|
||||
private MPT1327CallEvent( Builder builder )
|
||||
{
|
||||
this( builder.mCallEventType,
|
||||
builder.mAliasList,
|
||||
builder.mFromID,
|
||||
builder.mToID,
|
||||
builder.mChannel,
|
||||
builder.mFrequency,
|
||||
builder.mDetails );
|
||||
}
|
||||
|
||||
}
|
||||
346
src/decode/mpt1327/MPT1327ChannelState.java
Normal file
346
src/decode/mpt1327/MPT1327ChannelState.java
Normal file
@@ -0,0 +1,346 @@
|
||||
/*******************************************************************************
|
||||
* 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.mpt1327;
|
||||
|
||||
import message.Message;
|
||||
import sample.Listener;
|
||||
import source.config.SourceConfigTuner;
|
||||
import alias.AliasList;
|
||||
import audio.SquelchListener;
|
||||
import audio.SquelchListener.SquelchState;
|
||||
import controller.activity.CallEvent.CallEventType;
|
||||
import controller.channel.Channel;
|
||||
import controller.channel.Channel.ChannelType;
|
||||
import controller.channel.ChannelMap;
|
||||
import controller.channel.ProcessingChain;
|
||||
import controller.state.AuxChannelState;
|
||||
import controller.state.ChannelState;
|
||||
import decode.config.DecodeConfigMPT1327;
|
||||
|
||||
public class MPT1327ChannelState extends ChannelState
|
||||
{
|
||||
private int mSite;
|
||||
private String mFromTalkgroup;
|
||||
private String mToTalkgroup;
|
||||
private int mChannelNumber;
|
||||
private ChannelType mChannelType;
|
||||
private ChannelMap mChannelMap;
|
||||
private MPT1327ActivitySummary mActivitySummary;
|
||||
|
||||
public MPT1327ChannelState( ProcessingChain processingChain,
|
||||
AliasList aliasList,
|
||||
ChannelMap channelMap )
|
||||
{
|
||||
super( processingChain, aliasList );
|
||||
|
||||
mActivitySummary = new MPT1327ActivitySummary( aliasList );
|
||||
|
||||
mChannelMap = channelMap;
|
||||
|
||||
mChannelType = processingChain.getChannel().getChannelType();
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
super.dispose();
|
||||
|
||||
mActivitySummary.dispose();
|
||||
mActivitySummary = null;
|
||||
}
|
||||
|
||||
@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
|
||||
public void receive( Message message )
|
||||
{
|
||||
super.receive( message );
|
||||
|
||||
if( message instanceof MPT1327Message )
|
||||
{
|
||||
mActivitySummary.receive( message );
|
||||
|
||||
MPT1327Message mpt = (MPT1327Message)message;
|
||||
|
||||
if( mpt.isValid() )
|
||||
{
|
||||
switch( mpt.getMessageType() )
|
||||
{
|
||||
case AHYC:
|
||||
mCallEventModel.add(
|
||||
new MPT1327CallEvent.Builder( CallEventType.SDM )
|
||||
.aliasList( getProcessingChain().getAliasList() )
|
||||
.channel( mChannelNumber )
|
||||
.details( "Short Data Message" )
|
||||
.frequency( mChannelMap.getFrequency( mChannelNumber ) )
|
||||
.from( mpt.getFromID() )
|
||||
.to( mpt.getToID() )
|
||||
.build() );
|
||||
break;
|
||||
case AHYQ:
|
||||
mCallEventModel.add(
|
||||
new MPT1327CallEvent.Builder( CallEventType.STATUS )
|
||||
.aliasList( getProcessingChain().getAliasList() )
|
||||
.channel( mChannelNumber )
|
||||
.details( mpt.getStatusMessage() )
|
||||
.frequency( mChannelMap.getFrequency( mChannelNumber ) )
|
||||
.from( mpt.getFromID() )
|
||||
.to( mpt.getToID() )
|
||||
.build() );
|
||||
break;
|
||||
case ALH:
|
||||
int sys = mpt.getSiteID();
|
||||
|
||||
if( sys != 0 && sys != mSite )
|
||||
{
|
||||
mSite = sys;
|
||||
broadcastChange( ChangedAttribute.CHANNEL_SITE_NUMBER );
|
||||
}
|
||||
setState( State.CONTROL );
|
||||
break;
|
||||
case GTC:
|
||||
int channelNumber = mpt.getChannel();
|
||||
|
||||
if( !getProcessingChain().getChannel()
|
||||
.hasTrafficChannel( channelNumber ) )
|
||||
{
|
||||
String aliasListName = mProcessingChain
|
||||
.getChannel().getAliasListName();
|
||||
|
||||
Channel traffic = getTrafficChannel( channelNumber,
|
||||
aliasListName );
|
||||
|
||||
/* Set the system and site to same as control channel */
|
||||
traffic.setSystem( getProcessingChain().getChannel()
|
||||
.getSystem(), false );
|
||||
traffic.setSite( getProcessingChain().getChannel()
|
||||
.getSite(), false );
|
||||
|
||||
/* Add the traffic channel to the parent control channel */
|
||||
getProcessingChain().getChannel()
|
||||
.addTrafficChannel( channelNumber, traffic );
|
||||
|
||||
/* Start the traffic channel */
|
||||
traffic.setEnabled( true );
|
||||
|
||||
/* Set traffic channel state info */
|
||||
MPT1327ChannelState trafficState =
|
||||
(MPT1327ChannelState)traffic
|
||||
.getProcessingChain().getChannelState();
|
||||
|
||||
MPT1327ChannelState controlState =
|
||||
(MPT1327ChannelState)getProcessingChain()
|
||||
.getChannelState();
|
||||
|
||||
trafficState.setChannelMap( controlState.getChannelMap() );
|
||||
trafficState.setChannelNumber( channelNumber );
|
||||
trafficState.setFromTalkgroup( mpt.getFromID() );
|
||||
trafficState.setToTalkgroup( mpt.getToID() );
|
||||
|
||||
/* Add this control channel as message listener on the
|
||||
* traffic channel so we can receive call tear-down
|
||||
* messages */
|
||||
traffic.addListener( (Listener<Message>)this );
|
||||
|
||||
CallEventType type = traffic.isProcessing() ?
|
||||
CallEventType.CALL_START :
|
||||
CallEventType.CALL_START_NO_TUNER;
|
||||
|
||||
/*
|
||||
* Set the traffic channel's call event model to
|
||||
* share the control channel's call event model
|
||||
*/
|
||||
traffic.getProcessingChain().getChannelState()
|
||||
.setCallEventModel( mCallEventModel );
|
||||
|
||||
mCallEventModel.add(
|
||||
new MPT1327CallEvent.Builder( type )
|
||||
.aliasList( traffic.getProcessingChain().getAliasList() )
|
||||
.channel( channelNumber )
|
||||
.frequency( mChannelMap.getFrequency( channelNumber ) )
|
||||
.from( mpt.getFromID() )
|
||||
.to( mpt.getToID() )
|
||||
.build() );
|
||||
}
|
||||
|
||||
int frequency = mChannelMap.getFrequency( channelNumber );
|
||||
|
||||
break;
|
||||
case CLEAR:
|
||||
case MAINT:
|
||||
if( mChannelType == ChannelType.TRAFFIC )
|
||||
{
|
||||
fade( CallEventType.CALL_END );
|
||||
}
|
||||
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 MPT1327CallEvent.Builder( type )
|
||||
.aliasList( getAliasList() )
|
||||
.channel( mChannelNumber )
|
||||
.frequency( mChannelMap.getFrequency( mChannelNumber ) )
|
||||
.from( mFromTalkgroup )
|
||||
.to( mToTalkgroup )
|
||||
.build() );
|
||||
}
|
||||
|
||||
super.fade( type );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the ConventionalChannelState always unsquelched
|
||||
*/
|
||||
public void addListener( SquelchListener listener )
|
||||
{
|
||||
super.addListener( listener );
|
||||
|
||||
super.setSquelchState( SquelchState.UNSQUELCH );
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
mFromTalkgroup = null;
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP );
|
||||
mToTalkgroup = null;
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP );
|
||||
|
||||
super.reset();
|
||||
}
|
||||
|
||||
public int getSite()
|
||||
{
|
||||
return mSite;
|
||||
}
|
||||
|
||||
public ChannelMap getChannelMap()
|
||||
{
|
||||
return mChannelMap;
|
||||
}
|
||||
|
||||
public void setChannelMap( ChannelMap channelMap )
|
||||
{
|
||||
mChannelMap = channelMap;
|
||||
}
|
||||
|
||||
public String getFromTalkgroup()
|
||||
{
|
||||
return mFromTalkgroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the talkgroup. This is used primarily for traffic channels since
|
||||
* the talkgroup will already have been identified prior to the traffic
|
||||
* channel being created.
|
||||
*/
|
||||
public void setFromTalkgroup( String fromTalkgroup )
|
||||
{
|
||||
mFromTalkgroup = fromTalkgroup;
|
||||
|
||||
broadcastChange( ChangedAttribute.FROM_TALKGROUP );
|
||||
}
|
||||
|
||||
public String getToTalkgroup()
|
||||
{
|
||||
return mToTalkgroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the talkgroup. This is used primarily for traffic channels since
|
||||
* the talkgroup will already have been identified prior to the traffic
|
||||
* channel being created.
|
||||
*/
|
||||
public void setToTalkgroup( String toTalkgroup )
|
||||
{
|
||||
mToTalkgroup = toTalkgroup;
|
||||
|
||||
broadcastChange( ChangedAttribute.TO_TALKGROUP );
|
||||
}
|
||||
|
||||
public int getChannelNumber()
|
||||
{
|
||||
return mChannelNumber;
|
||||
}
|
||||
/**
|
||||
* Set the channel number. This is used primarily for traffic channels since
|
||||
* the channel will already have been identified prior to the traffic
|
||||
* channel being created.
|
||||
*/
|
||||
public void setChannelNumber( int channel )
|
||||
{
|
||||
mChannelNumber = channel;
|
||||
|
||||
broadcastChange( ChangedAttribute.CHANNEL_NUMBER );
|
||||
}
|
||||
|
||||
public String getAliasListName()
|
||||
{
|
||||
return mProcessingChain.getChannel().getAliasListName();
|
||||
}
|
||||
|
||||
private Channel getTrafficChannel( int channelNumber, String aliasListName )
|
||||
{
|
||||
Channel traffic = new Channel( "Traffic Channel " + channelNumber,
|
||||
ChannelType.TRAFFIC );
|
||||
|
||||
traffic.setResourceManager( getProcessingChain().getResourceManager() );
|
||||
|
||||
SourceConfigTuner source = new SourceConfigTuner();
|
||||
|
||||
int frequency = mChannelMap.getFrequency( channelNumber );
|
||||
|
||||
source.setFrequency( frequency );
|
||||
|
||||
traffic.setSourceConfiguration( source );
|
||||
|
||||
DecodeConfigMPT1327 decode = new DecodeConfigMPT1327();
|
||||
traffic.setDecodeConfiguration( decode );
|
||||
|
||||
traffic.setAliasListName( aliasListName );
|
||||
|
||||
return traffic;
|
||||
}
|
||||
}
|
||||
283
src/decode/mpt1327/MPT1327Decoder.java
Normal file
283
src/decode/mpt1327/MPT1327Decoder.java
Normal file
@@ -0,0 +1,283 @@
|
||||
/*******************************************************************************
|
||||
* 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.mpt1327;
|
||||
|
||||
import instrument.Instrumentable;
|
||||
import instrument.tap.Tap;
|
||||
import instrument.tap.stream.BinaryTap;
|
||||
import instrument.tap.stream.FloatTap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import sample.Broadcaster;
|
||||
import source.Source.SampleType;
|
||||
import alias.AliasList;
|
||||
import bits.MessageFramer;
|
||||
import bits.SyncPattern;
|
||||
import decode.Decoder;
|
||||
import decode.DecoderType;
|
||||
import dsp.filter.DCRemovalFilter;
|
||||
import dsp.filter.FilterFactory;
|
||||
import dsp.filter.Filters;
|
||||
import dsp.filter.FloatFIRFilter;
|
||||
import dsp.filter.FloatHalfBandFilter;
|
||||
import dsp.filter.Window.WindowType;
|
||||
import dsp.fsk.FSK2Decoder;
|
||||
import dsp.fsk.FSK2Decoder.Output;
|
||||
import dsp.nbfm.NBFMDemodulator;
|
||||
|
||||
/**
|
||||
* MPT1327 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate
|
||||
* complex or floating point samples and output fully framed MPT1327 control and
|
||||
* traffic messages.
|
||||
*/
|
||||
public class MPT1327Decoder extends Decoder implements Instrumentable
|
||||
{
|
||||
/* Determines how quickly the DC remove filter responds */
|
||||
private static final double sDC_REMOVAL_RATIO = 0.95;
|
||||
|
||||
/* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */
|
||||
private static final int sDECIMATED_SAMPLE_RATE = 24000;
|
||||
|
||||
/* Baud or Symbol Rate */
|
||||
private static final int sSYMBOL_RATE = 1200;
|
||||
|
||||
/* Message length -- longest possible message is:
|
||||
* 4xREVS + 16xSYNC + 64xADD1 + 64xDCW1 + 64xDCW2 + 64xDCW3 + 64xDCW4 */
|
||||
private static final int sMESSAGE_LENGTH = 350;
|
||||
|
||||
/* Instrumentation Taps */
|
||||
private static final String INSTRUMENT_INPUT = "Tap Point: Input";
|
||||
private static final String INSTRUMENT_HB1_FILTER_TO_LOW_PASS =
|
||||
"Tap Point: Half-band Decimation Filter > < Low Pass Filter";
|
||||
private static final String INSTRUMENT_LOW_PASS_TO_DECODER =
|
||||
"Tap Point: Low Pass Filter > < Decoder";
|
||||
private static final String INSTRUMENT_DECODER_TO_FRAMER =
|
||||
"Tap Point: Decoder > < Sync Detect/Message Framer";
|
||||
private List<Tap> mAvailableTaps;
|
||||
|
||||
private NBFMDemodulator mNBFMDemodulator;
|
||||
private FloatHalfBandFilter mDecimationFilter;
|
||||
private FloatFIRFilter mLowPassFilter;
|
||||
private DCRemovalFilter mDCRemovalFilter;
|
||||
private FSK2Decoder mFSKDecoder;
|
||||
private Broadcaster<Boolean> mSymbolBroadcaster;
|
||||
private MessageFramer mControlMessageFramer;
|
||||
private MessageFramer mTrafficMessageFramer;
|
||||
private MPT1327MessageProcessor mMessageProcessor;
|
||||
|
||||
public MPT1327Decoder( SampleType sampleType, AliasList aliasList )
|
||||
{
|
||||
super( sampleType );
|
||||
|
||||
/* If we're receiving complex samples, do FM demodulation and DC removal
|
||||
* and feed the output back to this decoder */
|
||||
if( mSourceSampleType == SampleType.COMPLEX )
|
||||
{
|
||||
/* I/Q low pass filtering narrow band FM demodulator */
|
||||
mNBFMDemodulator = new NBFMDemodulator(
|
||||
FilterFactory.getLowPass( 48000, 4000, 73, WindowType.HAMMING ),
|
||||
1.0002 );
|
||||
|
||||
this.addComplexListener( mNBFMDemodulator );
|
||||
|
||||
/* DC removal filter */
|
||||
mDCRemovalFilter = new DCRemovalFilter( sDC_REMOVAL_RATIO );
|
||||
|
||||
if( mNBFMDemodulator != null )
|
||||
{
|
||||
mNBFMDemodulator.addListener( mDCRemovalFilter );
|
||||
mDCRemovalFilter.setListener( this.getFloatReceiver() );
|
||||
}
|
||||
}
|
||||
|
||||
/* Demodulated float sample processing */
|
||||
|
||||
/* Decimation filter - 48000 / 2 = 24000 output */
|
||||
mDecimationFilter = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
addFloatListener( mDecimationFilter );
|
||||
|
||||
/* Low pass filter: 2kHz to pass the 1200 & 1800 Hz FSK */
|
||||
mLowPassFilter = new FloatFIRFilter(
|
||||
FilterFactory.getLowPass( 24000, //sampleRate,
|
||||
2000, //passFrequency,
|
||||
4000, //stopFrequency,
|
||||
60, //attenuation,
|
||||
WindowType.HANNING, //windowType,
|
||||
true ) //forceOddLength
|
||||
, 2.0 ); //Gain
|
||||
mDecimationFilter.setListener( mLowPassFilter );
|
||||
|
||||
/* 2FSK Decoder with output inverted */
|
||||
mFSKDecoder = new FSK2Decoder( 24000, 1200, Output.INVERTED );
|
||||
mLowPassFilter.setListener( mFSKDecoder );
|
||||
|
||||
mSymbolBroadcaster = new Broadcaster<Boolean>();
|
||||
mFSKDecoder.setListener( mSymbolBroadcaster );
|
||||
|
||||
/* Message framer for control channel messages */
|
||||
mControlMessageFramer =
|
||||
new MessageFramer( SyncPattern.MPT1327_CONTROL.getPattern(),
|
||||
sMESSAGE_LENGTH );
|
||||
mSymbolBroadcaster.addListener( mControlMessageFramer );
|
||||
|
||||
/* Message framer for traffic channel massages */
|
||||
mTrafficMessageFramer =
|
||||
new MessageFramer( SyncPattern.MPT1327_TRAFFIC.getPattern(),
|
||||
sMESSAGE_LENGTH );
|
||||
mSymbolBroadcaster.addListener( mTrafficMessageFramer );
|
||||
|
||||
/* Fully decoded and framed messages processor */
|
||||
mMessageProcessor = new MPT1327MessageProcessor( aliasList );
|
||||
mMessageProcessor.addMessageListener( this );
|
||||
mControlMessageFramer.addMessageListener( mMessageProcessor );
|
||||
mTrafficMessageFramer.addMessageListener( mMessageProcessor );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecoderType getType()
|
||||
{
|
||||
return DecoderType.MPT1327;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup method
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
super.dispose();
|
||||
|
||||
mSymbolBroadcaster.dispose();
|
||||
|
||||
mControlMessageFramer.dispose();
|
||||
|
||||
if( mDCRemovalFilter != null )
|
||||
{
|
||||
mDCRemovalFilter.dispose();
|
||||
}
|
||||
|
||||
if( mNBFMDemodulator != null )
|
||||
{
|
||||
mNBFMDemodulator.dispose();
|
||||
}
|
||||
|
||||
mFSKDecoder.dispose();
|
||||
|
||||
mDecimationFilter.dispose();
|
||||
|
||||
mLowPassFilter.dispose();
|
||||
|
||||
mMessageProcessor.dispose();
|
||||
|
||||
|
||||
mTrafficMessageFramer.dispose();
|
||||
}
|
||||
|
||||
/* Instrumentation Taps */
|
||||
@Override
|
||||
public List<Tap> getTaps()
|
||||
{
|
||||
if( mAvailableTaps == null )
|
||||
{
|
||||
mAvailableTaps = new ArrayList<Tap>();
|
||||
|
||||
mAvailableTaps.add(
|
||||
new FloatTap( INSTRUMENT_INPUT, 0, 1.0f ) );
|
||||
mAvailableTaps.add(
|
||||
new FloatTap( INSTRUMENT_HB1_FILTER_TO_LOW_PASS, 0, 0.5f ) );
|
||||
mAvailableTaps.add(
|
||||
new FloatTap( INSTRUMENT_LOW_PASS_TO_DECODER, 0, 0.5f ) );
|
||||
mAvailableTaps.add(
|
||||
new BinaryTap( INSTRUMENT_DECODER_TO_FRAMER, 0, 0.025f ) );
|
||||
|
||||
/* Add the taps from the FSK decoder */
|
||||
mAvailableTaps.addAll( mFSKDecoder.getTaps() );
|
||||
}
|
||||
|
||||
return mAvailableTaps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTap( Tap tap )
|
||||
{
|
||||
/* Send request to decoder */
|
||||
mFSKDecoder.addTap( tap );
|
||||
|
||||
switch( tap.getName() )
|
||||
{
|
||||
case INSTRUMENT_INPUT:
|
||||
FloatTap inputTap = (FloatTap)tap;
|
||||
if( mNBFMDemodulator != null )
|
||||
{
|
||||
mNBFMDemodulator.addListener( inputTap );
|
||||
}
|
||||
else
|
||||
{
|
||||
addFloatListener( inputTap );
|
||||
}
|
||||
break;
|
||||
case INSTRUMENT_HB1_FILTER_TO_LOW_PASS:
|
||||
FloatTap hb1Tap = (FloatTap)tap;
|
||||
mDecimationFilter.setListener( hb1Tap );
|
||||
hb1Tap.setListener( mLowPassFilter );
|
||||
break;
|
||||
case INSTRUMENT_LOW_PASS_TO_DECODER:
|
||||
FloatTap lowTap = (FloatTap)tap;
|
||||
mLowPassFilter.setListener( lowTap );
|
||||
lowTap.setListener( mFSKDecoder );
|
||||
break;
|
||||
case INSTRUMENT_DECODER_TO_FRAMER:
|
||||
BinaryTap decoderTap = (BinaryTap)tap;
|
||||
mFSKDecoder.setListener( decoderTap );
|
||||
decoderTap.setListener( mSymbolBroadcaster );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTap( Tap tap )
|
||||
{
|
||||
mFSKDecoder.removeTap( tap );
|
||||
|
||||
switch( tap.getName() )
|
||||
{
|
||||
case INSTRUMENT_INPUT:
|
||||
FloatTap inputTap = (FloatTap)tap;
|
||||
if( mNBFMDemodulator != null )
|
||||
{
|
||||
mNBFMDemodulator.removeListener( inputTap );
|
||||
}
|
||||
else
|
||||
{
|
||||
removeFloatListener( inputTap );
|
||||
}
|
||||
break;
|
||||
case INSTRUMENT_HB1_FILTER_TO_LOW_PASS:
|
||||
mDecimationFilter.setListener( mLowPassFilter );
|
||||
break;
|
||||
case INSTRUMENT_LOW_PASS_TO_DECODER:
|
||||
mLowPassFilter.setListener( mFSKDecoder );
|
||||
break;
|
||||
case INSTRUMENT_DECODER_TO_FRAMER:
|
||||
mFSKDecoder.setListener( mSymbolBroadcaster );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
103
src/decode/mpt1327/MPT1327Editor.java
Normal file
103
src/decode/mpt1327/MPT1327Editor.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/*******************************************************************************
|
||||
* 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.mpt1327;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import controller.channel.ChannelMap;
|
||||
import controller.channel.ChannelNode;
|
||||
import decode.DecodeEditor;
|
||||
import decode.config.DecodeConfigMPT1327;
|
||||
import decode.config.DecodeConfiguration;
|
||||
|
||||
public class MPT1327Editor extends DecodeEditor
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private JComboBox<ChannelMap> mComboChannelMaps;
|
||||
private ChannelNode mChannelNode;
|
||||
|
||||
public MPT1327Editor( DecodeConfiguration config, ChannelNode channelNode )
|
||||
{
|
||||
super( config );
|
||||
|
||||
mChannelNode = channelNode;
|
||||
|
||||
initGUI();
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
/**
|
||||
* ComboBox: Alias Lists
|
||||
*/
|
||||
ArrayList<ChannelMap> maps = mChannelNode.getModel().getResourceManager()
|
||||
.getPlaylistManager().getPlayist().getChannelMapList().getChannelMap();
|
||||
|
||||
ChannelMap[] mapArray = maps.toArray( new ChannelMap[ maps.size() ] );
|
||||
|
||||
mComboChannelMaps = new JComboBox<ChannelMap>();
|
||||
mComboChannelMaps.setModel(
|
||||
new DefaultComboBoxModel<ChannelMap>( mapArray ) );
|
||||
|
||||
add( new JLabel( "Channel Map" ), "wrap" );
|
||||
add( mComboChannelMaps, "wrap" );
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
ChannelMap selected = (ChannelMap)mComboChannelMaps.getSelectedItem();
|
||||
|
||||
if( selected != null )
|
||||
{
|
||||
((DecodeConfigMPT1327)mConfig).setChannelMapName( selected.getName() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
String name = ((DecodeConfigMPT1327)mConfig).getChannelMapName();
|
||||
|
||||
ChannelMap selected = null;
|
||||
|
||||
if( name != null )
|
||||
{
|
||||
for( ChannelMap map: mChannelNode.getModel().getResourceManager()
|
||||
.getPlaylistManager().getPlayist().getChannelMapList()
|
||||
.getChannelMap() )
|
||||
{
|
||||
if( map.getName().equalsIgnoreCase( name ) )
|
||||
{
|
||||
selected = map;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mComboChannelMaps.setSelectedItem( selected );
|
||||
mComboChannelMaps.requestFocus();
|
||||
mComboChannelMaps.requestFocusInWindow();
|
||||
}
|
||||
}
|
||||
986
src/decode/mpt1327/MPT1327Message.java
Normal file
986
src/decode/mpt1327/MPT1327Message.java
Normal file
@@ -0,0 +1,986 @@
|
||||
/*******************************************************************************
|
||||
* 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.mpt1327;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.BitSet;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import map.Plottable;
|
||||
import message.Message;
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
import crc.CRC;
|
||||
import crc.CRCFleetsync;
|
||||
|
||||
public class MPT1327Message extends Message
|
||||
{
|
||||
private static DecimalFormat mDecimalFormatter = new DecimalFormat("#.#####");
|
||||
private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" );
|
||||
|
||||
//Calendar to use in calculating time hacks
|
||||
Calendar mCalendar = new GregorianCalendar();
|
||||
|
||||
//Message parts are identified in big-endian order for correct translation
|
||||
private static int BLOCK_1_START = 20;
|
||||
private static int BLOCK_2_START = 84;
|
||||
private static int BLOCK_3_START = 148;
|
||||
private static int BLOCK_4_START = 212;
|
||||
private static int BLOCK_5_START = 276;
|
||||
private static int BLOCK_5_END = 340;
|
||||
private static int[] REVS = { 0,1,2,3 };
|
||||
private static int[] SYNC = { 4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 };
|
||||
|
||||
/* Block 1 Fields */
|
||||
private static int[] B1_PREFIX = { 21,22,23,24,25,26,27 };
|
||||
private static int[] B1_IDENT1 = { 28,29,30,31,32,33,34,35,36,37,38,39,40 };
|
||||
private static int[] B1_IDENT2 = { 53,54,55,56,57,58,59,60,61,62,63,64,65 };
|
||||
private static int[] B1_GTC_CHAN = { 43,44,45,46,47,48,49,50,51,52 };
|
||||
private static int[] B1_MESSAGE_TYPE = { 41,42,43,44,45,46,47,48,49 };
|
||||
private static int[] B1_STATUS_MESSAGE = { 66,67,68,69,70 };
|
||||
private static int[] B1_SYSTEM_ID = { 26,27,28,29,30,31,32,33,34,35,36,37,38,39,40 };
|
||||
private static int[] B1_SYSDEF = { 21,22,23,24,25 };
|
||||
private static int[] B1_CHANNEL = { 35,36,37,38,39,40,41,42,43,44 };
|
||||
private static int[] B1_TRAFFIC_CHANNEL = { 21,22,23,24,25,26,27,28,29,30 };
|
||||
private static int[] B1_CONTROL_CHANNEL = { 31,32,33,34,35,36,37,38,39,40 };
|
||||
private static int[] B1_ADJSITE = { 49,50,51,52 };
|
||||
|
||||
/* Block 2 Fields */
|
||||
private static int[] B2_SYSTEM_ID = { 85,86,87,88,89,90,91,92,93,94,95,96,97,98,99 };
|
||||
private static int[] B2_PREFIX = { 112,113,114,115,116,117,118 };
|
||||
private static int[] B2_IDENT2 = { 119,120,121,122,123,124,125,126,127,128,129,130,131 };
|
||||
|
||||
private BitSetBuffer mMessage;
|
||||
private CRC[] mCRC = new CRC[ 5 ];
|
||||
private AliasList mAliasList;
|
||||
private MPTMessageType mMessageType;
|
||||
|
||||
public MPT1327Message( BitSetBuffer message, AliasList list )
|
||||
{
|
||||
mMessage = message;
|
||||
mAliasList = list;
|
||||
|
||||
checkParity( 0, BLOCK_1_START, BLOCK_2_START );
|
||||
|
||||
if( isValid() )
|
||||
{
|
||||
mMessageType = getMessageType();
|
||||
|
||||
switch( mMessageType )
|
||||
{
|
||||
/* 1 data block messages */
|
||||
case CLEAR:
|
||||
case MAINT:
|
||||
case MOVE:
|
||||
break;
|
||||
/* 2 data block messages */
|
||||
case AHYQ:
|
||||
case ALH:
|
||||
checkParity( 1, BLOCK_2_START, BLOCK_3_START );
|
||||
break;
|
||||
/* 3 data block messages */
|
||||
/* 4 data block messages */
|
||||
case ACKT:
|
||||
checkParity( 1, BLOCK_2_START, BLOCK_3_START );
|
||||
checkParity( 2, BLOCK_3_START, BLOCK_4_START );
|
||||
checkParity( 3, BLOCK_4_START, BLOCK_5_START );
|
||||
break;
|
||||
/* 5 data block messages */
|
||||
case AHYC:
|
||||
checkParity( 1, BLOCK_2_START, BLOCK_3_START );
|
||||
checkParity( 2, BLOCK_3_START, BLOCK_4_START );
|
||||
checkParity( 3, BLOCK_4_START, BLOCK_5_START );
|
||||
checkParity( 3, BLOCK_5_START, BLOCK_5_END );
|
||||
break;
|
||||
case ACK:
|
||||
case ACKB:
|
||||
case ACKE:
|
||||
case ACKI:
|
||||
case ACKQ:
|
||||
case ACKV:
|
||||
case ACKX:
|
||||
case AHOY:
|
||||
case AHYP:
|
||||
case AHYX:
|
||||
case ALHD:
|
||||
case ALHE:
|
||||
case ALHF:
|
||||
case ALHR:
|
||||
case ALHS:
|
||||
case ALHX:
|
||||
case BCAST:
|
||||
case GTC:
|
||||
case MARK:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mMessageType = MPTMessageType.UNKN;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkParity( int section, int start, int end )
|
||||
{
|
||||
mCRC[ section ] = detectAndCorrect( start, end );
|
||||
}
|
||||
|
||||
private CRC detectAndCorrect( int start, int end )
|
||||
{
|
||||
BitSet original = mMessage.get( start, end );
|
||||
|
||||
CRC retVal = CRCFleetsync.check( original );
|
||||
|
||||
//Attempt to correct single-bit errors
|
||||
if( retVal == CRC.FAILED_PARITY )
|
||||
{
|
||||
int[] errorBitPositions = CRCFleetsync.findBitErrors( original );
|
||||
|
||||
if( errorBitPositions != null )
|
||||
{
|
||||
for( int errorBitPosition: errorBitPositions )
|
||||
{
|
||||
mMessage.flip( start + errorBitPosition );
|
||||
}
|
||||
|
||||
retVal = CRC.CORRECTED;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* String representing results of the parity check
|
||||
*
|
||||
* [P] = passes parity check
|
||||
* [f] = fails parity check
|
||||
* [C] = corrected message
|
||||
* [-] = message section not present
|
||||
*/
|
||||
public String getParity()
|
||||
{
|
||||
return "[" + CRC.format( mCRC ) + "]";
|
||||
}
|
||||
|
||||
public boolean isValid()
|
||||
{
|
||||
return mCRC[ 0 ] == CRC.PASSED ||
|
||||
mCRC[ 0 ] == CRC.CORRECTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( mSDF.format( new Date() ) );
|
||||
|
||||
sb.append( " MPT1327 " );
|
||||
|
||||
sb.append( getParity() );
|
||||
|
||||
sb.append( " " );
|
||||
|
||||
sb.append( getMessage() );
|
||||
|
||||
sb.append( getFiller( sb, 100 ) );
|
||||
|
||||
sb.append( " [" + mMessage.toString() + "]" );
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public MPTMessageType getMessageType()
|
||||
{
|
||||
int type = mMessage.getInt( B1_MESSAGE_TYPE );
|
||||
|
||||
return MPTMessageType.fromNumber( type );
|
||||
}
|
||||
|
||||
public int getSiteID()
|
||||
{
|
||||
if( mMessageType == MPTMessageType.BCAST )
|
||||
{
|
||||
return mMessage.getInt( B1_SYSTEM_ID );
|
||||
}
|
||||
else if( mMessageType == MPTMessageType.ALH )
|
||||
{
|
||||
return mMessage.getInt( B2_SYSTEM_ID );
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasSystemID()
|
||||
{
|
||||
return getSiteID() != 0;
|
||||
}
|
||||
|
||||
public SystemDefinition getSystemDefinition()
|
||||
{
|
||||
int sysdef = mMessage.getInt( B1_SYSDEF );
|
||||
|
||||
return SystemDefinition.fromNumber( sysdef );
|
||||
}
|
||||
|
||||
public int getChannel()
|
||||
{
|
||||
switch( mMessageType )
|
||||
{
|
||||
case CLEAR:
|
||||
return mMessage.getInt( B1_TRAFFIC_CHANNEL );
|
||||
case GTC:
|
||||
return mMessage.getInt( B1_GTC_CHAN );
|
||||
default:
|
||||
return mMessage.getInt( B1_CHANNEL );
|
||||
}
|
||||
}
|
||||
|
||||
public int getReturnToChannel()
|
||||
{
|
||||
if( mMessageType == MPTMessageType.CLEAR )
|
||||
{
|
||||
return mMessage.getInt( B1_CONTROL_CHANNEL );
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public int getAdjacentSiteSerialNumber()
|
||||
{
|
||||
return mMessage.getInt( B1_ADJSITE );
|
||||
}
|
||||
|
||||
public int getPrefix()
|
||||
{
|
||||
return mMessage.getInt( B1_PREFIX );
|
||||
}
|
||||
|
||||
public int getBlock2Prefix()
|
||||
{
|
||||
return mMessage.getInt( B2_PREFIX );
|
||||
}
|
||||
|
||||
public int getIdent1()
|
||||
{
|
||||
return mMessage.getInt( B1_IDENT1 );
|
||||
}
|
||||
|
||||
public int getIdent2()
|
||||
{
|
||||
return mMessage.getInt( B1_IDENT2 );
|
||||
}
|
||||
|
||||
public int getBlock2Ident2()
|
||||
{
|
||||
return mMessage.getInt( B2_IDENT2 );
|
||||
}
|
||||
|
||||
public String getStatusMessage()
|
||||
{
|
||||
int status = mMessage.getInt( B1_STATUS_MESSAGE );
|
||||
|
||||
switch( status )
|
||||
{
|
||||
case 0:
|
||||
return "STATUS: Request Speech Call";
|
||||
case 31:
|
||||
return "STATUS: Cancel Request Speech Call";
|
||||
default:
|
||||
return "STATUS: " + status;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns spaces to the fill the string builder to ensure length is >= index
|
||||
*/
|
||||
public String getFiller( StringBuilder sb, int index )
|
||||
{
|
||||
if( sb.length() < index )
|
||||
{
|
||||
return String.format( "%" + ( index - sb.length() ) + "s", " " );
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 getMessage()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( pad( mMessageType.toString(), 5, " " ) );
|
||||
|
||||
switch( mMessageType )
|
||||
{
|
||||
case ACK:
|
||||
sb.append( " MESSAGE ACKNOWLEDGED" );
|
||||
|
||||
if( hasFromID() )
|
||||
{
|
||||
sb.append( " FROM:" );
|
||||
sb.append( getFromID() );
|
||||
}
|
||||
break;
|
||||
case ACKI:
|
||||
sb.append( " SYSTEM:" );
|
||||
sb.append( format( getSiteID(), 5 ) );
|
||||
|
||||
sb.append( " MESSAGE ACKNOWLEDGED - MORE TO FOLLOW" );
|
||||
|
||||
if( hasFromID() )
|
||||
{
|
||||
sb.append( " FROM:" );
|
||||
sb.append( getFromID() );
|
||||
}
|
||||
|
||||
if( hasToID() )
|
||||
{
|
||||
sb.append( " TO:" );
|
||||
sb.append( getToID() );
|
||||
}
|
||||
break;
|
||||
case ACKT:
|
||||
sb.append( " SITE:" );
|
||||
sb.append( format( getSiteID(), 5 ) );
|
||||
|
||||
sb.append( " LONG ACK MESSAGE" );
|
||||
|
||||
if( hasFromID() )
|
||||
{
|
||||
sb.append( " FROM:" );
|
||||
sb.append( getFromID() );
|
||||
}
|
||||
|
||||
if( hasToID() )
|
||||
{
|
||||
sb.append( " TO:" );
|
||||
sb.append( getToID() );
|
||||
}
|
||||
|
||||
sb.append( "**********************************" );
|
||||
break;
|
||||
case ACKQ:
|
||||
sb.append( " SYSTEM:" );
|
||||
sb.append( format( getSiteID(), 5 ) );
|
||||
|
||||
sb.append( " CALL QUEUED FROM:" );
|
||||
|
||||
if( hasFromID() )
|
||||
{
|
||||
sb.append( " FROM:" );
|
||||
sb.append( getFromID() );
|
||||
}
|
||||
|
||||
if( hasToID() )
|
||||
{
|
||||
sb.append( " TO:" );
|
||||
sb.append( getToID() );
|
||||
}
|
||||
break;
|
||||
case ACKX:
|
||||
sb.append( " SYSTEM:" );
|
||||
sb.append( format( getSiteID(), 5 ) );
|
||||
|
||||
sb.append( " MESSAGE REJECTED FROM:" );
|
||||
|
||||
if( hasFromID() )
|
||||
{
|
||||
sb.append( " FROM:" );
|
||||
sb.append( getFromID() );
|
||||
}
|
||||
|
||||
if( hasToID() )
|
||||
{
|
||||
sb.append( " TO:" );
|
||||
sb.append( getToID() );
|
||||
}
|
||||
break;
|
||||
case AHYC:
|
||||
/* SDM */
|
||||
sb.append( " SHORT DATA INVITATION MESSAGE ********* " );
|
||||
break;
|
||||
case AHYQ:
|
||||
/* Status Message */
|
||||
sb.append( " STATUS MESSAGE" );
|
||||
|
||||
if( hasFromID() )
|
||||
{
|
||||
sb.append( " FROM:" );
|
||||
sb.append( getFromID() );
|
||||
}
|
||||
|
||||
if( hasToID() )
|
||||
{
|
||||
sb.append( " TO:" );
|
||||
sb.append( getToID() );
|
||||
}
|
||||
sb.append( " " );
|
||||
sb.append( getStatusMessage() );
|
||||
break;
|
||||
case ALH:
|
||||
case ALHD:
|
||||
case ALHS:
|
||||
case ALHE:
|
||||
case ALHR:
|
||||
case ALHX:
|
||||
case ALHF:
|
||||
sb.append( " SYSTEM:" );
|
||||
sb.append( format( getSiteID(), 5 ) );
|
||||
|
||||
if( hasToID() )
|
||||
{
|
||||
sb.append( " ID:" );
|
||||
sb.append( getToID() );
|
||||
}
|
||||
break;
|
||||
case BCAST:
|
||||
SystemDefinition sysdef = getSystemDefinition();
|
||||
|
||||
sb.append( " " );
|
||||
sb.append( sysdef.getLabel() );
|
||||
|
||||
switch( sysdef )
|
||||
{
|
||||
case ANNOUNCE_CONTROL_CHANNEL:
|
||||
case WITHDRAW_CONTROL_CHANNEL:
|
||||
sb.append( " CHAN:" );
|
||||
sb.append( getChannel() );
|
||||
break;
|
||||
case BROADCAST_ADJACENT_SITE_CONTROL_CHANNEL_NUMBER:
|
||||
sb.append( " CHAN:" );
|
||||
sb.append( getChannel() );
|
||||
sb.append( " SER:" );
|
||||
sb.append( getAdjacentSiteSerialNumber() );
|
||||
//debug
|
||||
sb.append( " *************************" );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case CLEAR:
|
||||
sb.append( " TRAFFIC CHANNEL:" );
|
||||
sb.append( getChannel() );
|
||||
sb.append( " RETURN TO CONTROL CHANNEL:" );
|
||||
sb.append( getReturnToChannel() );
|
||||
break;
|
||||
case GTC:
|
||||
if( hasFromID() )
|
||||
{
|
||||
sb.append( " FROM:" );
|
||||
sb.append( getFromID() );
|
||||
}
|
||||
|
||||
if( hasToID() )
|
||||
{
|
||||
sb.append( " TO:" );
|
||||
sb.append( getToID() );
|
||||
}
|
||||
|
||||
sb.append( " CHAN:" );
|
||||
sb.append( getChannel() );
|
||||
break;
|
||||
case MAINT:
|
||||
if( hasToID() )
|
||||
{
|
||||
sb.append( " ID:" );
|
||||
sb.append( getToID() );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBinaryMessage()
|
||||
{
|
||||
return mMessage.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol()
|
||||
{
|
||||
return "MPT-1327";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventType()
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFromID()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int ident2 = getIdent2();
|
||||
|
||||
IdentType type = IdentType.fromIdent( ident2 );
|
||||
|
||||
/* Inter-Prefix - the from and to idents are different prefixes */
|
||||
if( type == IdentType.IPFIXI )
|
||||
{
|
||||
sb.append( format( getBlock2Prefix(), 3 ) );
|
||||
sb.append( "-" );
|
||||
sb.append( format( getBlock2Ident2(), 4) );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( format( getPrefix(), 3 ) );
|
||||
sb.append( "-" );
|
||||
sb.append( format( ident2, 4) );
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public boolean hasFromID()
|
||||
{
|
||||
return getFromID() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getFromIDAlias()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToID()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int prefix = getPrefix();
|
||||
int ident = getIdent1();
|
||||
|
||||
switch( IdentType.fromIdent( ident ) )
|
||||
{
|
||||
case USER:
|
||||
sb.append( format( prefix, 3 ) );
|
||||
sb.append( "-" );
|
||||
sb.append( format( ident, 4) );
|
||||
break;
|
||||
case IPFIXI:
|
||||
sb.append( "INTER-PREFIX" );
|
||||
break;
|
||||
case ALLI:
|
||||
sb.append( "ALL RADIOS" );
|
||||
break;
|
||||
case PABXI:
|
||||
sb.append( "PABX EXT" );
|
||||
break;
|
||||
case PSTNSI1:
|
||||
case PSTNSI2:
|
||||
case PSTNSI3:
|
||||
case PSTNSI4:
|
||||
case PSTNSI5:
|
||||
case PSTNSI6:
|
||||
case PSTNSI7:
|
||||
case PSTNSI8:
|
||||
case PSTNSI9:
|
||||
case PSTNSI10:
|
||||
case PSTNSI11:
|
||||
case PSTNSI12:
|
||||
case PSTNSI13:
|
||||
case PSTNSI14:
|
||||
case PSTNSI15:
|
||||
sb.append( "PRE-DEFINED PSTN" );
|
||||
break;
|
||||
case PSTNGI:
|
||||
sb.append( "PSTN GATEWAY" );
|
||||
break;
|
||||
case TSCI:
|
||||
sb.append( "CONTROLLER" );
|
||||
break;
|
||||
case DIVERTI:
|
||||
sb.append( "CALL DIVERT" );
|
||||
break;
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public boolean hasToID()
|
||||
{
|
||||
return getToID() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Alias getToIDAlias()
|
||||
{
|
||||
if( hasToID() )
|
||||
{
|
||||
return mAliasList.getMPT1327Alias( getToID() );
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorStatus()
|
||||
{
|
||||
return getParity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Plottable getPlottable()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public enum IdentType
|
||||
{
|
||||
ALLI( "All Subscribers" ),
|
||||
DIVERTI( "Divert" ),
|
||||
DNI( "Data Network Gateway" ),
|
||||
DUMMYI( "Dummy Ident" ),
|
||||
INCI( "Include in Call" ),
|
||||
IPFIXI( "Inter-Prefix" ),
|
||||
PABXI( "PABX Gateway" ),
|
||||
PSTNGI( "PSTN Gateway" ),
|
||||
PSTNSI1( "PSTN or Network 1" ),
|
||||
PSTNSI2( "PSTN or Network 2" ),
|
||||
PSTNSI3( "PSTN or Network 3" ),
|
||||
PSTNSI4( "PSTN or Network 4" ),
|
||||
PSTNSI5( "PSTN or Network 5" ),
|
||||
PSTNSI6( "PSTN or Network 6" ),
|
||||
PSTNSI7( "PSTN or Network 7" ),
|
||||
PSTNSI8( "PSTN or Network 8" ),
|
||||
PSTNSI9( "PSTN or Network 9" ),
|
||||
PSTNSI10( "PSTN or Network 10" ),
|
||||
PSTNSI11( "PSTN or Network 11" ),
|
||||
PSTNSI12( "PSTN or Network 12" ),
|
||||
PSTNSI13( "PSTN or Network 13" ),
|
||||
PSTNSI14( "PSTN or Network 14" ),
|
||||
PSTNSI15( "PSTN or Network 15" ),
|
||||
REGI( "Registration" ),
|
||||
RESERVED( "Reserved" ),
|
||||
SDMI( "Short Data Message" ),
|
||||
SPARE( "Spare" ),
|
||||
TSCI( "TSC" ),
|
||||
USER( "Individual or Group Ident" ),
|
||||
UNKNOWN( "Unknown" );
|
||||
|
||||
private String mLabel;
|
||||
|
||||
private IdentType( String label )
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
public static IdentType fromIdent( int ident )
|
||||
{
|
||||
switch( ident )
|
||||
{
|
||||
case 0:
|
||||
return DUMMYI;
|
||||
case 8101:
|
||||
return PSTNGI;
|
||||
case 8102:
|
||||
return PABXI;
|
||||
case 8103:
|
||||
return DNI;
|
||||
case 8121:
|
||||
return PSTNSI1;
|
||||
case 8122:
|
||||
return PSTNSI2;
|
||||
case 8123:
|
||||
return PSTNSI3;
|
||||
case 8124:
|
||||
return PSTNSI4;
|
||||
case 8125:
|
||||
return PSTNSI5;
|
||||
case 8126:
|
||||
return PSTNSI6;
|
||||
case 8127:
|
||||
return PSTNSI7;
|
||||
case 8128:
|
||||
return PSTNSI8;
|
||||
case 8129:
|
||||
return PSTNSI9;
|
||||
case 8130:
|
||||
return PSTNSI10;
|
||||
case 8131:
|
||||
return PSTNSI11;
|
||||
case 8132:
|
||||
return PSTNSI12;
|
||||
case 8133:
|
||||
return PSTNSI13;
|
||||
case 8134:
|
||||
return PSTNSI14;
|
||||
case 8135:
|
||||
return PSTNSI15;
|
||||
case 8181:
|
||||
case 8182:
|
||||
case 8183:
|
||||
case 8184:
|
||||
return RESERVED;
|
||||
case 8185:
|
||||
return REGI;
|
||||
case 8186:
|
||||
return INCI;
|
||||
case 8187:
|
||||
return DIVERTI;
|
||||
case 8188:
|
||||
return SDMI;
|
||||
case 8189:
|
||||
return IPFIXI;
|
||||
case 8190:
|
||||
return TSCI;
|
||||
case 8191:
|
||||
return ALLI;
|
||||
default:
|
||||
if( 1 <= ident && ident <= 8100 )
|
||||
{
|
||||
return USER;
|
||||
}
|
||||
else if( ( 8104 <= ident && ident <= 8120 ) ||
|
||||
( 8136 <= ident && ident <= 8180 ) )
|
||||
{
|
||||
return SPARE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum SystemDefinition
|
||||
{
|
||||
UNKNOWN( "UNKNOWN" ),
|
||||
ANNOUNCE_CONTROL_CHANNEL( "ANNOUNCE CONTROL CHANNEL" ),
|
||||
WITHDRAW_CONTROL_CHANNEL( "WITHDRAW CONTROL CHANNEL" ),
|
||||
SPECIFY_CALL_MAINTENANCE_PARAMETERS( "CALL MAINT PARAMETERS" ),
|
||||
SPECIFY_REGISTRATION_PARAMETERS( "REGISTRATION PARAMETERS" ),
|
||||
BROADCAST_ADJACENT_SITE_CONTROL_CHANNEL_NUMBER( "NEIGHBOR" ),
|
||||
VOTE_NOW_ADVICE( "VOTE NOW" );
|
||||
|
||||
private String mLabel;
|
||||
|
||||
private SystemDefinition( String label )
|
||||
{
|
||||
mLabel = label;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return mLabel;
|
||||
}
|
||||
|
||||
public static SystemDefinition fromNumber( int number )
|
||||
{
|
||||
switch( number )
|
||||
{
|
||||
case 0:
|
||||
return ANNOUNCE_CONTROL_CHANNEL;
|
||||
case 1:
|
||||
return WITHDRAW_CONTROL_CHANNEL;
|
||||
case 2:
|
||||
return SPECIFY_CALL_MAINTENANCE_PARAMETERS;
|
||||
case 3:
|
||||
return SPECIFY_REGISTRATION_PARAMETERS;
|
||||
case 4:
|
||||
return BROADCAST_ADJACENT_SITE_CONTROL_CHANNEL_NUMBER;
|
||||
case 5:
|
||||
return VOTE_NOW_ADVICE;
|
||||
default:
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum MPTMessageType
|
||||
{
|
||||
UNKN( -1, "Unknown" ),
|
||||
GTC( 0, "GTC - Goto Channel" ),
|
||||
ALH( 256, "ALH - Aloha" ),
|
||||
ALHS( 257, "ALHS - Standard Data Excluded" ),
|
||||
ALHD( 258, "ALHD - Simple Calls Excluded" ),
|
||||
ALHE( 259, "ALHE - Emergency Calls Only" ),
|
||||
ALHR( 260, "ALHR - Emergency or Registration" ),
|
||||
ALHX( 261, "ALHX - Registration Excluded" ),
|
||||
ALHF( 262, "ALHF - Fallback Mode" ),
|
||||
|
||||
ACK( 264, "ACK - Acknowledge" ),
|
||||
ACKI( 265, "ACKI - More To Follow" ),
|
||||
ACKQ( 266, "ACKQ - Call Queued" ),
|
||||
ACKX( 267, "ACKX - Message Rejected" ),
|
||||
ACKV( 268, "ACKV - Called Unit Unavailable" ),
|
||||
ACKE( 269, "ACKE - Emergency" ),
|
||||
ACKT( 270, "ACKT - Try On Given Address" ),
|
||||
ACKB( 271, "ACKB - Call Back/Negative Ack" ),
|
||||
|
||||
AHOY( 272, "AHOY - General Availability Check" ),
|
||||
AHYX( 274, "AHYX - Cancel Alert/Waiting Status" ),
|
||||
AHYP( 277, "AHYP - Called Unit Presence Monitoring" ),
|
||||
AHYQ( 278, "AHYQ - Status Message" ),
|
||||
AHYC( 279, "AHYC - Short Data Message" ),
|
||||
|
||||
MARK( 280, "MARK - Control Channel Marker" ),
|
||||
MAINT(281, "MAINT - Call Maintenance Message"),
|
||||
CLEAR(282, "CLEAR - Down From Allocated Channel" ),
|
||||
MOVE( 283, "MOVE - To Specified Channel" ),
|
||||
BCAST(284, "BCAST - System Parameters" );
|
||||
|
||||
/* There are more to fill in here */
|
||||
|
||||
private int mNumber;
|
||||
private String mDescription;
|
||||
|
||||
private MPTMessageType( int number, String description )
|
||||
{
|
||||
mNumber = number;
|
||||
mDescription = description;
|
||||
}
|
||||
|
||||
public int getMessageNumber()
|
||||
{
|
||||
return mNumber;
|
||||
}
|
||||
|
||||
public String getDescription()
|
||||
{
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
public static MPTMessageType fromNumber( int number )
|
||||
{
|
||||
if( number < 256 )
|
||||
{
|
||||
return GTC;
|
||||
}
|
||||
|
||||
switch( number )
|
||||
{
|
||||
case 256:
|
||||
return ALH;
|
||||
case 257:
|
||||
return ALHS;
|
||||
case 258:
|
||||
return ALHD;
|
||||
case 259:
|
||||
return ALHE;
|
||||
case 260:
|
||||
return ALHR;
|
||||
case 261:
|
||||
return ALHX;
|
||||
case 262:
|
||||
return ALHF;
|
||||
case 264:
|
||||
return ACK;
|
||||
case 265:
|
||||
return ACKI;
|
||||
case 266:
|
||||
return ACKQ;
|
||||
case 267:
|
||||
return ACKX;
|
||||
case 268:
|
||||
return ACKV;
|
||||
case 269:
|
||||
return ACKE;
|
||||
case 270:
|
||||
return ACKT;
|
||||
case 271:
|
||||
return ACKB;
|
||||
case 272:
|
||||
return AHOY;
|
||||
case 274:
|
||||
return AHYX;
|
||||
case 277:
|
||||
return AHYP;
|
||||
case 278:
|
||||
return AHYQ;
|
||||
case 279:
|
||||
return AHYC;
|
||||
case 280:
|
||||
return MARK;
|
||||
case 281:
|
||||
return MAINT;
|
||||
case 282:
|
||||
return CLEAR;
|
||||
case 283:
|
||||
return MOVE;
|
||||
case 284:
|
||||
return BCAST;
|
||||
default:
|
||||
return UNKN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
59
src/decode/mpt1327/MPT1327MessageProcessor.java
Normal file
59
src/decode/mpt1327/MPT1327MessageProcessor.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/*******************************************************************************
|
||||
* 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.mpt1327;
|
||||
|
||||
import message.Message;
|
||||
import sample.Broadcaster;
|
||||
import sample.Listener;
|
||||
import alias.AliasList;
|
||||
import bits.BitSetBuffer;
|
||||
|
||||
public class MPT1327MessageProcessor implements Listener<BitSetBuffer>
|
||||
{
|
||||
private Broadcaster<Message> mBroadcaster = new Broadcaster<Message>();
|
||||
|
||||
private AliasList mAliasList;
|
||||
|
||||
public MPT1327MessageProcessor( AliasList list )
|
||||
{
|
||||
mAliasList = list;
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
mBroadcaster.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( BitSetBuffer buffer )
|
||||
{
|
||||
MPT1327Message message = new MPT1327Message( buffer, mAliasList );
|
||||
|
||||
mBroadcaster.receive( message );
|
||||
}
|
||||
|
||||
public void addMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
public void removeMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mBroadcaster.removeListener( listener );
|
||||
}
|
||||
}
|
||||
282
src/decode/mpt1327/MPT1327Panel.java
Normal file
282
src/decode/mpt1327/MPT1327Panel.java
Normal file
@@ -0,0 +1,282 @@
|
||||
/*******************************************************************************
|
||||
* 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.mpt1327;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import settings.ColorSetting;
|
||||
import settings.Setting;
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import controller.channel.Channel;
|
||||
import controller.state.ChannelState.ChangedAttribute;
|
||||
import controller.state.ChannelStatePanel;
|
||||
|
||||
public class MPT1327Panel extends ChannelStatePanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private JLabel mProtocol;
|
||||
private JLabel mSourceLabel;
|
||||
private JLabel mChannelLabel;
|
||||
|
||||
private JLabel mStateLabel;
|
||||
private JLabel mTalkgroup;
|
||||
private JLabel mTalkgroupAlias;
|
||||
|
||||
private JLabel mSiteLabel;
|
||||
private JLabel mSite;
|
||||
private JLabel mSiteAliasLabel;
|
||||
|
||||
private MPT1327ChannelState mState;
|
||||
|
||||
private AliasList mAliasList;
|
||||
|
||||
public MPT1327Panel( Channel channel )
|
||||
{
|
||||
super( channel );
|
||||
mState = (MPT1327ChannelState)channel.getProcessingChain().getChannelState();
|
||||
mState.addListener( this );
|
||||
|
||||
mAliasList = mState.getAliasList();
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
super.dispose();
|
||||
mState = null;
|
||||
mAliasList = null;
|
||||
}
|
||||
|
||||
public void init()
|
||||
{
|
||||
mProtocol = new JLabel( "MPT1327" );
|
||||
mProtocol.setFont( mFontDecoder );
|
||||
mProtocol.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 );
|
||||
|
||||
mStateLabel = new JLabel( mChannel.getProcessingChain().
|
||||
getChannelState().getState().getDisplayValue() );
|
||||
mStateLabel.setFont( mFontDecoder );
|
||||
mStateLabel.setForeground( mColorLabelDecoder );
|
||||
|
||||
mTalkgroup = new JLabel( mState.getFromTalkgroup() );
|
||||
mTalkgroup.setFont( mFontDecoder );
|
||||
mTalkgroup.setForeground( mColorLabelDecoder );
|
||||
|
||||
mTalkgroupAlias = new JLabel();
|
||||
mTalkgroupAlias.setFont( mFontDecoder );
|
||||
mTalkgroupAlias.setForeground( mColorLabelDecoder );
|
||||
|
||||
Alias alias = getTalkgroupAlias( mState.getFromTalkgroup() );
|
||||
|
||||
if( alias != null )
|
||||
{
|
||||
mTalkgroupAlias.setText( alias.getName() );
|
||||
mTalkgroupAlias.setIcon( getIcon( alias ) );
|
||||
}
|
||||
|
||||
mSiteLabel = new JLabel( "Site:" );
|
||||
mSiteLabel.setFont( mFontDetails );
|
||||
mSiteLabel.setForeground( mColorLabelDetails );
|
||||
|
||||
mSite = new JLabel( String.valueOf( mState.getSite() ) );
|
||||
mSite.setFont( mFontDecoder );
|
||||
mSite.setForeground( mColorLabelDecoder );
|
||||
|
||||
mSiteAliasLabel = new JLabel();
|
||||
mSiteAliasLabel.setFont( mFontDecoder );
|
||||
mSiteAliasLabel.setForeground( mColorLabelDecoder );
|
||||
|
||||
Alias siteAlias = getSiteAlias( mState.getSite() );
|
||||
|
||||
if( siteAlias != null )
|
||||
{
|
||||
mSiteAliasLabel.setText( siteAlias.getName() );
|
||||
}
|
||||
|
||||
add( mProtocol );
|
||||
add( mSourceLabel );
|
||||
add( mChannelLabel, "wrap" );
|
||||
|
||||
add( mStateLabel );
|
||||
add( mTalkgroup );
|
||||
add( mTalkgroupAlias, "wrap" );
|
||||
|
||||
add( mSiteLabel );
|
||||
add( mSite );
|
||||
add( mSiteAliasLabel, "wrap" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( final ChangedAttribute changedAttribute )
|
||||
{
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
switch( changedAttribute )
|
||||
{
|
||||
case CHANNEL_STATE:
|
||||
mStateLabel.setText( mState.getState().getDisplayValue() );
|
||||
break;
|
||||
case CHANNEL_SITE_NUMBER:
|
||||
mSite.setText( String.valueOf( mState.getSite() ) );
|
||||
|
||||
Alias siteAlias = getSiteAlias( mState.getSite() );
|
||||
|
||||
if( siteAlias != null )
|
||||
{
|
||||
mSiteAliasLabel.setText( siteAlias.getName() );
|
||||
}
|
||||
repaint();
|
||||
break;
|
||||
case FROM_TALKGROUP:
|
||||
mTalkgroup.setText( mState.getFromTalkgroup() );
|
||||
|
||||
Alias alias = getTalkgroupAlias( mState.getFromTalkgroup() );
|
||||
|
||||
if( alias != null )
|
||||
{
|
||||
mTalkgroupAlias.setText( alias.getName() );
|
||||
mTalkgroupAlias.setIcon( getIcon( alias ) );
|
||||
}
|
||||
repaint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
private ImageIcon getIcon( Alias alias )
|
||||
{
|
||||
if( alias != null )
|
||||
{
|
||||
String iconName = alias.getIconName();
|
||||
|
||||
if( iconName != null )
|
||||
{
|
||||
return getSettingsManager().getImageIcon( iconName, 12 );
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Alias getSiteAlias( int site )
|
||||
{
|
||||
if( site != 0 && mAliasList != null )
|
||||
{
|
||||
return mAliasList.getSiteID( site );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Alias getTalkgroupAlias( String talkgroup )
|
||||
{
|
||||
if( talkgroup != null && mAliasList != null )
|
||||
{
|
||||
return mAliasList.getTalkgroupAlias( talkgroup );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trafficChannelAdded( Channel traffic )
|
||||
{
|
||||
if( !getTrafficPanels().keySet().contains( traffic ) )
|
||||
{
|
||||
MPT1327TrafficPanel panel =
|
||||
new MPT1327TrafficPanel( traffic );
|
||||
|
||||
getTrafficPanels().put( traffic, panel );
|
||||
|
||||
addPanel( panel );
|
||||
}
|
||||
}
|
||||
|
||||
@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( mProtocol != null )
|
||||
{
|
||||
mProtocol.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mStateLabel != null )
|
||||
{
|
||||
mStateLabel.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mTalkgroup != null )
|
||||
{
|
||||
mTalkgroup.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mTalkgroupAlias != null )
|
||||
{
|
||||
mTalkgroupAlias.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mSite != null )
|
||||
{
|
||||
mSite.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mSiteAliasLabel != null )
|
||||
{
|
||||
mSiteAliasLabel.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
break;
|
||||
case CHANNEL_STATE_LABEL_DETAILS:
|
||||
if( mSourceLabel != null )
|
||||
{
|
||||
mSourceLabel.setForeground( mColorLabelDetails );
|
||||
}
|
||||
if( mChannelLabel != null )
|
||||
{
|
||||
mChannelLabel.setForeground( mColorLabelDetails );
|
||||
}
|
||||
if( mSiteLabel != null )
|
||||
{
|
||||
mSiteLabel.setForeground( mColorLabelDetails );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
258
src/decode/mpt1327/MPT1327TrafficPanel.java
Normal file
258
src/decode/mpt1327/MPT1327TrafficPanel.java
Normal file
@@ -0,0 +1,258 @@
|
||||
/*******************************************************************************
|
||||
* 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.mpt1327;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import settings.ColorSetting;
|
||||
import settings.Setting;
|
||||
import alias.Alias;
|
||||
import alias.AliasList;
|
||||
import controller.channel.Channel;
|
||||
import controller.channel.ProcessingChain;
|
||||
import controller.state.ChannelState.ChangedAttribute;
|
||||
import controller.state.ChannelStatePanel;
|
||||
|
||||
public class MPT1327TrafficPanel extends ChannelStatePanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private JLabel mProtocolLabel;
|
||||
private JLabel mFrequencyLabel;
|
||||
private JLabel mChannelLabel;
|
||||
|
||||
private JLabel mStateLabel;
|
||||
private JLabel mFromTalkgroupLabel;
|
||||
private JLabel mFromTalkgroupAliasLabel;
|
||||
|
||||
private JLabel mToLabel;
|
||||
private JLabel mToTalkgroupLabel;
|
||||
private JLabel mToTalkgroupAliasLabel;
|
||||
|
||||
private AliasList mAliasList;
|
||||
|
||||
public MPT1327TrafficPanel( Channel channel )
|
||||
{
|
||||
super( channel );
|
||||
|
||||
mAliasList = channel.getProcessingChain().getChannelState().getAliasList();
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
public void dispose()
|
||||
{
|
||||
super.dispose();
|
||||
mAliasList = null;
|
||||
}
|
||||
|
||||
public void init()
|
||||
{
|
||||
mProtocolLabel = new JLabel( "MPT1327" );
|
||||
mProtocolLabel.setFont( mFontDecoder );
|
||||
mProtocolLabel.setForeground( mColorLabelDecoder );
|
||||
|
||||
mFrequencyLabel = new JLabel( mChannel.getSourceConfiguration().getDescription() );
|
||||
mFrequencyLabel.setFont( mFontDetails );
|
||||
mFrequencyLabel.setForeground( mColorLabelDetails );
|
||||
|
||||
MPT1327ChannelState state =
|
||||
(MPT1327ChannelState)mChannel.getProcessingChain().getChannelState();
|
||||
int channelNumber = state.getChannelNumber();
|
||||
|
||||
mChannelLabel = new JLabel( "Channel " + channelNumber );
|
||||
mChannelLabel.setFont( mFontDetails );
|
||||
mChannelLabel.setForeground( mColorLabelDetails );
|
||||
|
||||
mStateLabel = new JLabel( mChannel.getProcessingChain().
|
||||
getChannelState().getState().getDisplayValue() );
|
||||
mStateLabel.setFont( mFontDecoder );
|
||||
mStateLabel.setForeground( mColorLabelDecoder );
|
||||
|
||||
mFromTalkgroupLabel = new JLabel( state.getFromTalkgroup() );
|
||||
mFromTalkgroupLabel.setFont( mFontDecoder );
|
||||
mFromTalkgroupLabel.setForeground( mColorLabelDecoder );
|
||||
|
||||
mFromTalkgroupAliasLabel = new JLabel();
|
||||
mFromTalkgroupAliasLabel.setFont( mFontDecoder );
|
||||
mFromTalkgroupAliasLabel.setForeground( mColorLabelDecoder );
|
||||
|
||||
if( mAliasList != null )
|
||||
{
|
||||
Alias alias = mAliasList.getMPT1327Alias( state.getFromTalkgroup() );
|
||||
|
||||
if( alias != null )
|
||||
{
|
||||
mFromTalkgroupAliasLabel.setText( alias.getName() );
|
||||
|
||||
mFromTalkgroupAliasLabel.setIcon( getChannel()
|
||||
.getResourceManager().getSettingsManager()
|
||||
.getImageIcon( alias.getIconName(), 12 ) );
|
||||
}
|
||||
}
|
||||
|
||||
mToLabel = new JLabel( "TO:" );
|
||||
mToLabel.setFont( mFontDetails );
|
||||
mToLabel.setForeground( mColorLabelDetails );
|
||||
|
||||
mToTalkgroupLabel = new JLabel( state.getFromTalkgroup() );
|
||||
mToTalkgroupLabel.setFont( mFontDecoder );
|
||||
mToTalkgroupLabel.setForeground( mColorLabelDecoder );
|
||||
|
||||
mToTalkgroupAliasLabel = new JLabel();
|
||||
mToTalkgroupAliasLabel.setFont( mFontDecoder );
|
||||
mToTalkgroupAliasLabel.setForeground( mColorLabelDecoder );
|
||||
|
||||
if( mAliasList != null )
|
||||
{
|
||||
Alias alias = mAliasList.getMPT1327Alias( state.getFromTalkgroup() );
|
||||
|
||||
if( alias != null )
|
||||
{
|
||||
mToTalkgroupAliasLabel.setText( alias.getName() );
|
||||
|
||||
mToTalkgroupAliasLabel.setIcon( getChannel()
|
||||
.getResourceManager().getSettingsManager()
|
||||
.getImageIcon( alias.getIconName(), 12 ) );
|
||||
}
|
||||
}
|
||||
|
||||
add( mProtocolLabel );
|
||||
add( mFrequencyLabel );
|
||||
add( mChannelLabel, "wrap" );
|
||||
|
||||
add( mStateLabel );
|
||||
add( mFromTalkgroupLabel );
|
||||
add( mFromTalkgroupAliasLabel, "wrap" );
|
||||
|
||||
add( mToLabel );
|
||||
add( mToTalkgroupLabel );
|
||||
add( mToTalkgroupAliasLabel, "wrap" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( final ChangedAttribute changedAttribute )
|
||||
{
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
ProcessingChain chain = getChannel().getProcessingChain();
|
||||
|
||||
if( chain != null )
|
||||
{
|
||||
final MPT1327ChannelState state =
|
||||
(MPT1327ChannelState)chain.getChannelState();
|
||||
|
||||
if( state != null )
|
||||
{
|
||||
switch( changedAttribute )
|
||||
{
|
||||
case CHANNEL_STATE:
|
||||
mStateLabel.setText( state.getState().getDisplayValue() );
|
||||
break;
|
||||
case CHANNEL_NUMBER:
|
||||
mChannelLabel.setText( "Channel " + state.getChannelNumber() );
|
||||
break;
|
||||
case FROM_TALKGROUP:
|
||||
String talkgroup = state.getFromTalkgroup();
|
||||
|
||||
mFromTalkgroupLabel.setText( talkgroup );
|
||||
|
||||
if( mAliasList != null )
|
||||
{
|
||||
Alias alias = mAliasList.getMPT1327Alias( talkgroup );
|
||||
|
||||
if( alias != null )
|
||||
{
|
||||
mFromTalkgroupAliasLabel.setText( alias.getName() );
|
||||
|
||||
String icon = alias.getIconName();
|
||||
|
||||
mFromTalkgroupAliasLabel.setIcon( getChannel()
|
||||
.getResourceManager().getSettingsManager()
|
||||
.getImageIcon( icon, 12 ) );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TO_TALKGROUP:
|
||||
String toTalkgroup = state.getToTalkgroup();
|
||||
|
||||
mToTalkgroupLabel.setText( toTalkgroup );
|
||||
|
||||
if( mAliasList != null )
|
||||
{
|
||||
Alias alias = mAliasList.getMPT1327Alias( toTalkgroup );
|
||||
|
||||
if( alias != null )
|
||||
{
|
||||
mToTalkgroupAliasLabel.setText( alias.getName() );
|
||||
|
||||
String icon = alias.getIconName();
|
||||
|
||||
if( icon != null )
|
||||
{
|
||||
mToTalkgroupAliasLabel.setIcon( getChannel()
|
||||
.getResourceManager().getSettingsManager()
|
||||
.getImageIcon( icon, 12 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
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:
|
||||
mProtocolLabel.setForeground( mColorLabelDecoder );
|
||||
mStateLabel.setForeground( mColorLabelDecoder );
|
||||
mFromTalkgroupLabel.setForeground( mColorLabelDecoder );
|
||||
mFromTalkgroupAliasLabel.setForeground( mColorLabelDecoder );
|
||||
mToTalkgroupLabel.setForeground( mColorLabelDecoder );
|
||||
mToTalkgroupAliasLabel.setForeground( mColorLabelDecoder );
|
||||
break;
|
||||
case CHANNEL_STATE_LABEL_DETAILS:
|
||||
mFrequencyLabel.setForeground( mColorLabelDetails );
|
||||
mChannelLabel.setForeground( mColorLabelDetails );
|
||||
mToLabel.setForeground( mColorLabelDetails );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
69
src/decode/nbfm/NBFMChannelState.java
Normal file
69
src/decode/nbfm/NBFMChannelState.java
Normal file
@@ -0,0 +1,69 @@
|
||||
/*******************************************************************************
|
||||
* 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.nbfm;
|
||||
|
||||
import alias.AliasList;
|
||||
import audio.SquelchListener;
|
||||
import audio.SquelchListener.SquelchState;
|
||||
import controller.channel.ProcessingChain;
|
||||
import controller.state.AuxChannelState;
|
||||
import controller.state.ChannelState;
|
||||
|
||||
/**
|
||||
* Conventional channel state - this class doesn't do anything at the moment,
|
||||
* it is a place holder for future expansion for the conventional decoder
|
||||
*/
|
||||
public class NBFMChannelState extends ChannelState
|
||||
{
|
||||
public NBFMChannelState( ProcessingChain channel, AliasList aliasList )
|
||||
{
|
||||
super( channel, aliasList );
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the ConventionalChannelState always unsquelched
|
||||
*/
|
||||
public void addListener( SquelchListener listener )
|
||||
{
|
||||
super.addListener( listener );
|
||||
|
||||
super.setSquelchState( SquelchState.UNSQUELCH );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSquelchState( SquelchState state )
|
||||
{
|
||||
//do nothing ... we want the squelch state always unsquelched (for now)
|
||||
}
|
||||
@Override
|
||||
public String getActivitySummary()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "Activity Summary\n" );
|
||||
sb.append( "\tDecoder:\tNBFM\n\n" );
|
||||
|
||||
for( AuxChannelState state: mAuxChannelStates )
|
||||
{
|
||||
sb.append( state.getActivitySummary() );
|
||||
sb.append( "\n\n" );
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
77
src/decode/nbfm/NBFMConventionalDecoder.java
Normal file
77
src/decode/nbfm/NBFMConventionalDecoder.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*******************************************************************************
|
||||
* 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.nbfm;
|
||||
|
||||
import source.Source.SampleType;
|
||||
import decode.Decoder;
|
||||
import decode.DecoderType;
|
||||
import dsp.filter.DCRemovalFilter2;
|
||||
import dsp.nbfm.FilteringNBFMDemodulator;
|
||||
|
||||
public class NBFMConventionalDecoder 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;
|
||||
|
||||
private FilteringNBFMDemodulator mDemodulator;
|
||||
private DCRemovalFilter2 mDCRemovalFilter;
|
||||
|
||||
public NBFMConventionalDecoder( SampleType sampleType )
|
||||
{
|
||||
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 float
|
||||
* 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
|
||||
*/
|
||||
mDemodulator = new FilteringNBFMDemodulator();
|
||||
this.addComplexListener( mDemodulator );
|
||||
|
||||
/**
|
||||
* Remove the DC component that is present when we're mistuned
|
||||
*/
|
||||
mDCRemovalFilter = new DCRemovalFilter2( sDC_REMOVAL_RATIO );
|
||||
mDemodulator.addListener( mDCRemovalFilter );
|
||||
|
||||
/**
|
||||
* Route the demodulated, filtered samples back to this class to send
|
||||
* to all registered listeners
|
||||
*/
|
||||
mDCRemovalFilter.setListener( this.getFloatReceiver() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecoderType getType()
|
||||
{
|
||||
return DecoderType.NBFM;
|
||||
}
|
||||
}
|
||||
31
src/decode/nbfm/NBFMEditor.java
Normal file
31
src/decode/nbfm/NBFMEditor.java
Normal 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.nbfm;
|
||||
|
||||
import decode.DecodeEditor;
|
||||
import decode.config.DecodeConfiguration;
|
||||
|
||||
public class NBFMEditor extends DecodeEditor
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public NBFMEditor( DecodeConfiguration config )
|
||||
{
|
||||
super( config );
|
||||
}
|
||||
}
|
||||
136
src/decode/nbfm/NBFMPanel.java
Normal file
136
src/decode/nbfm/NBFMPanel.java
Normal file
@@ -0,0 +1,136 @@
|
||||
/*******************************************************************************
|
||||
* 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.nbfm;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
|
||||
import settings.ColorSetting;
|
||||
import settings.Setting;
|
||||
import controller.channel.Channel;
|
||||
import controller.state.ChannelState.ChangedAttribute;
|
||||
import controller.state.ChannelStatePanel;
|
||||
|
||||
public class NBFMPanel extends ChannelStatePanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private JLabel mStateLabel;
|
||||
private JLabel mSourceLabel;
|
||||
private JLabel mChannelLabel;
|
||||
|
||||
private JLabel mProtocol = new JLabel( "NBFM" );
|
||||
|
||||
public NBFMPanel( Channel channel )
|
||||
{
|
||||
super( channel );
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
add( mStateLabel );
|
||||
add( mSourceLabel );
|
||||
add( mChannelLabel, "wrap" );
|
||||
|
||||
add( mProtocol, "wrap" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( final ChangedAttribute changedAttribute )
|
||||
{
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
switch( changedAttribute )
|
||||
{
|
||||
case CHANNEL_STATE:
|
||||
mStateLabel.setText( mChannel.getProcessingChain().
|
||||
getChannelState().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;
|
||||
}
|
||||
|
||||
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( mStateLabel != null )
|
||||
{
|
||||
mStateLabel.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
if( mProtocol != null )
|
||||
{
|
||||
mProtocol.setForeground( mColorLabelDecoder );
|
||||
}
|
||||
break;
|
||||
case CHANNEL_STATE_LABEL_DETAILS:
|
||||
if( mSourceLabel != null )
|
||||
{
|
||||
mSourceLabel.setForeground( mColorLabelDetails );
|
||||
}
|
||||
if( mChannelLabel != null )
|
||||
{
|
||||
mChannelLabel.setForeground( mColorLabelDetails );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
233
src/decode/passport/PassportActivitySummary.java
Normal file
233
src/decode/passport/PassportActivitySummary.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
71
src/decode/passport/PassportBand.java
Normal file
71
src/decode/passport/PassportBand.java
Normal 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;
|
||||
}
|
||||
}
|
||||
164
src/decode/passport/PassportCallEvent.java
Normal file
164
src/decode/passport/PassportCallEvent.java
Normal 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 );
|
||||
}
|
||||
|
||||
}
|
||||
236
src/decode/passport/PassportChannelState.java
Normal file
236
src/decode/passport/PassportChannelState.java
Normal 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 );
|
||||
}
|
||||
}
|
||||
108
src/decode/passport/PassportDecoder.java
Normal file
108
src/decode/passport/PassportDecoder.java
Normal 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;
|
||||
}
|
||||
}
|
||||
31
src/decode/passport/PassportEditor.java
Normal file
31
src/decode/passport/PassportEditor.java
Normal 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 );
|
||||
}
|
||||
}
|
||||
634
src/decode/passport/PassportMessage.java
Normal file
634
src/decode/passport/PassportMessage.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
109
src/decode/passport/PassportMessageProcessor.java
Normal file
109
src/decode/passport/PassportMessageProcessor.java
Normal 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
255
src/decode/passport/PassportPanel.java
Normal file
255
src/decode/passport/PassportPanel.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user