Adds new LJ1200 decoder for potential FSK signals that may be operating on 173.075 in the US

This commit is contained in:
dennis.sheirer
2014-11-16 00:47:41 +00:00
parent 20c9ae329b
commit 50a84b76b8
11 changed files with 827 additions and 1 deletions

View File

@@ -38,6 +38,14 @@ public enum SyncPattern
true, true, true, false, //1110 0xE
true, false, true, true //1011 0xB
} ),
LJ1200( new boolean[]
{
true, false, true, false,
true, false, true, false,
true, true, true, true,
false, false, false, false
}),
/**
* Note: we use a truncated portion of the NRZ-I encoded sync pattern.

View File

@@ -43,6 +43,8 @@ import decode.am.AMChannelState;
import decode.am.AMPanel;
import decode.fleetsync2.FleetsyncChannelState;
import decode.fleetsync2.FleetsyncPanel;
import decode.lj1200.LJ1200ChannelState;
import decode.lj1200.LJ1200Panel;
import decode.ltrnet.LTRNetChannelState;
import decode.ltrnet.LTRNetPanel;
import decode.ltrstandard.LTRChannelState;
@@ -172,6 +174,13 @@ public class ChannelStateList extends JPanel implements ChannelEventListener
panel.addPanel( mdsPanel );
}
else if( aux instanceof LJ1200ChannelState )
{
LJ1200Panel ljPanel = new LJ1200Panel( mSettingsManager,
(LJ1200ChannelState)aux );
panel.addPanel( ljPanel );
}
}
}
else if( state instanceof LTRChannelState )

View File

@@ -28,10 +28,11 @@ 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.lj1200.LJ1200ChannelState;
import decode.lj1200.LJ1200Decoder;
import decode.ltrnet.LTRNetChannelState;
import decode.ltrnet.LTRNetDecoder;
import decode.ltrstandard.LTRChannelState;
@@ -121,6 +122,9 @@ public class DecoderFactory
retVal.addAuxiliaryDecoder(
new MDCDecoder( aliasList ) );
break;
case LJ_1200:
retVal.addAuxiliaryDecoder(
new LJ1200Decoder( aliasList ) );
}
}
}
@@ -184,10 +188,15 @@ public class DecoderFactory
retVal.addAuxChannelState(
new FleetsyncChannelState( retVal ) );
break;
case LJ_1200:
retVal.addAuxChannelState(
new LJ1200ChannelState( retVal ) );
break;
case MDC1200:
retVal.addAuxChannelState(
new MDCChannelState( retVal ) );
break;
}
}
}

View File

@@ -27,6 +27,7 @@ public enum DecoderType
{
AM( "AM", "AM", "images/am", 10000 ),
FLEETSYNC2( "Fleetsync II", "Fsync2", "images/fm", 12500 ),
LJ_1200( "LJ1200 173.075", "LJ1200", "images/lj1200", 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 ),
@@ -69,6 +70,7 @@ public enum DecoderType
public static EnumSet<DecoderType> getInstrumentableDecoders()
{
return EnumSet.of( DecoderType.FLEETSYNC2,
DecoderType.LJ_1200,
DecoderType.LTR_NET,
DecoderType.MDC1200,
DecoderType.MPT1327,
@@ -84,6 +86,7 @@ public enum DecoderType
ArrayList<DecoderType> decoders = new ArrayList<DecoderType>();
decoders.add( DecoderType.FLEETSYNC2 );
decoders.add( DecoderType.LJ_1200 );
decoders.add( DecoderType.MDC1200 );
return decoders;

View 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.lj1200;
import message.Message;
import sample.Listener;
import alias.AliasList;
import controller.activity.ActivitySummaryProvider;
public class LJ1200ActivitySummary implements ActivitySummaryProvider,
Listener<Message>
{
private AliasList mAliasList;
public LJ1200ActivitySummary( AliasList list )
{
mAliasList = list;
}
public void dispose()
{
}
@Override
public void receive( Message message )
{
if( message instanceof LJ1200Message )
{
LJ1200Message lj = ((LJ1200Message)message);
//Do something here in the future
}
}
@Override
public String getSummary()
{
StringBuilder sb = new StringBuilder();
sb.append( "=============================\n" );
sb.append( "Decoder:\tLJ-1200I\n\n" );
return sb.toString();
}
}

View File

@@ -0,0 +1,139 @@
/*******************************************************************************
* 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.lj1200;
import alias.Alias;
import alias.AliasList;
import controller.activity.CallEvent;
import decode.DecoderType;
public class LJ1200CallEvent extends CallEvent
{
public LJ1200CallEvent( CallEventType callEventType,
AliasList aliasList,
String fromID,
String toID,
String details )
{
super( DecoderType.LJ_1200, 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 long 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 LJ1200CallEvent build()
{
return new LJ1200CallEvent( this );
}
}
/**
* Private constructor for the builder
*/
private LJ1200CallEvent( Builder builder )
{
this( builder.mCallEventType,
builder.mAliasList,
builder.mFromID,
builder.mToID,
builder.mDetails );
}
public static LJ1200CallEvent getLJ1200Event( LJ1200Message message )
{
CallEventType type = CallEventType.UNKNOWN;
StringBuilder sbDetails = new StringBuilder();
sbDetails.append( "LJ-1200 " );
return new LJ1200CallEvent.Builder( type )
.details( sbDetails.toString() )
.build();
}
}

