diff --git a/DroidStar.pro b/DroidStar.pro index 1bff558..e289105 100644 --- a/DroidStar.pro +++ b/DroidStar.pro @@ -15,15 +15,14 @@ ios:QMAKE_INFO_PLIST = Info.plist VERSION_BUILD='$(shell cd $$PWD;git rev-parse --short HEAD)' DEFINES += VERSION_NUMBER=\"\\\"$${VERSION_BUILD}\\\"\" DEFINES += QT_DEPRECATED_WARNINGS - #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 #DEFINES += USE_FLITE -DEFINES += AMBESW_SUPPORTED SOURCES += \ CRCenc.cpp \ DMRData.cpp \ Golay24128.cpp \ + M17Convolution.cpp \ SHA256.cpp \ YSFConvolution.cpp \ YSFFICH.cpp \ @@ -97,6 +96,8 @@ HEADERS += \ DMRData.h \ DMRDefines.h \ Golay24128.h \ + M17Convolution.h \ + M17Defines.h \ SHA256.h \ YSFConvolution.h \ YSFFICH.h \ diff --git a/Golay24128.cpp b/Golay24128.cpp index 417da00..6d0e19a 100644 --- a/Golay24128.cpp +++ b/Golay24128.cpp @@ -1106,3 +1106,38 @@ unsigned int CGolay24128::decode24128(unsigned char* bytes) return decode23127(code >> 1); } + +bool CGolay24128::decode24128(unsigned int in, unsigned int& out) +{ + unsigned int syndrome = ::get_syndrome_23127(in >> 1); + unsigned int error_pattern = DECODING_TABLE_23127[syndrome] << 1; + + out = in ^ error_pattern; + + bool valid = (countBits(syndrome) < 3U) || !(countBits(out) & 1); + + out >>= 12; + + return valid; +} + +bool CGolay24128::decode24128(unsigned char* in, unsigned int& out) +{ + assert(in != NULL); + + unsigned int code = (in[0U] << 16) | (in[1U] << 8) | (in[2U] << 0); + + return decode24128(code, out); +} + +unsigned int CGolay24128::countBits(unsigned int v) +{ + unsigned int count = 0U; + + while (v != 0U) { + v &= v - 1U; + count++; + } + + return count; +} diff --git a/Golay24128.h b/Golay24128.h index 1ac7852..fc90496 100644 --- a/Golay24128.h +++ b/Golay24128.h @@ -27,6 +27,9 @@ public: static unsigned int decode23127(unsigned int code); static unsigned int decode24128(unsigned int code); static unsigned int decode24128(unsigned char* bytes); + static bool decode24128(unsigned int in, unsigned int& out); + static bool decode24128(unsigned char* in, unsigned int& out); + static unsigned int countBits(unsigned int v); }; #endif diff --git a/M17Convolution.cpp b/M17Convolution.cpp new file mode 100644 index 0000000..56e689d --- /dev/null +++ b/M17Convolution.cpp @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2020,2021 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "M17Convolution.h" + +#include +#include +#include +#include + +const unsigned int PUNCTURE_LIST_LINK_SETUP_COUNT = 60U; + +const unsigned int PUNCTURE_LIST_LINK_SETUP[] = { + 2U, 6U, 10U, 14U, 18U, 22U, 26U, 30U, 34U, 38U, 42U, 46U, 50U, 54U, 58U, 63U, 67U, 71U, 75U, 79U, 83U, + 87U, 91U, 95U, 99U, 103U, 107U, 111U, 115U, 119U, 124U, 128U, 132U, 136U, 140U, 144U, 148U, 152U, 156U, 160U, 164U, 168U, + 172U, 176U, 180U, 185U, 189U, 193U, 197U, 201U, 205U, 209U, 213U, 217U, 221U, 225U, 229U, 233U, 237U, 241U, 246U, 250U, 254U, + 258U, 262U, 266U, 270U, 274U, 278U, 282U, 286U, 290U, 294U, 298U, 302U, 307U, 311U, 315U, 319U, 323U, 327U, 331U, 335U, 339U, + 343U, 347U, 351U, 355U, 359U, 363U, 368U, 372U, 376U, 380U, 384U, 388U, 392U, 396U, 400U, 404U, 408U, 412U, 416U, 420U, 424U, + 429U, 433U, 437U, 441U, 445U, 449U, 453U, 457U, 461U, 465U, 469U, 473U, 477U, 481U, 485U}; + +const unsigned int PUNCTURE_LIST_DATA_COUNT = 12U; + +const unsigned int PUNCTURE_LIST_DATA[] = { + 11U, 23U, 35U, 47U, 59U, 71U, 83U, 95U, 107U, 119U, 131U, 143U, 155U, 167U, 179U, 191U, 203U, 215U, 227U, 239U, 251U, + 263U, 275U, 287U}; + +const unsigned char BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; + +#define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) +#define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) + +const uint8_t BRANCH_TABLE1[] = {0U, 0U, 0U, 0U, 2U, 2U, 2U, 2U}; +const uint8_t BRANCH_TABLE2[] = {0U, 2U, 2U, 0U, 0U, 2U, 2U, 0U}; + +const unsigned int NUM_OF_STATES_D2 = 8U; +const unsigned int NUM_OF_STATES = 16U; +const uint32_t M = 4U; +const unsigned int K = 5U; + +CM17Convolution::CM17Convolution() : +m_metrics1(NULL), +m_metrics2(NULL), +m_oldMetrics(NULL), +m_newMetrics(NULL), +m_decisions(NULL), +m_dp(NULL) +{ + m_metrics1 = new uint16_t[20U]; + m_metrics2 = new uint16_t[20U]; + m_decisions = new uint64_t[300U]; +} + +CM17Convolution::~CM17Convolution() +{ + delete[] m_metrics1; + delete[] m_metrics2; + delete[] m_decisions; +} + +void CM17Convolution::encodeLinkSetup(const unsigned char* in, unsigned char* out) const +{ + assert(in != NULL); + assert(out != NULL); + + unsigned char temp1[31U]; + ::memset(temp1, 0x00U, 31U); + ::memcpy(temp1, in, 30U); + + unsigned char temp2[61U]; + encode(temp1, temp2, 244U); + + unsigned int n = 0U; + unsigned int index = 0U; + for (unsigned int i = 0U; i < 488U; i++) { + if (i != PUNCTURE_LIST_LINK_SETUP[index]) { + bool b = READ_BIT1(temp2, i); + WRITE_BIT1(out, n, b); + n++; + } else { + index++; + } + } +} + +void CM17Convolution::encodeData(const unsigned char* in, unsigned char* out) const +{ + assert(in != NULL); + assert(out != NULL); + + unsigned char temp1[19U]; + ::memset(temp1, 0x00U, 19U); + ::memcpy(temp1, in, 18U); + + unsigned char temp2[37U]; + encode(temp1, temp2, 148U); + + unsigned int n = 0U; + unsigned int index = 0U; + for (unsigned int i = 0U; i < 296U; i++) { + if (i != PUNCTURE_LIST_DATA[index]) { + bool b = READ_BIT1(temp2, i); + WRITE_BIT1(out, n, b); + n++; + } else { + index++; + } + } +} + +unsigned int CM17Convolution::decodeLinkSetup(const unsigned char* in, unsigned char* out) +{ + assert(in != NULL); + assert(out != NULL); + + uint8_t temp[500U]; + ::memset(temp, 0x00U, 500U); + + unsigned int n = 0U; + unsigned int index = 0U; + for (unsigned int i = 0U; i < 368U; i++) { + if (n == PUNCTURE_LIST_LINK_SETUP[index]) { + temp[n++] = 1U; + index++; + } + + bool b = READ_BIT1(in, i); + temp[n++] = b ? 2U : 0U; + } + + start(); + + n = 0U; + for (unsigned int i = 0U; i < 244U; i++) { + uint8_t s0 = temp[n++]; + uint8_t s1 = temp[n++]; + + decode(s0, s1); + } + + return chainback(out, 240U) - PUNCTURE_LIST_LINK_SETUP_COUNT; +} + +unsigned int CM17Convolution::decodeData(const unsigned char* in, unsigned char* out) +{ + assert(in != NULL); + assert(out != NULL); + + uint8_t temp[300U]; + ::memset(temp, 0x00U, 300U); + + unsigned int n = 0U; + unsigned int index = 0U; + for (unsigned int i = 0U; i < 272U; i++) { + if (n == PUNCTURE_LIST_DATA[index]) { + temp[n++] = 1U; + index++; + } + + bool b = READ_BIT1(in, i); + temp[n++] = b ? 2U : 0U; + } + + start(); + + n = 0U; + for (unsigned int i = 0U; i < 148U; i++) { + uint8_t s0 = temp[n++]; + uint8_t s1 = temp[n++]; + + decode(s0, s1); + } + + return chainback(out, 144U) - PUNCTURE_LIST_DATA_COUNT; +} + +void CM17Convolution::start() +{ + ::memset(m_metrics1, 0x00U, NUM_OF_STATES * sizeof(uint16_t)); + ::memset(m_metrics2, 0x00U, NUM_OF_STATES * sizeof(uint16_t)); + + m_oldMetrics = m_metrics1; + m_newMetrics = m_metrics2; + m_dp = m_decisions; +} + +void CM17Convolution::decode(uint8_t s0, uint8_t s1) +{ + *m_dp = 0U; + + for (uint8_t i = 0U; i < NUM_OF_STATES_D2; i++) { + uint8_t j = i * 2U; + + uint16_t metric = std::abs(BRANCH_TABLE1[i] - s0) + std::abs(BRANCH_TABLE2[i] - s1); + + uint16_t m0 = m_oldMetrics[i] + metric; + uint16_t m1 = m_oldMetrics[i + NUM_OF_STATES_D2] + (M - metric); + uint8_t decision0 = (m0 >= m1) ? 1U : 0U; + m_newMetrics[j + 0U] = decision0 != 0U ? m1 : m0; + + m0 = m_oldMetrics[i] + (M - metric); + m1 = m_oldMetrics[i + NUM_OF_STATES_D2] + metric; + uint8_t decision1 = (m0 >= m1) ? 1U : 0U; + m_newMetrics[j + 1U] = decision1 != 0U ? m1 : m0; + + *m_dp |= (uint64_t(decision1) << (j + 1U)) | (uint64_t(decision0) << (j + 0U)); + } + + ++m_dp; + + assert((m_dp - m_decisions) <= 300); + + uint16_t* tmp = m_oldMetrics; + m_oldMetrics = m_newMetrics; + m_newMetrics = tmp; +} + +unsigned int CM17Convolution::chainback(unsigned char* out, unsigned int nBits) +{ + assert(out != NULL); + + uint32_t state = 0U; + + while (nBits-- > 0) { + --m_dp; + + uint32_t i = state >> (9 - K); + uint8_t bit = uint8_t(*m_dp >> i) & 1; + state = (bit << 7) | (state >> 1); + + WRITE_BIT1(out, nBits, bit != 0U); + } + + unsigned int minCost = m_oldMetrics[0]; + + for (unsigned int i = 0U; i < NUM_OF_STATES; i++) { + if (m_oldMetrics[i] < minCost) + minCost = m_oldMetrics[i]; + } + + return minCost / (M >> 1); +} + +void CM17Convolution::encode(const unsigned char* in, unsigned char* out, unsigned int nBits) const +{ + assert(in != NULL); + assert(out != NULL); + assert(nBits > 0U); + + uint8_t d1 = 0U, d2 = 0U, d3 = 0U, d4 = 0U; + uint32_t k = 0U; + for (unsigned int i = 0U; i < nBits; i++) { + uint8_t d = READ_BIT1(in, i) ? 1U : 0U; + + uint8_t g1 = (d + d3 + d4) & 1; + uint8_t g2 = (d + d1 + d2 + d4) & 1; + + d4 = d3; + d3 = d2; + d2 = d1; + d1 = d; + + WRITE_BIT1(out, k, g1 != 0U); + k++; + + WRITE_BIT1(out, k, g2 != 0U); + k++; + } +} diff --git a/M17Convolution.h b/M17Convolution.h new file mode 100644 index 0000000..0c5c7c3 --- /dev/null +++ b/M17Convolution.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2020,2021 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(M17Convolution_H) +#define M17Convolution_H + +#include + +class CM17Convolution { +public: + CM17Convolution(); + ~CM17Convolution(); + + unsigned int decodeLinkSetup(const unsigned char* in, unsigned char* out); + unsigned int decodeData(const unsigned char* in, unsigned char* out); + + void encodeLinkSetup(const unsigned char* in, unsigned char* out) const; + void encodeData(const unsigned char* in, unsigned char* out) const; + +private: + uint16_t* m_metrics1; + uint16_t* m_metrics2; + uint16_t* m_oldMetrics; + uint16_t* m_newMetrics; + uint64_t* m_decisions; + uint64_t* m_dp; + + void start(); + void decode(uint8_t s0, uint8_t s1); + + unsigned int chainback(unsigned char* out, unsigned int nBits); + + void encode(const unsigned char* in, unsigned char* out, unsigned int nBits) const; +}; + +#endif diff --git a/M17Defines.h b/M17Defines.h new file mode 100644 index 0000000..0543d8e --- /dev/null +++ b/M17Defines.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2020,2021 by Jonathan Naylor G4KLX + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#if !defined(M17DEFINES_H) +#define M17DEFINES_H + +const unsigned int M17_RADIO_SYMBOL_LENGTH = 5U; // At 24 kHz sample rate + +const unsigned int M17_FRAME_LENGTH_BITS = 384U; +const unsigned int M17_FRAME_LENGTH_BYTES = M17_FRAME_LENGTH_BITS / 8U; + +const unsigned char M17_LINK_SETUP_SYNC_BYTES[] = {0x55U, 0xF7U}; +const unsigned char M17_STREAM_SYNC_BYTES[] = {0xFFU, 0x5DU}; +const unsigned char M17_EOT_SYNC_BYTES[] = {0x55U, 0x5DU}; + +const unsigned int M17_SYNC_LENGTH_BITS = 16U; +const unsigned int M17_SYNC_LENGTH_BYTES = M17_SYNC_LENGTH_BITS / 8U; + +const unsigned int M17_LSF_LENGTH_BITS = 240U; +const unsigned int M17_LSF_LENGTH_BYTES = M17_LSF_LENGTH_BITS / 8U; + +const unsigned int M17_LSF_FRAGMENT_LENGTH_BITS = M17_LSF_LENGTH_BITS / 6U; +const unsigned int M17_LSF_FRAGMENT_LENGTH_BYTES = M17_LSF_FRAGMENT_LENGTH_BITS / 8U; + +const unsigned int M17_LICH_FRAGMENT_LENGTH_BITS = M17_LSF_FRAGMENT_LENGTH_BITS + 8U; +const unsigned int M17_LICH_FRAGMENT_LENGTH_BYTES = M17_LICH_FRAGMENT_LENGTH_BITS / 8U; + +const unsigned int M17_LSF_FRAGMENT_FEC_LENGTH_BITS = M17_LSF_FRAGMENT_LENGTH_BITS * 2U; +const unsigned int M17_LSF_FRAGMENT_FEC_LENGTH_BYTES = M17_LSF_FRAGMENT_FEC_LENGTH_BITS / 8U; + +const unsigned int M17_LICH_FRAGMENT_FEC_LENGTH_BITS = M17_LICH_FRAGMENT_LENGTH_BITS * 2U; +const unsigned int M17_LICH_FRAGMENT_FEC_LENGTH_BYTES = M17_LICH_FRAGMENT_FEC_LENGTH_BITS / 8U; + +const unsigned int M17_PAYLOAD_LENGTH_BITS = 128U; +const unsigned int M17_PAYLOAD_LENGTH_BYTES = M17_PAYLOAD_LENGTH_BITS / 8U; + +const unsigned char M17_NULL_NONCE[] = {0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U}; +const unsigned int M17_META_LENGTH_BITS = 112U; +const unsigned int M17_META_LENGTH_BYTES = M17_META_LENGTH_BITS / 8U; + +const unsigned int M17_FN_LENGTH_BITS = 16U; +const unsigned int M17_FN_LENGTH_BYTES = M17_FN_LENGTH_BITS / 8U; + +const unsigned int M17_CRC_LENGTH_BITS = 16U; +const unsigned int M17_CRC_LENGTH_BYTES = M17_CRC_LENGTH_BITS / 8U; + +const unsigned char M17_3200_SILENCE[] = {0x01U, 0x00U, 0x09U, 0x43U, 0x9CU, 0xE4U, 0x21U, 0x08U}; +const unsigned char M17_1600_SILENCE[] = {0x0CU, 0x41U, 0x09U, 0x03U, 0x0CU, 0x41U, 0x09U, 0x03U}; + +const unsigned char M17_PACKET_TYPE = 0U; +const unsigned char M17_STREAM_TYPE = 1U; + +const unsigned char M17_DATA_TYPE_DATA = 0x01U; +const unsigned char M17_DATA_TYPE_VOICE = 0x02U; +const unsigned char M17_DATA_TYPE_VOICE_DATA = 0x03U; + +const unsigned char M17_ENCRYPTION_TYPE_NONE = 0x00U; +const unsigned char M17_ENCRYPTION_TYPE_AES = 0x01U; +const unsigned char M17_ENCRYPTION_TYPE_SCRAMBLE = 0x02U; + +const unsigned char M17_ENCRYPTION_SUB_TYPE_TEXT = 0x00U; +const unsigned char M17_ENCRYPTION_SUB_TYPE_GPS = 0x01U; +const unsigned char M17_ENCRYPTION_SUB_TYPE_CALLSIGNS = 0x02U; + +#endif diff --git a/README.md b/README.md index 06e1669..39edb3e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The DudeStar application used the Qt Widgets UI, while DroidStar uses the Qt Qui The Codec2 vocoder library is open source and is included as a C++ implementation of the original C library taken from the mvoice project. More info on M17 can be found here: https://m17project.org/ # MMDVM support -- work in progress -DroidStar supports MMDVM and MMDVM_HS (hotspot) modems, with basic (possibly buggy) support for D-STAR, Fusion, and DMR. Support for M17, P25, and NXDN coming soon. When connecting to a digital mode reflector/DMR server and selecting an MMDVM device under Modems, then DroidStar acts as a hotspot/repeater. When 'MMDVM Direct' is selected as the reflector/server, then DroidStar becomes a stand-alone transceiver. +DroidStar supports MMDVM and MMDVM_HS (hotspot) modems, with basic (possibly buggy) support for M17, D-STAR, Fusion, and DMR. Support for P25 and NXDN coming soon. When connecting to a digital mode reflector/DMR server and selecting an MMDVM device under Modems, then DroidStar acts as a hotspot/repeater. When 'MMDVM Direct' (currently M17 only) is selected as the host, then DroidStar becomes a stand-alone transceiver. # Software vocoder plugin API There is a vocoder plugin API available for loading of vocoder software. Any vocoder plugin used with DroidStar should be properly licensed by the user if any copyright patents apply. Do not use any patented vocoder plugin that you are not licensed to use. I have no information regarding aquiring a software vocoder. @@ -64,8 +64,8 @@ Port: UDP port of node, usually 4569. Add DTMF commands like \*3node, \*1node, \*70, etc in the IAX DTMF box and hit send to send the DTMF string. The asterisk (*) character is already added on the Droidstar app, so only input the numeric portion of the command (70 instead of *70, etc). Details on various commands can be found at the AllStar wiki and others. -# Building -This software is written primarily in C++ on Linux and requires Qt5, and natually the devel packages to build. Java, QML (Javascript based), and C# code is also used where necessary. +# General building instructions +This software is written primarily in C++ on Linux and requires Qt5 >= Qt5.15, and natually the devel packages to build. Java, QML (Javascript based), and C# code is also used where necessary. Qt5 and Qt5-Quick development packages are required to build this software from source. With these requirements met, run the following: ``` @@ -74,10 +74,20 @@ make ``` qmake may have a different name on your distribution i.e. on Fedora it's called qmake-qt5 -Notes for building/running Debian/Raspbian: In addition to the Linux build requirements, there are some additional requirements for running this QT application in order for the audio devices to be correctly detected: +# Building on Raspbian/RaspiOS +DroidStar requires >= Qt 5.15 and RaspiOS currently packages Qt 5.11. Fortunately there is a great Qt 5.15.2 installer for RaspiOS: + +https://github.com/koendv/qt5-opengl-raspberrypi + +with this version of Qt installed, simply run the qmake from that version: ``` -sudo apt-get install libqt5multimedia5-plugins libqt5serialport5-dev qtmultimedia5-dev libqt5multimediawidgets5 libqt5multimedia5-plugins libqt5multimedia5 +cd DroidStar +mkdir build +cd build +/usr/lib/qt5.15.2/bin/qmake .. +make ``` + And if pulseaudio is not currently installed: ``` sudo apt-get install pulseaudio diff --git a/android/res/xml/device_filter.xml b/android/res/xml/device_filter.xml index 9c97cab..623b3aa 100644 --- a/android/res/xml/device_filter.xml +++ b/android/res/xml/device_filter.xml @@ -2,5 +2,7 @@ - + + + diff --git a/codec.cpp b/codec.cpp index bccfa99..9ed73e9 100644 --- a/codec.cpp +++ b/codec.cpp @@ -92,7 +92,11 @@ void Codec::agc_state_changed(int s) void Codec::send_connect() { m_modeinfo.status = CONNECTING; - if(m_ipv6 && (m_modeinfo.host != "none")){ + + if(m_modeinfo.host == "MMDVM_DIRECT"){ + mmdvm_direct_connect(); + } + else if(m_ipv6 && (m_modeinfo.host != "none")){ qDebug() << "Host == " << m_modeinfo.host; QList h; QHostInfo i; @@ -184,8 +188,8 @@ bool Codec::load_vocoder_plugin() void Codec::deleteLater() { if(m_modeinfo.status == CONNECTED_RW){ - m_udp->disconnect(); - m_ping_timer->stop(); + //m_udp->disconnect(); + //m_ping_timer->stop(); send_disconnect(); delete m_audio; if(m_hwtx){ diff --git a/codec.h b/codec.h index 1dc73a4..9d0961a 100644 --- a/codec.h +++ b/codec.h @@ -36,7 +36,7 @@ public: Codec(QString callsign, char module, QString hostname, QString host, int port, bool ipv6, QString vocoder, QString modem, QString audioin, QString audioout); ~Codec(); void set_modem_flags(bool rxInvert, bool txInvert, bool pttInvert, bool useCOSAsLockout, bool duplex) { m_rxInvert = rxInvert; m_txInvert = txInvert; m_pttInvert = pttInvert; m_useCOSAsLockout = useCOSAsLockout; m_duplex = duplex; } - void set_modem_params(uint32_t rxfreq, uint32_t txfreq, uint32_t txDelay, float rxLevel, float rfLevel, uint32_t ysfTXHang, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagTXLevel) + void set_modem_params(uint32_t rxfreq, uint32_t txfreq, uint32_t txDelay, float rxLevel, float rfLevel, uint32_t ysfTXHang, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagTXLevel, float m17TXLevel) { m_rxfreq = rxfreq; m_txfreq = txfreq; @@ -51,6 +51,7 @@ public: m_p25TXLevel = p25TXLevel; m_nxdnTXLevel = nxdnTXLevel; m_pocsagTXLevel = pocsagTXLevel; + m_m17TXLevel = m17TXLevel; } bool get_hwrx() { return m_hwrx; } bool get_hwtx() { return m_hwtx; } @@ -109,6 +110,7 @@ signals: protected slots: virtual void send_disconnect(){} virtual void hostname_lookup(QHostInfo){} + virtual void mmdvm_direct_connect(){} void send_connect(); void input_src_changed(int id, QString t) { m_ttsid = id; m_ttstext = t; } void start_tx(); @@ -191,6 +193,7 @@ protected: float m_nxdnTXLevel; float m_pocsagTXLevel; float m_fmTXLevel; + float m_m17TXLevel; bool m_debug; bool m_useCOSAsLockout; bool m_dstarEnabled; diff --git a/dcscodec.cpp b/dcscodec.cpp index e38d0a3..b129b3e 100755 --- a/dcscodec.cpp +++ b/dcscodec.cpp @@ -19,7 +19,7 @@ #include "dcscodec.h" #include "CRCenc.h" -#define DEBUG +//#define DEBUG const unsigned char MMDVM_DSTAR_HEADER = 0x10U; const unsigned char MMDVM_DSTAR_DATA = 0x11U; @@ -83,7 +83,7 @@ void DCSCodec::process_udp() if(m_modemport != ""){ m_modem = new SerialModem("DCS"); m_modem->set_modem_flags(m_rxInvert, m_txInvert, m_pttInvert, m_useCOSAsLockout, m_duplex); - m_modem->set_modem_params(m_rxfreq, m_txfreq, m_txDelay, m_rxLevel, m_rfLevel, m_ysfTXHang, m_cwIdTXLevel, m_dstarTXLevel, m_dmrTXLevel, m_ysfTXLevel, m_p25TXLevel, m_nxdnTXLevel, m_pocsagTXLevel); + m_modem->set_modem_params(m_rxfreq, m_txfreq, m_txDelay, m_rxLevel, m_rfLevel, m_ysfTXHang, m_cwIdTXLevel, m_dstarTXLevel, m_dmrTXLevel, m_ysfTXLevel, m_p25TXLevel, m_nxdnTXLevel, m_pocsagTXLevel, m_m17TXLevel); m_modem->connect_to_serial(m_modemport); connect(m_modem, SIGNAL(modem_data_ready(QByteArray)), this, SLOT(process_modem_data(QByteArray))); } diff --git a/dmrcodec.cpp b/dmrcodec.cpp index c5de0a1..4c5f603 100755 --- a/dmrcodec.cpp +++ b/dmrcodec.cpp @@ -23,7 +23,7 @@ #include "SHA256.h" #include "CRCenc.h" -#define DEBUG +//#define DEBUG const unsigned char MMDVM_DMR_DATA1 = 0x18U; const unsigned char MMDVM_DMR_LOST1 = 0x19U; @@ -331,7 +331,7 @@ void DMRCodec::setup_connection() if(m_modemport != ""){ m_modem = new SerialModem("DMR"); m_modem->set_modem_flags(m_rxInvert, m_txInvert, m_pttInvert, m_useCOSAsLockout, m_duplex); - m_modem->set_modem_params(m_rxfreq, m_txfreq, m_txDelay, m_rxLevel, m_rfLevel, m_ysfTXHang, m_cwIdTXLevel, m_dstarTXLevel, m_dmrTXLevel, m_ysfTXLevel, m_p25TXLevel, m_nxdnTXLevel, m_pocsagTXLevel); + m_modem->set_modem_params(m_rxfreq, m_txfreq, m_txDelay, m_rxLevel, m_rfLevel, m_ysfTXHang, m_cwIdTXLevel, m_dstarTXLevel, m_dmrTXLevel, m_ysfTXLevel, m_p25TXLevel, m_nxdnTXLevel, m_pocsagTXLevel, m_m17TXLevel); m_modem->connect_to_serial(m_modemport); connect(m_modem, SIGNAL(modem_data_ready(QByteArray)), this, SLOT(process_modem_data(QByteArray))); } diff --git a/droidstar.cpp b/droidstar.cpp index 27ef215..ec4e70d 100644 --- a/droidstar.cpp +++ b/droidstar.cpp @@ -99,6 +99,14 @@ void DroidStar::keepScreenOn() } }}); } + +void DroidStar::reset_connect_status() +{ + if(connect_status == Codec::CONNECTED_RW){ + connect_status = Codec::CONNECTING; + process_connect(); + } +} #endif void DroidStar::discover_devices() @@ -197,7 +205,6 @@ void DroidStar::dtmf_send_clicked(QString dtmf) void DroidStar::process_connect() { - //QSize size = qApp->screens()[0]->size(); if(connect_status != Codec::DISCONNECTED){ connect_status = Codec::DISCONNECTED; m_modethread->quit(); @@ -214,6 +221,9 @@ void DroidStar::process_connect() #ifdef Q_OS_IOS MicPermission::check_permission(); #endif + +//#include "build.h" + if( (m_callsign.size() < 4) || (m_dmrid < 250000) || (m_callsign != m_dmrids[m_dmrid])) @@ -258,7 +268,7 @@ void DroidStar::process_connect() m_hostname = m_hostmap[m_host]; sl = m_hostname.split(','); - if( (m_protocol == "M17") && (m_ipv6) && (sl.size() > 2) && (sl.at(2) != "none") ){ + if( (m_protocol == "M17") && (m_host != "MMDVM_DIRECT") && (m_ipv6) && (sl.size() > 2) && (sl.at(2) != "none") ){ m_hostname = sl.at(2).simplified(); m_port = sl.at(1).toInt(); } @@ -266,6 +276,9 @@ void DroidStar::process_connect() m_hostname = sl.at(0).simplified(); m_port = sl.at(1).toInt(); } + else if( (m_protocol == "M17") && (m_host == "MMDVM_DIRECT") ){ + qDebug() << "Going MMDVM_DIRECT"; + } else{ m_errortxt = "Invalid host selection"; connect_status = Codec::DISCONNECTED; @@ -284,21 +297,21 @@ void DroidStar::process_connect() QStringList ml = m_modem.split(':'); modem = ml.at(0); } - //vocoder = "USB"; - //modem = "USB"; + const bool rxInvert = true; const bool txInvert = false; const bool pttInvert = false; const bool useCOSAsLockout = 0; const uint32_t ysfTXHang = 4; const float pocsagTXLevel = 50; + const float m17TXLevel = 50; const bool duplex = m_modemRxFreq.toUInt() != m_modemTxFreq.toUInt(); emit update_log("Connecting to " + m_hostname + ":" + QString::number(m_port) + "..."); if( (m_protocol == "REF") || ((m_protocol == "XRF") && m_xrf2ref) ){ m_ref = new REFCodec(m_callsign, m_host, m_module, m_hostname, 20001, false, vocoder, modem, m_playback, m_capture); m_ref->set_modem_flags(rxInvert, txInvert, pttInvert, useCOSAsLockout, duplex); - m_ref->set_modem_params(m_modemRxFreq.toInt(), m_modemTxFreq.toInt(), m_modemTxDelay.toInt(), m_modemRxLevel.toFloat(), m_modemRFLevel.toFloat(), ysfTXHang, m_modemCWIdTxLevel.toFloat(), m_modemDstarTxLevel.toFloat(), m_modemDMRTxLevel.toFloat(), m_modemYSFTxLevel.toFloat(), m_modemP25TxLevel.toFloat(), m_modemNXDNTxLevel.toFloat(), pocsagTXLevel); + m_ref->set_modem_params(m_modemRxFreq.toInt(), m_modemTxFreq.toInt(), m_modemTxDelay.toInt(), m_modemRxLevel.toFloat(), m_modemRFLevel.toFloat(), ysfTXHang, m_modemCWIdTxLevel.toFloat(), m_modemDstarTxLevel.toFloat(), m_modemDMRTxLevel.toFloat(), m_modemYSFTxLevel.toFloat(), m_modemP25TxLevel.toFloat(), m_modemNXDNTxLevel.toFloat(), pocsagTXLevel, m17TXLevel); m_modethread = new QThread; m_ref->moveToThread(m_modethread); connect(this, SIGNAL(module_changed(char)), m_ref, SLOT(module_changed(char))); @@ -327,10 +340,9 @@ void DroidStar::process_connect() if(m_protocol == "DCS"){ m_dcs = new DCSCodec(m_callsign, m_host, m_module, m_hostname, m_port, false, vocoder, modem, m_playback, m_capture); m_dcs->set_modem_flags(rxInvert, txInvert, pttInvert, useCOSAsLockout, duplex); - m_dcs->set_modem_params(m_modemRxFreq.toInt(), m_modemTxFreq.toInt(), m_modemTxDelay.toInt(), m_modemRxLevel.toFloat(), m_modemRFLevel.toFloat(), ysfTXHang, m_modemCWIdTxLevel.toFloat(), m_modemDstarTxLevel.toFloat(), m_modemDMRTxLevel.toFloat(), m_modemYSFTxLevel.toFloat(), m_modemP25TxLevel.toFloat(), m_modemNXDNTxLevel.toFloat(), pocsagTXLevel); + m_dcs->set_modem_params(m_modemRxFreq.toInt(), m_modemTxFreq.toInt(), m_modemTxDelay.toInt(), m_modemRxLevel.toFloat(), m_modemRFLevel.toFloat(), ysfTXHang, m_modemCWIdTxLevel.toFloat(), m_modemDstarTxLevel.toFloat(), m_modemDMRTxLevel.toFloat(), m_modemYSFTxLevel.toFloat(), m_modemP25TxLevel.toFloat(), m_modemNXDNTxLevel.toFloat(), pocsagTXLevel, m17TXLevel); m_modethread = new QThread; m_dcs->moveToThread(m_modethread); - //connect(this, SIGNAL(module_changed(char)), m_dcs, SLOT(module_changed(char))); connect(m_dcs, SIGNAL(update(Codec::MODEINFO)), this, SLOT(update_dcs_data(Codec::MODEINFO))); connect(m_dcs, SIGNAL(update_output_level(unsigned short)), this, SLOT(update_output_level(unsigned short))); connect(m_modethread, SIGNAL(started()), m_dcs, SLOT(send_connect())); @@ -346,7 +358,6 @@ void DroidStar::process_connect() connect(this, SIGNAL(urcall_changed(QString)), m_dcs, SLOT(urcall_changed(QString))); connect(this, SIGNAL(rptr1_changed(QString)), m_dcs, SLOT(rptr1_changed(QString))); connect(this, SIGNAL(rptr2_changed(QString)), m_dcs, SLOT(rptr2_changed(QString))); - //emit module_changed(m_module); emit mycall_changed(m_mycall); emit urcall_changed(m_urcall); emit rptr1_changed(m_rptr1); @@ -356,10 +367,9 @@ void DroidStar::process_connect() if( (m_protocol == "XRF") && (m_xrf2ref == false) ){ m_xrf = new XRFCodec(m_callsign, m_host, m_module, m_hostname, m_port, false, vocoder, modem, m_playback, m_capture); m_xrf->set_modem_flags(rxInvert, txInvert, pttInvert, useCOSAsLockout, duplex); - m_xrf->set_modem_params(m_modemRxFreq.toInt(), m_modemTxFreq.toInt(), m_modemTxDelay.toInt(), m_modemRxLevel.toFloat(), m_modemRFLevel.toFloat(), ysfTXHang, m_modemCWIdTxLevel.toFloat(), m_modemDstarTxLevel.toFloat(), m_modemDMRTxLevel.toFloat(), m_modemYSFTxLevel.toFloat(), m_modemP25TxLevel.toFloat(), m_modemNXDNTxLevel.toFloat(), pocsagTXLevel); + m_xrf->set_modem_params(m_modemRxFreq.toInt(), m_modemTxFreq.toInt(), m_modemTxDelay.toInt(), m_modemRxLevel.toFloat(), m_modemRFLevel.toFloat(), ysfTXHang, m_modemCWIdTxLevel.toFloat(), m_modemDstarTxLevel.toFloat(), m_modemDMRTxLevel.toFloat(), m_modemYSFTxLevel.toFloat(), m_modemP25TxLevel.toFloat(), m_modemNXDNTxLevel.toFloat(), pocsagTXLevel, m17TXLevel); m_modethread = new QThread; m_xrf->moveToThread(m_modethread); - //connect(this, SIGNAL(module_changed(char)), m_xrf, SLOT(module_changed(char))); connect(m_xrf, SIGNAL(update(Codec::MODEINFO)), this, SLOT(update_xrf_data(Codec::MODEINFO))); connect(m_xrf, SIGNAL(update_output_level(unsigned short)), this, SLOT(update_output_level(unsigned short))); connect(m_modethread, SIGNAL(started()), m_xrf, SLOT(send_connect())); @@ -375,7 +385,6 @@ void DroidStar::process_connect() connect(this, SIGNAL(urcall_changed(QString)), m_xrf, SLOT(urcall_changed(QString))); connect(this, SIGNAL(rptr1_changed(QString)), m_xrf, SLOT(rptr1_changed(QString))); connect(this, SIGNAL(rptr2_changed(QString)), m_xrf, SLOT(rptr2_changed(QString))); - //emit module_changed(m_module); emit mycall_changed(m_mycall); emit urcall_changed(m_urcall); emit rptr1_changed(m_rptr1); @@ -399,7 +408,7 @@ void DroidStar::process_connect() m_dmr = new DMRCodec(m_callsign, m_dmrid, m_essid, dmrpass, m_latitude, m_longitude, m_location, m_description, m_freq, m_url, m_swid, m_pkgid, m_dmropts, m_dmr_destid, m_hostname, m_port, false, vocoder, modem, m_playback, m_capture); m_dmr->set_modem_flags(rxInvert, txInvert, pttInvert, useCOSAsLockout, duplex); - m_dmr->set_modem_params(m_modemRxFreq.toInt(), m_modemTxFreq.toInt(), m_modemTxDelay.toInt(), m_modemRxLevel.toFloat(), m_modemRFLevel.toFloat(), ysfTXHang, m_modemCWIdTxLevel.toFloat(), m_modemDstarTxLevel.toFloat(), m_modemDMRTxLevel.toFloat(), m_modemYSFTxLevel.toFloat(), m_modemP25TxLevel.toFloat(), m_modemNXDNTxLevel.toFloat(), pocsagTXLevel); + m_dmr->set_modem_params(m_modemRxFreq.toInt(), m_modemTxFreq.toInt(), m_modemTxDelay.toInt(), m_modemRxLevel.toFloat(), m_modemRFLevel.toFloat(), ysfTXHang, m_modemCWIdTxLevel.toFloat(), m_modemDstarTxLevel.toFloat(), m_modemDMRTxLevel.toFloat(), m_modemYSFTxLevel.toFloat(), m_modemP25TxLevel.toFloat(), m_modemNXDNTxLevel.toFloat(), pocsagTXLevel, m17TXLevel); m_dmr->set_cc(1); m_modethread = new QThread; m_dmr->moveToThread(m_modethread); @@ -421,7 +430,7 @@ void DroidStar::process_connect() if( (m_protocol == "YSF") || (m_protocol == "FCS") ){ m_ysf = new YSFCodec(m_callsign, m_host, m_hostname, m_port, false, vocoder, modem, m_playback, m_capture); m_ysf->set_modem_flags(rxInvert, txInvert, pttInvert, useCOSAsLockout, duplex); - m_ysf->set_modem_params(m_modemRxFreq.toInt(), m_modemTxFreq.toInt(), m_modemTxDelay.toInt(), m_modemRxLevel.toFloat(), m_modemRFLevel.toFloat(), ysfTXHang, m_modemCWIdTxLevel.toFloat(), m_modemDstarTxLevel.toFloat(), m_modemDMRTxLevel.toFloat(), m_modemYSFTxLevel.toFloat(), m_modemP25TxLevel.toFloat(), m_modemNXDNTxLevel.toFloat(), pocsagTXLevel); + m_ysf->set_modem_params(m_modemRxFreq.toInt(), m_modemTxFreq.toInt(), m_modemTxDelay.toInt(), m_modemRxLevel.toFloat(), m_modemRFLevel.toFloat(), ysfTXHang, m_modemCWIdTxLevel.toFloat(), m_modemDstarTxLevel.toFloat(), m_modemDMRTxLevel.toFloat(), m_modemYSFTxLevel.toFloat(), m_modemP25TxLevel.toFloat(), m_modemNXDNTxLevel.toFloat(), pocsagTXLevel, m17TXLevel); m_modethread = new QThread; m_ysf->moveToThread(m_modethread); connect(m_ysf, SIGNAL(update(Codec::MODEINFO)), this, SLOT(update_ysf_data(Codec::MODEINFO))); @@ -475,6 +484,8 @@ void DroidStar::process_connect() } if(m_protocol == "M17"){ m_m17 = new M17Codec(m_callsign, m_module, m_host, m_hostname, m_port, false, modem, m_playback, m_capture); + m_m17->set_modem_flags(rxInvert, txInvert, pttInvert, useCOSAsLockout, duplex); + m_m17->set_modem_params(m_modemRxFreq.toInt(), m_modemTxFreq.toInt(), m_modemTxDelay.toInt(), m_modemRxLevel.toFloat(), m_modemRFLevel.toFloat(), ysfTXHang, m_modemCWIdTxLevel.toFloat(), m_modemDstarTxLevel.toFloat(), m_modemDMRTxLevel.toFloat(), m_modemYSFTxLevel.toFloat(), m_modemP25TxLevel.toFloat(), m_modemNXDNTxLevel.toFloat(), pocsagTXLevel, m17TXLevel); m_modethread = new QThread; m_m17->moveToThread(m_modethread); connect(m_m17, SIGNAL(update(Codec::MODEINFO)), this, SLOT(update_m17_data(Codec::MODEINFO))); @@ -1128,6 +1139,8 @@ void DroidStar::process_m17_hosts() { m_hostmap.clear(); m_hostsmodel.clear(); + m_hostmap["MMDVM_DIRECT"] = "MMDVM_DIRECT"; + QFileInfo check_file(config_path + "/M17Hosts-full.csv"); if(check_file.exists() && check_file.isFile()){ QFile f(config_path + "/M17Hosts-full.csv"); diff --git a/droidstar.h b/droidstar.h index 37e463b..1d392b7 100644 --- a/droidstar.h +++ b/droidstar.h @@ -227,8 +227,10 @@ public slots: QString get_modemNXDNTxLevel() { return m_modemNXDNTxLevel; } #if defined(Q_OS_ANDROID) QString get_platform() { return QSysInfo::productType(); } + void reset_connect_status(); #else QString get_platform() { return QSysInfo::kernelType(); } + void reset_connect_status() {} #endif QString get_arch() { return QSysInfo::currentCpuArchitecture(); } QString get_build_abi() { return QSysInfo::buildAbi(); } diff --git a/iaxcodec.cpp b/iaxcodec.cpp index 547976c..7ddcc9c 100644 --- a/iaxcodec.cpp +++ b/iaxcodec.cpp @@ -22,7 +22,7 @@ #else #include #endif -#define DEBUG +//#define DEBUG #ifdef USE_FLITE extern "C" { diff --git a/m17codec.cpp b/m17codec.cpp index 5af52d0..c3c9ed0 100755 --- a/m17codec.cpp +++ b/m17codec.cpp @@ -1,5 +1,6 @@ /* Copyright (C) 2019-2021 Doug McLain + Copyright (C) 2020,2021 Jonathan Naylor, G4KLX 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 @@ -18,9 +19,68 @@ #include #include #include "m17codec.h" +#include "M17Defines.h" +#include "M17Convolution.h" +#include "Golay24128.h" + #define M17CHARACTERS " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/." -#define DEBUG +//#define DEBUG + +const uint8_t SCRAMBLER[] = { + 0x00U, 0x00U, 0xD6U, 0xB5U, 0xE2U, 0x30U, 0x82U, 0xFFU, 0x84U, 0x62U, 0xBAU, 0x4EU, 0x96U, 0x90U, 0xD8U, 0x98U, 0xDDU, + 0x5DU, 0x0CU, 0xC8U, 0x52U, 0x43U, 0x91U, 0x1DU, 0xF8U, 0x6EU, 0x68U, 0x2FU, 0x35U, 0xDAU, 0x14U, 0xEAU, 0xCDU, 0x76U, + 0x19U, 0x8DU, 0xD5U, 0x80U, 0xD1U, 0x33U, 0x87U, 0x13U, 0x57U, 0x18U, 0x2DU, 0x29U, 0x78U, 0xC3U}; + +const uint32_t INTERLEAVER[] = { + 0U, 137U, 90U, 227U, 180U, 317U, 270U, 39U, 360U, 129U, 82U, 219U, 172U, 309U, 262U, 31U, 352U, 121U, 74U, 211U, 164U, + 301U, 254U, 23U, 344U, 113U, 66U, 203U, 156U, 293U, 246U, 15U, 336U, 105U, 58U, 195U, 148U, 285U, 238U, 7U, 328U, 97U, + 50U, 187U, 140U, 277U, 230U, 367U, 320U, 89U, 42U, 179U, 132U, 269U, 222U, 359U, 312U, 81U, 34U, 171U, 124U, 261U, 214U, + 351U, 304U, 73U, 26U, 163U, 116U, 253U, 206U, 343U, 296U, 65U, 18U, 155U, 108U, 245U, 198U, 335U, 288U, 57U, 10U, 147U, + 100U, 237U, 190U, 327U, 280U, 49U, 2U, 139U, 92U, 229U, 182U, 319U, 272U, 41U, 362U, 131U, 84U, 221U, 174U, 311U, 264U, + 33U, 354U, 123U, 76U, 213U, 166U, 303U, 256U, 25U, 346U, 115U, 68U, 205U, 158U, 295U, 248U, 17U, 338U, 107U, 60U, 197U, + 150U, 287U, 240U, 9U, 330U, 99U, 52U, 189U, 142U, 279U, 232U, 1U, 322U, 91U, 44U, 181U, 134U, 271U, 224U, 361U, 314U, 83U, + 36U, 173U, 126U, 263U, 216U, 353U, 306U, 75U, 28U, 165U, 118U, 255U, 208U, 345U, 298U, 67U, 20U, 157U, 110U, 247U, 200U, + 337U, 290U, 59U, 12U, 149U, 102U, 239U, 192U, 329U, 282U, 51U, 4U, 141U, 94U, 231U, 184U, 321U, 274U, 43U, 364U, 133U, 86U, + 223U, 176U, 313U, 266U, 35U, 356U, 125U, 78U, 215U, 168U, 305U, 258U, 27U, 348U, 117U, 70U, 207U, 160U, 297U, 250U, 19U, + 340U, 109U, 62U, 199U, 152U, 289U, 242U, 11U, 332U, 101U, 54U, 191U, 144U, 281U, 234U, 3U, 324U, 93U, 46U, 183U, 136U, 273U, + 226U, 363U, 316U, 85U, 38U, 175U, 128U, 265U, 218U, 355U, 308U, 77U, 30U, 167U, 120U, 257U, 210U, 347U, 300U, 69U, 22U, + 159U, 112U, 249U, 202U, 339U, 292U, 61U, 14U, 151U, 104U, 241U, 194U, 331U, 284U, 53U, 6U, 143U, 96U, 233U, 186U, 323U, + 276U, 45U, 366U, 135U, 88U, 225U, 178U, 315U, 268U, 37U, 358U, 127U, 80U, 217U, 170U, 307U, 260U, 29U, 350U, 119U, 72U, + 209U, 162U, 299U, 252U, 21U, 342U, 111U, 64U, 201U, 154U, 291U, 244U, 13U, 334U, 103U, 56U, 193U, 146U, 283U, 236U, 5U, + 326U, 95U, 48U, 185U, 138U, 275U, 228U, 365U, 318U, 87U, 40U, 177U, 130U, 267U, 220U, 357U, 310U, 79U, 32U, 169U, 122U, + 259U, 212U, 349U, 302U, 71U, 24U, 161U, 114U, 251U, 204U, 341U, 294U, 63U, 16U, 153U, 106U, 243U, 196U, 333U, 286U, 55U, + 8U, 145U, 98U, 235U, 188U, 325U, 278U, 47U}; + +const uint16_t CRC16_TABLE[] = {0x0000U, 0x5935U, 0xB26AU, 0xEB5FU, 0x3DE1U, 0x64D4U, 0x8F8BU, 0xD6BEU, 0x7BC2U, 0x22F7U, 0xC9A8U, + 0x909DU, 0x4623U, 0x1F16U, 0xF449U, 0xAD7CU, 0xF784U, 0xAEB1U, 0x45EEU, 0x1CDBU, 0xCA65U, 0x9350U, + 0x780FU, 0x213AU, 0x8C46U, 0xD573U, 0x3E2CU, 0x6719U, 0xB1A7U, 0xE892U, 0x03CDU, 0x5AF8U, 0xB63DU, + 0xEF08U, 0x0457U, 0x5D62U, 0x8BDCU, 0xD2E9U, 0x39B6U, 0x6083U, 0xCDFFU, 0x94CAU, 0x7F95U, 0x26A0U, + 0xF01EU, 0xA92BU, 0x4274U, 0x1B41U, 0x41B9U, 0x188CU, 0xF3D3U, 0xAAE6U, 0x7C58U, 0x256DU, 0xCE32U, + 0x9707U, 0x3A7BU, 0x634EU, 0x8811U, 0xD124U, 0x079AU, 0x5EAFU, 0xB5F0U, 0xECC5U, 0x354FU, 0x6C7AU, + 0x8725U, 0xDE10U, 0x08AEU, 0x519BU, 0xBAC4U, 0xE3F1U, 0x4E8DU, 0x17B8U, 0xFCE7U, 0xA5D2U, 0x736CU, + 0x2A59U, 0xC106U, 0x9833U, 0xC2CBU, 0x9BFEU, 0x70A1U, 0x2994U, 0xFF2AU, 0xA61FU, 0x4D40U, 0x1475U, + 0xB909U, 0xE03CU, 0x0B63U, 0x5256U, 0x84E8U, 0xDDDDU, 0x3682U, 0x6FB7U, 0x8372U, 0xDA47U, 0x3118U, + 0x682DU, 0xBE93U, 0xE7A6U, 0x0CF9U, 0x55CCU, 0xF8B0U, 0xA185U, 0x4ADAU, 0x13EFU, 0xC551U, 0x9C64U, + 0x773BU, 0x2E0EU, 0x74F6U, 0x2DC3U, 0xC69CU, 0x9FA9U, 0x4917U, 0x1022U, 0xFB7DU, 0xA248U, 0x0F34U, + 0x5601U, 0xBD5EU, 0xE46BU, 0x32D5U, 0x6BE0U, 0x80BFU, 0xD98AU, 0x6A9EU, 0x33ABU, 0xD8F4U, 0x81C1U, + 0x577FU, 0x0E4AU, 0xE515U, 0xBC20U, 0x115CU, 0x4869U, 0xA336U, 0xFA03U, 0x2CBDU, 0x7588U, 0x9ED7U, + 0xC7E2U, 0x9D1AU, 0xC42FU, 0x2F70U, 0x7645U, 0xA0FBU, 0xF9CEU, 0x1291U, 0x4BA4U, 0xE6D8U, 0xBFEDU, + 0x54B2U, 0x0D87U, 0xDB39U, 0x820CU, 0x6953U, 0x3066U, 0xDCA3U, 0x8596U, 0x6EC9U, 0x37FCU, 0xE142U, + 0xB877U, 0x5328U, 0x0A1DU, 0xA761U, 0xFE54U, 0x150BU, 0x4C3EU, 0x9A80U, 0xC3B5U, 0x28EAU, 0x71DFU, + 0x2B27U, 0x7212U, 0x994DU, 0xC078U, 0x16C6U, 0x4FF3U, 0xA4ACU, 0xFD99U, 0x50E5U, 0x09D0U, 0xE28FU, + 0xBBBAU, 0x6D04U, 0x3431U, 0xDF6EU, 0x865BU, 0x5FD1U, 0x06E4U, 0xEDBBU, 0xB48EU, 0x6230U, 0x3B05U, + 0xD05AU, 0x896FU, 0x2413U, 0x7D26U, 0x9679U, 0xCF4CU, 0x19F2U, 0x40C7U, 0xAB98U, 0xF2ADU, 0xA855U, + 0xF160U, 0x1A3FU, 0x430AU, 0x95B4U, 0xCC81U, 0x27DEU, 0x7EEBU, 0xD397U, 0x8AA2U, 0x61FDU, 0x38C8U, + 0xEE76U, 0xB743U, 0x5C1CU, 0x0529U, 0xE9ECU, 0xB0D9U, 0x5B86U, 0x02B3U, 0xD40DU, 0x8D38U, 0x6667U, + 0x3F52U, 0x922EU, 0xCB1BU, 0x2044U, 0x7971U, 0xAFCFU, 0xF6FAU, 0x1DA5U, 0x4490U, 0x1E68U, 0x475DU, + 0xAC02U, 0xF537U, 0x2389U, 0x7ABCU, 0x91E3U, 0xC8D6U, 0x65AAU, 0x3C9FU, 0xD7C0U, 0x8EF5U, 0x584BU, + 0x017EU, 0xEA21U, 0xB314U}; + +const uint8_t BIT_MASK_TABLE[] = {0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U}; + +#define WRITE_BIT(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7]) +#define READ_BIT(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7]) M17Codec::M17Codec(QString callsign, char module, QString hostname, QString host, int port, bool ipv6, QString modem, QString audioin, QString audioout) : Codec(callsign, module, hostname, host, port, ipv6, NULL, modem, audioin, audioout), @@ -107,7 +167,7 @@ void M17Codec::process_udp() #ifdef DEBUG fprintf(stderr, "RECV: "); for(int i = 0; i < buf.size(); ++i){ - fprintf(stderr, "%02x ", (unsigned char)buf.data()[i]); + fprintf(stderr, "%02x ", (uint8_t)buf.data()[i]); } fprintf(stderr, "\n"); fflush(stderr); @@ -118,6 +178,15 @@ void M17Codec::process_udp() if((buf.size() == 4) && (::memcmp(buf.data(), "ACKN", 4U) == 0)){ if(m_modeinfo.status == CONNECTING){ m_modeinfo.status = CONNECTED_RW; + + if(m_modemport != ""){ + m_modem = new SerialModem("M17"); + m_modem->set_modem_flags(m_rxInvert, m_txInvert, m_pttInvert, m_useCOSAsLockout, m_duplex); + m_modem->set_modem_params(m_rxfreq, m_txfreq, m_txDelay, m_rxLevel, m_rfLevel, m_ysfTXHang, m_cwIdTXLevel, m_dstarTXLevel, m_dmrTXLevel, m_ysfTXLevel, m_p25TXLevel, m_nxdnTXLevel, m_pocsagTXLevel, m_m17TXLevel); + m_modem->connect_to_serial(m_modemport); + connect(m_modem, SIGNAL(modem_data_ready(QByteArray)), this, SLOT(process_modem_data(QByteArray))); + } + m_c2 = new CCodec2(true); m_txtimer = new QTimer(); connect(m_txtimer, SIGNAL(timeout()), this, SLOT(transmit())); @@ -203,6 +272,9 @@ void M17Codec::process_udp() else{ emit update(m_modeinfo); } + if(m_modem){ + send_modem_data(buf); + } } //emit update(m_modeinfo); } @@ -230,7 +302,7 @@ void M17Codec::hostname_lookup(QHostInfo i) #ifdef DEBUG fprintf(stderr, "CONN: "); for(int i = 0; i < out.size(); ++i){ - fprintf(stderr, "%02x ", (unsigned char)out.data()[i]); + fprintf(stderr, "%02x ", (uint8_t)out.data()[i]); } fprintf(stderr, "\n"); fflush(stderr); @@ -238,6 +310,32 @@ void M17Codec::hostname_lookup(QHostInfo i) } } +void M17Codec::mmdvm_direct_connect() +{ + if(m_modemport != ""){ + m_modem = new SerialModem("M17"); + m_modem->set_modem_flags(m_rxInvert, m_txInvert, m_pttInvert, m_useCOSAsLockout, m_duplex); + m_modem->set_modem_params(m_rxfreq, m_txfreq, m_txDelay, m_rxLevel, m_rfLevel, m_ysfTXHang, m_cwIdTXLevel, m_dstarTXLevel, m_dmrTXLevel, m_ysfTXLevel, m_p25TXLevel, m_nxdnTXLevel, m_pocsagTXLevel, m_m17TXLevel); + m_modem->connect_to_serial(m_modemport); + connect(m_modem, SIGNAL(modem_data_ready(QByteArray)), this, SLOT(process_modem_data(QByteArray))); + if(m_modeinfo.status == CONNECTING){ + m_modeinfo.status = CONNECTED_RW; + } + } + else{ + qDebug() << "No modem, cant do MMDVM_DIRECT"; + } + + m_c2 = new CCodec2(true); + m_txtimer = new QTimer(); + connect(m_txtimer, SIGNAL(timeout()), this, SLOT(transmit())); + m_rxtimer = new QTimer(); + connect(m_rxtimer, SIGNAL(timeout()), this, SLOT(process_rx_data())); + m_audio = new AudioEngine(m_audioin, m_audioout); + m_audio->init(); + emit update(m_modeinfo); +} + void M17Codec::send_ping() { QByteArray out; @@ -256,7 +354,7 @@ void M17Codec::send_ping() #ifdef DEBUG fprintf(stderr, "PING: "); for(int i = 0; i < out.size(); ++i){ - fprintf(stderr, "%02x ", (unsigned char)out.data()[i]); + fprintf(stderr, "%02x ", (uint8_t)out.data()[i]); } fprintf(stderr, "\n"); fflush(stderr); @@ -265,6 +363,10 @@ void M17Codec::send_ping() void M17Codec::send_disconnect() { + if(m_modeinfo.host == "MMDVM_DIRECT"){ + return; + } + qDebug() << "send_disconnect()"; QByteArray out; uint8_t cs[10]; @@ -282,13 +384,215 @@ void M17Codec::send_disconnect() #ifdef DEBUG fprintf(stderr, "SEND: "); for(int i = 0; i < out.size(); ++i){ - fprintf(stderr, "%02x ", (unsigned char)out.data()[i]); + fprintf(stderr, "%02x ", (uint8_t)out.data()[i]); } fprintf(stderr, "\n"); fflush(stderr); #endif } +void M17Codec::send_modem_data(QByteArray d) +{ + CM17Convolution conv; + static uint8_t lsf[M17_LSF_LENGTH_BYTES]; + static uint8_t lsfcnt = 0; + uint8_t txframe[M17_FRAME_LENGTH_BYTES]; + uint8_t tmp[M17_FRAME_LENGTH_BYTES]; + + // FIXME: Hard code dst to "ALL " until I better understand what to do here + ::memset(&d.data()[6], 0, 4); + d.data()[10] = 0x4c; + d.data()[11] = 0xe1; + + if(m_modeinfo.stream_state == STREAM_NEW){ + ::memcpy(lsf, &d.data()[6], M17_LSF_LENGTH_BYTES); + ::memcpy(txframe, M17_LINK_SETUP_SYNC_BYTES, 2); + conv.encodeLinkSetup(lsf, txframe + M17_SYNC_LENGTH_BYTES); + interleave(txframe, tmp); + decorrelate(tmp, txframe); + + m_rxmodemq.append(0xe0); + m_rxmodemq.append(M17_FRAME_LENGTH_BYTES + 4); + m_rxmodemq.append(0x45); + m_rxmodemq.append('\x00'); + + for(uint32_t i = 0; i < M17_FRAME_LENGTH_BYTES; ++i){ + m_rxmodemq.append(txframe[i]); + } + } + + if(lsfcnt == 0){ + ::memcpy(lsf, &d.data()[6], M17_LSF_LENGTH_BYTES); + } + + ::memcpy(txframe, M17_STREAM_SYNC_BYTES, 2); + + uint8_t lich[M17_LICH_FRAGMENT_LENGTH_BYTES]; + encodeCRC16(lsf, M17_LSF_LENGTH_BYTES); + ::memcpy(lich, lsf + (lsfcnt * M17_LSF_FRAGMENT_LENGTH_BYTES), M17_LSF_FRAGMENT_LENGTH_BYTES); + lich[5U] = (lsfcnt & 0x07U) << 5; + + uint32_t frag1, frag2, frag3, frag4; + splitFragmentLICH(lich, frag1, frag2, frag3, frag4); + uint32_t lich1 = CGolay24128::encode24128(frag1); + uint32_t lich2 = CGolay24128::encode24128(frag2); + uint32_t lich3 = CGolay24128::encode24128(frag3); + uint32_t lich4 = CGolay24128::encode24128(frag4); + combineFragmentLICHFEC(lich1, lich2, lich3, lich4, txframe + M17_SYNC_LENGTH_BYTES); + + conv.encodeData((uint8_t *)&d.data()[34], txframe + M17_SYNC_LENGTH_BYTES + M17_LICH_FRAGMENT_FEC_LENGTH_BYTES); + interleave(txframe, tmp); + decorrelate(tmp, txframe); + + m_rxmodemq.append(0xe0); + m_rxmodemq.append(M17_FRAME_LENGTH_BYTES + 4); + m_rxmodemq.append(0x46); + m_rxmodemq.append('\x00'); + + for(uint32_t i = 0; i < M17_FRAME_LENGTH_BYTES; ++i){ + m_rxmodemq.append(txframe[i]); + } + lsfcnt++; + if (lsfcnt >= 6U) + lsfcnt = 0U; +} + +void M17Codec::process_modem_data(QByteArray d) +{ + QByteArray txframe; + static uint16_t txstreamid = 0; + static uint8_t lsf[M17_LSF_LENGTH_BYTES]; + CM17Convolution conv; + uint8_t tmp[M17_FRAME_LENGTH_BYTES]; + + if(d.size() < 3){ + return; + } + uint8_t *p = (uint8_t *)d.data(); + + if((d.data()[2] == 0x45) || (d.data()[2] == 0x46)){ + p += 4; + decorrelate(p, tmp); + interleave(tmp, p); + } + + if((d.data()[2] == 0x48) || (d.data()[2] == 0x49)){ + txstreamid = 0; + if(m_modeinfo.host == "MMDVM_DIRECT"){ + m_modeinfo.streamid = 0; + m_modeinfo.stream_state = STREAM_END; + } + } + + else if(d.data()[2] == 0x45){ + ::memset(lsf, 0x00U, M17_LSF_LENGTH_BYTES); + uint32_t ber = conv.decodeLinkSetup(p + M17_SYNC_LENGTH_BYTES, lsf); + bool valid = checkCRC16(lsf, M17_LSF_LENGTH_BYTES); + txstreamid = static_cast((::rand() & 0xFFFF)); + qDebug() << "LSF valid == " << valid; + + if(m_modeinfo.host == "MMDVM_DIRECT"){ + uint8_t cs[10]; + ::memcpy(cs, lsf, 6); + decode_callsign(cs); + m_modeinfo.dst = QString((char *)cs); + ::memcpy(cs, lsf+6, 6); + decode_callsign(cs); + m_modeinfo.src = QString((char *)cs); + } + } + else if(d.data()[2] == 0x46){ + uint8_t frame[M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES]; + uint32_t errors = conv.decodeData(p + M17_SYNC_LENGTH_BYTES + M17_LICH_FRAGMENT_FEC_LENGTH_BYTES, frame); + //uint16_t fn = (frame[0U] << 8) + (frame[1U] << 0); + + uint8_t netframe[M17_LSF_LENGTH_BYTES + M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES + M17_CRC_LENGTH_BYTES]; + ::memcpy(netframe, lsf, M17_LSF_LENGTH_BYTES); + ::memcpy(netframe + M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES, frame, M17_FN_LENGTH_BYTES + M17_PAYLOAD_LENGTH_BYTES); + netframe[M17_LSF_LENGTH_BYTES - M17_CRC_LENGTH_BYTES + 0U] &= 0x7FU; + + if(m_modeinfo.host == "MMDVM_DIRECT"){ + if( !m_tx && (m_modeinfo.streamid == 0) ){ + if(txstreamid == 0){ + qDebug() << "No header, late entry..."; + txstreamid = static_cast((::rand() & 0xFFFF)); + } + m_modeinfo.streamid = txstreamid; + m_audio->start_playback(); + + if((netframe[13] & 0x06U) == 0x04U){ + m_modeinfo.type = 1;//"3200 Voice"; + set_mode(true); + } + else{ + m_modeinfo.type = 0;//"1600 V/D"; + set_mode(false); + } + + if(!m_rxtimer->isActive()){ + #ifdef Q_OS_WIN + m_rxtimer->start(m_modeinfo.type ? m_rxtimerint : 32); + #else + m_rxtimer->start(m_modeinfo.type ? m_rxtimerint : m_rxtimerint*2); + #endif + } + + m_modeinfo.stream_state = STREAM_NEW; + m_modeinfo.ts = QDateTime::currentMSecsSinceEpoch(); + qDebug() << "New RF stream from " << m_modeinfo.src << " to " << m_modeinfo.dst << " id == " << QString::number(m_modeinfo.streamid, 16); + } + else{ + m_modeinfo.stream_state = STREAMING; + } + m_modeinfo.frame_number = (netframe[28] << 8) | (netframe[29] & 0xff); + m_rxwatchdog = 0; + int s = 8; + if(get_mode()){ + s = 16; + } + + for(int i = 0; i < s; ++i){ + m_rxcodecq.append(netframe[30+i]); + } + emit update(m_modeinfo); + } + else{ + uint8_t dst[10]; + memset(dst, ' ', 9); + memcpy(dst, m_hostname.toLocal8Bit(), m_hostname.size()); + dst[8] = m_module; + dst[9] = 0x00; + encode_callsign(dst); + + txframe.append('M'); + txframe.append('1'); + txframe.append('7'); + txframe.append(' '); + txframe.append(txstreamid >> 8); + txframe.append(txstreamid & 0xff); + txframe.append((char *)dst, 6); + //txframe.append((char *)src, 6); + txframe.append((char *)&netframe[6], 6); + txframe.append('\x00'); + txframe.append(netframe[13]); // Frame type voice only + txframe.append(14, 0x00); //Blank nonce + txframe.append((char)(netframe[28] >> 8)); + txframe.append((char)netframe[29] & 0xff); + txframe.append((char *)&netframe[30], 16); + txframe.append(2, 0x00); + m_udp->writeDatagram(txframe, m_address, m_modeinfo.port); +#ifdef DEBUG + fprintf(stderr, "NETFRAME:%02x:", (uint8_t)d.data()[2]); + for(int i = 0; i < 50; ++i){ + fprintf(stderr, "%02x ", netframe[i]); + } + fprintf(stderr, "\n"); + fflush(stderr); +#endif + } + } +} + void M17Codec::toggle_tx(bool tx) { qDebug() << "M17Codec::toggle_tx(bool tx) == " << tx; @@ -346,7 +650,22 @@ void M17Codec::transmit() if(txstreamid == 0){ txstreamid = static_cast((::rand() & 0xFFFF)); //std::cerr << "txstreamid == " << txstreamid << std::endl; + if(!m_rxtimer->isActive() && (m_modeinfo.host == "MMDVM_DIRECT")){ + m_modeinfo.stream_state = STREAM_NEW; +#ifdef Q_OS_WIN + m_rxtimer->start(m_modeinfo.type ? m_rxtimerint : 32); +#else + m_rxtimer->start(19); +#endif + } + } + else{ + if(m_modeinfo.host == "MMDVM_DIRECT"){ + m_modeinfo.stream_state = STREAMING; + } + } + uint8_t src[10]; uint8_t dst[10]; memset(dst, ' ', 9); @@ -379,7 +698,14 @@ void M17Codec::transmit() //QString ss = QString("%1").arg(txstreamid, 4, 16, QChar('0')); //QString n = QString("TX %1").arg(tx_cnt, 4, 16, QChar('0')); - m_udp->writeDatagram(txframe, m_address, m_modeinfo.port); + if(m_modeinfo.host == "MMDVM_DIRECT"){ + send_modem_data(txframe); + m_rxwatchdog = 0; + } + else{ + m_udp->writeDatagram(txframe, m_address, m_modeinfo.port); + } + ++tx_cnt; m_modeinfo.src = m_modeinfo.callsign; m_modeinfo.dst = m_hostname; @@ -390,7 +716,7 @@ void M17Codec::transmit() fprintf(stderr, "SEND:%d: ", txframe.size()); for(int i = 0; i < txframe.size(); ++i){ - fprintf(stderr, "%02x ", (unsigned char)txframe.data()[i]); + fprintf(stderr, "%02x ", (uint8_t)txframe.data()[i]); } fprintf(stderr, "\n"); fflush(stderr); @@ -431,7 +757,13 @@ void M17Codec::transmit() txframe.append(2, 0x00); //QString n = QString("%1").arg(tx_cnt, 4, 16, QChar('0')); - m_udp->writeDatagram(txframe, m_address, m_modeinfo.port); + if(m_modeinfo.host == "MMDVM_DIRECT"){ + send_modem_data(txframe); + m_modeinfo.stream_state = STREAM_END; + } + else{ + m_udp->writeDatagram(txframe, m_address, m_modeinfo.port); + } txstreamid = 0; tx_cnt = 0; #ifdef USE_FLITE @@ -449,7 +781,7 @@ void M17Codec::transmit() emit update(m_modeinfo); fprintf(stderr, "LAST:%d: ", txframe.size()); for(int i = 0; i < txframe.size(); ++i){ - fprintf(stderr, "%02x ", (unsigned char)txframe.data()[i]); + fprintf(stderr, "%02x ", (uint8_t)txframe.data()[i]); } fprintf(stderr, "\n"); fflush(stderr); @@ -460,6 +792,7 @@ void M17Codec::process_rx_data() { int16_t pcm[320]; uint8_t codec2[8]; + static uint8_t cnt = 0; if(m_rxwatchdog++ > 50){ qDebug() << "RX stream timeout "; @@ -470,6 +803,18 @@ void M17Codec::process_rx_data() m_modeinfo.streamid = 0; } + if((m_rxmodemq.size() > 2) && (++cnt >= 2)){ + QByteArray out; + int s = m_rxmodemq[1]; + if((m_rxmodemq[0] == 0xe0) && (m_rxmodemq.size() >= s)){ + for(int i = 0; i < s; ++i){ + out.append(m_rxmodemq.dequeue()); + } + m_modem->write(out); + } + cnt = 0; + } + if((!m_tx) && (m_rxcodecq.size() > 7) ){ for(int i = 0; i < 8; ++i){ codec2[i] = m_rxcodecq.dequeue(); @@ -489,3 +834,153 @@ void M17Codec::process_rx_data() return; } } + +void M17Codec::decorrelate(uint8_t *in, uint8_t *out) +{ + for (uint32_t i = M17_SYNC_LENGTH_BYTES; i < M17_FRAME_LENGTH_BYTES; i++) { + out[i] = in[i] ^ SCRAMBLER[i]; + } +} + +void M17Codec::interleave(uint8_t *in, uint8_t *out) +{ + for (uint32_t i = 0U; i < (M17_FRAME_LENGTH_BITS - M17_SYNC_LENGTH_BITS); i++) { + uint32_t n1 = i + M17_SYNC_LENGTH_BITS; + bool b = READ_BIT(in, n1) != 0U; + uint32_t n2 = INTERLEAVER[i] + M17_SYNC_LENGTH_BITS; + WRITE_BIT(out, n2, b); + } +} + +void M17Codec::splitFragmentLICH(const uint8_t* data, uint32_t& frag1, uint32_t& frag2, uint32_t& frag3, uint32_t& frag4) +{ + assert(data != NULL); + + frag1 = frag2 = frag3 = frag4 = 0x00U; + + uint32_t offset = 0U; + uint32_t MASK = 0x800U; + for (uint32_t i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) { + bool b = READ_BIT(data, offset) != 0x00U; + if (b) + frag1 |= MASK; + } + + MASK = 0x800U; + for (uint32_t i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) { + bool b = READ_BIT(data, offset) != 0x00U; + if (b) + frag2 |= MASK; + } + + MASK = 0x800U; + for (uint32_t i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) { + bool b = READ_BIT(data, offset) != 0x00U; + if (b) + frag3 |= MASK; + } + + MASK = 0x800U; + for (uint32_t i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) { + bool b = READ_BIT(data, offset) != 0x00U; + if (b) + frag4 |= MASK; + } +} + +void M17Codec::combineFragmentLICH(uint32_t frag1, uint32_t frag2, uint32_t frag3, uint32_t frag4, uint8_t* data) +{ + assert(data != NULL); + + unsigned int offset = 0U; + unsigned int MASK = 0x800U; + for (uint32_t i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) { + bool b = (frag1 & MASK) == MASK; + WRITE_BIT(data, offset, b); + } + + MASK = 0x800U; + for (uint32_t i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) { + bool b = (frag2 & MASK) == MASK; + WRITE_BIT(data, offset, b); + } + + MASK = 0x800U; + for (uint32_t i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) { + bool b = (frag3 & MASK) == MASK; + WRITE_BIT(data, offset, b); + } + + MASK = 0x800U; + for (uint32_t i = 0U; i < (M17_LICH_FRAGMENT_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) { + bool b = (frag4 & MASK) == MASK; + WRITE_BIT(data, offset, b); + } +} + +void M17Codec::combineFragmentLICHFEC(uint32_t frag1, uint32_t frag2, uint32_t frag3, uint32_t frag4, uint8_t* data) +{ + assert(data != NULL); + + uint32_t offset = 0U; + uint32_t MASK = 0x800000U; + for (uint32_t i = 0U; i < (M17_LICH_FRAGMENT_FEC_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) { + bool b = (frag1 & MASK) == MASK; + WRITE_BIT(data, offset, b); + } + + MASK = 0x800000U; + for (uint32_t i = 0U; i < (M17_LICH_FRAGMENT_FEC_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) { + bool b = (frag2 & MASK) == MASK; + WRITE_BIT(data, offset, b); + } + + MASK = 0x800000U; + for (uint32_t i = 0U; i < (M17_LICH_FRAGMENT_FEC_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) { + bool b = (frag3 & MASK) == MASK; + WRITE_BIT(data, offset, b); + } + + MASK = 0x800000U; + for (uint32_t i = 0U; i < (M17_LICH_FRAGMENT_FEC_LENGTH_BITS / 4U); i++, offset++, MASK >>= 1) { + bool b = (frag4 & MASK) == MASK; + WRITE_BIT(data, offset, b); + } +} + +bool M17Codec::checkCRC16(const uint8_t* in, uint32_t nBytes) +{ + assert(in != NULL); + assert(nBytes > 2U); + + uint16_t crc = createCRC16(in, nBytes - 2U); + + uint8_t temp[2U]; + temp[0U] = (crc >> 8) & 0xFFU; + temp[1U] = (crc >> 0) & 0xFFU; + + return temp[0U] == in[nBytes - 2U] && temp[1U] == in[nBytes - 1U]; +} + +void M17Codec::encodeCRC16(uint8_t* in, uint32_t nBytes) +{ + assert(in != NULL); + assert(nBytes > 2U); + + uint16_t crc = createCRC16(in, nBytes - 2U); + + in[nBytes - 2U] = (crc >> 8) & 0xFFU; + in[nBytes - 1U] = (crc >> 0) & 0xFFU; +} + +uint16_t M17Codec::createCRC16(const uint8_t* in, uint32_t nBytes) +{ + assert(in != NULL); + + uint16_t crc = 0xFFFFU; + + for (uint32_t i = 0U; i < nBytes; i++) + crc = (crc << 8) ^ CRC16_TABLE[((crc >> 8) ^ uint16_t(in[i])) & 0x00FFU]; + + return crc; +} diff --git a/m17codec.h b/m17codec.h index d5970b0..b40ce5d 100755 --- a/m17codec.h +++ b/m17codec.h @@ -37,14 +37,25 @@ public: CCodec2 *m_c2; private slots: void process_udp(); + void process_modem_data(QByteArray); + void send_modem_data(QByteArray); void send_ping(); void send_disconnect(); void toggle_tx(bool); void start_tx(); void transmit(); void hostname_lookup(QHostInfo i); + void mmdvm_direct_connect(); void rate_changed(int r) { m_txrate = r; } void process_rx_data(); + void splitFragmentLICH(const uint8_t*, uint32_t&, uint32_t&, uint32_t&, uint32_t&); + void combineFragmentLICH(uint32_t, uint32_t, uint32_t, uint32_t, uint8_t*); + void combineFragmentLICHFEC(uint32_t, uint32_t, uint32_t, uint32_t, uint8_t*); + void interleave(uint8_t *, uint8_t *); + void decorrelate(uint8_t *, uint8_t *); + bool checkCRC16(const uint8_t* in, uint32_t nBytes); + void encodeCRC16(uint8_t* in, uint32_t nBytes); + uint16_t createCRC16(const uint8_t* in, uint32_t nBytes); private: int m_txrate; }; diff --git a/main.qml b/main.qml index 9965073..bec4da6 100644 --- a/main.qml +++ b/main.qml @@ -137,7 +137,9 @@ ApplicationWindow { Connections { target: Qt.application function onStateChanged() { - //console.debug("applicationStateChanged: " + Qt.application.state) + if (Qt.application.state !== Qt.ApplicationActive) { + droidstar.reset_connect_status(); + } } } Connections { @@ -463,7 +465,6 @@ ApplicationWindow { mainTab.agcBox.checked = true; } if(c === 3){ - connectDialog.open(); } if(c === 4){ idcheckDialog.open(); diff --git a/nxdncodec.cpp b/nxdncodec.cpp index 26f494a..caea928 100755 --- a/nxdncodec.cpp +++ b/nxdncodec.cpp @@ -18,7 +18,7 @@ #include "nxdncodec.h" #include -#define DEBUG +//#define DEBUG const int dvsi_interleave[49] = { 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 41, 43, 45, 47, diff --git a/p25codec.cpp b/p25codec.cpp index 4c73cf1..18fe442 100755 --- a/p25codec.cpp +++ b/p25codec.cpp @@ -19,7 +19,7 @@ #include #include "p25codec.h" -#define DEBUG +//#define DEBUG const unsigned char REC62[] = {0x62U, 0x02U, 0x02U, 0x0CU, 0x0BU, 0x12U, 0x64U, 0x00U, 0x00U, 0x80U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U,0x00U, 0x00U, 0x00U, 0x00U, 0x00U}; const unsigned char REC63[] = {0x63U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x02U}; diff --git a/refcodec.cpp b/refcodec.cpp index 9878fa8..087eca6 100755 --- a/refcodec.cpp +++ b/refcodec.cpp @@ -20,7 +20,7 @@ #include "refcodec.h" #include "CRCenc.h" -#define DEBUG +//#define DEBUG const unsigned char MMDVM_DSTAR_HEADER = 0x10U; const unsigned char MMDVM_DSTAR_DATA = 0x11U; @@ -105,7 +105,7 @@ void REFCodec::process_udp() if(m_modemport != ""){ m_modem = new SerialModem("REF"); m_modem->set_modem_flags(m_rxInvert, m_txInvert, m_pttInvert, m_useCOSAsLockout, m_duplex); - m_modem->set_modem_params(m_rxfreq, m_txfreq, m_txDelay, m_rxLevel, m_rfLevel, m_ysfTXHang, m_cwIdTXLevel, m_dstarTXLevel, m_dmrTXLevel, m_ysfTXLevel, m_p25TXLevel, m_nxdnTXLevel, m_pocsagTXLevel); + m_modem->set_modem_params(m_rxfreq, m_txfreq, m_txDelay, m_rxLevel, m_rfLevel, m_ysfTXHang, m_cwIdTXLevel, m_dstarTXLevel, m_dmrTXLevel, m_ysfTXLevel, m_p25TXLevel, m_nxdnTXLevel, m_pocsagTXLevel, m_m17TXLevel); m_modem->connect_to_serial(m_modemport); connect(m_modem, SIGNAL(modem_data_ready(QByteArray)), this, SLOT(process_modem_data(QByteArray))); } diff --git a/serialambe.cpp b/serialambe.cpp index 8042f48..f8c3e85 100755 --- a/serialambe.cpp +++ b/serialambe.cpp @@ -72,7 +72,7 @@ QMap SerialAMBE::discover_devices() + "Product Identifier: " + (serialPortInfo.hasProductIdentifier() ? QByteArray::number(serialPortInfo.productIdentifier(), 16) : blankString) + ENDLINE + "Busy: " + (serialPortInfo.isBusy() ? "Yes" : "No") + ENDLINE; fprintf(stderr, "%s", out.toStdString().c_str());fflush(stderr); - if((!serialPortInfo.description().isEmpty()) && (!serialPortInfo.isBusy())){ + if(!serialPortInfo.isBusy()){ //devlist[serialPortInfo.systemLocation()] = serialPortInfo.portName() + " - " + serialPortInfo.manufacturer() + " " + serialPortInfo.description() + " - " + serialPortInfo.serialNumber(); devlist[serialPortInfo.systemLocation()] = serialPortInfo.systemLocation() + ":" + serialPortInfo.description(); } diff --git a/serialmodem.cpp b/serialmodem.cpp index 3d2bce9..af0ec2c 100644 --- a/serialmodem.cpp +++ b/serialmodem.cpp @@ -20,7 +20,7 @@ #include #include "serialmodem.h" -#define DEBUGHW +//#define DEBUGHW #define ENDLINE "\n" const unsigned char MODE_IDLE = 0U; @@ -29,6 +29,7 @@ const unsigned char MODE_DMR = 2U; const unsigned char MODE_YSF = 3U; const unsigned char MODE_P25 = 4U; const unsigned char MODE_NXDN = 5U; +const unsigned char MODE_M17 = 7U; const unsigned char MODE_CW = 98U; const unsigned char MODE_LOCKOUT = 99U; const unsigned char MODE_ERROR = 100U; @@ -103,6 +104,12 @@ const unsigned char MMDVM_P25_LOST = 0x32U; const unsigned char MMDVM_NXDN_DATA = 0x40U; const unsigned char MMDVM_NXDN_LOST = 0x41U; +const unsigned char MMDVM_M17_LINK_SETUP = 0x45U; +const unsigned char MMDVM_M17_STREAM = 0x46U; +const unsigned char MMDVM_M17_PACKET = 0x47U; +const unsigned char MMDVM_M17_LOST = 0x48U; +const unsigned char MMDVM_M17_EOT = 0x49U; + const unsigned char MMDVM_POCSAG_DATA = 0x50U; const unsigned char MMDVM_FM_PARAMS1 = 0x60U; @@ -133,6 +140,8 @@ SerialModem::SerialModem(QString protocol) m_dmrDelay = 0; m_debug = false; m_dmrColorCode = 1; + m_m17support = 1; + m_m17TXHang = 5; } SerialModem::~SerialModem() @@ -164,6 +173,9 @@ void SerialModem::set_mode(QString m) else if(m == "NXDN"){ m_nxdnEnabled = 1; } + else if(m == "M17"){ + m_m17Enabled = 1; + } } void SerialModem::set_modem_flags(bool rxInvert, bool txInvert, bool pttInvert, bool useCOSAsLockout, bool duplex) @@ -176,7 +188,7 @@ void SerialModem::set_modem_flags(bool rxInvert, bool txInvert, bool pttInvert, m_ysfLoDev = 0; } -void SerialModem::set_modem_params(uint32_t rxfreq, uint32_t txfreq, uint32_t txDelay, float rxLevel, float rfLevel, uint32_t ysfTXHang, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagTXLevel) +void SerialModem::set_modem_params(uint32_t rxfreq, uint32_t txfreq, uint32_t txDelay, float rxLevel, float rfLevel, uint32_t ysfTXHang, float cwIdTXLevel, float dstarTXLevel, float dmrTXLevel, float ysfTXLevel, float p25TXLevel, float nxdnTXLevel, float pocsagTXLevel, float m17TXLevel) { m_rxfreq = rxfreq; m_txfreq = txfreq; @@ -190,6 +202,7 @@ void SerialModem::set_modem_params(uint32_t rxfreq, uint32_t txfreq, uint32_t tx m_p25TXLevel = p25TXLevel; m_nxdnTXLevel = nxdnTXLevel; m_pocsagTXLevel = pocsagTXLevel; + m_m17TXLevel = m17TXLevel; m_ysfTXHang = ysfTXHang; } @@ -230,7 +243,7 @@ void SerialModem::connect_to_serial(QString p) //if((d.data()[i] == 0x61) && (data.data()[i+1] == 0x01) && (data.data()[i+2] == 0x42) && (data.data()[i+3] == 0x02)){ // i+= 6; //} - fprintf(stderr, "%02x ", (unsigned char)a.data()[i]); + fprintf(stderr, "%02x ", (uint8_t)a.data()[i]); } fprintf(stderr, "\n"); fflush(stderr); @@ -260,7 +273,7 @@ void SerialModem::process_serial() //if((d.data()[i] == 0x61) && (data.data()[i+1] == 0x01) && (data.data()[i+2] == 0x42) && (data.data()[i+3] == 0x02)){ // i+= 6; //} - fprintf(stderr, "%02x ", (unsigned char)d.data()[i]); + fprintf(stderr, "%02x ", (uint8_t)d.data()[i]); } fprintf(stderr, "\n"); fflush(stderr); @@ -329,7 +342,7 @@ void SerialModem::set_freq() out.append((m_txfreq >> 8) & 0xFFU); out.append((m_txfreq >> 16) & 0xFFU); out.append((m_txfreq >> 24) & 0xFFU); - out.append((unsigned char)(m_rfLevel * 2.55F + 0.5F)); + out.append((uint8_t)(m_rfLevel * 2.55F + 0.5F)); out.append((pfreq >> 0) & 0xFFU); out.append((pfreq >> 8) & 0xFFU); out.append((pfreq >> 16) & 0xFFU); @@ -341,7 +354,7 @@ void SerialModem::set_freq() //if((d.data()[i] == 0x61) && (data.data()[i+1] == 0x01) && (data.data()[i+2] == 0x42) && (data.data()[i+3] == 0x02)){ // i+= 6; //} - fprintf(stderr, "%02x ", (unsigned char)out.data()[i]); + fprintf(stderr, "%02x ", (uint8_t)out.data()[i]); } fprintf(stderr, "\n"); fflush(stderr); @@ -352,11 +365,17 @@ void SerialModem::set_config() { QByteArray out; uint8_t c; - out.clear(); out.append(MMDVM_FRAME_START); - out.append(24U); + + if(m_m17support){ + out.append(26U); + } + else{ + out.append(24U); + } + out.append(MMDVM_SET_CONFIG); c = 0x00U; @@ -390,28 +409,36 @@ void SerialModem::set_config() c |= 0x10U; if (m_pocsagEnabled) c |= 0x20U; + if (m_m17Enabled) + c |= 0x40U; out.append(c); out.append(m_txDelay / 10U); // In 10ms units out.append(MODE_IDLE); - out.append((unsigned char)(m_rxLevel * 2.55F + 0.5F)); - out.append((unsigned char)(m_cwIdTXLevel * 2.55F + 0.5F)); + out.append((uint8_t)(m_rxLevel * 2.55F + 0.5F)); + out.append((uint8_t)(m_cwIdTXLevel * 2.55F + 0.5F)); out.append(m_dmrColorCode); out.append(m_dmrDelay); out.append(128U); // Was OscOffset - out.append((unsigned char)(m_dstarTXLevel * 2.55F + 0.5F)); - out.append((unsigned char)(m_dmrTXLevel * 2.55F + 0.5F)); - out.append((unsigned char)(m_ysfTXLevel * 2.55F + 0.5F)); - out.append((unsigned char)(m_p25TXLevel * 2.55F + 0.5F)); - out.append((unsigned char)(m_txDCOffset + 128)); - out.append((unsigned char)(m_rxDCOffset + 128)); - out.append((unsigned char)(m_nxdnTXLevel * 2.55F + 0.5F)); - out.append((unsigned char)m_ysfTXHang); - out.append((unsigned char)(m_pocsagTXLevel * 2.55F + 0.5F)); - out.append((unsigned char)(m_fmTXLevel * 2.55F + 0.5F)); - out.append((unsigned char)m_p25TXHang); - out.append((unsigned char)m_nxdnTXHang); + out.append((uint8_t)(m_dstarTXLevel * 2.55F + 0.5F)); + out.append((uint8_t)(m_dmrTXLevel * 2.55F + 0.5F)); + out.append((uint8_t)(m_ysfTXLevel * 2.55F + 0.5F)); + out.append((uint8_t)(m_p25TXLevel * 2.55F + 0.5F)); + out.append((uint8_t)(m_txDCOffset + 128)); + out.append((uint8_t)(m_rxDCOffset + 128)); + out.append((uint8_t)(m_nxdnTXLevel * 2.55F + 0.5F)); + out.append((uint8_t)m_ysfTXHang); + out.append((uint8_t)(m_pocsagTXLevel * 2.55F + 0.5F)); + out.append((uint8_t)(m_fmTXLevel * 2.55F + 0.5F)); + out.append((uint8_t)m_p25TXHang); + out.append((uint8_t)m_nxdnTXHang); + + if(m_m17support){ + out.append((uint8_t)(m_m17TXLevel * 2.55F + 0.5F)); + out.append((uint8_t)m_m17TXHang); + } + m_serial->write(out); #ifdef DEBUGHW fprintf(stderr, "MODEMTX %d:%d:", out.size(), m_serialdata.size()); diff --git a/serialmodem.h b/serialmodem.h index 2e42f6e..d625262 100644 --- a/serialmodem.h +++ b/serialmodem.h @@ -34,7 +34,7 @@ public: ~SerialModem(); void set_mode(QString); void set_modem_flags(bool, bool, bool, bool, bool); - void set_modem_params(uint32_t, uint32_t, uint32_t, float, float, uint32_t, float, float, float, float, float, float, float); + void set_modem_params(uint32_t, uint32_t, uint32_t, float, float, uint32_t, float, float, float, float, float, float, float, float); static QMap discover_devices(); void connect_to_serial(QString); void write(QByteArray); @@ -61,6 +61,7 @@ private: uint32_t m_ysfTXHang; uint32_t m_p25TXHang; uint32_t m_nxdnTXHang; + uint32_t m_m17TXHang; bool m_duplex; bool m_rxInvert; bool m_txInvert; @@ -76,6 +77,7 @@ private: float m_p25TXLevel; float m_nxdnTXLevel; float m_pocsagTXLevel; + float m_m17TXLevel; float m_fmTXLevel; bool m_debug; bool m_useCOSAsLockout; @@ -85,7 +87,9 @@ private: bool m_p25Enabled; bool m_nxdnEnabled; bool m_pocsagEnabled; + bool m_m17Enabled; bool m_fmEnabled; + bool m_m17support; int m_rxDCOffset; int m_txDCOffset; signals: diff --git a/xrfcodec.cpp b/xrfcodec.cpp index a6de062..39cd691 100755 --- a/xrfcodec.cpp +++ b/xrfcodec.cpp @@ -20,7 +20,7 @@ #include "xrfcodec.h" #include "CRCenc.h" -#define DEBUG +//#define DEBUG const unsigned char MMDVM_DSTAR_HEADER = 0x10U; const unsigned char MMDVM_DSTAR_DATA = 0x11U; @@ -76,7 +76,7 @@ void XRFCodec::process_udp() if(m_modemport != ""){ m_modem = new SerialModem("XRF"); m_modem->set_modem_flags(m_rxInvert, m_txInvert, m_pttInvert, m_useCOSAsLockout, m_duplex); - m_modem->set_modem_params(m_rxfreq, m_txfreq, m_txDelay, m_rxLevel, m_rfLevel, m_ysfTXHang, m_cwIdTXLevel, m_dstarTXLevel, m_dmrTXLevel, m_ysfTXLevel, m_p25TXLevel, m_nxdnTXLevel, m_pocsagTXLevel); + m_modem->set_modem_params(m_rxfreq, m_txfreq, m_txDelay, m_rxLevel, m_rfLevel, m_ysfTXHang, m_cwIdTXLevel, m_dstarTXLevel, m_dmrTXLevel, m_ysfTXLevel, m_p25TXLevel, m_nxdnTXLevel, m_pocsagTXLevel, m_m17TXLevel); m_modem->connect_to_serial(m_modemport); connect(m_modem, SIGNAL(modem_data_ready(QByteArray)), this, SLOT(process_modem_data(QByteArray))); } diff --git a/ysfcodec.cpp b/ysfcodec.cpp index dfac2f0..692a71d 100755 --- a/ysfcodec.cpp +++ b/ysfcodec.cpp @@ -157,7 +157,7 @@ void YSFCodec::process_udp() if(m_modemport != ""){ m_modem = new SerialModem("YSF"); m_modem->set_modem_flags(m_rxInvert, m_txInvert, m_pttInvert, m_useCOSAsLockout, m_duplex); - m_modem->set_modem_params(m_rxfreq, m_txfreq, m_txDelay, m_rxLevel, m_rfLevel, m_ysfTXHang, m_cwIdTXLevel, m_dstarTXLevel, m_dmrTXLevel, m_ysfTXLevel, m_p25TXLevel, m_nxdnTXLevel, m_pocsagTXLevel); + m_modem->set_modem_params(m_rxfreq, m_txfreq, m_txDelay, m_rxLevel, m_rfLevel, m_ysfTXHang, m_cwIdTXLevel, m_dstarTXLevel, m_dmrTXLevel, m_ysfTXLevel, m_p25TXLevel, m_nxdnTXLevel, m_pocsagTXLevel, m_m17TXLevel); m_modem->connect_to_serial(m_modemport); connect(m_modem, SIGNAL(modem_data_ready(QByteArray)), this, SLOT(process_modem_data(QByteArray))); }