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_ */