View File

@@ -0,0 +1,65 @@
/*******************************************************************************
* 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.lj1200;
import message.Message;
import controller.state.AuxChannelState;
import controller.state.ChannelState;
import controller.state.ChannelState.State;
public class LJ1200ChannelState extends AuxChannelState
{
private LJ1200ActivitySummary mActivitySummary;
public LJ1200ChannelState( ChannelState parentState )
{
super( parentState );
mActivitySummary =
new LJ1200ActivitySummary( parentState.getAliasList() );
}
@Override
public void reset()
{
}
@Override
public void fade()
{
}
@Override
public void receive( Message message )
{
if( message instanceof LJ1200Message )
{
mActivitySummary.receive( message );
/* 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 );
}
}
@Override
public String getActivitySummary()
{
return mActivitySummary.getSummary();
}
}

View 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.lj1200;
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.real.RealSampleListener;
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;
/**
* LJ1200 - 1200 baud 2FSK decoder
*/
public class LJ1200Decoder extends Decoder implements Instrumentable
{
/* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */
private static final int DECIMATED_SAMPLE_RATE = 24000;
/* Baud or Symbol Rate */
private static final int SYMBOL_RATE = 1200;
/* Message length - 16-bit sync plus 64 bit message */
private static final int MESSAGE_LENGTH = 80;
/* 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 LJ1200MessageProcessor mMessageProcessor;
public LJ1200Decoder( AliasList aliasList )
{
super( SampleType.REAL );
mDecimationFilter = new FloatHalfBandFilter(
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
addRealSampleListener( mDecimationFilter );
mBandPassFilter = new FloatFIRFilter(
Filters.FIRBP_1200FSK_24000FS.getCoefficients(), 1.02 );
mDecimationFilter.setListener( mBandPassFilter );
mFSKDecoder = new FSK2Decoder( DECIMATED_SAMPLE_RATE,
SYMBOL_RATE, Output.INVERTED );
mBandPassFilter.setListener( mFSKDecoder );
mMessageFramer = new MessageFramer(
SyncPattern.LJ1200.getPattern(), MESSAGE_LENGTH );
mFSKDecoder.setListener( mMessageFramer );
mMessageProcessor = new LJ1200MessageProcessor( 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 RealSampleListener getRealReceiver()
{
return (RealSampleListener)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;
addRealSampleListener( 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;
removeRealListener( inputTap );
break;
case INSTRUMENT_BANDPASS_FILTER_TO_FSK2_DEMOD:
mBandPassFilter.setListener( mFSKDecoder );
break;
case INSTRUMENT_FSK2_DECODER_TO_MESSAGE_FRAMER:
mFSKDecoder.setListener( mMessageFramer );
break;
}
}
@Override
public void addUnfilteredRealSampleListener( RealSampleListener listener )
{
//Not implemented
}
}

View 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.lj1200;
import java.text.SimpleDateFormat;
import java.util.BitSet;
import java.util.Date;
import map.Plottable;
import message.Message;
import alias.Alias;
import alias.AliasList;
import bits.BitSetBuffer;
import crc.CRC;
import crc.CRCFleetsync;
public class LJ1200Message extends Message
{
public static int[] SYNC = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
public static int[] MESSAGE = { 16,17,18,19,20,21,22,23,
24,25,26,27,28,29,30,31,
32,33,34,35,36,37,38,39,
40,41,42,43,44,45,46,47,
48,49,50,51,52,53,54,55,
56,57,58,59,60,61,62,63 };
public static int[] CRC = { 64,65,66,67,68,69,70,71,
72,73,74,75,76,77,78,79 };
private static SimpleDateFormat mSDF = new SimpleDateFormat( "yyyyMMdd HHmmss" );
private BitSetBuffer mMessage;
private AliasList mAliasList;
private CRC mCRC;
public LJ1200Message( BitSetBuffer message, AliasList list )
{
mMessage = message;
mAliasList = list;
checkParity();
}
private void checkParity()
{
mCRC = detectAndCorrect( 16, 79 );
}
private CRC detectAndCorrect( int start, int end )
{
BitSet original = mMessage.get( start, end );
CRC crc = CRCFleetsync.check( original );
if( crc == crc.FAILED_PARITY )
{
int[] errorBitPositions = CRCFleetsync.findBitErrors( original );
if( errorBitPositions != null )
{
for( int errorBitPosition: errorBitPositions )
{
mMessage.flip( start + errorBitPosition );
}
crc = crc.CORRECTED;
}
}
return crc;
}
public boolean isValid()
{
return true;
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( mSDF.format( new Date( System.currentTimeMillis() ) ) );
sb.append( " LJ1200 [" );
sb.append( mMessage.toString() );
sb.append( "]" );
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();
}
@Override
public String getBinaryMessage()
{
return mMessage.toString();
}
@Override
public String getProtocol()
{
return "LJ 1200";
}
@Override
public String getEventType()
{
return "MESSAGE";
}
@Override
public String getFromID()
{
return null;
}
@Override
public Alias getFromIDAlias()
{
return null;
}
@Override
public String getToID()
{
return null;
}
@Override
public Alias getToIDAlias()
{
return null;
}
@Override
public String getMessage()
{
return toString();
}
@Override
public String getErrorStatus()
{
return "UNKNOWN";
}
@Override
public Plottable getPlottable()
{
return null;
}
}

View 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.lj1200;
import message.Message;
import sample.Broadcaster;
import sample.Listener;
import alias.AliasList;
import bits.BitSetBuffer;
public class LJ1200MessageProcessor implements Listener<BitSetBuffer>
{
private Broadcaster<Message> mBroadcaster = new Broadcaster<Message>();
private AliasList mAliasList;
public LJ1200MessageProcessor( AliasList list )
{
mAliasList = list;
}
public void dispose()
{
mBroadcaster.dispose();
}
@Override
public void receive( BitSetBuffer buffer )
{
LJ1200Message message = new LJ1200Message( buffer, mAliasList );
mBroadcaster.receive( message );
}
public void addMessageListener( Listener<Message> listener )
{
mBroadcaster.addListener( listener );
}
public void removeMessageListener( Listener<Message> listener )
{
mBroadcaster.removeListener( listener );
}
}

View File

@@ -0,0 +1,84 @@
/*******************************************************************************
* 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.lj1200;
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 LJ1200Panel extends AuxStatePanel
{
private static final long serialVersionUID = 1L;
private static final String PROTOCOL = "LJ-1200";
private JLabel mProtocol = new JLabel( PROTOCOL );
public LJ1200Panel( SettingsManager settingsManager, LJ1200ChannelState state )
{
super( settingsManager, state );
init();
}
public void dispose()
{
super.dispose();
}
public LJ1200ChannelState getState()
{
return (LJ1200ChannelState)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( mProtocol, false );
}
@Override
public void receive( final ChangedAttribute changedAttribute )
{
}
@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 )
{
mProtocol.setForeground( color.getColor() );
}
}
}
}