mirror of
https://github.com/g4klx/MMDVMHost
synced 2025-12-28 14:05:36 +08:00
Merge remote-tracking branch 'g4klx/master'
This commit is contained in:
@@ -29,6 +29,8 @@
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
const unsigned char UDTF_NMEA = 0x05U;
|
||||
|
||||
CDMRDataHeader::CDMRDataHeader() :
|
||||
m_data(NULL),
|
||||
m_GI(false),
|
||||
@@ -78,13 +80,13 @@ bool CDMRDataHeader::put(const unsigned char* bytes)
|
||||
|
||||
switch (dpf) {
|
||||
case DPF_UNCONFIRMED_DATA:
|
||||
CUtils::dump(1U, "Unconfirmed Data Header", m_data, 12U);
|
||||
CUtils::dump(1U, "DMR, Unconfirmed Data Header", m_data, 12U);
|
||||
m_F = (m_data[8U] & 0x80U) == 0x80U;
|
||||
m_blocks = m_data[8U] & 0x7FU;
|
||||
break;
|
||||
|
||||
case DPF_CONFIRMED_DATA:
|
||||
CUtils::dump(1U, "Confirmed Data Header", m_data, 12U);
|
||||
CUtils::dump(1U, "DMR, Confirmed Data Header", m_data, 12U);
|
||||
m_F = (m_data[8U] & 0x80U) == 0x80U;
|
||||
m_blocks = m_data[8U] & 0x7FU;
|
||||
m_S = (m_data[9U] & 0x80U) == 0x80U;
|
||||
@@ -92,38 +94,46 @@ bool CDMRDataHeader::put(const unsigned char* bytes)
|
||||
break;
|
||||
|
||||
case DPF_RESPONSE:
|
||||
CUtils::dump(1U, "Response Data Header", m_data, 12U);
|
||||
CUtils::dump(1U, "DMR, Response Data Header", m_data, 12U);
|
||||
m_blocks = m_data[8U] & 0x7FU;
|
||||
break;
|
||||
|
||||
case DPF_PROPRIETARY:
|
||||
CUtils::dump(1U, "Proprietary Data Header", m_data, 12U);
|
||||
CUtils::dump(1U, "DMR, Proprietary Data Header", m_data, 12U);
|
||||
break;
|
||||
|
||||
case DPF_DEFINED_RAW:
|
||||
CUtils::dump(1U, "Raw or Status/Precoded Short Data Header", m_data, 12U);
|
||||
CUtils::dump(1U, "DMR, Raw or Status/Precoded Short Data Header", m_data, 12U);
|
||||
m_blocks = (m_data[0U] & 0x30U) + (m_data[1U] & 0x0FU);
|
||||
m_F = (m_data[8U] & 0x01U) == 0x01U;
|
||||
m_S = (m_data[8U] & 0x02U) == 0x02U;
|
||||
break;
|
||||
|
||||
case DPF_DEFINED_SHORT:
|
||||
CUtils::dump(1U, "Defined Short Data Header", m_data, 12U);
|
||||
CUtils::dump(1U, "DMR, Defined Short Data Header", m_data, 12U);
|
||||
m_blocks = (m_data[0U] & 0x30U) + (m_data[1U] & 0x0FU);
|
||||
m_F = (m_data[8U] & 0x01U) == 0x01U;
|
||||
m_S = (m_data[8U] & 0x02U) == 0x02U;
|
||||
break;
|
||||
|
||||
case DPF_UDT:
|
||||
CUtils::dump(1U, "Unified Data Transport Header", m_data, 12U);
|
||||
CUtils::dump(1U, "DMR, Unified Data Transport Header", m_data, 12U);
|
||||
m_blocks = m_data[8U] & 0x03U;
|
||||
break;
|
||||
|
||||
default:
|
||||
CUtils::dump("Unknown Data Header", m_data, 12U);
|
||||
CUtils::dump("DMR, Unknown Data Header", m_data, 12U);
|
||||
break;
|
||||
}
|
||||
|
||||
if (dpf == DPF_UDT && m_blocks == 0U) {
|
||||
unsigned char format = m_data[1U] & 0x0FU;
|
||||
if (format == UDTF_NMEA) {
|
||||
LogDebug("DMR, fixing broken Tytera MD-390 GPS data block count");
|
||||
m_blocks = 3U;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -522,8 +522,16 @@ bool CDMRNetwork::writeConfig()
|
||||
char longitude[20U];
|
||||
::sprintf(longitude, "%09f", m_longitude);
|
||||
|
||||
unsigned int power = m_power;
|
||||
if (power > 99U)
|
||||
power = 99U;
|
||||
|
||||
int height = m_height;
|
||||
if (height > 999)
|
||||
height = 999;
|
||||
|
||||
::sprintf(buffer + 8U, "%-8.8s%09u%09u%02u%02u%8.8s%9.9s%03d%-20.20s%-19.19s%c%-124.124s%-40.40s%-40.40s", m_callsign.c_str(),
|
||||
m_rxFrequency, m_txFrequency, m_power, m_colorCode, latitude, longitude, m_height, m_location.c_str(),
|
||||
m_rxFrequency, m_txFrequency, power, m_colorCode, latitude, longitude, height, m_location.c_str(),
|
||||
m_description.c_str(), slots, m_url.c_str(), m_version, software);
|
||||
|
||||
return write((unsigned char*)buffer, 302U);
|
||||
|
||||
@@ -1144,6 +1144,12 @@ void CMMDVMHost::createDisplay()
|
||||
m_display = new CNullDisplay;
|
||||
}
|
||||
|
||||
if (m_display == NULL) {
|
||||
LogWarning("No valid display found, disabling");
|
||||
m_display = new CNullDisplay;
|
||||
return;
|
||||
}
|
||||
|
||||
bool ret = m_display->open();
|
||||
if (!ret) {
|
||||
delete m_display;
|
||||
|
||||
37
UMP/UMP.ino
37
UMP/UMP.ino
@@ -16,20 +16,26 @@
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#if !defined(__AVR_ATmega1280__) && !defined(__AVR_ATmega2560__) && !defined(__AVR_ATmega32U4__) && !defined(__SAM3X8E__) && !defined(__MK20DX256__)
|
||||
#include <AltSoftSerial.h>
|
||||
#endif
|
||||
|
||||
#if !defined(PIN_LED)
|
||||
#define PIN_LED 13
|
||||
#endif
|
||||
|
||||
#if defined(__MK20DX256__)
|
||||
#define PIN_DSTAR 2
|
||||
#define PIN_DMR 3
|
||||
#define PIN_YSF 4
|
||||
#define PIN_P25 5
|
||||
#define PIN_DSTAR 3
|
||||
#define PIN_DMR 4
|
||||
#define PIN_YSF 5
|
||||
#define PIN_P25 6
|
||||
|
||||
#define PIN_TX 10
|
||||
#define PIN_CD 11
|
||||
|
||||
#define PIN_LOCKOUT 12
|
||||
|
||||
#define FLASH_DELAY 200000U
|
||||
#else
|
||||
#define PIN_DSTAR 2
|
||||
#define PIN_DMR 3
|
||||
@@ -39,7 +45,13 @@
|
||||
#define PIN_TX 6
|
||||
#define PIN_CD 7
|
||||
|
||||
#define PIN_LOCKOUT 8
|
||||
#define PIN_LOCKOUT 12
|
||||
|
||||
#define FLASH_DELAY 3200U
|
||||
#endif
|
||||
|
||||
#if !defined(__AVR_ATmega1280__) && !defined(__AVR_ATmega2560__) && !defined(__AVR_ATmega32U4__) && !defined(__SAM3X8E__) && !defined(__MK20DX256__)
|
||||
AltSoftSerial mySerial;
|
||||
#endif
|
||||
|
||||
// Use the LOCKOUT function on the UMP
|
||||
@@ -51,6 +63,8 @@ void setup()
|
||||
|
||||
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__SAM3X8E__) || defined(__MK20DX256__)
|
||||
Serial1.begin(9600);
|
||||
#else
|
||||
mySerial.begin(9600);
|
||||
#endif
|
||||
|
||||
pinMode(PIN_LED, OUTPUT);
|
||||
@@ -132,11 +146,13 @@ void loop()
|
||||
case UMP_SET_CD:
|
||||
digitalWrite(PIN_CD, m_buffer[3U] == 0x01U ? HIGH : LOW);
|
||||
break;
|
||||
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__SAM3X8E__) || defined(__MK20DX256__)
|
||||
case UMP_WRITE_SERIAL:
|
||||
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__SAM3X8E__) || defined(__MK20DX256__)
|
||||
Serial1.write(m_buffer + 3U, m_length - 3U);
|
||||
break;
|
||||
#else
|
||||
mySerial.write(m_buffer + 3U, m_length - 3U);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -165,17 +181,20 @@ void loop()
|
||||
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega32U4__) || defined(__SAM3X8E__) || defined(__MK20DX256__)
|
||||
while (Serial1.available())
|
||||
Serial1.read();
|
||||
#else
|
||||
while (mySerial.available())
|
||||
mySerial.read();
|
||||
#endif
|
||||
|
||||
m_count++;
|
||||
if (m_started) {
|
||||
if (m_count > 3200U) {
|
||||
if (m_count > FLASH_DELAY) {
|
||||
digitalWrite(PIN_LED, m_led ? LOW : HIGH);
|
||||
m_led = !m_led;
|
||||
m_count = 0U;
|
||||
}
|
||||
} else {
|
||||
if (m_count > 32000U) {
|
||||
if (m_count > (FLASH_DELAY * 3U)) {
|
||||
digitalWrite(PIN_LED, m_led ? LOW : HIGH);
|
||||
m_led = !m_led;
|
||||
m_count = 0U;
|
||||
|
||||
@@ -19,6 +19,6 @@
|
||||
#if !defined(VERSION_H)
|
||||
#define VERSION_H
|
||||
|
||||
const char* VERSION = "20161021";
|
||||
const char* VERSION = "20161124";
|
||||
|
||||
#endif
|
||||
|
||||
@@ -254,7 +254,7 @@ bool CYSFControl::writeModem(unsigned char *data, unsigned int len)
|
||||
unsigned int errors = m_rfPayload.processVoiceFRModeAudio(data + 2U);
|
||||
m_rfErrs += errors;
|
||||
m_rfBits += 720U;
|
||||
LogDebug("YSF, V Mode 3, seq %u, IMBE FEC %u/720 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 7.2F);
|
||||
LogDebug("YSF, V Mode 3, seq %u, AMBE FEC %u/720 (%.1f%%)", m_rfFrames % 128, errors, float(errors) / 7.2F);
|
||||
}
|
||||
valid = false;
|
||||
break;
|
||||
@@ -525,7 +525,7 @@ void CYSFControl::writeNetwork()
|
||||
// if (send) {
|
||||
m_netErrs += errors;
|
||||
m_netBits += 720U;
|
||||
LogDebug("YSF, V Mode 3, seq %u, IMBE FEC %u/720 (%.1f%%)", n, errors, float(errors) / 7.2F);
|
||||
LogDebug("YSF, V Mode 3, seq %u, AMBE FEC %u/720 (%.1f%%)", n, errors, float(errors) / 7.2F);
|
||||
// }
|
||||
}
|
||||
break;
|
||||
|
||||
109
YSFPayload.cpp
109
YSFPayload.cpp
@@ -393,11 +393,51 @@ unsigned int CYSFPayload::processVDMode2Audio(unsigned char* data)
|
||||
data += YSF_SYNC_LENGTH_BYTES + YSF_FICH_LENGTH_BYTES;
|
||||
|
||||
unsigned int errors = 0U;
|
||||
errors += processVDMode2AudioBlock(data + 5U);
|
||||
errors += processVDMode2AudioBlock(data + 23U);
|
||||
errors += processVDMode2AudioBlock(data + 41U);
|
||||
errors += processVDMode2AudioBlock(data + 59U);
|
||||
errors += processVDMode2AudioBlock(data + 77U);
|
||||
unsigned int offset = 40U; // DCH(0)
|
||||
|
||||
// We have a total of 5 VCH sections, iterate through each
|
||||
for (unsigned int j = 0U; j < 5U; j++, offset += 144U) {
|
||||
unsigned char vch[13U];
|
||||
|
||||
// Deinterleave
|
||||
for (unsigned int i = 0U; i < 104U; i++) {
|
||||
unsigned int n = INTERLEAVE_TABLE_26_4[i];
|
||||
bool s = READ_BIT1(data, offset + n) != 0x00U;
|
||||
WRITE_BIT1(vch, i, s);
|
||||
}
|
||||
|
||||
// "Un-whiten" (descramble)
|
||||
for (unsigned int i = 0U; i < 13U; i++)
|
||||
vch[i] ^= WHITENING_DATA[i];
|
||||
|
||||
// errors += READ_BIT1(vch, 103); // Padding bit must be zero but apparently it is not...
|
||||
|
||||
for (unsigned int i = 0U; i < 81U; i += 3) {
|
||||
uint8_t vote = bool(READ_BIT1(vch, i)) + bool(READ_BIT1(vch, i + 1)) + bool(READ_BIT1(vch, i + 2));
|
||||
if (vote == 1 || vote == 2) {
|
||||
bool decision = vote / 2; // exploit integer division: 1/2 == 0, 2/2 == 1.
|
||||
WRITE_BIT1(vch, i, decision);
|
||||
WRITE_BIT1(vch, i + 1, decision);
|
||||
WRITE_BIT1(vch, i + 2, decision);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
// Reconstruct only if we have bit errors. Technically we could even
|
||||
// constrain it individually to the 5 VCH sections.
|
||||
if (errors > 0U) {
|
||||
// Scramble
|
||||
for (unsigned int i = 0U; i < 13U; i++)
|
||||
vch[i] ^= WHITENING_DATA[i];
|
||||
|
||||
// Interleave
|
||||
for (unsigned int i = 0U; i < 104U; i++) {
|
||||
unsigned int n = INTERLEAVE_TABLE_26_4[i];
|
||||
bool s = READ_BIT1(vch, i);
|
||||
WRITE_BIT1(data, offset + n, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "errors" is the number of triplets that were recognized to be corrupted
|
||||
// and that were corrected. There are 27 of those per VCH and 5 VCH per CC,
|
||||
@@ -407,65 +447,6 @@ unsigned int CYSFPayload::processVDMode2Audio(unsigned char* data)
|
||||
return errors;
|
||||
}
|
||||
|
||||
unsigned int CYSFPayload::processVDMode2AudioBlock(unsigned char* data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
|
||||
unsigned int errors = 0U;
|
||||
unsigned char vch[13U];
|
||||
|
||||
// Deinterleave
|
||||
for (unsigned int i = 0U; i < 104U; i++) {
|
||||
unsigned int n = INTERLEAVE_TABLE_26_4[i];
|
||||
bool s = READ_BIT1(data, n);
|
||||
WRITE_BIT1(vch, i, s);
|
||||
}
|
||||
|
||||
// "Un-whiten" (descramble)
|
||||
for (unsigned int i = 0U; i < 13U; i++)
|
||||
vch[i] ^= WHITENING_DATA[i];
|
||||
|
||||
for (unsigned int i = 0U; i < 81U; i += 3U) {
|
||||
unsigned int n = i;
|
||||
bool bit1 = READ_BIT1(vch, n);
|
||||
n++;
|
||||
bool bit2 = READ_BIT1(vch, n);
|
||||
n++;
|
||||
bool bit3 = READ_BIT1(vch, n);
|
||||
|
||||
if ((bit1 && bit2 && !bit3) || (bit1 && !bit2 && bit3) || (!bit1 && bit2 && bit3)) {
|
||||
unsigned int n = i;
|
||||
WRITE_BIT1(vch, n, true);
|
||||
n++;
|
||||
WRITE_BIT1(vch, n, true);
|
||||
n++;
|
||||
WRITE_BIT1(vch, n, true);
|
||||
errors++;
|
||||
} else if ((!bit1 && !bit2 && bit3) || (!bit1 && bit2 && !bit3) || (bit1 && !bit2 && !bit3)) {
|
||||
unsigned int n = i;
|
||||
WRITE_BIT1(vch, n, false);
|
||||
n++;
|
||||
WRITE_BIT1(vch, n, false);
|
||||
n++;
|
||||
WRITE_BIT1(vch, n, false);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
|
||||
// Scramble
|
||||
for (unsigned int i = 0U; i < 13U; i++)
|
||||
vch[i] ^= WHITENING_DATA[i];
|
||||
|
||||
// Interleave
|
||||
for (unsigned int i = 0U; i < 104U; i++) {
|
||||
unsigned int n = INTERLEAVE_TABLE_26_4[i];
|
||||
bool s = READ_BIT1(vch, i);
|
||||
WRITE_BIT1(data, n, s);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
bool CYSFPayload::processVDMode2Data(unsigned char* data, unsigned char fn, bool gateway)
|
||||
{
|
||||
assert(data != NULL);
|
||||
|
||||
@@ -54,8 +54,6 @@ private:
|
||||
unsigned char* m_source;
|
||||
unsigned char* m_dest;
|
||||
CAMBEFEC m_fec;
|
||||
|
||||
unsigned int processVDMode2AudioBlock(unsigned char* data);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user