diff --git a/CMakeLists.txt b/CMakeLists.txt
index 055b6eba7..296076532 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -375,6 +375,7 @@ add_subdirectory(plugins)
if(LIBMBE_FOUND)
add_subdirectory(dsd)
+ add_subdirectory(dsdplus)
endif(LIBMBE_FOUND)
if(LIBUSB_FOUND AND UNIX)
diff --git a/dsdplus/CMakeLists.txt b/dsdplus/CMakeLists.txt
new file mode 100644
index 000000000..5f21a4d2b
--- /dev/null
+++ b/dsdplus/CMakeLists.txt
@@ -0,0 +1,31 @@
+project(dsdplus)
+
+set(dsdplus_SOURCES
+ dmr_voice.cpp
+ dsd_decoder.cpp
+ dsd_filters.cpp
+ dsd_opts.cpp
+ dsd_state.cpp
+)
+
+set(dsdplus_HEADERS
+ dmr_voice.h
+ dsd_decoder.h
+ dsd_filters.h
+ dsd_opts.h
+ dsd_state.h
+)
+
+include_directories(
+ ${PROJECT_SOURCE_DIR}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${LIBMBE_INCLUDE_DIR}
+)
+
+add_library(dsdplus SHARED
+ ${dsdplus_SOURCES}
+)
+
+target_link_libraries(dsdplus ${LIBMBE_LIBRARY})
+
+install(TARGETS dsdplus DESTINATION lib)
diff --git a/dsdplus/dmr_voice.cpp b/dsdplus/dmr_voice.cpp
new file mode 100644
index 000000000..ec064d72d
--- /dev/null
+++ b/dsdplus/dmr_voice.cpp
@@ -0,0 +1,46 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2016 Edouard Griffiths, F4EXB. //
+// //
+// 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 as version 3 of the License, or //
+// //
+// 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 V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include "dmr_voice.h"
+#include "dsd_decoder.h"
+
+namespace DSDPlus
+{
+
+DSDDMRVoice::DSDDMRVoice(DSDDecoder *dsdDecoder) :
+ m_dsdDecoder(dsdDecoder)
+{
+}
+
+DSDDMRVoice::~DSDDMRVoice()
+{
+}
+
+void DSDDMRVoice::init()
+{
+ mutecurrentslot = 0;
+ msMode = 0;
+ dibit_p = m_dsdDecoder->m_state.dibit_buf_p - 144;
+ m_slotIndex = 0;
+ m_majorBlock = 0;
+}
+
+void DSDDMRVoice::process()
+{
+
+}
+
+} // namespace dsdplus
diff --git a/dsdplus/dmr_voice.h b/dsdplus/dmr_voice.h
new file mode 100644
index 000000000..a154e63d5
--- /dev/null
+++ b/dsdplus/dmr_voice.h
@@ -0,0 +1,82 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2016 Edouard Griffiths, F4EXB. //
+// //
+// 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 as version 3 of the License, or //
+// //
+// 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 V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+/*
+ * DMR voice frames
+ * 6 Major blocks of 10 slots. 2 groups of slots of 144 bytes each.
+ * Map is as follows in number of bytes
+ *
+ * 0 ... 5
+ * A 0 54 ... 54 <- this one is always skipped
+ * 1 12 12 <- cache data
+ * 2 36 36 <- AMBE slot
+ * 3 18 18 <- AMBE slot
+ * 4 24 24 <- sync data
+ * B 5 18 18 <- AMBE slot
+ * 6 36 36 <- AMBE slot
+ * 7 12 12 <- cache data
+ * 8 54 54 <- this one is always skipped
+ * 9 24 24 <- sync data
+ *
+ * The A gtoup of the first major block is already in memory and is processed
+ * at initialization time
+ * Then dibits for each slot are stored in cache and processed right after the
+ * last dibit for the slot has been added.
+ * For skipped slots the dibits are simply thrown away
+ *
+ */
+
+#ifndef DSDPLUS_DMR_VOICE_H_
+#define DSDPLUS_DMR_VOICE_H_
+
+namespace DSDPlus
+{
+
+class DSDDecoder;
+
+class DSDDMRVoice
+{
+public:
+ DSDDMRVoice(DSDDecoder *dsdDecoder);
+ ~DSDDMRVoice();
+
+ void init();
+ void process();
+
+private:
+
+ DSDDecoder *m_dsdDecoder;
+ // extracts AMBE frames from DMR frame
+ int m_slotIndex; //!< Slot index in major block 0..9 //i;
+ int m_majorBlock; //!< Major block index 0..5 //j;
+ int dibit;
+ int *dibit_p;
+ char ambe_fr[4][24];
+ char ambe_fr2[4][24];
+ char ambe_fr3[4][24];
+ const int *w, *x, *y, *z;
+ char sync[25];
+ char syncdata[25];
+ char cachdata[13];
+ int mutecurrentslot;
+ int msMode;
+ int m_dibitCache[54]; // biggest slot is 54 dibits
+ int m_dibitIndex; // index in dibit cache
+};
+
+}
+
+#endif /* DSDPLUS_DMR_VOICE_H_ */
diff --git a/dsdplus/dsd_decoder.cpp b/dsdplus/dsd_decoder.cpp
new file mode 100644
index 000000000..6d7669acc
--- /dev/null
+++ b/dsdplus/dsd_decoder.cpp
@@ -0,0 +1,1380 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2016 Edouard Griffiths, F4EXB. //
+// //
+// 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 as version 3 of the License, or //
+// //
+// 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 V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include
+#include "dsd_decoder.h"
+
+namespace DSDPlus
+{
+
+DSDDecoder::DSDDecoder() :
+ m_symbol(0),
+ m_fsmState(DSDLookForSync),
+ m_dsdDMRVoice(this)
+{
+ resetSymbol();
+ resetFrameSync();
+ noCarrier();
+}
+
+DSDDecoder::~DSDDecoder()
+{
+}
+
+void DSDDecoder::run(short sample)
+{
+ if (pushSample(sample, 0))
+ {
+ switch (m_fsmState)
+ {
+ case DSDLookForSync:
+ m_state.synctype = getFrameSync();
+
+ if (m_state.synctype > -2)
+ {
+ // TODO: deal with the noCarrier() call
+ // recalibrate center/umid/lmid
+ m_state.center = ((m_state.max) + (m_state.min)) / 2;
+ m_state.umid = (((m_state.max) - m_state.center) * 5 / 8) + m_state.center;
+ m_state.lmid = (((m_state.min) - m_state.center) * 5 / 8) + m_state.center;
+
+ if (m_state.synctype > -1)
+ {
+ processFrame();
+ }
+ }
+
+ break;
+ case DSDprocessDMRvoice:
+ m_dsdDMRVoice.process();
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void DSDDecoder::resetSymbol()
+{
+ m_sampleIndex = 0;
+ m_sum = 0;
+ m_count = 0;
+}
+
+bool DSDDecoder::pushSample(short sample, int have_sync)
+{
+ // timing control
+ if ((m_sampleIndex == 0) && (have_sync == 0))
+ {
+ if (m_state.samplesPerSymbol == 20)
+ {
+ if ((m_state.jitter >= 7) && (m_state.jitter <= 10))
+ {
+ m_sampleIndex--;
+ }
+ else if ((m_state.jitter >= 11) && (m_state.jitter <= 14))
+ {
+ m_sampleIndex++;
+ }
+ }
+ else if (m_state.rf_mod == 1)
+ {
+ if ((m_state.jitter >= 0)
+ && (m_state.jitter < m_state.symbolCenter))
+ {
+ m_sampleIndex++; // fall back
+ }
+ else if ((m_state.jitter > m_state.symbolCenter)
+ && (m_state.jitter < 10))
+ {
+ m_sampleIndex--; // catch up
+ }
+ }
+ else if (m_state.rf_mod == 2)
+ {
+ if ((m_state.jitter >= m_state.symbolCenter - 1)
+ && (m_state.jitter <= m_state.symbolCenter))
+ {
+ m_sampleIndex--;
+ }
+ else if ((m_state.jitter >= m_state.symbolCenter + 1)
+ && (m_state.jitter <= m_state.symbolCenter + 2))
+ {
+ m_sampleIndex++;
+ }
+ }
+ else if (m_state.rf_mod == 0)
+ {
+ if ((m_state.jitter > 0)
+ && (m_state.jitter <= m_state.symbolCenter))
+ {
+ m_sampleIndex--; // catch up
+ }
+ else if ((m_state.jitter > m_state.symbolCenter)
+ && (m_state.jitter < m_state.samplesPerSymbol))
+ {
+ m_sampleIndex++; // fall back
+ }
+ }
+
+ m_state.jitter = -1;
+ }
+
+ // process sample
+ if (m_opts.use_cosine_filter)
+ {
+ if (m_state.lastsynctype >= 10 && m_state.lastsynctype <= 13)
+ {
+ sample = m_dsdFilters.dmr_filter(sample);
+ }
+ else if (m_state.lastsynctype == 8 || m_state.lastsynctype == 9
+ || m_state.lastsynctype == 16 || m_state.lastsynctype == 17)
+ {
+ sample = m_dsdFilters.nxdn_filter(sample);
+ }
+ }
+
+ if ((sample > m_state.max) && (have_sync == 1) && (m_state.rf_mod == 0))
+ {
+ sample = m_state.max;
+ }
+ else if ((sample < m_state.min) && (have_sync == 1)
+ && (m_state.rf_mod == 0))
+ {
+ sample = m_state.min;
+ }
+
+ if (sample > m_state.center)
+ {
+ if (m_state.lastsample < m_state.center)
+ {
+ m_state.numflips += 1;
+ }
+ if (sample > (m_state.maxref * 1.25))
+ {
+ if (m_state.lastsample < (m_state.maxref * 1.25))
+ {
+ m_state.numflips += 1;
+ }
+ if ((m_state.jitter < 0) && (m_state.rf_mod == 1))
+ { // first spike out of place
+ m_state.jitter = m_sampleIndex;
+ }
+ if ((m_opts.symboltiming == 1) && (have_sync == 0)
+ && (m_state.lastsynctype != -1))
+ {
+ fprintf(stderr, "O");
+ }
+ }
+ else
+ {
+ if ((m_opts.symboltiming == 1) && (have_sync == 0)
+ && (m_state.lastsynctype != -1))
+ {
+ fprintf(stderr, "+");
+ }
+ if ((m_state.jitter < 0)
+ && (m_state.lastsample < m_state.center)
+ && (m_state.rf_mod != 1))
+ { // first transition edge
+ m_state.jitter = m_sampleIndex;
+ }
+ }
+ }
+ else
+ { // sample < 0
+ if (m_state.lastsample > m_state.center)
+ {
+ m_state.numflips += 1;
+ }
+ if (sample < (m_state.minref * 1.25))
+ {
+ if (m_state.lastsample > (m_state.minref * 1.25))
+ {
+ m_state.numflips += 1;
+ }
+ if ((m_state.jitter < 0) && (m_state.rf_mod == 1))
+ { // first spike out of place
+ m_state.jitter = m_sampleIndex;
+ }
+ if ((m_opts.symboltiming == 1) && (have_sync == 0)
+ && (m_state.lastsynctype != -1))
+ {
+ fprintf(stderr, "X");
+ }
+ }
+ else
+ {
+ if ((m_opts.symboltiming == 1) && (have_sync == 0)
+ && (m_state.lastsynctype != -1))
+ {
+ fprintf(stderr, "-");
+ }
+ if ((m_state.jitter < 0)
+ && (m_state.lastsample > m_state.center)
+ && (m_state.rf_mod != 1))
+ { // first transition edge
+ m_state.jitter = m_sampleIndex;
+ }
+ }
+ }
+ if (m_state.samplesPerSymbol == 20)
+ {
+ if ((m_sampleIndex >= 9) && (m_sampleIndex <= 11))
+ {
+ m_sum += sample;
+ m_count++;
+ }
+ }
+ if (m_state.samplesPerSymbol == 5)
+ {
+ if (m_sampleIndex == 2)
+ {
+ m_sum += sample;
+ m_count++;
+ }
+ }
+ else
+ {
+ if (((m_sampleIndex >= m_state.symbolCenter - 1)
+ && (m_sampleIndex <= m_state.symbolCenter + 2)
+ && (m_state.rf_mod == 0))
+ || (((m_sampleIndex == m_state.symbolCenter)
+ || (m_sampleIndex == m_state.symbolCenter + 1))
+ && (m_state.rf_mod != 0)))
+ {
+ m_sum += sample;
+ m_count++;
+ }
+ }
+
+ m_state.lastsample = sample;
+
+ if (m_sampleIndex == m_state.samplesPerSymbol - 1) // conclusion
+ {
+ m_symbol = m_sum / m_count;
+ if ((m_opts.symboltiming == 1) && (have_sync == 0)
+ && (m_state.lastsynctype != -1))
+ {
+ if (m_state.jitter >= 0)
+ {
+ fprintf(stderr, " %i\n", m_state.jitter);
+ }
+ else
+ {
+ fprintf(stderr, "\n");
+ }
+ }
+
+ m_state.symbolcnt++;
+ resetSymbol();
+ return true; // new symbol available
+ }
+ else
+ {
+ m_sampleIndex++; // wait for next sample
+ return false;
+ }
+}
+
+void DSDDecoder::resetFrameSync()
+{
+ for (int i = 18; i < 24; i++)
+ {
+ m_lbuf[i] = 0;
+ m_lbuf2[i] = 0;
+ }
+
+ // reset detect frame sync engine
+ m_t = 0;
+ m_synctest[24] = 0;
+ m_synctest18[18] = 0;
+ m_synctest32[32] = 0;
+ m_synctest_pos = 0;
+ m_synctest_p = m_synctest_buf + 10;
+ m_lmin = 0;
+ m_lmax = 0;
+ m_lidx = 0;
+ m_lastt = 0;
+ m_state.numflips = 0;
+ m_sync = -2; // make in progress
+
+ if ((m_opts.symboltiming == 1) && (m_state.carrier == 1))
+ {
+ fprintf(stderr, "\nSymbol Timing:\n");
+ }
+
+ m_fsmState = DSDLookForSync;
+}
+
+void DSDDecoder::printFrameSync(const char *frametype, int offset, char *modulation)
+{
+ if (m_opts.verbose > 0)
+ {
+ fprintf(stderr, "Sync: %s ", frametype);
+ }
+ if (m_opts.verbose > 2)
+ {
+ fprintf(stderr, "o: %4i ", offset);
+ }
+ if (m_opts.verbose > 1)
+ {
+ fprintf(stderr, "mod: %s ", modulation);
+ }
+ if (m_opts.verbose > 2)
+ {
+ fprintf(stderr, "g: %f ", m_state.aout_gain);
+ }
+}
+
+int DSDDecoder::getFrameSync()
+{
+ /* detects frame sync and returns frame type
+ * -2 = in progress
+ * -1 = no sync
+ * 0 = +P25p1
+ * 1 = -P25p1
+ * 2 = +X2-TDMA (non inverted signal data frame)
+ * 3 = +X2-TDMA (inverted signal voice frame)
+ * 4 = -X2-TDMA (non inverted signal voice frame)
+ * 5 = -X2-TDMA (inverted signal data frame)
+ * 6 = +D-STAR
+ * 7 = -D-STAR
+ * 8 = +NXDN (non inverted voice frame)
+ * 9 = -NXDN (inverted voice frame)
+ * 10 = +DMR (non inverted singlan data frame)
+ * 11 = -DMR (inverted signal voice frame)
+ * 12 = +DMR (non inverted signal voice frame)
+ * 13 = -DMR (inverted signal data frame)
+ * 14 = +ProVoice
+ * 15 = -ProVoice
+ * 16 = +NXDN (non inverted data frame)
+ * 17 = -NXDN (inverted data frame)
+ * 18 = +D-STAR_HD
+ * 19 = -D-STAR_HD
+ */
+
+ // smelly while was starting here
+ //symbol = getSymbol(opts, state, 0);
+ m_t++;
+ m_lbuf[m_lidx] = m_symbol;
+ m_state.sbuf[m_state.sidx] = m_symbol;
+
+ if (m_lidx == 23)
+ {
+ m_lidx = 0;
+ }
+ else
+ {
+ m_lidx++;
+ }
+
+ if (m_state.sidx == (m_opts.ssize - 1))
+ {
+ m_state.sidx = 0;
+ }
+ else
+ {
+ m_state.sidx++;
+ }
+
+ if (m_lastt == 23)
+ {
+ m_lastt = 0;
+
+ if (m_state.numflips > m_opts.mod_threshold)
+ {
+ if (m_opts.mod_qpsk == 1)
+ {
+ m_state.rf_mod = 1;
+ }
+ }
+ else if (m_state.numflips > 18)
+ {
+ if (m_opts.mod_gfsk == 1)
+ {
+ m_state.rf_mod = 2;
+ }
+ }
+ else
+ {
+ if (m_opts.mod_c4fm == 1)
+ {
+ m_state.rf_mod = 0;
+ }
+ }
+
+ m_state.numflips = 0;
+ }
+ else
+ {
+ m_lastt++;
+ }
+
+ if (m_state.dibit_buf_p > m_state.dibit_buf + 900000)
+ {
+ m_state.dibit_buf_p = m_state.dibit_buf + 200;
+ }
+
+ //determine dibit state
+ if (m_symbol > 0)
+ {
+ *m_state.dibit_buf_p = 1;
+ m_state.dibit_buf_p++;
+ m_dibit = 49;
+ }
+ else
+ {
+ *m_state.dibit_buf_p = 3;
+ m_state.dibit_buf_p++;
+ m_dibit = 51;
+ }
+
+ *m_synctest_p = m_dibit;
+
+ if (m_t >= 18)
+ {
+ for (int i = 0; i < 24; i++)
+ {
+ m_lbuf2[i] = m_lbuf[i];
+ }
+
+ qsort(m_lbuf2, 24, sizeof(int), comp);
+
+ m_lmin = (m_lbuf2[2] + m_lbuf2[3] + m_lbuf2[4]) / 3;
+ m_lmax = (m_lbuf2[21] + m_lbuf2[20] + m_lbuf2[19]) / 3;
+
+ if (m_state.rf_mod == 1)
+ {
+ m_state.minbuf[m_state.midx] = m_lmin;
+ m_state.maxbuf[m_state.midx] = m_lmax;
+
+ if (m_state.midx == (m_opts.msize - 1))
+ {
+ m_state.midx = 0;
+ }
+ else
+ {
+ m_state.midx++;
+ }
+
+ m_lsum = 0;
+
+ for (int i = 0; i < m_opts.msize; i++)
+ {
+ m_lsum += m_state.minbuf[i];
+ }
+
+ m_state.min = m_lsum / m_opts.msize;
+ m_lsum = 0;
+
+ for (int i = 0; i < m_opts.msize; i++)
+ {
+ m_lsum += m_state.maxbuf[i];
+ }
+
+ m_state.max = m_lsum / m_opts.msize;
+ m_state.center = ((m_state.max) + (m_state.min)) / 2;
+ m_state.maxref = ((m_state.max) * 0.80);
+ m_state.minref = ((m_state.min) * 0.80);
+ }
+ else
+ {
+ m_state.maxref = m_state.max;
+ m_state.minref = m_state.min;
+ }
+
+ if (m_state.rf_mod == 0)
+ {
+ sprintf(m_modulation, "C4FM");
+ }
+ else if (m_state.rf_mod == 1)
+ {
+ sprintf(m_modulation, "QPSK");
+ }
+ else if (m_state.rf_mod == 2)
+ {
+ sprintf(m_modulation, "GFSK");
+ }
+
+ if (m_opts.datascope == 1)
+ {
+ if (m_lidx == 0)
+ {
+ for (int i = 0; i < 64; i++)
+ {
+ m_spectrum[i] = 0;
+ }
+
+ for (int i = 0; i < 24; i++)
+ {
+ int o = (m_lbuf2[i] + 32768) / 1024;
+ m_spectrum[o]++;
+ }
+ if (m_state.symbolcnt > (4800 / m_opts.scoperate))
+ {
+ m_state.symbolcnt = 0;
+
+ fprintf(stderr, "\n");
+ fprintf(stderr,
+ "Demod mode: %s Nac: %4X\n",
+ m_modulation, m_state.nac);
+ fprintf(stderr,
+ "Frame Type: %s Talkgroup: %7i\n",
+ m_state.ftype, m_state.lasttg);
+ fprintf(stderr,
+ "Frame Subtype: %s Source: %12i\n",
+ m_state.fsubtype, m_state.lastsrc);
+ fprintf(stderr,
+ "TDMA activity: %s %s Voice errors: %s\n",
+ m_state.slot0light, m_state.slot1light,
+ m_state.err_str);
+ fprintf(stderr,
+ "+----------------------------------------------------------------+\n");
+
+ for (int i = 0; i < 10; i++)
+ {
+ fprintf(stderr, "|");
+
+ for (int j = 0; j < 64; j++)
+ {
+ if (i == 0)
+ {
+ if ((j == ((m_state.min) + 32768) / 1024) || (j == ((m_state.max) + 32768) / 1024))
+ {
+ fprintf(stderr, "#");
+ }
+ else if (j == (m_state.center + 32768) / 1024)
+ {
+ fprintf(stderr, "!");
+ }
+ else
+ {
+ if (j == 32)
+ {
+ fprintf(stderr, "|");
+ }
+ else
+ {
+ fprintf(stderr, " ");
+ }
+ }
+ }
+ else
+ {
+ if (m_spectrum[j] > 9 - i)
+ {
+ fprintf(stderr, "*");
+ }
+ else
+ {
+ if (j == 32)
+ {
+ fprintf(stderr, "|");
+ }
+ else
+ {
+ fprintf(stderr, " ");
+ }
+ }
+ }
+ }
+
+ fprintf(stderr, "|\n");
+ }
+
+ fprintf(stderr,
+ "+----------------------------------------------------------------+\n");
+ }
+ }
+ } // m_opts.datascope == 1
+
+ strncpy(m_synctest, (m_synctest_p - 23), 24);
+
+ if (m_opts.frame_p25p1 == 1)
+ {
+ if (strcmp(m_synctest, P25P1_SYNC) == 0)
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ sprintf(m_state.ftype, " P25 Phase 1 ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" +P25p1 ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = 0;
+ return(0);
+ }
+ if (strcmp(m_synctest, INV_P25P1_SYNC) == 0)
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ sprintf(m_state.ftype, " P25 Phase 1 ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" -P25p1 ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = 1;
+ return(1);
+ }
+ }
+ if (m_opts.frame_x2tdma == 1)
+ {
+ if ((strcmp(m_synctest, X2TDMA_BS_DATA_SYNC) == 0)
+ || (strcmp(m_synctest, X2TDMA_MS_DATA_SYNC) == 0))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + (m_lmax)) / 2;
+ m_state.min = ((m_state.min) + (m_lmin)) / 2;
+
+ if (m_opts.inverted_x2tdma == 0)
+ {
+ // data frame
+ sprintf(m_state.ftype, " X2-TDMA ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" +X2-TDMA ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = 2;
+ return(2); // done
+ }
+ else
+ {
+ // inverted voice frame
+ sprintf(m_state.ftype, " X2-TDMA ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" -X2-TDMA ", m_synctest_pos + 1, m_modulation);
+ }
+
+ if (m_state.lastsynctype != 3)
+ {
+ m_state.firstframe = 1;
+ }
+
+ m_state.lastsynctype = 3;
+ return(3); // done
+ }
+ }
+ if ((strcmp(m_synctest, X2TDMA_BS_VOICE_SYNC) == 0)
+ || (strcmp(m_synctest, X2TDMA_MS_VOICE_SYNC) == 0))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ if (m_opts.inverted_x2tdma == 0)
+ {
+ // voice frame
+ sprintf(m_state.ftype, " X2-TDMA ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" +X2-TDMA ", m_synctest_pos + 1, m_modulation);
+ }
+
+ if (m_state.lastsynctype != 4)
+ {
+ m_state.firstframe = 1;
+ }
+
+ m_state.lastsynctype = 4;
+ return(4); // done
+ }
+ else
+ {
+ // inverted data frame
+ sprintf(m_state.ftype, " X2-TDMA ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" -X2-TDMA ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = 5;
+ return(5); // done
+ }
+ }
+ }
+ if (m_opts.frame_dmr == 1)
+ {
+ if ((strcmp(m_synctest, DMR_MS_DATA_SYNC) == 0)
+ || (strcmp(m_synctest, DMR_BS_DATA_SYNC) == 0))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + (m_lmax)) / 2;
+ m_state.min = ((m_state.min) + (m_lmin)) / 2;
+
+ if (m_opts.inverted_dmr == 0)
+ {
+ // data frame
+ sprintf(m_state.ftype, " DMR ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" +DMR ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = 10;
+ return(10); // done
+ }
+ else
+ {
+ // inverted voice frame
+ sprintf(m_state.ftype, " DMR ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" -DMR ", m_synctest_pos + 1, m_modulation);
+ }
+
+ if (m_state.lastsynctype != 11)
+ {
+ m_state.firstframe = 1;
+ }
+
+ m_state.lastsynctype = 11;
+ return(11); // done
+ }
+ }
+ if ((strcmp(m_synctest, DMR_MS_VOICE_SYNC) == 0)
+ || (strcmp(m_synctest, DMR_BS_VOICE_SYNC) == 0))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ if (m_opts.inverted_dmr == 0)
+ {
+ // voice frame
+ sprintf(m_state.ftype, " DMR ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" +DMR ", m_synctest_pos + 1, m_modulation);
+ }
+
+ if (m_state.lastsynctype != 12)
+ {
+ m_state.firstframe = 1;
+ }
+
+ m_state.lastsynctype = 12;
+ return(12); // done
+ }
+ else
+ {
+ // inverted data frame
+ sprintf(m_state.ftype, " DMR ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" -DMR ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = 13;
+ return(13); // done
+ }
+ }
+ }
+ if (m_opts.frame_provoice == 1)
+ {
+ strncpy(m_synctest32, (m_synctest_p - 31), 32);
+
+ if ((strcmp(m_synctest32, PROVOICE_SYNC) == 0)
+ || (strcmp(m_synctest32, PROVOICE_EA_SYNC) == 0))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ sprintf(m_state.ftype, " ProVoice ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" -ProVoice ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = 14;
+ return(14); // done
+ }
+ else if ((strcmp(m_synctest32, INV_PROVOICE_SYNC) == 0)
+ || (strcmp(m_synctest32, INV_PROVOICE_EA_SYNC) == 0))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ sprintf(m_state.ftype, " ProVoice ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" -ProVoice ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = 15;
+ return(15); // done
+ }
+
+ }
+ if ((m_opts.frame_nxdn96 == 1) || (m_opts.frame_nxdn48 == 1))
+ {
+ strncpy(m_synctest18, (m_synctest_p - 17), 18);
+
+ if ((strcmp(m_synctest18, NXDN_BS_VOICE_SYNC) == 0)
+ || (strcmp(m_synctest18, NXDN_MS_VOICE_SYNC) == 0))
+ {
+ if ((m_state.lastsynctype == 8)
+ || (m_state.lastsynctype == 16))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ if (m_state.samplesPerSymbol == 20)
+ {
+ sprintf(m_state.ftype, " NXDN48 ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" +NXDN48 ", m_synctest_pos + 1, m_modulation);
+ }
+ }
+ else
+ {
+ sprintf(m_state.ftype, " NXDN96 ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" +NXDN96 ", m_synctest_pos + 1, m_modulation);
+ }
+ }
+
+ m_state.lastsynctype = 8;
+ return(8); // done
+ }
+ else
+ {
+ m_state.lastsynctype = 8;
+ }
+ }
+ else if ((strcmp(m_synctest18, INV_NXDN_BS_VOICE_SYNC) == 0)
+ || (strcmp(m_synctest18, INV_NXDN_MS_VOICE_SYNC) == 0))
+ {
+ if ((m_state.lastsynctype == 9)
+ || (m_state.lastsynctype == 17))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ if (m_state.samplesPerSymbol == 20)
+ {
+ sprintf(m_state.ftype, " NXDN48 ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" -NXDN48 ", m_synctest_pos + 1, m_modulation);
+ }
+ }
+ else
+ {
+ sprintf(m_state.ftype, " NXDN96 ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" -NXDN96 ", m_synctest_pos + 1, m_modulation);
+ }
+ }
+
+ m_state.lastsynctype = 9;
+ return(9); // done
+ }
+ else
+ {
+ m_state.lastsynctype = 9;
+ }
+ }
+ else if ((strcmp(m_synctest18, NXDN_BS_DATA_SYNC) == 0)
+ || (strcmp(m_synctest18, NXDN_MS_DATA_SYNC) == 0))
+ {
+ if ((m_state.lastsynctype == 8)
+ || (m_state.lastsynctype == 16))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ if (m_state.samplesPerSymbol == 20)
+ {
+ sprintf(m_state.ftype, " NXDN48 ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" +NXDN48 ", m_synctest_pos + 1, m_modulation);
+ }
+ }
+ else
+ {
+ sprintf(m_state.ftype, " NXDN96 ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" +NXDN96 ", m_synctest_pos + 1, m_modulation);
+ }
+ }
+
+ m_state.lastsynctype = 16;
+ return(16); // done
+ }
+ else
+ {
+ m_state.lastsynctype = 16;
+ }
+ }
+ else if ((strcmp(m_synctest18, INV_NXDN_BS_DATA_SYNC) == 0)
+ || (strcmp(m_synctest18, INV_NXDN_MS_DATA_SYNC) == 0))
+ {
+ if ((m_state.lastsynctype == 9)
+ || (m_state.lastsynctype == 17))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ sprintf(m_state.ftype, " NXDN ");
+
+ if (m_state.samplesPerSymbol == 20)
+ {
+ sprintf(m_state.ftype, " NXDN48 ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" -NXDN48 ", m_synctest_pos + 1, m_modulation);
+ }
+ }
+ else
+ {
+ sprintf(m_state.ftype, " NXDN96 ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" -NXDN96 ", m_synctest_pos + 1, m_modulation);
+ }
+ }
+
+ m_state.lastsynctype = 17;
+ return(17); // done
+ }
+ else
+ {
+ m_state.lastsynctype = 17;
+ }
+ }
+ }
+ if (m_opts.frame_dstar == 1)
+ {
+ if (strcmp(m_synctest, DSTAR_SYNC) == 0)
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ sprintf(m_state.ftype, " D-STAR ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" +D-STAR ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = 6;
+ return(6);
+ }
+ if (strcmp(m_synctest, INV_DSTAR_SYNC) == 0)
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ sprintf(m_state.ftype, " D-STAR ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" -D-STAR ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = 7;
+ return(7); // done
+ }
+ if (strcmp(m_synctest, DSTAR_HD) == 0)
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ sprintf(m_state.ftype, " D-STAR_HD ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" +D-STAR_HD ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = 18;
+ return(18); // done
+ }
+ if (strcmp(m_synctest, INV_DSTAR_HD) == 0)
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ sprintf(m_state.ftype, " D-STAR_HD ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync(" -D-STAR_HD ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = 19;
+ return(19);
+ }
+ }
+
+ if ((m_t == 24) && (m_state.lastsynctype != -1))
+ {
+ if ((m_state.lastsynctype == 0)
+ && ((m_state.lastp25type == 1)
+ || (m_state.lastp25type == 2)))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + (m_lmax)) / 2;
+ m_state.min = ((m_state.min) + (m_lmin)) / 2;
+
+ sprintf(m_state.ftype, "(P25 Phase 1)");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync("(+P25p1) ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = -1;
+ return(0); // done
+ }
+ else if ((m_state.lastsynctype == 1) && ((m_state.lastp25type == 1) || (m_state.lastp25type == 2)))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+ sprintf(m_state.ftype, "(P25 Phase 1)");
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync("(-P25p1) ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = -1;
+ return(1); // done
+ }
+ else if ((m_state.lastsynctype == 3) && ((strcmp(m_synctest, X2TDMA_BS_VOICE_SYNC) != 0) || (strcmp(m_synctest, X2TDMA_MS_VOICE_SYNC) != 0)))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ sprintf(m_state.ftype, "(X2-TDMA) ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync("(-X2-TDMA) ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = -1;
+ return(3);
+ }
+ else if ((m_state.lastsynctype == 4) && ((strcmp(m_synctest, X2TDMA_BS_DATA_SYNC) != 0) || (strcmp(m_synctest, X2TDMA_MS_DATA_SYNC) != 0)))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ sprintf(m_state.ftype, "(X2-TDMA) ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync("(+X2-TDMA) ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = -1;
+ return(4);
+ }
+ else if ((m_state.lastsynctype == 11) && ((strcmp(m_synctest, DMR_BS_VOICE_SYNC) != 0) || (strcmp(m_synctest, DMR_MS_VOICE_SYNC) != 0)))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ sprintf(m_state.ftype, "(DMR) ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync("(-DMR) ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = -1;
+ return(11); // done
+ }
+ else if ((m_state.lastsynctype == 12) && ((strcmp(m_synctest, DMR_BS_DATA_SYNC) != 0) || (strcmp(m_synctest, DMR_MS_DATA_SYNC) != 0)))
+ {
+ m_state.carrier = 1;
+ m_state.offset = m_synctest_pos;
+ m_state.max = ((m_state.max) + m_lmax) / 2;
+ m_state.min = ((m_state.min) + m_lmin) / 2;
+
+ sprintf(m_state.ftype, "(DMR) ");
+
+ if (m_opts.errorbars == 1)
+ {
+ printFrameSync("(+DMR) ", m_synctest_pos + 1, m_modulation);
+ }
+
+ m_state.lastsynctype = -1;
+ return(12); // done
+ }
+ }
+ }
+
+ if (m_synctest_pos < 10200)
+ {
+ m_synctest_pos++;
+ m_synctest_p++;
+ }
+ else
+ {
+ // buffer reset
+ m_synctest_pos = 0;
+ m_synctest_p = m_synctest_buf;
+ noCarrier();
+ }
+
+ if (m_state.lastsynctype != 1)
+ {
+ if (m_synctest_pos >= 1800)
+ {
+ if ((m_opts.errorbars == 1) && (m_opts.verbose > 1)
+ && (m_state.carrier == 1))
+ {
+ printf("Sync: no sync\n");
+ }
+
+ noCarrier();
+ return(-1); // done
+ }
+ }
+
+ return(-2); // still searching
+}
+
+void DSDDecoder::noCarrier()
+{
+ m_state.dibit_buf_p = m_state.dibit_buf + 200;
+ memset(m_state.dibit_buf, 0, sizeof(int) * 200);
+ m_state.jitter = -1;
+ m_state.lastsynctype = -1;
+ m_state.carrier = 0;
+ m_state.max = 15000;
+ m_state.min = -15000;
+ m_state.center = 0;
+ m_state.err_str[0] = 0;
+
+ sprintf(m_state.fsubtype, " ");
+ sprintf(m_state.ftype, " ");
+
+ m_state.errs = 0;
+ m_state.errs2 = 0;
+ m_state.lasttg = 0;
+ m_state.lastsrc = 0;
+ m_state.lastp25type = 0;
+ m_state.repeat = 0;
+ m_state.nac = 0;
+ m_state.numtdulc = 0;
+
+ sprintf(m_state.slot0light, " slot0 ");
+ sprintf(m_state.slot1light, " slot1 ");
+
+ m_state.firstframe = 0;
+
+ if (m_opts.audio_gain == (float) 0)
+ {
+ m_state.aout_gain = 25;
+ }
+
+ memset(m_state.aout_max_buf, 0, sizeof(float) * 200);
+ m_state.aout_max_buf_p = m_state.aout_max_buf;
+ m_state.aout_max_buf_idx = 0;
+ sprintf(m_state.algid, "________");
+ sprintf(m_state.keyid, "________________");
+ mbe_initMbeParms(m_state.cur_mp, m_state.prev_mp, m_state.prev_mp_enhanced);
+}
+
+void DSDDecoder::printFrameInfo()
+{
+
+ int level = (int) m_state.max / 164;
+
+ if (m_opts.verbose > 0)
+ {
+ fprintf(stderr, "inlvl: %2i%% ", level);
+ }
+ if (m_state.nac != 0)
+ {
+ fprintf(stderr, "nac: %4X ", m_state.nac);
+ }
+
+ if (m_opts.verbose > 1)
+ {
+ fprintf(stderr, "src: %8i ", m_state.lastsrc);
+ }
+
+ fprintf(stderr, "tg: %5i ", m_state.lasttg);
+}
+
+int DSDDecoder::comp(const void *a, const void *b)
+{
+ if (*((const int *) a) == *((const int *) b))
+ return 0;
+ else if (*((const int *) a) < *((const int *) b))
+ return -1;
+ else
+ return 1;
+}
+
+void DSDDecoder::processFrame()
+{
+ if (m_state.rf_mod == 1)
+ {
+ m_state.maxref = (m_state.max * 0.80);
+ m_state.minref = (m_state.min * 0.80);
+ }
+ else
+ {
+ m_state.maxref = m_state.max;
+ m_state.minref = m_state.min;
+ }
+
+ if ((m_state.synctype >= 10) && (m_state.synctype <= 13))
+ {
+ m_state.nac = 0;
+ m_state.lastsrc = 0;
+ m_state.lasttg = 0;
+
+ if (m_opts.errorbars == 1)
+ {
+ if (m_opts.verbose > 0)
+ {
+ int level = (int) m_state.max / 164;
+ fprintf(stderr, "inlvl: %2i%% ", level);
+ }
+ }
+
+ if ((m_state.synctype == 11) || (m_state.synctype == 12))
+ {
+ sprintf(m_state.fsubtype, " VOICE ");
+ m_dsdDMRVoice.init();
+ m_fsmState = DSDprocessDMRvoice;
+ }
+ else
+ {
+ m_state.err_str[0] = 0;
+ m_fsmState = DSDprocessDMRdata;
+ }
+ }
+ else if ((m_state.synctype == 6) || (m_state.synctype == 7))
+ {
+ m_state.nac = 0;
+ m_state.lastsrc = 0;
+ m_state.lasttg = 0;
+
+ if (m_opts.errorbars == 1)
+ {
+ if (m_opts.verbose > 0)
+ {
+ int level = (int) m_state.max / 164;
+ printf("inlvl: %2i%% ", level);
+ }
+ }
+
+ m_state.nac = 0;
+ sprintf(m_state.fsubtype, " VOICE ");
+ m_fsmState = DSDprocessDSTAR;
+ }
+ else if ((m_state.synctype == 18) || (m_state.synctype == 19))
+ {
+ m_state.nac = 0;
+ m_state.lastsrc = 0;
+ m_state.lasttg = 0;
+
+ if (m_opts.errorbars == 1)
+ {
+ if (m_opts.verbose > 0)
+ {
+ int level = (int) m_state.max / 164;
+ fprintf(stderr, "inlvl: %2i%% ", level);
+ }
+ }
+
+ m_state.nac = 0;
+ sprintf(m_state.fsubtype, " DATA ");
+ m_fsmState = DSDprocessDSTAR_HD;
+ }
+ else
+ {
+ noCarrier();
+ m_fsmState = DSDLookForSync;
+ }
+}
+
+} // namespace dsdplus
diff --git a/dsdplus/dsd_decoder.h b/dsdplus/dsd_decoder.h
new file mode 100644
index 000000000..8dfe74590
--- /dev/null
+++ b/dsdplus/dsd_decoder.h
@@ -0,0 +1,130 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2016 Edouard Griffiths, F4EXB. //
+// //
+// 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 as version 3 of the License, or //
+// //
+// 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 V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef DSDPLUS_DSD_DECODER_H_
+#define DSDPLUS_DSD_DECODER_H_
+
+#include "dsd_opts.h"
+#include "dsd_state.h"
+#include "dsd_filters.h"
+#include "dmr_voice.h"
+
+/*
+ * Frame sync patterns
+ */
+#define INV_P25P1_SYNC "333331331133111131311111"
+#define P25P1_SYNC "111113113311333313133333"
+
+#define X2TDMA_BS_VOICE_SYNC "113131333331313331113311"
+#define X2TDMA_BS_DATA_SYNC "331313111113131113331133"
+#define X2TDMA_MS_DATA_SYNC "313113333111111133333313"
+#define X2TDMA_MS_VOICE_SYNC "131331111333333311111131"
+
+#define DSTAR_HD "131313131333133113131111"
+#define INV_DSTAR_HD "313131313111311331313333"
+#define DSTAR_SYNC "313131313133131113313111"
+#define INV_DSTAR_SYNC "131313131311313331131333"
+
+#define NXDN_MS_DATA_SYNC "313133113131111333"
+#define INV_NXDN_MS_DATA_SYNC "131311331313333111"
+#define NXDN_MS_VOICE_SYNC "313133113131113133"
+#define INV_NXDN_MS_VOICE_SYNC "131311331313331311"
+#define INV_NXDN_BS_DATA_SYNC "131311331313333131"
+#define NXDN_BS_DATA_SYNC "313133113131111313"
+#define INV_NXDN_BS_VOICE_SYNC "131311331313331331"
+#define NXDN_BS_VOICE_SYNC "313133113131113113"
+
+#define DMR_BS_DATA_SYNC "313333111331131131331131"
+#define DMR_BS_VOICE_SYNC "131111333113313313113313"
+#define DMR_MS_DATA_SYNC "311131133313133331131113"
+#define DMR_MS_VOICE_SYNC "133313311131311113313331"
+
+#define INV_PROVOICE_SYNC "31313111333133133311331133113311"
+#define PROVOICE_SYNC "13131333111311311133113311331133"
+#define INV_PROVOICE_EA_SYNC "13313133113113333311313133133311"
+#define PROVOICE_EA_SYNC "31131311331331111133131311311133"
+
+namespace DSDPlus
+{
+
+class DSDDecoder
+{
+ friend class DSDDMRVoice;
+public:
+ typedef enum
+ {
+ DSDLookForSync,
+ DSDNoSync,
+ DSDprocessFrame,
+ DSDprocessNXDNVoice,
+ DSDprocessNXDNData,
+ DSDprocessDSTAR,
+ DSDprocessDSTAR_HD,
+ DSDprocessDMRvoice,
+ DSDprocessDMRdata,
+ DSDprocessX2TDMAvoice,
+ DSDprocessX2TDMAdata,
+ DSDprocessProVoice,
+ DSDprocessUnknown
+ } DSDFSMState;
+
+ DSDDecoder();
+ ~DSDDecoder();
+
+ void run(short sample);
+
+private:
+ bool pushSample(short sample, int have_sync); //!< push a new sample into the decoder. Returns true if a new symbol is available
+ int getFrameSync();
+ void resetSymbol();
+ void resetFrameSync();
+ void printFrameSync(const char *frametype, int offset, char *modulation);
+ void noCarrier();
+ void printFrameInfo();
+ void processFrame();
+ static int comp(const void *a, const void *b);
+
+ DSDOpts m_opts;
+ DSDState m_state;
+ DSDFSMState m_fsmState;
+ // symbol engine
+ int m_symbol; //!< the last retrieved symbol
+ int m_sampleIndex; //!< the current sample index for the symbol in progress
+ int m_sum;
+ int m_count;
+ // sync engine:
+ int m_sync; //!< The current sync type
+ int m_dibit, m_synctest_pos, m_lastt;
+ char m_synctest[25];
+ char m_synctest18[19];
+ char m_synctest32[33];
+ char m_modulation[8];
+ char *m_synctest_p;
+ char m_synctest_buf[10240];
+ int m_lmin, m_lmax, m_lidx;
+ int m_lbuf[24], m_lbuf2[24];
+ int m_lsum;
+ char m_spectrum[64];
+ int m_t;
+ // Other
+ DSDFilters m_dsdFilters;
+ // Frame decoders
+ DSDDMRVoice m_dsdDMRVoice;
+};
+
+} // namespace dsdplus
+
+#endif /* DSDPLUS_DSD_DECODER_H_ */
diff --git a/dsdplus/dsd_filters.cpp b/dsdplus/dsd_filters.cpp
new file mode 100644
index 000000000..5c1aeffc6
--- /dev/null
+++ b/dsdplus/dsd_filters.cpp
@@ -0,0 +1,141 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2016 Edouard Griffiths, F4EXB. //
+// //
+// 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 as version 3 of the License, or //
+// //
+// 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 V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include "dsd_filters.h"
+
+namespace DSDPlus
+{
+
+// DMR filter
+const float DSDFilters::xcoeffs[] =
+{ -0.0083649323f, -0.0265444850f, -0.0428141462f, -0.0537571943f,
+ -0.0564141052f, -0.0489161045f, -0.0310068662f, -0.0043393881f,
+ +0.0275375106f, +0.0595423283f, +0.0857543325f, +0.1003565948f,
+ +0.0986944931f, +0.0782804830f, +0.0395670487f, -0.0136691535f,
+ -0.0744390415f, -0.1331834575f, -0.1788967208f, -0.2005995448f,
+ -0.1889627181f, -0.1378439993f, -0.0454976231f, +0.0847488694f,
+ +0.2444859269f, +0.4209222342f, +0.5982295474f, +0.7593684540f,
+ +0.8881539892f, +0.9712773915f, +0.9999999166f, +0.9712773915f,
+ +0.8881539892f, +0.7593684540f, +0.5982295474f, +0.4209222342f,
+ +0.2444859269f, +0.0847488694f, -0.0454976231f, -0.1378439993f,
+ -0.1889627181f, -0.2005995448f, -0.1788967208f, -0.1331834575f,
+ -0.0744390415f, -0.0136691535f, +0.0395670487f, +0.0782804830f,
+ +0.0986944931f, +0.1003565948f, +0.0857543325f, +0.0595423283f,
+ +0.0275375106f, -0.0043393881f, -0.0310068662f, -0.0489161045f,
+ -0.0564141052f, -0.0537571943f, -0.0428141462f, -0.0265444850f,
+ -0.0083649323f, };
+
+// NXDN filter
+const float DSDFilters::nxcoeffs[] =
+{ +0.031462429f, +0.031747267f, +0.030401148f, +0.027362877f, +0.022653298f,
+ +0.016379869f, +0.008737200f, +0.000003302f, -0.009468531f,
+ -0.019262057f, -0.028914291f, -0.037935027f, -0.045828927f,
+ -0.052119261f, -0.056372283f, -0.058221106f, -0.057387924f,
+ -0.053703443f, -0.047122444f, -0.037734535f, -0.025769308f,
+ -0.011595336f, +0.004287292f, +0.021260954f, +0.038610717f,
+ +0.055550276f, +0.071252765f, +0.084885375f, +0.095646450f,
+ +0.102803611f, +0.105731303f, +0.103946126f, +0.097138329f,
+ +0.085197939f, +0.068234131f, +0.046586711f, +0.020828821f,
+ -0.008239664f, -0.039608255f, -0.072081234f, -0.104311776f,
+ -0.134843790f, -0.162160200f, -0.184736015f, -0.201094346f,
+ -0.209863285f, -0.209831516f, -0.200000470f, -0.179630919f,
+ -0.148282051f, -0.105841323f, -0.052543664f, +0.011020985f,
+ +0.083912428f, +0.164857408f, +0.252278939f, +0.344336996f,
+ +0.438979335f, +0.534000832f, +0.627109358f, +0.715995947f,
+ +0.798406824f, +0.872214756f, +0.935487176f, +0.986548646f,
+ +1.024035395f, +1.046939951f, +1.054644241f, +1.046939951f,
+ +1.024035395f, +0.986548646f, +0.935487176f, +0.872214756f,
+ +0.798406824f, +0.715995947f, +0.627109358f, +0.534000832f,
+ +0.438979335f, +0.344336996f, +0.252278939f, +0.164857408f,
+ +0.083912428f, +0.011020985f, -0.052543664f, -0.105841323f,
+ -0.148282051f, -0.179630919f, -0.200000470f, -0.209831516f,
+ -0.209863285f, -0.201094346f, -0.184736015f, -0.162160200f,
+ -0.134843790f, -0.104311776f, -0.072081234f, -0.039608255f,
+ -0.008239664f, +0.020828821f, +0.046586711f, +0.068234131f,
+ +0.085197939f, +0.097138329f, +0.103946126f, +0.105731303f,
+ +0.102803611f, +0.095646450f, +0.084885375f, +0.071252765f,
+ +0.055550276f, +0.038610717f, +0.021260954f, +0.004287292f,
+ -0.011595336f, -0.025769308f, -0.037734535f, -0.047122444f,
+ -0.053703443f, -0.057387924f, -0.058221106f, -0.056372283f,
+ -0.052119261f, -0.045828927f, -0.037935027f, -0.028914291f,
+ -0.019262057f, -0.009468531f, +0.000003302f, +0.008737200f,
+ +0.016379869f, +0.022653298f, +0.027362877f, +0.030401148f,
+ +0.031747267f, +0.031462429f, };
+
+DSDFilters::DSDFilters()
+{
+ for (int i=0; i < NZEROS+1; i++) {
+ xv[i] = 0.0f;
+ }
+
+ for (int i=0; i < NXZEROS+1; i++) {
+ nxv[i] = 0.0f;
+ }
+}
+
+short DSDFilters::dmr_filter(short sample)
+{
+ return dsd_input_filter(sample, 1);
+}
+
+short DSDFilters::nxdn_filter(short sample)
+{
+ return dsd_input_filter(sample, 2);
+}
+
+short DSDFilters::dsd_input_filter(short sample, int mode)
+{
+ float sum;
+ int i;
+ float gain;
+ int zeros;
+ float *v;
+ const float *coeffs;
+
+ switch (mode)
+ {
+ case 1:
+ gain = ngain;
+ v = xv;
+ coeffs = xcoeffs;
+ zeros = NZEROS;
+ break;
+ case 2:
+ gain = nxgain;
+ v = nxv;
+ coeffs = nxcoeffs;
+ zeros = NXZEROS;
+ break;
+ default:
+ return sample;
+ }
+
+ for (i = 0; i < zeros; i++) {
+ v[i] = v[i + 1];
+ }
+
+ v[zeros] = sample; // unfiltered sample in
+ sum = 0.0f;
+
+ for (i = 0; i <= zeros; i++) {
+ sum += (coeffs[i] * v[i]);
+ }
+
+ return sum / ngain; // filtered sample out
+}
+
+} // namespace dsdplus
+
diff --git a/dsdplus/dsd_filters.h b/dsdplus/dsd_filters.h
new file mode 100644
index 000000000..77f83d491
--- /dev/null
+++ b/dsdplus/dsd_filters.h
@@ -0,0 +1,49 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2016 Edouard Griffiths, F4EXB. //
+// //
+// 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 as version 3 of the License, or //
+// //
+// 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 V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef DSDPLUS_DSD_FILTERS_H_
+#define DSDPLUS_DSD_FILTERS_H_
+
+#define NZEROS 60
+#define NXZEROS 134
+
+
+namespace DSDPlus
+{
+
+class DSDFilters
+{
+public:
+ DSDFilters();
+ ~DSDFilters();
+
+ static const float ngain = 7.423339364f;
+ static const float xcoeffs[];
+ static const float nxgain = 15.95930463f;
+ static const float nxcoeffs[];
+
+ short dsd_input_filter(short sample, int mode);
+ short dmr_filter(short sample);
+ short nxdn_filter(short sample);
+
+private:
+ float xv[NZEROS+1];
+ float nxv[NXZEROS+1];
+};
+
+}
+
+#endif /* DSDPLUS_DSD_FILTERS_H_ */
diff --git a/dsdplus/dsd_opts.cpp b/dsdplus/dsd_opts.cpp
new file mode 100644
index 000000000..cecdf45fb
--- /dev/null
+++ b/dsdplus/dsd_opts.cpp
@@ -0,0 +1,65 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2016 Edouard Griffiths, F4EXB. //
+// //
+// 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 as version 3 of the License, or //
+// //
+// 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 V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include "dsd_opts.h"
+
+namespace DSDPlus
+{
+
+DSDOpts::DSDOpts()
+{
+ onesymbol = 10;
+ errorbars = 1;
+ datascope = 0;
+ symboltiming = 0;
+ verbose = 2;
+ p25enc = 0;
+ p25lc = 0;
+ p25status = 0;
+ p25tg = 0;
+ scoperate = 15;
+ split = 0;
+ playoffset = 0;
+ audio_gain = 0;
+ audio_out = 1;
+ resume = 0;
+ frame_dstar = 0;
+ frame_x2tdma = 1;
+ frame_p25p1 = 1;
+ frame_nxdn48 = 0;
+ frame_nxdn96 = 1;
+ frame_dmr = 1;
+ frame_provoice = 0;
+ mod_c4fm = 1;
+ mod_qpsk = 1;
+ mod_gfsk = 1;
+ uvquality = 3;
+ inverted_x2tdma = 1; // most transmitter + scanner + sound card combinations show inverted signals for this
+ inverted_dmr = 0; // most transmitter + scanner + sound card combinations show non-inverted signals for this
+ mod_threshold = 26;
+ ssize = 36;
+ msize = 15;
+ delay = 0;
+ use_cosine_filter = 1;
+ unmute_encrypted_p25 = 0;
+}
+
+DSDOpts::~DSDOpts()
+{
+}
+
+} // namespace dsdplus
+
diff --git a/dsdplus/dsd_opts.h b/dsdplus/dsd_opts.h
new file mode 100644
index 000000000..4cea3c200
--- /dev/null
+++ b/dsdplus/dsd_opts.h
@@ -0,0 +1,67 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2016 Edouard Griffiths, F4EXB. //
+// //
+// 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 as version 3 of the License, or //
+// //
+// 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 V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef DSDPLUS_DSD_OPTS_H_
+#define DSDPLUS_DSD_OPTS_H_
+
+namespace DSDPlus
+{
+
+class DSDOpts
+{
+public:
+ DSDOpts();
+ ~DSDOpts();
+
+ int onesymbol;
+ int errorbars;
+ int datascope;
+ int symboltiming;
+ int verbose;
+ int p25enc;
+ int p25lc;
+ int p25status;
+ int p25tg;
+ int scoperate;
+ int split;
+ int playoffset;
+ float audio_gain;
+ int audio_out;
+ int resume;
+ int frame_dstar;
+ int frame_x2tdma;
+ int frame_p25p1;
+ int frame_nxdn48;
+ int frame_nxdn96;
+ int frame_dmr;
+ int frame_provoice;
+ int mod_c4fm;
+ int mod_qpsk;
+ int mod_gfsk;
+ int uvquality;
+ int inverted_x2tdma;
+ int inverted_dmr;
+ int mod_threshold;
+ int ssize;
+ int msize;
+ int delay;
+ int use_cosine_filter;
+ int unmute_encrypted_p25;
+};
+
+} // namespace dsdplus
+
+#endif /* DSDPLUS_DSD_OPTS_H_ */
diff --git a/dsdplus/dsd_state.cpp b/dsdplus/dsd_state.cpp
new file mode 100644
index 000000000..e6195b4a3
--- /dev/null
+++ b/dsdplus/dsd_state.cpp
@@ -0,0 +1,128 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2016 Edouard Griffiths, F4EXB. //
+// //
+// 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 as version 3 of the License, or //
+// //
+// 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 V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#include "dsd_state.h"
+
+namespace DSDPlus
+{
+
+DSDState::DSDState()
+{
+ int i, j;
+
+ dibit_buf = (int *) malloc(sizeof(int) * 1000000);
+ dibit_buf_p = dibit_buf + 200;
+ memset (dibit_buf, 0, sizeof (int) * 200);
+ repeat = 0;
+ audio_out_buf = (short *) malloc(sizeof(short) * 1000000);
+ memset (audio_out_buf, 0, 100 * sizeof (short));
+ audio_out_buf_p = audio_out_buf + 100;
+ audio_out_float_buf = (float *) malloc(sizeof(float) * 1000000);
+ memset (audio_out_float_buf, 0, 100 * sizeof (float));
+ audio_out_float_buf_p = audio_out_float_buf + 100;
+ audio_out_idx = 0;
+ audio_out_idx2 = 0;
+ audio_out_temp_buf_p = audio_out_temp_buf;
+ center = 0;
+ jitter = -1;
+ synctype = -1;
+ min = -15000;
+ max = 15000;
+ lmid = 0;
+ umid = 0;
+ minref = -12000;
+ maxref = 12000;
+ lastsample = 0;
+ for (i = 0; i < 128; i++)
+ {
+ sbuf[i] = 0;
+ }
+ sidx = 0;
+ for (i = 0; i < 1024; i++)
+ {
+ maxbuf[i] = 15000;
+ }
+ for (i = 0; i < 1024; i++)
+ {
+ minbuf[i] = -15000;
+ }
+ midx = 0;
+ err_str[0] = 0;
+ sprintf (fsubtype, " ");
+ sprintf (ftype, " ");
+ symbolcnt = 0;
+ rf_mod = 0;
+ numflips = 0;
+ lastsynctype = -1;
+ lastp25type = 0;
+ offset = 0;
+ carrier = 0;
+ for (i = 0; i < 25; i++)
+ {
+ for (j = 0; j < 16; j++)
+ {
+ tg[i][j] = 48;
+ }
+ }
+ tgcount = 0;
+ lasttg = 0;
+ lastsrc = 0;
+ nac = 0;
+ errs = 0;
+ errs2 = 0;
+ mbe_file_type = -1;
+ optind = 0;
+ numtdulc = 0;
+ firstframe = 0;
+ sprintf (slot0light, " slot0 ");
+ sprintf (slot1light, " slot1 ");
+ aout_gain = 25;
+ memset (aout_max_buf, 0, sizeof (float) * 200);
+ aout_max_buf_p = aout_max_buf;
+ aout_max_buf_idx = 0;
+ samplesPerSymbol = 10;
+ symbolCenter = 4;
+ sprintf (algid, "________");
+ sprintf (keyid, "________________");
+ currentslot = 0;
+ cur_mp = (mbe_parms *) malloc (sizeof (mbe_parms));
+ prev_mp = (mbe_parms *) malloc (sizeof (mbe_parms));
+ prev_mp_enhanced = (mbe_parms *) malloc (sizeof (mbe_parms));
+ mbe_initMbeParms (cur_mp, prev_mp, prev_mp_enhanced);
+ p25kid = 0;
+
+ output_finished = 0;
+ input_offset = 0;
+ output_offset = 0;
+ input_samples = 0;
+ output_num_samples = 0;
+ output_samples = 0;
+ input_length = 0;
+ output_length = 0;
+ output_buffer = 0;
+}
+
+DSDState::~DSDState()
+{
+ free(prev_mp_enhanced);
+ free(prev_mp);
+ free(cur_mp);
+ free(audio_out_float_buf);
+ free(audio_out_buf);
+ free(dibit_buf);
+}
+
+} // namespace dsdplus
diff --git a/dsdplus/dsd_state.h b/dsdplus/dsd_state.h
new file mode 100644
index 000000000..81b573de8
--- /dev/null
+++ b/dsdplus/dsd_state.h
@@ -0,0 +1,111 @@
+///////////////////////////////////////////////////////////////////////////////////
+// Copyright (C) 2016 Edouard Griffiths, F4EXB. //
+// //
+// 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 as version 3 of the License, or //
+// //
+// 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 V3 for more details. //
+// //
+// You should have received a copy of the GNU General Public License //
+// along with this program. If not, see . //
+///////////////////////////////////////////////////////////////////////////////////
+
+#ifndef DSDPLUS_DSD_STATE_H_
+#define DSDPLUS_DSD_STATE_H_
+
+#include
+#include
+#include
+#include
+
+namespace DSDPlus
+{
+
+class DSDState
+{
+public:
+ DSDState();
+ ~DSDState();
+
+ int *dibit_buf;
+ int *dibit_buf_p;
+ int repeat;
+ short *audio_out_buf;
+ short *audio_out_buf_p;
+ float *audio_out_float_buf;
+ float *audio_out_float_buf_p;
+ float audio_out_temp_buf[160];
+ float *audio_out_temp_buf_p;
+ int audio_out_idx;
+ int audio_out_idx2;
+ int center;
+ int jitter;
+ int synctype;
+ int min;
+ int max;
+ int lmid;
+ int umid;
+ int minref;
+ int maxref;
+ int lastsample;
+ int sbuf[128];
+ int sidx;
+ int maxbuf[1024];
+ int minbuf[1024];
+ int midx;
+ char err_str[64];
+ char fsubtype[16];
+ char ftype[16];
+ int symbolcnt;
+ int rf_mod;
+ int numflips;
+ int lastsynctype;
+ int lastp25type;
+ int offset;
+ int carrier;
+ char tg[25][16];
+ int tgcount;
+ int lasttg;
+ int lastsrc;
+ int nac;
+ int errs;
+ int errs2;
+ int mbe_file_type;
+ int optind;
+ int numtdulc;
+ int firstframe;
+ char slot0light[8];
+ char slot1light[8];
+ float aout_gain;
+ float aout_max_buf[200];
+ float *aout_max_buf_p;
+ int aout_max_buf_idx;
+ int samplesPerSymbol;
+ int symbolCenter;
+ char algid[9];
+ char keyid[17];
+ int currentslot;
+ mbe_parms *cur_mp;
+ mbe_parms *prev_mp;
+ mbe_parms *prev_mp_enhanced;
+ int p25kid;
+
+ const float *input_samples;
+ int input_length;
+ int input_offset;
+
+ short *output_buffer;
+ int output_offset;
+ float *output_samples;
+ int output_num_samples;
+ int output_length;
+ int output_finished;
+};
+
+} // namespace dsdplus
+
+#endif /* DSDPLUS_DSD_STATE_H_ */