diff --git a/src/main/java/io/github/dsheirer/message/StuffBitsMessage.java b/src/main/java/io/github/dsheirer/message/StuffBitsMessage.java
new file mode 100644
index 00000000..9d903b0d
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/message/StuffBitsMessage.java
@@ -0,0 +1,84 @@
+/*
+ *
+ * * ******************************************************************************
+ * * 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
+ * * *****************************************************************************
+ *
+ *
+ */
+package io.github.dsheirer.message;
+
+import io.github.dsheirer.identifier.Identifier;
+import io.github.dsheirer.protocol.Protocol;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Empty message to convey that a number of stuff/tail bits were processed with no sync or valid message detected.
+ *
+ * This message supports periodic updates to external processor(s) that may be tracking bit error rates.
+ */
+public class StuffBitsMessage extends Message
+{
+ private int mBitsProcessed;
+ private Protocol mProtocol;
+
+ /**
+ * Constructs a sync loss message.
+ *
+ * @param timestamp of the message
+ * @param bitsProcessed without a sync pattern detection of message decode
+ */
+ public StuffBitsMessage(long timestamp, int bitsProcessed, Protocol protocol)
+ {
+ super(timestamp);
+ mBitsProcessed = bitsProcessed;
+ mProtocol = protocol;
+ }
+
+ /**
+ * Indicates the number of bits that were processed without a sync dectection or message decode
+ */
+ public int getBitsProcessed()
+ {
+ return mBitsProcessed;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "<-> TAIL/STUFF BITS PROCESSED [" + getBitsProcessed() + "]";
+ }
+
+ @Override
+ public boolean isValid()
+ {
+ return true;
+ }
+
+ @Override
+ public Protocol getProtocol()
+ {
+ return mProtocol;
+ }
+
+ @Override
+ public List getIdentifiers()
+ {
+ return Collections.EMPTY_LIST;
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java
index 3fb2cf1c..71bc630a 100644
--- a/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java
+++ b/src/main/java/io/github/dsheirer/module/decode/event/MessageActivityModel.java
@@ -23,6 +23,7 @@ package io.github.dsheirer.module.decode.event;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
+import io.github.dsheirer.message.StuffBitsMessage;
import io.github.dsheirer.sample.Listener;
import javax.swing.table.AbstractTableModel;
@@ -114,6 +115,12 @@ public class MessageActivityModel extends AbstractTableModel implements Listener
public void receive(final IMessage message)
{
+ //Don't process tail bits or stuff bits message fragments
+ if(message instanceof StuffBitsMessage)
+ {
+ return;
+ }
+
if(mMessageFilter.passes(message))
{
final MessageItem messageItem = new MessageItem(message);
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageFramer.java b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageFramer.java
index d660ed29..07666bdb 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageFramer.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/phase1/P25P1MessageFramer.java
@@ -28,16 +28,18 @@ import io.github.dsheirer.dsp.symbol.Dibit;
import io.github.dsheirer.dsp.symbol.ISyncDetectListener;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.Message;
+import io.github.dsheirer.message.StuffBitsMessage;
import io.github.dsheirer.message.SyncLossMessage;
import io.github.dsheirer.module.decode.DecoderType;
-import io.github.dsheirer.module.decode.p25.audio.P25P1CallSequenceRecorder;
import io.github.dsheirer.module.decode.p25.phase1.message.P25Message;
import io.github.dsheirer.module.decode.p25.phase1.message.P25MessageFactory;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.PDUMessageFactory;
import io.github.dsheirer.module.decode.p25.phase1.message.pdu.PDUSequence;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessage;
import io.github.dsheirer.module.decode.p25.phase1.message.tsbk.TSBKMessageFactory;
-import io.github.dsheirer.preference.UserPreferences;
+import io.github.dsheirer.module.decode.p25.phase2.P25P2MessageFramer;
+import io.github.dsheirer.module.decode.p25.phase2.P25P2MessageProcessor;
+import io.github.dsheirer.module.decode.p25.phase2.enumeration.ScrambleParameters;
import io.github.dsheirer.protocol.Protocol;
import io.github.dsheirer.record.binary.BinaryReader;
import io.github.dsheirer.sample.Listener;
@@ -45,8 +47,12 @@ import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.function.Consumer;
/**
* P25 Sync Detector and Message Framer. Includes capability to detect PLL out-of-phase lock errors
@@ -59,7 +65,6 @@ public class P25P1MessageFramer implements Listener, IP25P1DataUnitDetect
private P25P1DataUnitDetector mDataUnitDetector;
private P25P1ChannelStatusProcessor mChannelStatusProcessor = new P25P1ChannelStatusProcessor();
private Listener mMessageListener;
-
private boolean mAssemblingMessage = false;
private CorrectedBinaryMessage mBinaryMessage;
private P25P1DataUnitID mDataUnitID;
@@ -422,58 +427,101 @@ public class P25P1MessageFramer implements Listener, IP25P1DataUnitDetect
if(bitsProcessed > 0 && mMessageListener != null)
{
- mMessageListener.receive(new SyncLossMessage(getTimestamp(), bitsProcessed, Protocol.APCO25));
+ if(bitsProcessed < 64)
+ {
+ mMessageListener.receive(new StuffBitsMessage(getTimestamp(), bitsProcessed, Protocol.APCO25));
+ }
+ else
+ {
+ mMessageListener.receive(new SyncLossMessage(getTimestamp(), bitsProcessed, Protocol.APCO25));
+ }
}
}
public static void main(String[] args)
{
- P25P1DecoderLSM decoderLSM = new P25P1DecoderLSM();
+ Path directory = Paths.get("/media/denny/500G1EXT4/RadioRecordings/Harris APCO25 Phase II");
+// Path directory = Paths.get("/media/denny/500G1EXT4/PBITRecordings");
+ final boolean phase1 = false;
-// Path path = Paths.get("/media/denny/500G1EXT4/RadioRecordings/APCO25P2/DFW Airport Encrypted/20190719_073751_9600BPS_APCO25PHASE1_DFWAirport_Site_857_3875_baseband_20181213_223236.bits");
- Path path = Paths.get("/media/denny/500G1EXT4/RadioRecordings/20190720_071117_9600BPS_APCO25PHASE1_P25P2_HCPM_Metrocrest_Dallas_857_7625_phase_2_not_motorola_baseband_20181213_224616_control_channel.bits");
-
- P25P1MessageFramer messageFramer = new P25P1MessageFramer(null, DecoderType.P25_PHASE1.getProtocol().getBitRate());
-
- try(BinaryReader reader = new BinaryReader(path, 200))
+ try
{
- while(reader.hasNext())
- {
- messageFramer.receive(reader.next());
- }
+ DirectoryStream stream = Files.newDirectoryStream(directory, "*Hills*TRAFFIC.bits");
+
+ stream.forEach(new Consumer()
+ {
+ @Override
+ public void accept(Path path)
+ {
+ mLog.debug("Processing:" + path.toString());
+
+ if(phase1)
+ {
+ P25P1MessageFramer messageFramer = new P25P1MessageFramer(null, DecoderType.P25_PHASE1.getProtocol().getBitRate());
+ P25P1MessageProcessor messageProcessor = new P25P1MessageProcessor();
+ messageFramer.setListener(messageProcessor);
+ messageProcessor.setMessageListener(new Listener()
+ {
+ @Override
+ public void receive(IMessage message)
+ {
+ if(!(message instanceof StuffBitsMessage))
+ {
+ mLog.debug(message.toString());
+ }
+ }
+ });
+
+ try(BinaryReader reader = new BinaryReader(path, 200))
+ {
+ while(reader.hasNext())
+ {
+ ReusableByteBuffer buffer = reader.next();
+ messageFramer.receive(buffer);
+ }
+ }
+ catch(Exception ioe)
+ {
+ ioe.printStackTrace();
+ }
+ }
+ else
+ {
+ P25P2MessageFramer messageFramer = new P25P2MessageFramer(null, DecoderType.P25_PHASE2.getProtocol().getBitRate());
+ ScrambleParameters scrambleParameters = new ScrambleParameters(123654, 813, 10); //Hills County
+ messageFramer.setScrambleParameters(scrambleParameters);
+ P25P2MessageProcessor messageProcessor = new P25P2MessageProcessor();
+ messageFramer.setListener(messageProcessor);
+ messageProcessor.setMessageListener(new Listener()
+ {
+ @Override
+ public void receive(IMessage message)
+ {
+ mLog.debug(message.toString());
+ }
+ });
+
+ try(BinaryReader reader = new BinaryReader(path, 200))
+ {
+ while(reader.hasNext())
+ {
+ ReusableByteBuffer buffer = reader.next();
+ messageFramer.receive(buffer);
+ }
+ }
+ catch(Exception ioe)
+ {
+ ioe.printStackTrace();
+ }
+ }
+ }
+ }
+ );
}
- catch(Exception ioe)
+ catch(IOException ioe)
{
- ioe.printStackTrace();
+ mLog.error("Error", ioe);
}
-
- P25P1MessageProcessor messageProcessor = new P25P1MessageProcessor();
- P25P1CallSequenceRecorder frameRecorder = new P25P1CallSequenceRecorder(new UserPreferences(), 154250000, "", "");
- messageFramer.setListener(messageProcessor);
- messageProcessor.setMessageListener(new Listener()
- {
- @Override
- public void receive(IMessage message)
- {
- frameRecorder.receive(message);
- mLog.debug(message.toString());
- }
- });
-
-
- try(BinaryReader reader = new BinaryReader(path, 200))
- {
- while(reader.hasNext())
- {
- messageFramer.receive(reader.next());
- }
- }
- catch(Exception ioe)
- {
- ioe.printStackTrace();
- }
-
- frameRecorder.stop();
}
}