Add MMDVM support for M17 with MMDVM_DIRECT option. This is still a work in progress

pull/2/head
Doug McLain 3 years ago
parent 8afe4f4a1f
commit e28a626559

@ -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 \

@ -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;
}

@ -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

@ -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 <cstdio>
#include <cassert>
#include <cstring>
#include <cstdlib>
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++;
}
}

@ -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 <cstdint>
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

@ -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

@ -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

@ -2,5 +2,7 @@
<resources>
<usb-device vendor-id="1027" product-id="24597" />
<usb-device vendor-id="3405" product-id="567" />
<usb-device vendor-id="1155" product-id="22336" />
<usb-device vendor-id="1155" product-id="22336" />
<usb-device vendor-id="7855" product-id="0004" />
<usb-device vendor-id="4292" product-id="60000" />
</resources>

@ -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<QHostAddress> 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){

@ -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;

@ -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)));
}

@ -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)));
}

@ -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");

@ -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(); }

@ -22,7 +22,7 @@
#else
#include <arpa/inet.h>
#endif
#define DEBUG
//#define DEBUG
#ifdef USE_FLITE
extern "C" {

@ -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 <cstring>
#include <iostream>
#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<uint16_t>((::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<uint16_t>((::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<uint16_t>((::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;
}

@ -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;
};

@ -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();

@ -18,7 +18,7 @@
#include "nxdncodec.h"
#include <cstring>
#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,

@ -19,7 +19,7 @@
#include <cstring>
#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};

@ -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)));
}

@ -72,7 +72,7 @@ QMap<QString, QString> 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();
}

@ -20,7 +20,7 @@
#include <QDebug>
#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());

@ -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<QString, QString> 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:

@ -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)));
}

@ -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)));
}

Loading…
Cancel
Save