#1573 Adds Digital Coded Squelch (DCS) auxiliary decoder support. (#1574)

Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
Denny Sheirer
2023-06-19 05:13:58 -04:00
committed by GitHub
parent f792876c0a
commit 366eacd234
11 changed files with 967 additions and 39 deletions

View File

@@ -39,6 +39,9 @@ import io.github.dsheirer.module.decode.am.AMDecoderState;
import io.github.dsheirer.module.decode.am.DecodeConfigAM;
import io.github.dsheirer.module.decode.config.AuxDecodeConfiguration;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.dcs.DCSDecoder;
import io.github.dsheirer.module.decode.dcs.DCSDecoderState;
import io.github.dsheirer.module.decode.dcs.DCSMessageFilter;
import io.github.dsheirer.module.decode.dmr.DMRDecoder;
import io.github.dsheirer.module.decode.dmr.DMRDecoderState;
import io.github.dsheirer.module.decode.dmr.DMRTrafficChannelManager;
@@ -456,6 +459,10 @@ public class DecoderFactory
{
switch(auxDecoder)
{
case DCS:
modules.add(new DCSDecoder());
modules.add(new DCSDecoderState());
break;
case FLEETSYNC2:
modules.add(new Fleetsync2Decoder());
modules.add(new Fleetsync2DecoderState());
@@ -516,6 +523,9 @@ public class DecoderFactory
switch(decoder)
{
case DCS:
filters.add(new DCSMessageFilter());
break;
case FLEETSYNC2:
filters.add(new FleetsyncMessageFilter());
break;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -19,7 +19,6 @@
package io.github.dsheirer.module.decode;
import io.github.dsheirer.protocol.Protocol;
import java.util.EnumSet;
/**
@@ -39,6 +38,7 @@ public enum DecoderType
P25_PHASE2("P25 Phase 2", "P25-2", Protocol.APCO25_PHASE2),
//Auxiliary Decoders
DCS("Digital Coded Squelch (DCS)", "DCS", Protocol.DCS),
FLEETSYNC2("Fleetsync II", "Fleetsync2", Protocol.FLEETSYNC),
LJ_1200("LJ1200 173.075", "LJ1200", Protocol.LOJACK),
MDC1200("MDC1200", "MDC1200", Protocol.MDC1200),
@@ -73,7 +73,8 @@ public enum DecoderType
* Auxiliary decoders that operate on in-band signalling in the decoded audio channel
*/
public static final EnumSet<DecoderType> AUX_DECODERS =
EnumSet.of(DecoderType.FLEETSYNC2,
EnumSet.of(DecoderType.DCS,
DecoderType.FLEETSYNC2,
DecoderType.LJ_1200,
DecoderType.MDC1200,
DecoderType.TAIT_1200);

View File

@@ -121,6 +121,7 @@ public abstract class AnalogDecoderState extends DecoderState implements ISource
startCallEvent();
}
getIdentifierCollection().update(getTalkgroupIdentifier());
mDecodeEvent.update(System.currentTimeMillis());
broadcast(mDecodeEvent);
}

View File

@@ -0,0 +1,281 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.dcs;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
/**
* DCS codes as defined in ETSI TS 103 236 V1.1.1
*
* Code values are the bit reversed integer values of the 23-bit binary sequence representation. The values are bit
* reversed for normal (N) codes and as specified in the ETSI chart for the inverted (I) codes to align with the
* transmitted format.
*/
public enum DCSCode
{
//Inverted codes
I023(7747603),
I025(7043093),
I026(6674454),
I031(5371929),
I032(6248474),
I043(5990435),
I047(1038375),
I051(8169513),
I054(7292972),
I065(6101045),
I071(6789177),
I072(6895674),
I073(3041339),
I074(7632956),
I114(3532876),
I115(7518285),
I116(8132686),
I125(505941),
I131(4012121),
I132(3381338),
I134(3070044),
I143(3647587),
I152(2017386),
I155(4511853),
I156(4880494),
I162(7063666),
I165(3266677),
I172(391290),
I174(1620092),
I205(7248005),
I223(6875283),
I226(8063126),
I243(4569251),
I244(2074788),
I245(5830821),
I251(6453417),
I261(1538225),
I263(6195379),
I265(4442293),
I271(7948473),
I306(850118),
I311(3725513),
I315(7104717),
I331(2353369),
I343(2717923),
I346(3840230),
I351(964841),
I364(6838516),
I365(3082485),
I371(1411321),
I411(7825673),
I412(7981322),
I413(4102411),
I423(4954387),
I431(7100697),
I432(6486298),
I445(8096037),
I464(2615604),
I465(6338869),
I466(7215414),
I503(3959107),
I506(3115334),
I516(4307278),
I532(932186),
I546(1698150),
I565(817525),
I606(6134150),
I612(6756746),
I624(1005972),
I627(129431),
I631(7506329),
I632(8137114),
I654(4995500),
I662(2390450),
I664(3750324),
I703(2275779),
I712(776650),
I723(3770835),
I731(1984985),
I732(1108442),
I734(895452),
I743(1366499),
I754(2161132),
//Normal codes
N023(6557239),
N025(5508971),
N026(3411411),
N031(4984773),
N032(2887037),
N043(6425453),
N047(7474680),
N051(4852383),
N054(1706363),
N065(5639261),
N071(5115123),
N072(3018315),
N073(7211834),
N074(1969943),
N114(1641430),
N115(5836455),
N116(3738655),
N125(5574384),
N131(5049950),
N132(2952422),
N134(1904058),
N143(6490870),
N152(2820540),
N155(5967249),
N156(3870505),
N162(2558443),
N165(5705158),
N172(3084240),
N174(2035340),
N205(5278907),
N223(6589323),
N226(3442799),
N243(6459089),
N244(1215228),
N245(5410701),
N251(4886307),
N261(4624244),
N263(6719677),
N265(5671393),
N271(5146959),
N306(3248024),
N311(4820366),
N315(5868315),
N331(5082082),
N343(6524746),
N346(3378350),
N351(4951736),
N364(1543435),
N365(5736570),
N371(5212372),
N411(4737911),
N412(2640335),
N413(6835390),
N423(6573289),
N431(5000475),
N432(2903971),
N445(5392623),
N464(1461234),
N465(5656195),
N466(3558459),
N503(6376222),
N506(3229946),
N516(3755713),
N532(2969144),
N546(3361740),
N565(5721880),
N606(3198173),
N612(2673779),
N624(1363320),
N627(7655360),
N631(5032103),
N632(2935327),
N654(1756697),
N662(2543378),
N664(1494606),
N703(6409890),
N712(2739688),
N723(6670542),
N731(5097788),
N732(3001220),
N734(1952472),
N743(6540692),
N754(1822594),
UNKNOWN(0);
private int mValue;
DCSCode(int value)
{
mValue = value;
}
/**
* Inverted DCS codes
*/
private static final EnumSet<DCSCode> INVERTED_CODES = EnumSet.range(I023, I754);
/**
* Lookup map for quickly finding a DCS code from the transmitted value.
*/
private static final Map<Integer,DCSCode> CODE_MAP = new HashMap<>();
static
{
for(DCSCode code: EnumSet.range(I023, N754))
{
CODE_MAP.put(code.getValue(), code);
}
}
/**
* Integer value of the code from transmitted bits.
* @return value
*/
public int getValue()
{
return mValue;
}
/**
* Indicates if this is an inverted DCS code (true) or a normal DCS code (false).
* @return true if inverted.
*/
public boolean isInverted()
{
return INVERTED_CODES.contains(this);
}
@Override
public String toString()
{
return "DCS-" + name().substring(1, 4) + (isInverted() ? " Inverted" : "");
}
/**
* Indicates if the value maps to a known DCS code.
* @param value to test
* @return true if the value matches a DCS code.
*/
public static boolean hasValue(int value)
{
return CODE_MAP.containsKey(value);
}
/**
* Get the DCS code that matches the value, otherwise return UNKNOWN.
* @param value to lookup
* @return matching DCS code or UNKNOWN.
*/
public static DCSCode fromValue(int value)
{
DCSCode code = CODE_MAP.get(value);
if(code == null)
{
code = UNKNOWN;
}
return code;
}
}

View File

@@ -0,0 +1,293 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.dcs;
import io.github.dsheirer.dsp.filter.FilterFactory;
import io.github.dsheirer.dsp.filter.design.FilterDesignException;
import io.github.dsheirer.dsp.filter.fir.FIRFilterSpecification;
import io.github.dsheirer.dsp.filter.fir.real.IRealFilter;
import io.github.dsheirer.dsp.filter.fir.remez.RemezFIRFilterDesigner;
import io.github.dsheirer.module.decode.Decoder;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.real.IRealBufferListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Digital Coded Squelch (DCS) demodulator/decoder designed to work with FM demodulated 8 kHz input audio samples and
* decode 134.4-baud signalling present in the unfiltered demodulated audio stream.
*
* This decoder monitors the incoming sample stream looking for either a positive or negative sequence of samples where
* the slope of these values exceeds a threshold and causes the symbol state to flip. The symbol state remains across
* successive baud periods until the next opposing slope state change sequence is detected.
*
* This decoder uses a slope detector to decode the 134.4 bits per second stream and identify the DCS code. It is
* designed to work with 8 kHz audio sample rate. Each baud period is (8,000 / 134.4) = 59.52 samples per symbol and
* a full DCS tone sequence is 23 bits * 59.52 samples/bit = 1369 samples. The DCS tone sequence is continuously
* transmitted at a rate of 134.4 bits-per-second / 23 = 5.84 repeats per second while the source continues transmitting.
*/
public class DCSDecoder extends Decoder implements IRealBufferListener, Listener<float[]>
{
private static final Logger mLog = LoggerFactory.getLogger(DCSDecoder.class);
//Slope calculation sumXX value is always the same for 59 evenly spaced samples
private static final double SLOPE_CALCULATION_SUM_XX = 2247.5;
private static final float BAUD_LENGTH = 8000.0f / 134.4f;
private static final float SLOPE_THRESHOLD = 0.002750f;
private static final int MAX_ONES_SEQUENCE = 6;
private static final int OVERLAP = (int)Math.ceil(BAUD_LENGTH);
private static final int POST_TRANSITION_SAMPLES_TO_SKIP = 30;
private static final int SLOPE_CALCULATION_PERIOD = 30;
private static final int IDEAL_SYMBOL_TRANSITION_MIN = 11;
private static final int IDEAL_SYMBOL_TRANSITION_MAX = 19;
private static final int CODE_MASK = 0x7FFFFF; //23-bit mask
private static float[] sLowPassFilterCoefficients;
private boolean mSymbol = false;
private float mBaudCounter = 0f;
private float mMaxSlope = 0;
private float[] mResidual = new float[OVERLAP];
private int mCode = 0;
private int mExcessiveOneSequenceCounter = 0;
static
{
FIRFilterSpecification specification = FIRFilterSpecification.lowPassBuilder()
.sampleRate(8000)
.gridDensity(16)
.oddLength(true)
.passBandCutoff(200)
.passBandAmplitude(1.0)
.passBandRipple(0.01)
.stopBandStart(300)
.stopBandAmplitude(0.0)
.stopBandRipple(0.03) //Approximately 60 dB attenuation
.build();
try
{
RemezFIRFilterDesigner designer = new RemezFIRFilterDesigner(specification);
if(designer.isValid())
{
sLowPassFilterCoefficients = designer.getImpulseResponse();
}
}
catch(FilterDesignException fde)
{
mLog.error("Filter design error", fde);
}
}
private IRealFilter mLowPassFilter = FilterFactory.getRealFilter(sLowPassFilterCoefficients);
/**
* Constructs an instance
*/
public DCSDecoder()
{
}
/**
* Decoder type
*/
@Override
public DecoderType getDecoderType()
{
return DecoderType.DCS;
}
/**
* Implementation of the IRealBufferListener interface
*/
@Override
public Listener<float[]> getBufferListener()
{
return this::receive;
}
/**
* Processes the demodulated 8 kHz audio samples to extract the 300-baud LTR signalling and delivers the decoded
* symbol array to the registered listener.
* @param samples to demodulate.
*/
public void receive(float[] samples)
{
if(getMessageListener() != null)
{
float[] filtered = mLowPassFilter.filter(samples);
float[] buffer = new float[filtered.length + OVERLAP];
int samplesToSkip = 0;
int timingAdjust = 0;
float slope = 0;
try
{
System.arraycopy(mResidual, 0, buffer, 0, mResidual.length);
System.arraycopy(filtered, 0, buffer, mResidual.length, filtered.length);
System.arraycopy(filtered, filtered.length - OVERLAP, mResidual, 0, OVERLAP);
for(int bufferPointer = 0; bufferPointer < samples.length; bufferPointer++)
{
//Don't calculate slope if we're within the baud period following the last symbol transition
if(samplesToSkip > 0)
{
samplesToSkip--;
}
else
{
slope = calculateSlope(buffer, bufferPointer);
if(mSymbol)
{
//Look for crest of slope at lowest point below the negative slope threshold for transition
if(slope > mMaxSlope && mMaxSlope < -SLOPE_THRESHOLD)
{
mSymbol = false;
//Coarse symbol timing adjust - ideal toggle is middle of baud, between 11 & 19 samples
if(mBaudCounter < IDEAL_SYMBOL_TRANSITION_MIN)
{
timingAdjust = 1;
}
else if(mBaudCounter > IDEAL_SYMBOL_TRANSITION_MAX)
{
timingAdjust = -1;
}
else
{
timingAdjust = 0;
}
mBaudCounter += timingAdjust;
samplesToSkip = POST_TRANSITION_SAMPLES_TO_SKIP + timingAdjust;
}
else if(slope < mMaxSlope)
{
mMaxSlope = slope;
}
}
else
{
//Loop for crest of slope at the highest point above the positive threshold
if(slope < mMaxSlope && mMaxSlope > SLOPE_THRESHOLD)
{
mSymbol = true;
//Coarse symbol timing adjust - ideal toggle is middle of baud, between 11 & 19 samples
if(mBaudCounter < IDEAL_SYMBOL_TRANSITION_MIN)
{
timingAdjust = 1;
}
else if(mBaudCounter > IDEAL_SYMBOL_TRANSITION_MAX)
{
timingAdjust = -1;
}
else
{
timingAdjust = 0;
}
mBaudCounter += timingAdjust;
samplesToSkip = POST_TRANSITION_SAMPLES_TO_SKIP + timingAdjust;
}
else if(slope > mMaxSlope)
{
mMaxSlope = slope;
}
}
}
mBaudCounter++;
//Update the current code value once we've processed a baud/symbol period of samples
if(mBaudCounter > BAUD_LENGTH)
{
//Update the code value adding in the current symbol
mCode = (Integer.rotateLeft(mCode, 1) + (mSymbol ? 1 : 0)) & CODE_MASK;
if(DCSCode.hasValue(mCode))
{
getMessageListener().receive(new DCSMessage(DCSCode.fromValue(mCode), System.currentTimeMillis()));
}
mBaudCounter -= BAUD_LENGTH;
//Check for a continuous sequence of ones that exceeds the max (6) for any DCS code and
//flip/revert the symbol state back to zero.
if(mSymbol)
{
mExcessiveOneSequenceCounter++;
if(mExcessiveOneSequenceCounter > MAX_ONES_SEQUENCE)
{
mSymbol = false;
mMaxSlope = -1f;
mExcessiveOneSequenceCounter = 0;
}
}
else
{
mExcessiveOneSequenceCounter = 0;
}
}
}
}
catch(Exception e)
{
mLog.warn("Unexpected error while processing DCS samples", e);
}
}
}
/**
* Calculates the slope of the 30 samples in the values array starting at the specified offset. The baud period
* is slightly more than 59 samples, however the voltage state transition occurs over a 30-sample period.
*
* Note: code modified from Apache Commons Math library, SimpleRegression class.
* @param samples to load from
* @param offset to the sample start for the slope calculation
*/
public float calculateSlope(float[] samples, int offset)
{
/** mean of accumulated x & y values, used in updating formulas */
double xbar = 0;
double ybar = samples[offset];
// double sumXX = 0;
double sumXY = 0;
double fact1, dx, dy;
for(int x = 1; x < SLOPE_CALCULATION_PERIOD; x++)
{
fact1 = 1.0 + x;
// final double fact2 = x / (1.0 + x);
dx = x - xbar;
dy = samples[offset + x] - ybar;
// sumXX += dx * dx * fact2;
sumXY += dx * dy * (x / (1.0 + x));
xbar += dx / fact1;
ybar += dy / fact1;
}
//Transfer the sumXX value to SLOPE_CALCULATION_SUM_XX since it's a constant value
// mLog.info("SumXX: " + sumXX);
return (float)(sumXY / SLOPE_CALCULATION_SUM_XX);
}
}

View File

@@ -0,0 +1,123 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.dcs;
import io.github.dsheirer.channel.state.DecoderState;
import io.github.dsheirer.channel.state.DecoderStateEvent;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.DecoderType;
import java.util.HashMap;
import java.util.Map;
/**
* Maintains the decoder state from the stream of decoded DCS code messages.
*/
public class DCSDecoderState extends DecoderState
{
private Map<DCSCode,Integer> mDcsCodeCountsMap = new HashMap<>();
private DCSCode mCurrentCode = DCSCode.UNKNOWN;
/**
* Constructs an instance
*/
public DCSDecoderState()
{
}
@Override
public DecoderType getDecoderType()
{
return DecoderType.DCS;
}
@Override
public void receive(IMessage message)
{
if(message instanceof DCSMessage dcsMessage)
{
mCurrentCode = dcsMessage.getDCSCode();
if(!mDcsCodeCountsMap.containsKey(mCurrentCode))
{
mDcsCodeCountsMap.put(mCurrentCode, 1);
}
else
{
mDcsCodeCountsMap.put(mCurrentCode, mDcsCodeCountsMap.get(mCurrentCode) + 1);
}
getIdentifierCollection().update(dcsMessage.getIdentifiers());
}
}
@Override
public void receiveDecoderStateEvent(DecoderStateEvent event)
{
switch(event.getEvent())
{
case REQUEST_RESET:
resetState();
break;
default:
break;
}
}
@Override
public String getActivitySummary()
{
StringBuilder sb = new StringBuilder();
sb.append("=============================\n");
sb.append("Decoder:\tDigital Coded Squelch (DCS)\n\n");
if(mDcsCodeCountsMap.isEmpty())
{
sb.append(" Detected Codes: (none)\n");
}
else
{
for(Map.Entry<DCSCode,Integer> entry: mDcsCodeCountsMap.entrySet())
{
sb.append(" ").append(entry.getKey()).append(" - Count: ").append(entry.getValue());
if(entry.getKey().equals(mCurrentCode))
{
sb.append(" - Current\n");
}
else
{
sb.append("\n");
}
}
}
return sb.toString();
}
@Override
public void init()
{
}
@Override
public void stop()
{
}
}

View File

@@ -0,0 +1,79 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.dcs;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.dcs.DCSIdentifier;
import io.github.dsheirer.message.Message;
import io.github.dsheirer.protocol.Protocol;
import java.util.Collections;
import java.util.List;
/**
* Digital Coded Squelch (DCS) tone detected message
*/
public class DCSMessage extends Message
{
private final DCSCode mDCSCode;
/**
* Constructs an instance
* @param code that was detected
* @param timestamp when the code was detected
*/
public DCSMessage(DCSCode code, long timestamp)
{
super(timestamp);
mDCSCode = code;
}
@Override
public String toString()
{
return "Digital Coded Squelch (DCS) Detected: " + mDCSCode.name();
}
/**
* The DCS code that was detected.
* @return code
*/
public DCSCode getDCSCode()
{
return mDCSCode;
}
@Override
public boolean isValid()
{
return true; //We only send a message when the tone was valid
}
@Override
public Protocol getProtocol()
{
return Protocol.DCS;
}
@Override
public List<Identifier> getIdentifiers()
{
return Collections.singletonList(new DCSIdentifier(mDCSCode));
}
}

View File

@@ -0,0 +1,66 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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 io.github.dsheirer.module.decode.dcs;
import io.github.dsheirer.filter.Filter;
import io.github.dsheirer.filter.FilterElement;
import io.github.dsheirer.message.IMessage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Message filter for Digital Coded Squelch (DCS)
*/
public class DCSMessageFilter extends Filter<IMessage>
{
private Map<String,FilterElement<String>> mElements = new HashMap<>();
private static final String DCS_MESSAGE_FILTER_TAG = "DCS";
public DCSMessageFilter()
{
super("DCS Message Filter");
mElements.put(DCS_MESSAGE_FILTER_TAG, new FilterElement<>("DCS Code", true));
}
@Override
public boolean passes(IMessage message)
{
if(mEnabled && canProcess(message))
{
return mElements.get(DCS_MESSAGE_FILTER_TAG).isEnabled();
}
return false;
}
@Override
public boolean canProcess(IMessage message)
{
return message instanceof DCSMessage;
}
@Override
public List<FilterElement<?>> getFilterElements()
{
return new ArrayList<FilterElement<?>>(mElements.values());
}
}

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2019 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/>
* * *****************************************************************************
* 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 io.github.dsheirer.module.decode.fleetsync2;
@@ -33,13 +30,16 @@ import io.github.dsheirer.module.decode.event.PlottableDecodeEvent;
import io.github.dsheirer.module.decode.fleetsync2.identifier.FleetsyncIdentifier;
import io.github.dsheirer.module.decode.fleetsync2.message.Fleetsync2Message;
import io.github.dsheirer.protocol.Protocol;
import java.util.Set;
import java.util.TreeSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Fleetsync2DecoderState extends DecoderState
{
private Set<FleetsyncIdentifier> mIdents = new TreeSet<>();
private Map<FleetsyncIdentifier,Integer> mFromIdentCountsMap = new HashMap<>();
private Map<FleetsyncIdentifier,Integer> mToIdentCountsMap = new HashMap<>();
public Fleetsync2DecoderState()
{
@@ -52,16 +52,6 @@ public class Fleetsync2DecoderState extends DecoderState
return DecoderType.FLEETSYNC2;
}
/**
* Resets the overall decoder state and clears any accumulated event details
*/
@Override
public void reset()
{
super.reset();
mIdents.clear();
}
@Override
public void start()
{
@@ -87,8 +77,30 @@ public class Fleetsync2DecoderState extends DecoderState
getIdentifierCollection().update(fleetsync.getIdentifiers());
mIdents.add(fleetsync.getFromIdentifier());
mIdents.add(fleetsync.getToIdentifier());
FleetsyncIdentifier from = fleetsync.getFromIdentifier();
if(mFromIdentCountsMap.containsKey(from))
{
mFromIdentCountsMap.put(from, mFromIdentCountsMap.get(from) + 1);
}
else
{
mFromIdentCountsMap.put(from, 1);
}
FleetsyncIdentifier to = fleetsync.getToIdentifier();
if(to != null)
{
if(mToIdentCountsMap.containsKey(to))
{
mToIdentCountsMap.put(to, mToIdentCountsMap.get(to) + 1);
}
else
{
mToIdentCountsMap.put(to, 1);
}
}
switch(fleetsync.getMessageType())
{
@@ -158,17 +170,30 @@ public class Fleetsync2DecoderState extends DecoderState
sb.append("=============================\n");
sb.append("Decoder:\tFleetsync II\n\n");
sb.append("Fleetsync Idents\n");
if(mIdents.isEmpty())
if(mFromIdentCountsMap.isEmpty() && mToIdentCountsMap.isEmpty())
{
sb.append("Fleetsync Idents\n");
sb.append(" None\n");
}
else
{
sb.append("Fleetsync From Idents\n");
for (FleetsyncIdentifier mIdent : mIdents) {
sb.append(" ").append(mIdent.formatted()).append("\n");
List<FleetsyncIdentifier> fromIdents = new ArrayList<>(mFromIdentCountsMap.keySet());
Collections.sort(fromIdents);
for(FleetsyncIdentifier from: fromIdents)
{
sb.append(" ").append(from.formatted()).append(" - Count:").append(mFromIdentCountsMap.get(from)).append("\n");
}
sb.append("\nFleetsync To Idents\n");
List<FleetsyncIdentifier> toIdents = new ArrayList<>(mToIdentCountsMap.keySet());
Collections.sort(toIdents);
for(FleetsyncIdentifier to: toIdents)
{
sb.append(" ").append(to.formatted()).append(" - Count:").append(mToIdentCountsMap.get(to)).append("\n");
}
}