mirror of
https://github.com/g4klx/MMDVMHost.git
synced 2026-04-21 06:13:49 +00:00
Merge 4007f86df2 into b5b119fa8e
This commit is contained in:
commit
2fa7fb0e0c
16 changed files with 696 additions and 55 deletions
40
Conf.cpp
40
Conf.cpp
|
|
@ -181,6 +181,11 @@ m_dmrEnabled(false),
|
|||
m_dmrBeacons(DMR_BEACONS::OFF),
|
||||
m_dmrBeaconInterval(60U),
|
||||
m_dmrBeaconDuration(3U),
|
||||
m_dmrTrunking(false),
|
||||
m_dmrControlChannel(false),
|
||||
m_dmrControlChannelAlternateSlot(false),
|
||||
m_dmrSystemCode(0),
|
||||
m_dmrRegistrationRequired(true),
|
||||
#endif
|
||||
m_dmrId(0U),
|
||||
#if defined(USE_DMR)
|
||||
|
|
@ -729,6 +734,16 @@ bool CConf::read()
|
|||
} else if (section == SECTION::DMR) {
|
||||
if (::strcmp(key, "Enable") == 0)
|
||||
m_dmrEnabled = ::atoi(value) == 1;
|
||||
if (::strcmp(key, "Trunking") == 0)
|
||||
m_dmrTrunking = ::atoi(value) == 1;
|
||||
if (::strcmp(key, "ControlChannel") == 0)
|
||||
m_dmrControlChannel = ::atoi(value) == 1;
|
||||
if (::strcmp(key, "ControlChannelAlternateSlot") == 0)
|
||||
m_dmrControlChannelAlternateSlot = ::atoi(value) == 1;
|
||||
if (::strcmp(key, "SystemCode") == 0)
|
||||
m_dmrSystemCode = (unsigned int)::atoi(value);
|
||||
if (::strcmp(key, "RegistrationRequired") == 0)
|
||||
m_dmrRegistrationRequired = ::atoi(value) == 1;
|
||||
else if (::strcmp(key, "Beacons") == 0)
|
||||
m_dmrBeacons = ::atoi(value) == 1 ? DMR_BEACONS::NETWORK : DMR_BEACONS::OFF;
|
||||
else if (::strcmp(key, "BeaconInterval") == 0) {
|
||||
|
|
@ -1528,6 +1543,31 @@ bool CConf::getDMREnabled() const
|
|||
return m_dmrEnabled;
|
||||
}
|
||||
|
||||
bool CConf::getDMRTrunking() const
|
||||
{
|
||||
return m_dmrTrunking;
|
||||
}
|
||||
|
||||
bool CConf::getDMRControlChannel() const
|
||||
{
|
||||
return m_dmrControlChannel;
|
||||
}
|
||||
|
||||
bool CConf::getDMRControlChannelAlternateSlot() const
|
||||
{
|
||||
return m_dmrControlChannelAlternateSlot;
|
||||
}
|
||||
|
||||
unsigned int CConf::getDMRSystemCode() const
|
||||
{
|
||||
return m_dmrSystemCode;
|
||||
}
|
||||
|
||||
bool CConf::getDMRRegistrationRequired() const
|
||||
{
|
||||
return m_dmrRegistrationRequired;
|
||||
}
|
||||
|
||||
DMR_BEACONS CConf::getDMRBeacons() const
|
||||
{
|
||||
return m_dmrBeacons;
|
||||
|
|
|
|||
10
Conf.h
10
Conf.h
|
|
@ -173,6 +173,11 @@ public:
|
|||
unsigned int getDMRModeHang() const;
|
||||
DMR_OVCM getDMROVCM() const;
|
||||
bool getDMRProtect() const;
|
||||
bool getDMRTrunking() const;
|
||||
bool getDMRControlChannel() const;
|
||||
bool getDMRControlChannelAlternateSlot() const;
|
||||
unsigned int getDMRSystemCode() const;
|
||||
bool getDMRRegistrationRequired() const;
|
||||
#endif
|
||||
|
||||
#if defined(USE_YSF)
|
||||
|
|
@ -449,6 +454,11 @@ private:
|
|||
DMR_BEACONS m_dmrBeacons;
|
||||
unsigned int m_dmrBeaconInterval;
|
||||
unsigned int m_dmrBeaconDuration;
|
||||
bool m_dmrTrunking;
|
||||
bool m_dmrControlChannel;
|
||||
bool m_dmrControlChannelAlternateSlot;
|
||||
unsigned int m_dmrSystemCode;
|
||||
bool m_dmrRegistrationRequired;
|
||||
#endif
|
||||
unsigned int m_dmrId;
|
||||
#if defined(USE_DMR)
|
||||
|
|
|
|||
89
DMRCSBK.cpp
89
DMRCSBK.cpp
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright (C) 2015,2016,2020,2021,2022,2023,2025 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2019 by Patrick Maier DK5MP
|
||||
* Copyright (C) 2026 by Adrian Musceac YO8RZZ
|
||||
*
|
||||
* 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
|
||||
|
|
@ -37,7 +38,8 @@ m_srcId(0U),
|
|||
m_dstId(0U),
|
||||
m_dataContent(false),
|
||||
m_CBF(0U),
|
||||
m_OVCM(false)
|
||||
m_OVCM(false),
|
||||
m_dataType(DT_CSBK)
|
||||
{
|
||||
m_data = new unsigned char[12U];
|
||||
}
|
||||
|
|
@ -47,23 +49,46 @@ CDMRCSBK::~CDMRCSBK()
|
|||
delete[] m_data;
|
||||
}
|
||||
|
||||
void CDMRCSBK::setCSBKData(unsigned char* bytes)
|
||||
{
|
||||
for(unsigned int i=0; i<10; i++)
|
||||
{
|
||||
m_data[i] = bytes[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool CDMRCSBK::put(const unsigned char* bytes)
|
||||
{
|
||||
assert(bytes != nullptr);
|
||||
|
||||
CBPTC19696 bptc;
|
||||
bptc.decode(bytes, m_data);
|
||||
|
||||
m_data[10U] ^= CSBK_CRC_MASK[0U];
|
||||
m_data[11U] ^= CSBK_CRC_MASK[1U];
|
||||
if(m_dataType == DT_CSBK)
|
||||
{
|
||||
m_data[10U] ^= CSBK_CRC_MASK[0U];
|
||||
m_data[11U] ^= CSBK_CRC_MASK[1U];
|
||||
}
|
||||
else if(m_dataType == DT_MBC_HEADER)
|
||||
{
|
||||
m_data[10U] ^= MBC_CRC_MASK[0U];
|
||||
m_data[11U] ^= MBC_CRC_MASK[1U];
|
||||
}
|
||||
|
||||
bool valid = CCRC::checkCCITT162(m_data, 12U);
|
||||
if (!valid)
|
||||
return false;
|
||||
|
||||
// Restore the checksum
|
||||
m_data[10U] ^= CSBK_CRC_MASK[0U];
|
||||
m_data[11U] ^= CSBK_CRC_MASK[1U];
|
||||
if(m_dataType == DT_CSBK)
|
||||
{
|
||||
m_data[10U] ^= CSBK_CRC_MASK[0U];
|
||||
m_data[11U] ^= CSBK_CRC_MASK[1U];
|
||||
}
|
||||
else if(m_dataType == DT_MBC_HEADER)
|
||||
{
|
||||
m_data[10U] ^= MBC_CRC_MASK[0U];
|
||||
m_data[11U] ^= MBC_CRC_MASK[1U];
|
||||
}
|
||||
|
||||
m_CSBKO = CSBKO(m_data[0U] & 0x3FU);
|
||||
m_FID = m_data[1U];
|
||||
|
|
@ -148,6 +173,22 @@ bool CDMRCSBK::put(const unsigned char* bytes)
|
|||
m_dataContent = false;
|
||||
m_CBF = 0U;
|
||||
break;
|
||||
case CSBKO::ACKU:
|
||||
m_GI = false;
|
||||
m_dstId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U];
|
||||
m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
|
||||
m_dataContent = false;
|
||||
m_CBF = m_data[3U];
|
||||
CUtils::dump(1U, "ACKU CSBK", m_data, 12U);
|
||||
break;
|
||||
case CSBKO::MAINT:
|
||||
m_GI = false;
|
||||
m_dstId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U];
|
||||
m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
|
||||
m_dataContent = false;
|
||||
m_CBF = m_data[3U];
|
||||
CUtils::dump(1U, "MAINT CSBK", m_data, 12U);
|
||||
break;
|
||||
|
||||
case CSBKO::CALL_EMERGENCY:
|
||||
m_GI = true;
|
||||
|
|
@ -174,14 +215,30 @@ bool CDMRCSBK::put(const unsigned char* bytes)
|
|||
void CDMRCSBK::get(unsigned char* bytes) const
|
||||
{
|
||||
assert(bytes != nullptr);
|
||||
if(m_dataType == DT_CSBK)
|
||||
{
|
||||
m_data[10U] ^= CSBK_CRC_MASK[0U];
|
||||
m_data[11U] ^= CSBK_CRC_MASK[1U];
|
||||
|
||||
m_data[10U] ^= CSBK_CRC_MASK[0U];
|
||||
m_data[11U] ^= CSBK_CRC_MASK[1U];
|
||||
CCRC::addCCITT162(m_data, 12U);
|
||||
|
||||
CCRC::addCCITT162(m_data, 12U);
|
||||
m_data[10U] ^= CSBK_CRC_MASK[0U];
|
||||
m_data[11U] ^= CSBK_CRC_MASK[1U];
|
||||
}
|
||||
else if(m_dataType == DT_MBC_HEADER)
|
||||
{
|
||||
m_data[10U] ^= MBC_CRC_MASK[0U];
|
||||
m_data[11U] ^= MBC_CRC_MASK[1U];
|
||||
|
||||
m_data[10U] ^= CSBK_CRC_MASK[0U];
|
||||
m_data[11U] ^= CSBK_CRC_MASK[1U];
|
||||
CCRC::addCCITT162(m_data, 12U);
|
||||
|
||||
m_data[10U] ^= MBC_CRC_MASK[0U];
|
||||
m_data[11U] ^= MBC_CRC_MASK[1U];
|
||||
}
|
||||
else if(m_dataType == DT_MBC_CONTINUATION)
|
||||
{
|
||||
CCRC::addCCITT162(m_data, 12U);
|
||||
}
|
||||
|
||||
CBPTC19696 bptc;
|
||||
bptc.encode(m_data, bytes);
|
||||
|
|
@ -249,5 +306,15 @@ void CDMRCSBK::setCBF(unsigned char cbf)
|
|||
m_CBF = m_data[3U] = cbf;
|
||||
}
|
||||
|
||||
void CDMRCSBK::setDataType(unsigned char dataType)
|
||||
{
|
||||
m_dataType = dataType;
|
||||
}
|
||||
|
||||
unsigned char CDMRCSBK::getDataType()
|
||||
{
|
||||
return m_dataType;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
13
DMRCSBK.h
13
DMRCSBK.h
|
|
@ -35,7 +35,14 @@ enum class CSBKO : unsigned char {
|
|||
NACKRSP = 0x26,
|
||||
CALL_EMERGENCY = 0x27,
|
||||
BSDWNACT = 0x38,
|
||||
PRECCSBK = 0x3D
|
||||
PRECCSBK = 0x3D,
|
||||
MAINT = 0x2A,
|
||||
TV_GRANT = 0x31,
|
||||
PV_GRANT = 0x30,
|
||||
AHOY = 0x1C,
|
||||
ACKU = 0x21,
|
||||
P_CLEAR = 0x2E,
|
||||
C_BCAST = 0x28
|
||||
};
|
||||
|
||||
class CDMRCSBK
|
||||
|
|
@ -47,6 +54,7 @@ public:
|
|||
bool put(const unsigned char* bytes);
|
||||
|
||||
void get(unsigned char* bytes) const;
|
||||
void setCSBKData(unsigned char* bytes);
|
||||
|
||||
// Generic fields
|
||||
CSBKO getCSBKO() const;
|
||||
|
|
@ -69,6 +77,8 @@ public:
|
|||
unsigned char getCBF() const;
|
||||
|
||||
void setCBF(unsigned char cbf);
|
||||
void setDataType(unsigned char dataType);
|
||||
unsigned char getDataType();
|
||||
|
||||
private:
|
||||
unsigned char* m_data;
|
||||
|
|
@ -81,6 +91,7 @@ private:
|
|||
bool m_dataContent;
|
||||
unsigned char m_CBF;
|
||||
bool m_OVCM;
|
||||
unsigned char m_dataType;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2015-2021,2023,2025 Jonathan Naylor, G4KLX
|
||||
* Copyright (C) 2026 Adrian Musceac, YO8RZZ
|
||||
*
|
||||
* 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
|
||||
|
|
@ -108,13 +109,39 @@ void CDMRControl::clock()
|
|||
{
|
||||
if (m_network != nullptr) {
|
||||
CDMRData data;
|
||||
bool ret = m_network->read(data);
|
||||
TrunkingCommandParameters command;
|
||||
bool ret = m_network->read(data, command);
|
||||
if (ret) {
|
||||
unsigned int slotNo = data.getSlotNo();
|
||||
switch (slotNo) {
|
||||
case 1U: m_slot1.writeNetwork(data); break;
|
||||
case 2U: m_slot2.writeNetwork(data); break;
|
||||
default: LogError("Invalid slot no %u", slotNo); break;
|
||||
if(m_modem->getDMRTrunking() && command.trunkingParams) {
|
||||
switch(command.commandType) {
|
||||
case DMRCommand::ChannelEnableDisable:
|
||||
{
|
||||
if(command.slot == 1) {
|
||||
if(!m_modem->getControlChannel())
|
||||
m_slot1.enable(command.channelEnable);
|
||||
}
|
||||
else {
|
||||
m_slot2.enable(command.channelEnable);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
if(command.slot == 1)
|
||||
m_slot1.setReverseChannelCommand(command.commandType);
|
||||
else
|
||||
m_slot2.setReverseChannelCommand(command.commandType);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
unsigned int slotNo = data.getSlotNo();
|
||||
switch (slotNo) {
|
||||
case 1U: m_slot1.writeNetwork(data); break;
|
||||
case 2U: m_slot2.writeNetwork(data); break;
|
||||
default: LogError("Invalid slot no %u", slotNo); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
DMRData.h
19
DMRData.h
|
|
@ -19,6 +19,25 @@
|
|||
|
||||
#if defined(USE_DMR)
|
||||
|
||||
struct TrunkingCommandParameters {
|
||||
unsigned int commandType = 0;
|
||||
bool trunkingParams = false;
|
||||
bool channelEnable = false;
|
||||
unsigned int slot = 1;
|
||||
bool ceaseTransmission = false;
|
||||
};
|
||||
|
||||
enum DMRCommand {
|
||||
ChannelEnableDisable = 1,
|
||||
RCCeaseTransmission = 2,
|
||||
RCRequestCeaseTransmission = 3,
|
||||
RCPowerIncreaseOneStep = 4,
|
||||
RCPowerDecreaseOneStep = 5,
|
||||
RCMaximumPower = 6,
|
||||
RCMinimumPower = 7,
|
||||
RCNoCommand = 0,
|
||||
};
|
||||
|
||||
class CDMRData {
|
||||
public:
|
||||
CDMRData(const CDMRData& data);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright (C) 2012 by Ian Wraith
|
||||
* Copyright (C) 2015,2016,2017,2023,2025 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2026 by Adrian Musceac YO8RZZ
|
||||
*
|
||||
* 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
|
||||
|
|
@ -42,7 +43,8 @@ m_dstId(0U),
|
|||
m_blocks(0U),
|
||||
m_F(false),
|
||||
m_S(false),
|
||||
m_Ns(0U)
|
||||
m_Ns(0U),
|
||||
m_UDT(false)
|
||||
{
|
||||
m_data = new unsigned char[12U];
|
||||
}
|
||||
|
|
@ -121,6 +123,7 @@ bool CDMRDataHeader::put(const unsigned char* bytes)
|
|||
case DPF_UDT:
|
||||
CUtils::dump(1U, "DMR, Unified Data Transport Header", m_data, 12U);
|
||||
m_blocks = (m_data[8U] & 0x03U) + 1U;
|
||||
m_UDT = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -134,6 +137,17 @@ bool CDMRDataHeader::put(const unsigned char* bytes)
|
|||
void CDMRDataHeader::get(unsigned char* bytes) const
|
||||
{
|
||||
assert(bytes != nullptr);
|
||||
assert(bytes != NULL);
|
||||
if(m_UDT)
|
||||
{
|
||||
// Table B.1: CSBK/MBC/UDT Opcode List
|
||||
// Convert to Unified Data Transport outbound Header
|
||||
m_data[9U] &= 0xFE;
|
||||
}
|
||||
CCRC::addCCITT162(m_data, 12U);
|
||||
// Restore the checksum
|
||||
m_data[10U] ^= DATA_HEADER_CRC_MASK[0U];
|
||||
m_data[11U] ^= DATA_HEADER_CRC_MASK[1U];
|
||||
|
||||
CBPTC19696 bptc;
|
||||
bptc.encode(m_data, bytes);
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ private:
|
|||
bool m_F;
|
||||
bool m_S;
|
||||
unsigned char m_Ns;
|
||||
bool m_UDT;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ const unsigned char TERMINATOR_WITH_LC_CRC_MASK[] = {0x99U, 0x99U, 0x99U};
|
|||
const unsigned char PI_HEADER_CRC_MASK[] = {0x69U, 0x69U};
|
||||
const unsigned char DATA_HEADER_CRC_MASK[] = {0xCCU, 0xCCU};
|
||||
const unsigned char CSBK_CRC_MASK[] = {0xA5U, 0xA5U};
|
||||
const unsigned char MBC_CRC_MASK[] = {0xAAU, 0xAAU};
|
||||
|
||||
const unsigned int DMR_SLOT_TIME = 60U;
|
||||
const unsigned int AMBE_PER_SLOT = 3U;
|
||||
|
|
@ -82,6 +83,8 @@ const unsigned char DT_VOICE_PI_HEADER = 0x00U;
|
|||
const unsigned char DT_VOICE_LC_HEADER = 0x01U;
|
||||
const unsigned char DT_TERMINATOR_WITH_LC = 0x02U;
|
||||
const unsigned char DT_CSBK = 0x03U;
|
||||
const unsigned char DT_MBC_HEADER = 0x04U;
|
||||
const unsigned char DT_MBC_CONTINUATION = 0x05U;
|
||||
const unsigned char DT_DATA_HEADER = 0x06U;
|
||||
const unsigned char DT_RATE_12_DATA = 0x07U;
|
||||
const unsigned char DT_RATE_34_DATA = 0x08U;
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ void CDMRNetwork::enable(bool enabled)
|
|||
m_enabled = enabled;
|
||||
}
|
||||
|
||||
bool CDMRNetwork::read(CDMRData& data)
|
||||
bool CDMRNetwork::read(CDMRData& data, TrunkingCommandParameters &command)
|
||||
{
|
||||
if (m_rxData.isEmpty())
|
||||
return false;
|
||||
|
|
@ -131,6 +131,28 @@ bool CDMRNetwork::read(CDMRData& data)
|
|||
unsigned char length = 0U;
|
||||
m_rxData.getData(&length, 1U);
|
||||
m_rxData.getData(m_buffer, length);
|
||||
if (::memcmp(m_buffer, "DMRT", 4U) == 0)
|
||||
{
|
||||
unsigned int command_type = (unsigned int)m_buffer[4U];
|
||||
switch(command_type)
|
||||
{
|
||||
case DMRCommand::ChannelEnableDisable:
|
||||
{
|
||||
command.channelEnable = (m_buffer[5U] & 0x01U) == 0x01U ? true : false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
command.slot = (m_buffer[5U] & 0x80U) == 0x80U ? 2U : 1U;
|
||||
command.commandType = command_type;
|
||||
command.trunkingParams = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
command.trunkingParams = false;
|
||||
}
|
||||
|
||||
// Is this a data packet?
|
||||
if (::memcmp(m_buffer, "DMRD", 4U) != 0)
|
||||
|
|
@ -318,7 +340,7 @@ void CDMRNetwork::clock(unsigned int ms)
|
|||
if (m_debug)
|
||||
CUtils::dump(1U, "DMR Network Received", m_buffer, length);
|
||||
|
||||
if (::memcmp(m_buffer, "DMRD", 4U) == 0) {
|
||||
if ((::memcmp(m_buffer, "DMRD", 4U) == 0) || (::memcmp(m_buffer, "DMRT", 4U) == 0)) {
|
||||
if (m_enabled) {
|
||||
unsigned char len = length;
|
||||
m_rxData.addData(&len, 1U);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public:
|
|||
|
||||
void enable(bool enabled);
|
||||
|
||||
bool read(CDMRData& data);
|
||||
bool read(CDMRData& data, TrunkingCommandParameters &command);
|
||||
|
||||
bool write(const CDMRData& data);
|
||||
|
||||
|
|
|
|||
341
DMRSlot.cpp
341
DMRSlot.cpp
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2015-2021,2023,2025 Jonathan Naylor, G4KLX
|
||||
* Copyright (C) 2026 Adrian Musceac, YO8RZZ
|
||||
*
|
||||
* 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
|
||||
|
|
@ -20,7 +21,6 @@
|
|||
#include "BPTC19696.h"
|
||||
#include "DMRSlot.h"
|
||||
#include "DMRCSBK.h"
|
||||
#include "DMREMB.h"
|
||||
#include "Utils.h"
|
||||
#include "Sync.h"
|
||||
#include "CRC.h"
|
||||
|
|
@ -71,6 +71,14 @@ const unsigned int NO_HEADERS_SIMPLEX = 8U;
|
|||
const unsigned int NO_HEADERS_DUPLEX = 3U;
|
||||
const unsigned int NO_PREAMBLE_CSBK = 15U;
|
||||
|
||||
|
||||
const unsigned int RC_CEASE_TRANSMIT[5] = {0x07, 0x0D, 0x04, 0xF1, 0xF0};
|
||||
const unsigned int RC_REQUEST_CEASE_TRANSMIT[5] = {0x02, 0x7B, 0x4E, 0x48, 0x70 };
|
||||
const unsigned int RC_MAX_POWER[5] = {0x01, 0xC3, 0xDD, 0x3C, 0x10 };
|
||||
const unsigned int RC_MIN_POWER[5] = {0x04, 0xB5, 0x97, 0x85, 0x90 };
|
||||
const unsigned int RC_POWER_INCREASE[5] = {0x04, 0x5B, 0xA7, 0x58, 0xA0 };
|
||||
const unsigned int RC_POWER_DECREASE[5] = {0x01, 0x2D, 0xED, 0xE1, 0x20 };
|
||||
|
||||
const unsigned int RSSI_COUNT = 3U; // 3 * 360ms = 1080ms
|
||||
const unsigned int BER_COUNT = 18U * 141U; // 18 * 60ms = 1080ms
|
||||
|
||||
|
|
@ -124,7 +132,8 @@ m_rssiAccum(0),
|
|||
m_rssiCount(0U),
|
||||
m_bitErrsAccum(0U),
|
||||
m_bitsCount(0U),
|
||||
m_enabled(true)
|
||||
m_enabled(true),
|
||||
m_reverseChannelCommand(0)
|
||||
{
|
||||
m_lastFrame = new unsigned char[DMR_FRAME_LENGTH_BYTES + 2U];
|
||||
|
||||
|
|
@ -227,6 +236,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len)
|
|||
if (dataType == DT_VOICE_LC_HEADER) {
|
||||
if (m_rfState == RPT_RF_STATE::AUDIO)
|
||||
return true;
|
||||
m_reverseChannelCommand = DMRCommand::RCNoCommand;
|
||||
|
||||
CDMRFullLC fullLC;
|
||||
CDMRLC* lc = fullLC.decode(data + 2U, DT_VOICE_LC_HEADER);
|
||||
|
|
@ -363,7 +373,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len)
|
|||
} else if (dataType == DT_TERMINATOR_WITH_LC) {
|
||||
if (m_rfState != RPT_RF_STATE::AUDIO)
|
||||
return false;
|
||||
|
||||
m_reverseChannelCommand = DMRCommand::RCNoCommand;
|
||||
// Regenerate the LC data
|
||||
CDMRFullLC fullLC;
|
||||
fullLC.encode(*m_rfLC, data + 2U, DT_TERMINATOR_WITH_LC);
|
||||
|
|
@ -381,7 +391,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len)
|
|||
writeNetworkRF(data, DT_TERMINATOR_WITH_LC);
|
||||
|
||||
if (m_duplex) {
|
||||
for (unsigned int i = 0U; i < m_hangCount; i++)
|
||||
for (unsigned int i = 0U; i <= m_hangCount; i++)
|
||||
writeQueueRF(data);
|
||||
}
|
||||
}
|
||||
|
|
@ -485,10 +495,13 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len)
|
|||
return false;
|
||||
|
||||
// set the OVCM bit for the supported csbk
|
||||
if ((m_ovcm == DMR_OVCM::TX_ON) || (m_ovcm == DMR_OVCM::ON))
|
||||
csbk.setOVCM(true);
|
||||
else if (m_ovcm == DMR_OVCM::FORCE_OFF)
|
||||
csbk.setOVCM(false);
|
||||
if(!m_modem->getDMRTrunking())
|
||||
{
|
||||
if ((m_ovcm == DMR_OVCM::TX_ON) || (m_ovcm == DMR_OVCM::ON))
|
||||
csbk.setOVCM(true);
|
||||
else if (m_ovcm == DMR_OVCM::FORCE_OFF)
|
||||
csbk.setOVCM(false);
|
||||
}
|
||||
|
||||
bool gi = csbk.getGI();
|
||||
unsigned int srcId = csbk.getSrcId();
|
||||
|
|
@ -524,7 +537,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len)
|
|||
data[0U] = TAG_DATA;
|
||||
data[1U] = 0x00U;
|
||||
|
||||
if (m_duplex)
|
||||
if (m_duplex && !m_modem->getDMRTrunking())
|
||||
writeQueueRF(data);
|
||||
|
||||
writeNetworkRF(data, DT_CSBK, gi ? FLCO::GROUP : FLCO::USER_USER, srcId, dstId);
|
||||
|
|
@ -563,7 +576,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len)
|
|||
writeJSONRF("csbk", "Call Emergency", srcId, src, gi, dstId);
|
||||
break;
|
||||
default:
|
||||
LogWarning("DMR Slot %u, unhandled RF CSBK type - 0x%02X", m_slotNo, csbko);
|
||||
LogMessage("DMR Slot %u, received unhandled CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG " : "", dst.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -848,7 +861,7 @@ bool CDMRSlot::writeModem(unsigned char *data, unsigned int len)
|
|||
emb.setLCSS(lcss);
|
||||
emb.getData(data + 2U);
|
||||
}
|
||||
|
||||
createReverseChannel(data, emb);
|
||||
if (m_duplex)
|
||||
writeQueueRF(data);
|
||||
|
||||
|
|
@ -1051,7 +1064,7 @@ void CDMRSlot::writeEndRF(bool writeEnd)
|
|||
data[0U] = TAG_EOT;
|
||||
data[1U] = 0x00U;
|
||||
|
||||
for (unsigned int i = 0U; i < m_hangCount; i++)
|
||||
for (unsigned int i = 0U; i <= m_hangCount; i++)
|
||||
writeQueueRF(data);
|
||||
}
|
||||
}
|
||||
|
|
@ -1099,7 +1112,7 @@ void CDMRSlot::writeEndNet(bool writeEnd)
|
|||
data[1U] = 0x00U;
|
||||
|
||||
if (m_duplex) {
|
||||
for (unsigned int i = 0U; i < m_hangCount; i++)
|
||||
for (unsigned int i = 0U; i <= m_hangCount; i++)
|
||||
writeQueueNet(data);
|
||||
} else {
|
||||
for (unsigned int i = 0U; i < 3U; i++)
|
||||
|
|
@ -1129,7 +1142,7 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData)
|
|||
if (!m_enabled)
|
||||
return;
|
||||
|
||||
if ((m_rfState != RPT_RF_STATE::LISTENING) && (m_netState == RPT_NET_STATE::IDLE))
|
||||
if ((m_rfState != RPT_RF_STATE::LISTENING) && (m_netState == RPT_NET_STATE::IDLE) && !m_modem->getDMRTrunking())
|
||||
return;
|
||||
|
||||
m_networkWatchdog.start();
|
||||
|
|
@ -1335,7 +1348,7 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData)
|
|||
data[1U] = 0x00U;
|
||||
|
||||
if (m_duplex) {
|
||||
for (unsigned int i = 0U; i < m_hangCount; i++)
|
||||
for (unsigned int i = 0U; i <= m_hangCount; i++)
|
||||
writeQueueNet(data);
|
||||
} else {
|
||||
for (unsigned int i = 0U; i < 3U; i++)
|
||||
|
|
@ -1686,10 +1699,13 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData)
|
|||
return;
|
||||
|
||||
// set the OVCM bit for the supported csbk
|
||||
if ((m_ovcm == DMR_OVCM::RX_ON) || (m_ovcm == DMR_OVCM::ON))
|
||||
csbk.setOVCM(true);
|
||||
else if (m_ovcm == DMR_OVCM::FORCE_OFF)
|
||||
csbk.setOVCM(false);
|
||||
if(!m_modem->getDMRTrunking())
|
||||
{
|
||||
if ((m_ovcm == DMR_OVCM::RX_ON) || (m_ovcm == DMR_OVCM::ON))
|
||||
csbk.setOVCM(true);
|
||||
else if (m_ovcm == DMR_OVCM::FORCE_OFF)
|
||||
csbk.setOVCM(false);
|
||||
}
|
||||
|
||||
bool gi = csbk.getGI();
|
||||
unsigned int srcId = csbk.getSrcId();
|
||||
|
|
@ -1770,6 +1786,216 @@ void CDMRSlot::writeNetwork(const CDMRData& dmrData)
|
|||
break;
|
||||
}
|
||||
|
||||
// If data preamble, signal its existence
|
||||
if ((csbko == CSBKO::PRECCSBK) && csbk.getDataContent())
|
||||
setShortLC(m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::USER_USER, ACTIVITY_TYPE::DATA);
|
||||
} else if (dataType == DT_MBC_HEADER) {
|
||||
CDMRCSBK csbk;
|
||||
csbk.setDataType(DT_MBC_HEADER);
|
||||
bool valid = csbk.put(data + 2U);
|
||||
if (!valid) {
|
||||
LogMessage("DMR Slot %u, unable to decode the network CSBK", m_slotNo);
|
||||
return;
|
||||
}
|
||||
|
||||
CSBKO csbko = csbk.getCSBKO();
|
||||
if (csbko == CSBKO::BSDWNACT)
|
||||
return;
|
||||
|
||||
// set the OVCM bit for the supported csbk
|
||||
if(!m_modem->getDMRTrunking())
|
||||
{
|
||||
if ((m_ovcm == DMR_OVCM::RX_ON) || (m_ovcm == DMR_OVCM::ON))
|
||||
csbk.setOVCM(true);
|
||||
else if (m_ovcm == DMR_OVCM::FORCE_OFF)
|
||||
csbk.setOVCM(false);
|
||||
}
|
||||
|
||||
bool gi = csbk.getGI();
|
||||
unsigned int srcId = csbk.getSrcId();
|
||||
unsigned int dstId = csbk.getDstId();
|
||||
|
||||
// Regenerate the CSBK data
|
||||
csbk.get(data + 2U);
|
||||
|
||||
// Regenerate the Slot Type
|
||||
CDMRSlotType slotType;
|
||||
slotType.setDataType(DT_MBC_HEADER);
|
||||
slotType.putData(data + 2U);
|
||||
slotType.setColorCode(m_colorCode);
|
||||
slotType.getData(data + 2U);
|
||||
|
||||
// Convert the Data Sync to be from the BS or MS as needed
|
||||
CSync::addDMRDataSync(data + 2U, m_duplex);
|
||||
|
||||
data[0U] = TAG_DATA;
|
||||
data[1U] = 0x00U;
|
||||
|
||||
if ((csbko == CSBKO::PRECCSBK) && csbk.getDataContent()) {
|
||||
unsigned int cbf = NO_PREAMBLE_CSBK + csbk.getCBF() - 1U;
|
||||
for (unsigned int i = 0U; i < NO_PREAMBLE_CSBK; i++, cbf--) {
|
||||
// Change blocks to follow
|
||||
csbk.setCBF(cbf);
|
||||
|
||||
// Regenerate the CSBK data
|
||||
csbk.get(data + 2U);
|
||||
|
||||
// Regenerate the Slot Type
|
||||
CDMRSlotType slotType;
|
||||
slotType.putData(data + 2U);
|
||||
slotType.setColorCode(m_colorCode);
|
||||
slotType.getData(data + 2U);
|
||||
|
||||
// Convert the Data Sync to be from the BS or MS as needed
|
||||
CSync::addDMRDataSync(data + 2U, m_duplex);
|
||||
|
||||
writeQueueNet(data);
|
||||
}
|
||||
} else
|
||||
writeQueueNet(data);
|
||||
|
||||
std::string src = m_lookup->find(srcId);
|
||||
std::string dst = m_lookup->find(dstId);
|
||||
|
||||
switch (csbko) {
|
||||
case CSBKO::UUVREQ:
|
||||
LogMessage("DMR Slot %u, received network Unit to Unit Voice Service Request CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG ": "", dst.c_str());
|
||||
writeJSONNet("csbk", "Unit to Unit Voice Service Request", srcId, src, gi, dstId);
|
||||
break;
|
||||
case CSBKO::UUANSRSP:
|
||||
LogMessage("DMR Slot %u, received network Unit to Unit Voice Service Answer Response CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG ": "", dst.c_str());
|
||||
writeJSONNet("csbk", "Unit to Unit Voice Service Answer Response", srcId, src, gi, dstId);
|
||||
break;
|
||||
case CSBKO::NACKRSP:
|
||||
LogMessage("DMR Slot %u, received network Negative Acknowledgment Response CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG ": "", dst.c_str());
|
||||
writeJSONNet("csbk", "UNegative Acknowledgment Response", srcId, src, gi, dstId);
|
||||
break;
|
||||
case CSBKO::PRECCSBK:
|
||||
LogMessage("DMR Slot %u, received network %s Preamble CSBK (%u to follow) from %s to %s%s", m_slotNo, csbk.getDataContent() ? "Data" : "CSBK", csbk.getCBF(), src.c_str(), gi ? "TG " : "", dst.c_str());
|
||||
writeJSONNet("csbk", "Preamble", srcId, src, gi, dstId);
|
||||
break;
|
||||
case CSBKO::CALL_ALERT:
|
||||
LogMessage("DMR Slot %u, received network Call Alert CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG " : "", dst.c_str());
|
||||
writeJSONNet("csbk", "Call Alert", srcId, src, gi, dstId);
|
||||
break;
|
||||
case CSBKO::CALL_ALERT_ACK:
|
||||
LogMessage("DMR Slot %u, received network Call Alert Ack CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG " : "", dst.c_str());
|
||||
writeJSONNet("csbk", "Call Alert Ack", srcId, src, gi, dstId);
|
||||
break;
|
||||
case CSBKO::RADIO_CHECK:
|
||||
LogMessage("DMR Slot %u, received network Radio Check %s CSBK from %s to %s%s", m_slotNo, /* TBD */ 1 ? "Req" : "Ack", src.c_str(), gi ? "TG " : "", dst.c_str());
|
||||
writeJSONNet("csbk", "Radio Check", srcId, src, gi, dstId);
|
||||
break;
|
||||
default:
|
||||
LogWarning("DMR Slot %u, unhandled network CSBK type - 0x%02X", m_slotNo, csbko);
|
||||
break;
|
||||
}
|
||||
|
||||
// If data preamble, signal its existence
|
||||
if ((csbko == CSBKO::PRECCSBK) && csbk.getDataContent())
|
||||
setShortLC(m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::USER_USER, ACTIVITY_TYPE::DATA);
|
||||
} else if (dataType == DT_MBC_CONTINUATION) {
|
||||
CDMRCSBK csbk;
|
||||
csbk.setDataType(DT_MBC_CONTINUATION);
|
||||
bool valid = csbk.put(data + 2U);
|
||||
if (!valid) {
|
||||
LogMessage("DMR Slot %u, unable to decode the network CSBK", m_slotNo);
|
||||
return;
|
||||
}
|
||||
|
||||
CSBKO csbko = csbk.getCSBKO();
|
||||
if (csbko == CSBKO::BSDWNACT)
|
||||
return;
|
||||
|
||||
// set the OVCM bit for the supported csbk
|
||||
if(!m_modem->getDMRTrunking())
|
||||
{
|
||||
if ((m_ovcm == DMR_OVCM::RX_ON) || (m_ovcm == DMR_OVCM::ON))
|
||||
csbk.setOVCM(true);
|
||||
else if (m_ovcm == DMR_OVCM::FORCE_OFF)
|
||||
csbk.setOVCM(false);
|
||||
}
|
||||
|
||||
bool gi = csbk.getGI();
|
||||
unsigned int srcId = csbk.getSrcId();
|
||||
unsigned int dstId = csbk.getDstId();
|
||||
|
||||
// Regenerate the CSBK data
|
||||
csbk.get(data + 2U);
|
||||
|
||||
// Regenerate the Slot Type
|
||||
CDMRSlotType slotType;
|
||||
slotType.setDataType(DT_MBC_CONTINUATION);
|
||||
slotType.putData(data + 2U);
|
||||
slotType.setColorCode(m_colorCode);
|
||||
slotType.getData(data + 2U);
|
||||
|
||||
// Convert the Data Sync to be from the BS or MS as needed
|
||||
CSync::addDMRDataSync(data + 2U, m_duplex);
|
||||
|
||||
data[0U] = TAG_DATA;
|
||||
data[1U] = 0x00U;
|
||||
|
||||
if ((csbko == CSBKO::PRECCSBK) && csbk.getDataContent()) {
|
||||
unsigned int cbf = NO_PREAMBLE_CSBK + csbk.getCBF() - 1U;
|
||||
for (unsigned int i = 0U; i < NO_PREAMBLE_CSBK; i++, cbf--) {
|
||||
// Change blocks to follow
|
||||
csbk.setCBF(cbf);
|
||||
|
||||
// Regenerate the CSBK data
|
||||
csbk.get(data + 2U);
|
||||
|
||||
// Regenerate the Slot Type
|
||||
CDMRSlotType slotType;
|
||||
slotType.putData(data + 2U);
|
||||
slotType.setColorCode(m_colorCode);
|
||||
slotType.getData(data + 2U);
|
||||
|
||||
// Convert the Data Sync to be from the BS or MS as needed
|
||||
CSync::addDMRDataSync(data + 2U, m_duplex);
|
||||
|
||||
writeQueueNet(data);
|
||||
}
|
||||
} else
|
||||
writeQueueNet(data);
|
||||
|
||||
std::string src = m_lookup->find(srcId);
|
||||
std::string dst = m_lookup->find(dstId);
|
||||
|
||||
switch (csbko) {
|
||||
case CSBKO::UUVREQ:
|
||||
LogMessage("DMR Slot %u, received network Unit to Unit Voice Service Request CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG ": "", dst.c_str());
|
||||
writeJSONNet("csbk", "Unit to Unit Voice Service Request", srcId, src, gi, dstId);
|
||||
break;
|
||||
case CSBKO::UUANSRSP:
|
||||
LogMessage("DMR Slot %u, received network Unit to Unit Voice Service Answer Response CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG ": "", dst.c_str());
|
||||
writeJSONNet("csbk", "Unit to Unit Voice Service Answer Response", srcId, src, gi, dstId);
|
||||
break;
|
||||
case CSBKO::NACKRSP:
|
||||
LogMessage("DMR Slot %u, received network Negative Acknowledgment Response CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG ": "", dst.c_str());
|
||||
writeJSONNet("csbk", "UNegative Acknowledgment Response", srcId, src, gi, dstId);
|
||||
break;
|
||||
case CSBKO::PRECCSBK:
|
||||
LogMessage("DMR Slot %u, received network %s Preamble CSBK (%u to follow) from %s to %s%s", m_slotNo, csbk.getDataContent() ? "Data" : "CSBK", csbk.getCBF(), src.c_str(), gi ? "TG " : "", dst.c_str());
|
||||
writeJSONNet("csbk", "Preamble", srcId, src, gi, dstId);
|
||||
break;
|
||||
case CSBKO::CALL_ALERT:
|
||||
LogMessage("DMR Slot %u, received network Call Alert CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG " : "", dst.c_str());
|
||||
writeJSONNet("csbk", "Call Alert", srcId, src, gi, dstId);
|
||||
break;
|
||||
case CSBKO::CALL_ALERT_ACK:
|
||||
LogMessage("DMR Slot %u, received network Call Alert Ack CSBK from %s to %s%s", m_slotNo, src.c_str(), gi ? "TG " : "", dst.c_str());
|
||||
writeJSONNet("csbk", "Call Alert Ack", srcId, src, gi, dstId);
|
||||
break;
|
||||
case CSBKO::RADIO_CHECK:
|
||||
LogMessage("DMR Slot %u, received network Radio Check %s CSBK from %s to %s%s", m_slotNo, /* TBD */ 1 ? "Req" : "Ack", src.c_str(), gi ? "TG " : "", dst.c_str());
|
||||
writeJSONNet("csbk", "Radio Check", srcId, src, gi, dstId);
|
||||
break;
|
||||
default:
|
||||
LogWarning("DMR Slot %u, unhandled network CSBK type - 0x%02X", m_slotNo, csbko);
|
||||
break;
|
||||
}
|
||||
|
||||
// If data preamble, signal its existence
|
||||
if ((csbko == CSBKO::PRECCSBK) && csbk.getDataContent())
|
||||
setShortLC(m_slotNo, dstId, gi ? FLCO::GROUP : FLCO::USER_USER, ACTIVITY_TYPE::DATA);
|
||||
|
|
@ -2136,7 +2362,7 @@ void CDMRSlot::setShortLC(unsigned int slotNo, unsigned int id, FLCO flco, ACTIV
|
|||
CDMRShortLC shortLC;
|
||||
shortLC.encode(lc, sLC);
|
||||
|
||||
m_modem->writeDMRShortLC(sLC);
|
||||
m_modem->writeDMRShortLC(sLC, false);
|
||||
}
|
||||
|
||||
bool CDMRSlot::insertSilence(const unsigned char* data, unsigned char seqNo)
|
||||
|
|
@ -2295,6 +2521,81 @@ void CDMRSlot::enable(bool enabled)
|
|||
m_enabled = enabled;
|
||||
}
|
||||
|
||||
void CDMRSlot::setReverseChannelCommand(unsigned int rc_command) {
|
||||
m_reverseChannelCommand = rc_command;
|
||||
}
|
||||
|
||||
void CDMRSlot::createReverseChannel(unsigned char *data, CDMREMB &emb) {
|
||||
if(m_rfN == 5U) {
|
||||
if (m_reverseChannelCommand == DMRCommand::RCCeaseTransmission) {
|
||||
data[16U] = (data[16U] & 0xF0U) | (RC_CEASE_TRANSMIT[0U] & 0x0FU);
|
||||
data[17U] = RC_CEASE_TRANSMIT[1U];
|
||||
data[18U] = RC_CEASE_TRANSMIT[2U];
|
||||
data[19U] = RC_CEASE_TRANSMIT[3U];
|
||||
data[20U] = (data[20U] & 0x0FU) | (RC_CEASE_TRANSMIT[4U] & 0xF0U);
|
||||
emb.setColorCode(m_colorCode);
|
||||
emb.setLCSS(0);
|
||||
emb.setPI(true);
|
||||
emb.getData(data + 2U);
|
||||
}
|
||||
else if (m_reverseChannelCommand == DMRCommand::RCRequestCeaseTransmission) {
|
||||
data[16U] = (data[16U] & 0xF0U) | (RC_REQUEST_CEASE_TRANSMIT[0U] & 0x0FU);
|
||||
data[17U] = RC_REQUEST_CEASE_TRANSMIT[1U];
|
||||
data[18U] = RC_REQUEST_CEASE_TRANSMIT[2U];
|
||||
data[19U] = RC_REQUEST_CEASE_TRANSMIT[3U];
|
||||
data[20U] = (data[20U] & 0x0FU) | (RC_REQUEST_CEASE_TRANSMIT[4U] & 0xF0U);
|
||||
emb.setColorCode(m_colorCode);
|
||||
emb.setLCSS(0);
|
||||
emb.setPI(true);
|
||||
emb.getData(data + 2U);
|
||||
}
|
||||
else if (m_reverseChannelCommand == DMRCommand::RCMaximumPower) {
|
||||
data[16U] = (data[16U] & 0xF0U) | (RC_MAX_POWER[0U] & 0x0FU);
|
||||
data[17U] = RC_MAX_POWER[1U];
|
||||
data[18U] = RC_MAX_POWER[2U];
|
||||
data[19U] = RC_MAX_POWER[3U];
|
||||
data[20U] = (data[20U] & 0x0FU) | (RC_MAX_POWER[4U] & 0xF0U);
|
||||
emb.setColorCode(m_colorCode);
|
||||
emb.setLCSS(0);
|
||||
emb.setPI(true);
|
||||
emb.getData(data + 2U);
|
||||
}
|
||||
else if (m_reverseChannelCommand == DMRCommand::RCMinimumPower) {
|
||||
data[16U] = (data[16U] & 0xF0U) | (RC_MIN_POWER[0U] & 0x0FU);
|
||||
data[17U] = RC_MIN_POWER[1U];
|
||||
data[18U] = RC_MIN_POWER[2U];
|
||||
data[19U] = RC_MIN_POWER[3U];
|
||||
data[20U] = (data[20U] & 0x0FU) | (RC_MIN_POWER[4U] & 0xF0U);
|
||||
emb.setColorCode(m_colorCode);
|
||||
emb.setLCSS(0);
|
||||
emb.setPI(true);
|
||||
emb.getData(data + 2U);
|
||||
}
|
||||
else if (m_reverseChannelCommand == DMRCommand::RCPowerIncreaseOneStep) {
|
||||
data[16U] = (data[16U] & 0xF0U) | (RC_POWER_INCREASE[0U] & 0x0FU);
|
||||
data[17U] = RC_POWER_INCREASE[1U];
|
||||
data[18U] = RC_POWER_INCREASE[2U];
|
||||
data[19U] = RC_POWER_INCREASE[3U];
|
||||
data[20U] = (data[20U] & 0x0FU) | (RC_POWER_INCREASE[4U] & 0xF0U);
|
||||
emb.setColorCode(m_colorCode);
|
||||
emb.setLCSS(0);
|
||||
emb.setPI(true);
|
||||
emb.getData(data + 2U);
|
||||
}
|
||||
else if (m_reverseChannelCommand == DMRCommand::RCPowerDecreaseOneStep) {
|
||||
data[16U] = (data[16U] & 0xF0U) | (RC_POWER_DECREASE[0U] & 0x0FU);
|
||||
data[17U] = RC_POWER_DECREASE[1U];
|
||||
data[18U] = RC_POWER_DECREASE[2U];
|
||||
data[19U] = RC_POWER_DECREASE[3U];
|
||||
data[20U] = (data[20U] & 0x0FU) | (RC_POWER_DECREASE[4U] & 0xF0U);
|
||||
emb.setColorCode(m_colorCode);
|
||||
emb.setLCSS(0);
|
||||
emb.setPI(true);
|
||||
emb.getData(data + 2U);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CDMRSlot::writeJSONRSSI()
|
||||
{
|
||||
if (m_rssi == 0U)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "AMBEFEC.h"
|
||||
#include "DMRSlot.h"
|
||||
#include "DMRData.h"
|
||||
#include "DMREMB.h"
|
||||
#include "Defines.h"
|
||||
#include "Timer.h"
|
||||
#include "Modem.h"
|
||||
|
|
@ -67,6 +68,8 @@ public:
|
|||
|
||||
static void init(unsigned int colorCode, bool embeddedLCOnly, bool dumpTAData, unsigned int callHang, CModem* modem, CDMRNetwork* network, bool duplex, CDMRLookup* lookup, CRSSIInterpolator* rssiMapper, unsigned int jitter, DMR_OVCM ovcm, bool protect);
|
||||
|
||||
void setReverseChannelCommand(unsigned int rc_command);
|
||||
|
||||
private:
|
||||
unsigned int m_slotNo;
|
||||
CRingBuffer<unsigned char> m_queue;
|
||||
|
|
@ -145,6 +148,7 @@ private:
|
|||
static FLCO m_flco2;
|
||||
static unsigned char m_id2;
|
||||
static ACTIVITY_TYPE m_activity2;
|
||||
unsigned int m_reverseChannelCommand;
|
||||
|
||||
void logGPSPosition(const unsigned char* data);
|
||||
|
||||
|
|
@ -159,6 +163,8 @@ private:
|
|||
bool insertSilence(const unsigned char* data, unsigned char seqNo);
|
||||
void insertSilence(unsigned int count);
|
||||
|
||||
void createReverseChannel(unsigned char *data, CDMREMB &emb);
|
||||
|
||||
static void setShortLC(unsigned int slotNo, unsigned int id, FLCO flco = FLCO::GROUP, ACTIVITY_TYPE type = ACTIVITY_TYPE::NONE);
|
||||
|
||||
void writeJSONRSSI();
|
||||
|
|
|
|||
|
|
@ -704,6 +704,18 @@ int CMMDVMHost::run()
|
|||
m_dmr = new CDMRControl(id, colorCode, callHang, selfOnly, embeddedLCOnly, dumpTAData, prefixes, blackList, whiteList, slot1TGWhiteList, slot2TGWhiteList, m_timeout, m_modem, m_dmrNetwork, m_duplex, m_dmrLookup, rssi, jitter, ovcm, protect);
|
||||
|
||||
m_dmrTXTimer.setTimeout(txHang);
|
||||
|
||||
// Tier III options
|
||||
if(m_conf.getDMRTrunking())
|
||||
{
|
||||
setMode(MODE_DMR);
|
||||
m_modem->setShortLC(m_conf.getDMRSystemCode(), m_conf.getDMRControlChannel(), m_conf.getDMRRegistrationRequired());
|
||||
if(m_conf.getDMRControlChannel())
|
||||
{
|
||||
m_modem->writeDMRAloha(m_conf.getDMRSystemCode(), m_conf.getDMRRegistrationRequired(), m_conf.getDMRControlChannelAlternateSlot());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -812,8 +824,8 @@ int CMMDVMHost::run()
|
|||
bool remoteControlEnabled = m_conf.getRemoteControlEnabled();
|
||||
if (remoteControlEnabled)
|
||||
m_remoteControl = new CRemoteControl(this, m_mqtt);
|
||||
|
||||
setMode(MODE_IDLE);
|
||||
if(!m_conf.getDMRTrunking())
|
||||
setMode(MODE_IDLE);
|
||||
|
||||
while (!m_killed) {
|
||||
bool lockout = m_modem->hasLockout();
|
||||
|
|
@ -895,7 +907,7 @@ int CMMDVMHost::run()
|
|||
if (m_mode == MODE_IDLE) {
|
||||
if (m_duplex) {
|
||||
bool ret = m_dmr->processWakeup(data);
|
||||
if (ret) {
|
||||
if (ret || m_conf.getDMRTrunking()) {
|
||||
m_modeTimer.setTimeout(m_dmrRFModeHang);
|
||||
setMode(MODE_DMR);
|
||||
dmrBeaconDurationTimer.stop();
|
||||
|
|
@ -909,7 +921,7 @@ int CMMDVMHost::run()
|
|||
} else if (m_mode == MODE_DMR) {
|
||||
if (m_duplex && !m_modem->hasTX()) {
|
||||
bool ret = m_dmr->processWakeup(data);
|
||||
if (ret) {
|
||||
if (ret || m_conf.getDMRTrunking()) {
|
||||
m_modem->writeDMRStart(true);
|
||||
m_dmrTXTimer.start();
|
||||
}
|
||||
|
|
@ -1008,7 +1020,7 @@ int CMMDVMHost::run()
|
|||
if (transparentSocket != nullptr && len > 0U)
|
||||
transparentSocket->write(data, len, transparentAddress, transparentAddrLen);
|
||||
|
||||
if (!m_fixedMode) {
|
||||
if (!m_fixedMode && !m_conf.getDMRTrunking()) {
|
||||
if (m_modeTimer.isRunning() && m_modeTimer.hasExpired())
|
||||
setMode(MODE_IDLE);
|
||||
}
|
||||
|
|
@ -1341,14 +1353,24 @@ int CMMDVMHost::run()
|
|||
|
||||
dmrBeaconDurationTimer.clock(ms);
|
||||
if (dmrBeaconDurationTimer.isRunning() && dmrBeaconDurationTimer.hasExpired()) {
|
||||
if (!m_fixedMode)
|
||||
if (!m_fixedMode && !m_conf.getDMRTrunking())
|
||||
setMode(MODE_IDLE);
|
||||
dmrBeaconDurationTimer.stop();
|
||||
}
|
||||
|
||||
m_dmrTXTimer.clock(ms);
|
||||
if (m_dmrTXTimer.isRunning() && m_dmrTXTimer.hasExpired()) {
|
||||
m_modem->writeDMRStart(false);
|
||||
if(m_conf.getDMRTrunking())
|
||||
{
|
||||
if(!m_conf.getDMRControlChannel())
|
||||
{
|
||||
m_modem->writeDMRStart(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_modem->writeDMRStart(false);
|
||||
}
|
||||
m_dmrTXTimer.stop();
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1566,6 +1588,8 @@ bool CMMDVMHost::createModem()
|
|||
int txDCOffset = m_conf.getModemTXDCOffset();
|
||||
float rfLevel = m_conf.getModemRFLevel();
|
||||
bool useCOSAsLockout = m_conf.getModemUseCOSAsLockout();
|
||||
bool trunking = m_conf.getDMRTrunking();
|
||||
bool controlChannel = m_conf.getDMRControlChannel();
|
||||
|
||||
LogInfo("Modem Parameters");
|
||||
LogInfo(" Protocol: %s", protocol.c_str());
|
||||
|
|
@ -1625,7 +1649,7 @@ bool CMMDVMHost::createModem()
|
|||
LogInfo(" RX Frequency: %uHz (%uHz)", rxFrequency, rxFrequency + rxOffset);
|
||||
LogInfo(" Use COS as Lockout: %s", useCOSAsLockout ? "yes" : "no");
|
||||
|
||||
m_modem = new CModem(m_duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, useCOSAsLockout, trace, debug);
|
||||
m_modem = new CModem(m_duplex, rxInvert, txInvert, pttInvert, txDelay, dmrDelay, useCOSAsLockout, trace, debug, trunking, controlChannel);
|
||||
|
||||
IModemPort* port = nullptr;
|
||||
if (protocol == "uart")
|
||||
|
|
|
|||
93
Modem.cpp
93
Modem.cpp
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2011-2018,2020,2021,2023,2025 by Jonathan Naylor G4KLX
|
||||
* Copyright (C) 2026 by Adrian Musceac YO8RZZ
|
||||
*
|
||||
* 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
|
||||
|
|
@ -26,6 +27,8 @@
|
|||
#include "Modem.h"
|
||||
#include "Utils.h"
|
||||
#include "Log.h"
|
||||
#include "CRC.h"
|
||||
#include "DMRShortLC.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
|
|
@ -57,6 +60,7 @@ const unsigned char MMDVM_DSTAR_EOT = 0x13U;
|
|||
#endif
|
||||
|
||||
#if defined(USE_DMR)
|
||||
const unsigned char MMDVM_DMR_ALOHA = 0x17U;
|
||||
const unsigned char MMDVM_DMR_DATA1 = 0x18U;
|
||||
const unsigned char MMDVM_DMR_LOST1 = 0x19U;
|
||||
const unsigned char MMDVM_DMR_DATA2 = 0x1AU;
|
||||
|
|
@ -124,7 +128,7 @@ const unsigned char CAP1_NXDN = 0x10U;
|
|||
const unsigned char CAP1_FM = 0x40U;
|
||||
const unsigned char CAP2_POCSAG = 0x01U;
|
||||
|
||||
CModem::CModem(bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool useCOSAsLockout, bool trace, bool debug) :
|
||||
CModem::CModem(bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool useCOSAsLockout, bool trace, bool debug, bool trunking, bool controlChannel) :
|
||||
m_protocolVersion(0U),
|
||||
#if defined(USE_DMR)
|
||||
m_dmrColorCode(0U),
|
||||
|
|
@ -312,6 +316,8 @@ m_fmExtEnable(false),
|
|||
#endif
|
||||
m_capabilities1(0x00U),
|
||||
m_capabilities2(0x00U),
|
||||
m_trunking(trunking),
|
||||
m_controlChannel(controlChannel),
|
||||
m_serialDataLen(0U)
|
||||
{
|
||||
m_buffer = new unsigned char[BUFFER_LENGTH];
|
||||
|
|
@ -2408,7 +2414,7 @@ bool CModem::setConfig2()
|
|||
|
||||
buffer[26U] = 0U;
|
||||
buffer[27U] = 0x00U;
|
||||
buffer[28U] = 0x00U;
|
||||
buffer[28U] = (m_trunking ? 1 : 0) << 7;
|
||||
|
||||
#if defined(USE_DMR)
|
||||
buffer[29U] = m_dmrColorCode;
|
||||
|
|
@ -2737,8 +2743,78 @@ bool CModem::writeDMRAbort(unsigned int slotNo)
|
|||
return m_port->write(buffer, 4U) == 4;
|
||||
}
|
||||
|
||||
bool CModem::writeDMRShortLC(const unsigned char* lc)
|
||||
bool CModem::writeDMRAloha(unsigned int systemCode, bool registrationRequired, bool alternateSlot)
|
||||
{
|
||||
// 7.1.1.1.4 Aloha (C_ALOHA) CSBK PDU
|
||||
assert(m_port != NULL);
|
||||
CDMRCSBK csbk;
|
||||
unsigned char data[DMR_FRAME_LENGTH_BYTES + 2U];
|
||||
unsigned int tsccas = alternateSlot ? 1 : 0;
|
||||
unsigned int timeslot_synchronization = 0;
|
||||
unsigned int version = 2; // 3 bits
|
||||
unsigned int offset_timing = 0; // MMDVM uses aligned timing
|
||||
unsigned int net_connection = 1;
|
||||
unsigned int mask = 0; // 5 bits
|
||||
unsigned int service_function = 0; // 2 bits (ALL)
|
||||
unsigned int n_rand_wait = 5; // 4 bits
|
||||
unsigned int backoff = 4; // 4 bits
|
||||
unsigned int ALLMSI = 0xFFFED4;
|
||||
|
||||
data[2U] = 0x99;
|
||||
data[3U] = 0x00; // FID
|
||||
data[4U] = (unsigned char)((tsccas << 6) | (timeslot_synchronization << 5) | ((version & 0x07) << 2) | (offset_timing << 1) | net_connection);
|
||||
data[5U] = (unsigned char)(((mask & 0x1F) << 3) | ((service_function & 0x03) << 1) | ((n_rand_wait & 0x0F) >> 3)); // TODO
|
||||
data[6U] = (unsigned char)(((n_rand_wait & 0x0F) << 5) | (((unsigned int)registrationRequired) << 4) | (backoff & 0xF)); // TODO
|
||||
data[7U] = (unsigned char)((systemCode >> 6) & 0xFF);
|
||||
data[8U] = (unsigned char)(((systemCode << 2) | 0x03) & 0xFF); // PAR hardcoded to 11
|
||||
// MS Address
|
||||
data[9U] = (unsigned char)(ALLMSI >> 16);
|
||||
data[10U] = (unsigned char)((ALLMSI >> 8) & 0xFF);
|
||||
data[11U] = (unsigned char)(ALLMSI & 0xFF);
|
||||
csbk.setCSBKData(data + 2U);
|
||||
csbk.get(data + 2U);
|
||||
|
||||
CDMRSlotType slotType;
|
||||
slotType.setColorCode(m_dmrColorCode);
|
||||
slotType.setDataType(DT_CSBK);
|
||||
slotType.getData(data + 2U);
|
||||
|
||||
CSync::addDMRDataSync(data + 2U, m_duplex);
|
||||
unsigned char buffer[DMR_FRAME_LENGTH_BYTES + 3U];
|
||||
|
||||
buffer[0U] = MMDVM_FRAME_START;
|
||||
buffer[1U] = DMR_FRAME_LENGTH_BYTES + 3U;
|
||||
buffer[2U] = MMDVM_DMR_ALOHA;
|
||||
for(unsigned int i=0; i<DMR_FRAME_LENGTH_BYTES; i++)
|
||||
{
|
||||
buffer[i + 3U] = data[i+ 2U];
|
||||
}
|
||||
|
||||
return m_port->write(buffer, DMR_FRAME_LENGTH_BYTES + 3U) == 12;
|
||||
}
|
||||
|
||||
void CModem::setShortLC(unsigned int systemCode, bool isControlChannel, bool registrationRequired)
|
||||
{
|
||||
// Used for setting CACH (C_SysParm and P_SysParm) on TSCC and Payload Ch
|
||||
unsigned char lc[5U];
|
||||
// 7.1.2.1 Control Channel System Parameters
|
||||
// 7.1.2.2 Payload Channel System Parameters
|
||||
lc[0U] = isControlChannel? 0x2U : 0x3U;
|
||||
lc[1U] = (unsigned char)((systemCode >> 6) & 0XFF);
|
||||
unsigned int regi = (unsigned int) registrationRequired;
|
||||
lc[2U] = (unsigned char)(((systemCode << 2) & 0xFF) | (regi << 1)); // 6 bit System code, 1 bit Reg_Required, rest of bits CSC
|
||||
lc[3U] = 0x01U; // CSC: This should increment from 1 to 511, but not implemented yet
|
||||
lc[4U] = CCRC::crc8(lc, 4U);
|
||||
unsigned char sLC[9U];
|
||||
CDMRShortLC shortLC;
|
||||
shortLC.encode(lc, sLC);
|
||||
this->writeDMRShortLC(sLC, true);
|
||||
}
|
||||
|
||||
bool CModem::writeDMRShortLC(const unsigned char* lc, bool control)
|
||||
{
|
||||
if(!control && m_trunking)
|
||||
return true;
|
||||
assert(m_port != nullptr);
|
||||
assert(lc != nullptr);
|
||||
|
||||
|
|
@ -3081,3 +3157,14 @@ void CModem::printDebug()
|
|||
CUtils::dump(1U, "Debug: Data", m_buffer + m_offset, m_length - m_offset);
|
||||
}
|
||||
}
|
||||
|
||||
bool CModem::getDMRTrunking() const
|
||||
{
|
||||
return m_trunking;
|
||||
}
|
||||
|
||||
bool CModem::getControlChannel() const
|
||||
{
|
||||
return m_controlChannel;
|
||||
}
|
||||
|
||||
|
|
|
|||
13
Modem.h
13
Modem.h
|
|
@ -23,6 +23,9 @@
|
|||
#include "RingBuffer.h"
|
||||
#include "Defines.h"
|
||||
#include "Timer.h"
|
||||
#include "DMRCSBK.h"
|
||||
#include "DMRSlotType.h"
|
||||
#include "Sync.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
|
@ -42,7 +45,7 @@ enum class SERIAL_STATE {
|
|||
|
||||
class CModem {
|
||||
public:
|
||||
CModem(bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool useCOSAsLockout, bool trace, bool debug);
|
||||
CModem(bool duplex, bool rxInvert, bool txInvert, bool pttInvert, unsigned int txDelay, unsigned int dmrDelay, bool useCOSAsLockout, bool trace, bool debug, bool trunking, bool controlChannel);
|
||||
~CModem();
|
||||
|
||||
void setPort(IModemPort* port);
|
||||
|
|
@ -179,7 +182,9 @@ public:
|
|||
|
||||
#if defined(USE_DMR)
|
||||
bool writeDMRStart(bool tx);
|
||||
bool writeDMRShortLC(const unsigned char* lc);
|
||||
bool writeDMRAloha(unsigned int systemCode, bool registrationRequired, bool alternateSlot);
|
||||
void setShortLC(unsigned int systemCode, bool isControlChannel, bool registrationRequired);
|
||||
bool writeDMRShortLC(const unsigned char* lc, bool control);
|
||||
bool writeDMRAbort(unsigned int slotNo);
|
||||
#endif
|
||||
bool writeTransparentData(const unsigned char* data, unsigned int length);
|
||||
|
|
@ -198,6 +203,8 @@ public:
|
|||
void clock(unsigned int ms);
|
||||
|
||||
void close();
|
||||
bool getDMRTrunking() const;
|
||||
bool getControlChannel() const;
|
||||
|
||||
private:
|
||||
unsigned int m_protocolVersion;
|
||||
|
|
@ -388,6 +395,8 @@ private:
|
|||
#endif
|
||||
unsigned char m_capabilities1;
|
||||
unsigned char m_capabilities2;
|
||||
bool m_trunking;
|
||||
bool m_controlChannel;
|
||||
|
||||
bool readVersion();
|
||||
bool readStatus();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue