diff --git a/src/main/java/io/github/dsheirer/dsp/symbol/DibitToByteBufferAssembler.java b/src/main/java/io/github/dsheirer/dsp/symbol/DibitToByteBufferAssembler.java
new file mode 100644
index 00000000..edc34e28
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/dsp/symbol/DibitToByteBufferAssembler.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * sdr-trunk
+ * Copyright (C) 2014-2018 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.dsp.symbol;
+
+import io.github.dsheirer.sample.Listener;
+import io.github.dsheirer.sample.buffer.IReusableByteBufferProvider;
+import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
+import io.github.dsheirer.sample.buffer.ReusableByteBufferQueue;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Assembles reusable byte buffers from an incoming stream of Dibits.
+ */
+public class DibitToByteBufferAssembler implements Listener, IReusableByteBufferProvider
+{
+ private final static Logger mLog = LoggerFactory.getLogger(DibitToByteBufferAssembler.class);
+
+ private ReusableByteBufferQueue mBufferQueue = new ReusableByteBufferQueue("DibitToByteBufferAssembler");
+ private ReusableByteBuffer mCurrentBuffer;
+ private int mBufferPointer;
+ private int mBufferSize;
+ private byte mCurrentByte;
+ private int mDibitCount;
+ private Listener mBufferListener;
+
+ /**
+ * Constructs an assembler to produce reusable byte buffers of the specified size
+ *
+ * @param bufferSize
+ */
+ public DibitToByteBufferAssembler(int bufferSize)
+ {
+ mBufferSize = bufferSize;
+ getNextBuffer();
+ }
+
+ /**
+ * Broadcasts the current buffer to the registered listener and creates a new buffer, resetting
+ * the buffer pointer to zero so that new dibits can be loaded.
+ */
+ private void getNextBuffer()
+ {
+ if(mCurrentBuffer != null && mBufferListener != null)
+ {
+ mBufferListener.receive(mCurrentBuffer);
+ }
+
+ mCurrentBuffer = mBufferQueue.getBuffer(mBufferSize);
+ mBufferPointer = 0;
+ }
+
+ @Override
+ public void receive(Dibit dibit)
+ {
+ mCurrentByte <<= 2;
+
+ switch(dibit)
+ {
+ case D01_PLUS_3:
+ mCurrentByte |= 0x01;
+ break;
+ case D00_PLUS_1:
+ //no-op - value is already 00
+ break;
+ case D10_MINUS_1:
+ mCurrentByte |= 0x02;
+ break;
+ case D11_MINUS_3:
+ mCurrentByte |= 0x03;
+ break;
+ }
+
+ mDibitCount++;
+
+ if(mDibitCount >= 4)
+ {
+ mCurrentBuffer.getBytes()[mBufferPointer++] = mCurrentByte;
+ mCurrentByte = 0x00;
+ mDibitCount = 0;
+
+ if(mBufferPointer >= mBufferSize)
+ {
+ getNextBuffer();
+ }
+ }
+ }
+
+ /**
+ * Registers the listener to receive fully assembled byte buffers from this assembler.
+ */
+ @Override
+ public void setBufferListener(Listener listener)
+ {
+ mBufferListener = listener;
+ }
+
+ /**
+ * Removes the listener from receiving buffers from this assembler
+ */
+ @Override
+ public void removeBufferListener(Listener listener)
+ {
+ mBufferListener = null;
+ }
+
+ @Override
+ public boolean hasBufferListeners()
+ {
+ return mBufferListener != null;
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java
index 0218aed5..7a53ce4d 100644
--- a/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java
+++ b/src/main/java/io/github/dsheirer/module/decode/DecoderFactory.java
@@ -476,9 +476,6 @@ public class DecoderFactory
*/
public static EnumSet getBitstreamDecoders()
{
-// return EnumSet.of(DecoderType.P25_PHASE1);
-
- //None of the decoders currently support bitstream output.
- return EnumSet.noneOf(DecoderType.class);
+ return EnumSet.of(DecoderType.P25_PHASE1);
}
}
\ No newline at end of file
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25Decoder.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25Decoder.java
index 0fe9f4a1..95a6e592 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/P25Decoder.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25Decoder.java
@@ -16,19 +16,25 @@
package io.github.dsheirer.module.decode.p25;
import io.github.dsheirer.alias.AliasList;
+import io.github.dsheirer.dsp.symbol.DibitToByteBufferAssembler;
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.buffer.IReusableByteBufferListener;
+import io.github.dsheirer.sample.buffer.IReusableByteBufferProvider;
import io.github.dsheirer.sample.buffer.IReusableComplexBufferListener;
+import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
import io.github.dsheirer.source.ISourceEventListener;
import io.github.dsheirer.source.ISourceEventProvider;
import io.github.dsheirer.source.SourceEvent;
-public abstract class P25Decoder extends Decoder implements ISourceEventListener, ISourceEventProvider,
- IReusableComplexBufferListener, Listener
+public abstract class P25Decoder extends Decoder
+ implements ISourceEventListener, ISourceEventProvider, IReusableComplexBufferListener, Listener,
+ IReusableByteBufferProvider, IReusableByteBufferListener
{
private double mSampleRate;
+ private DibitToByteBufferAssembler mByteBufferAssembler = new DibitToByteBufferAssembler(300);
private P25MessageProcessor mMessageProcessor;
private AliasList mAliasList;
private Listener mSourceEventListener;
@@ -42,6 +48,41 @@ public abstract class P25Decoder extends Decoder implements ISourceEventListener
mMessageProcessor.setMessageListener(getMessageListener());
}
+ /**
+ * Assembler for packaging Dibit stream into reusable byte buffers.
+ */
+ protected DibitToByteBufferAssembler getByteBufferAssembler()
+ {
+ return mByteBufferAssembler;
+ }
+
+ /**
+ * Implements the IByteBufferProvider interface - delegates to the byte buffer assembler
+ */
+ @Override
+ public void setBufferListener(Listener listener)
+ {
+ getByteBufferAssembler().setBufferListener(listener);
+ }
+
+ /**
+ * Implements the IByteBufferProvider interface - delegates to the byte buffer assembler
+ */
+ @Override
+ public void removeBufferListener(Listener listener)
+ {
+ getByteBufferAssembler().removeBufferListener(listener);
+ }
+
+ /**
+ * Implements the IByteBufferProvider interface - delegates to the byte buffer assembler
+ */
+ @Override
+ public boolean hasBufferListeners()
+ {
+ return getByteBufferAssembler().hasBufferListeners();
+ }
+
protected double getSymbolRate()
{
return mSymbolRate;
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FM.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FM.java
index a67eeed5..c2623dac 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FM.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FM.java
@@ -25,6 +25,7 @@ import io.github.dsheirer.dsp.psk.DQPSKDecisionDirectedDemodulator;
import io.github.dsheirer.dsp.psk.InterpolatingSampleBuffer;
import io.github.dsheirer.dsp.psk.pll.AdaptivePLLGainMonitor;
import io.github.dsheirer.dsp.psk.pll.CostasLoop;
+import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
import io.github.dsheirer.source.SourceEvent;
import org.slf4j.Logger;
@@ -76,7 +77,15 @@ public class P25DecoderC4FM extends P25Decoder
mMessageFramer = new P25MessageFramer(getAliasList(), mCostasLoop, mPLLGainMonitor);
mMessageFramer.setListener(getMessageProcessor());
mMessageFramer.setSampleRate(sampleRate);
- mQPSKDemodulator.setSymbolListener(mMessageFramer);
+
+// mQPSKDemodulator.setSymbolListener(mMessageFramer);
+ mQPSKDemodulator.setSymbolListener(getByteBufferAssembler());
+ }
+
+ @Override
+ public Listener getReusableByteBufferListener()
+ {
+ return mMessageFramer;
}
/**
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FMInstrumented.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FMInstrumented.java
index c69a44e6..e7dbb7aa 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FMInstrumented.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderC4FMInstrumented.java
@@ -78,7 +78,7 @@ public class P25DecoderC4FMInstrumented extends P25DecoderC4FM
instrumented.setPLLFrequencyListener(mPLLFrequencyListener);
instrumented.setSymbolDecisionDataListener(mSymbolDecisionDataListener);
instrumented.setSamplesPerSymbolListener(mSamplesPerSymbolListener);
- instrumented.setSymbolListener(mMessageFramer);
+ instrumented.setSymbolListener(getByteBufferAssembler());
}
public void setComplexSymbolListener(Listener listener)
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSM.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSM.java
index 4f2356dd..580aeb45 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSM.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSM.java
@@ -24,6 +24,7 @@ import io.github.dsheirer.dsp.psk.DQPSKGardnerDemodulator;
import io.github.dsheirer.dsp.psk.InterpolatingSampleBuffer;
import io.github.dsheirer.dsp.psk.pll.AdaptivePLLGainMonitor;
import io.github.dsheirer.dsp.psk.pll.CostasLoop;
+import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
import io.github.dsheirer.source.SourceEvent;
@@ -78,7 +79,14 @@ public class P25DecoderLSM extends P25Decoder
mMessageFramer = new P25MessageFramer(getAliasList(), mCostasLoop, mPLLGainMonitor);
mMessageFramer.setListener(getMessageProcessor());
mMessageFramer.setSampleRate(sampleRate);
- mQPSKDemodulator.setSymbolListener(mMessageFramer);
+// mQPSKDemodulator.setSymbolListener(mMessageFramer);
+ mQPSKDemodulator.setSymbolListener(getByteBufferAssembler());
+ }
+
+ @Override
+ public Listener getReusableByteBufferListener()
+ {
+ return mMessageFramer;
}
/**
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSMInstrumented.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSMInstrumented.java
index 7997ae9f..366851b4 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSMInstrumented.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25DecoderLSMInstrumented.java
@@ -79,7 +79,7 @@ public class P25DecoderLSMInstrumented extends P25DecoderLSM
instrumented.setPLLFrequencyListener(mPLLFrequencyListener);
instrumented.setSymbolDecisionDataListener(mSymbolDecisionDataListener);
instrumented.setSamplesPerSymbolListener(mSamplesPerSymbolListener);
- instrumented.setSymbolListener(mMessageFramer);
+ instrumented.setSymbolListener(getByteBufferAssembler());
}
public void setComplexSymbolListener(Listener listener)
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer.java
index 4f6579b1..14db0e8c 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageFramer.java
@@ -32,13 +32,14 @@ import io.github.dsheirer.module.decode.p25.message.vselp.VSELP1Message;
import io.github.dsheirer.module.decode.p25.message.vselp.VSELP2Message;
import io.github.dsheirer.module.decode.p25.reference.DataUnitID;
import io.github.dsheirer.sample.Listener;
+import io.github.dsheirer.sample.buffer.ReusableByteBuffer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
-public class P25MessageFramer implements Listener
+public class P25MessageFramer implements Listener
{
private final static Logger mLog = LoggerFactory.getLogger(P25MessageFramer.class);
@@ -199,24 +200,30 @@ public class P25MessageFramer implements Listener
}
@Override
- public void receive(Dibit symbol)
+ public void receive(ReusableByteBuffer reusableByteBuffer)
{
- for(P25MessageAssembler assembler : mAssemblers)
- {
- if(assembler.isActive())
- {
- assembler.receive(symbol);
-
- if(assembler.complete())
- {
- assembler.reset();
- }
- }
- }
-
- mMatcher.receive(symbol.getBit1(), symbol.getBit2());
+ //process the buffer received
}
+ // @Override
+// public void receive(Dibit symbol)
+// {
+// for(P25MessageAssembler assembler : mAssemblers)
+// {
+// if(assembler.isActive())
+// {
+// assembler.receive(symbol);
+//
+// if(assembler.complete())
+// {
+// assembler.reset();
+// }
+// }
+// }
+//
+// mMatcher.receive(symbol.getBit1(), symbol.getBit2());
+// }
+
public void setListener(Listener listener)
{
mListener = listener;
diff --git a/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageProcessor.java b/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageProcessor.java
index 51d3978d..c6e5c6be 100644
--- a/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageProcessor.java
+++ b/src/main/java/io/github/dsheirer/module/decode/p25/P25MessageProcessor.java
@@ -1,3 +1,18 @@
+/*******************************************************************************
+ * sdr-trunk
+ * Copyright (C) 2014-2018 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.module.decode.p25;
import io.github.dsheirer.alias.AliasList;
@@ -13,8 +28,7 @@ import java.util.HashMap;
public class P25MessageProcessor implements Listener
{
- private final static Logger mLog =
- LoggerFactory.getLogger( P25MessageProcessor.class );
+ private final static Logger mLog = LoggerFactory.getLogger( P25MessageProcessor.class );
private Listener mMessageListener;
diff --git a/src/main/java/io/github/dsheirer/record/binary/BinaryRecorder.java b/src/main/java/io/github/dsheirer/record/binary/BinaryRecorder.java
index a1f000a7..697a85c5 100644
--- a/src/main/java/io/github/dsheirer/record/binary/BinaryRecorder.java
+++ b/src/main/java/io/github/dsheirer/record/binary/BinaryRecorder.java
@@ -45,6 +45,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
public class BinaryRecorder extends Module implements IReusableByteBufferListener
{
private final static Logger mLog = LoggerFactory.getLogger(BinaryRecorder.class);
+ private static final int MAX_RECORDING_BYTE_SIZE = 524288; //500 kB
private ContinuousReusableBufferProcessor mBufferProcessor =
new ContinuousReusableBufferProcessor<>(500, 50);
@@ -53,6 +54,7 @@ public class BinaryRecorder extends Module implements IReusableByteBufferListene
private Path mBaseRecordingPath;
private String mRecordingIdentifier;
private BinaryWriter mBinaryWriter = new BinaryWriter();
+ private int mBytesRecordedCounter;
/**
* Constructs a binary recorder.
@@ -115,7 +117,7 @@ public class BinaryRecorder extends Module implements IReusableByteBufferListene
StringBuilder sb = new StringBuilder();
sb.append(TimeStamp.getTimeStamp("_"));
sb.append("_");
- sb.append(mRecordingIdentifier);
+ sb.append(mRecordingIdentifier.trim());
sb.append(".bits");
return mBaseRecordingPath.resolve(sb.toString());
@@ -148,22 +150,62 @@ public class BinaryRecorder extends Module implements IReusableByteBufferListene
public void start(Path path) throws IOException
{
- mCurrentPath = path;
- mWritableByteChannel = Files.newByteChannel(path,
- EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE));
+ synchronized(this)
+ {
+ mCurrentPath = path;
+ mWritableByteChannel = Files.newByteChannel(path,
+ EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE));
+ mLog.info("Binary (bitstream) recording started: " + mCurrentPath.toString());
+ }
}
public void stop() throws IOException
{
- if(mWritableByteChannel != null)
+ synchronized(this)
{
- mWritableByteChannel.close();
- }
+ if(mWritableByteChannel != null)
+ {
+ mWritableByteChannel.close();
+ }
- mWritableByteChannel = null;
- mCurrentPath = null;
+ mWritableByteChannel = null;
+ mCurrentPath = null;
+ }
}
+ /**
+ * Closes the current recording and starts a new recording. This method is normally used
+ * when the current recording size has reached the maximum threshold.
+ */
+ private void cycleRecording()
+ {
+ synchronized(this)
+ {
+ try
+ {
+ if(mWritableByteChannel != null)
+ {
+ mWritableByteChannel.close();
+ }
+
+ mCurrentPath = getRecordingPath();
+ mWritableByteChannel = Files.newByteChannel(mCurrentPath,
+ EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE));
+ mLog.info("Binary (bitstream) recording started: " + mCurrentPath.toString());
+ }
+ catch(IOException ioe)
+ {
+ mLog.error("Error while cycling a max-size bit stream recorder", ioe);
+ }
+
+ mBytesRecordedCounter = 0;
+ }
+ }
+
+ /**
+ * Primary receive method for incoming byte buffers
+ * @param reusableComplexBuffers to record
+ */
@Override
public void receive(List reusableComplexBuffers)
{
@@ -174,7 +216,12 @@ public class BinaryRecorder extends Module implements IReusableByteBufferListene
try
{
ByteBuffer byteBuffer = ByteBuffer.wrap(buffer.getSamplesCopy());
- mWritableByteChannel.write(byteBuffer);
+ mBytesRecordedCounter += mWritableByteChannel.write(byteBuffer);
+
+ if(mBytesRecordedCounter > MAX_RECORDING_BYTE_SIZE)
+ {
+ cycleRecording();
+ }
}
catch(IOException ioe)
{