implement marking builds with the Git hash automatically; fix issue with DMR CC not terminating when dvmhost is shutting down; fix issue with DMR CC incorrectly transmitting frames; implement DVM specific opcodes that transmit the Git hash over the air;

pull/12/head
Bryan Biedenkapp 4 years ago
parent c7f1d20c3d
commit 9a4906ecac

@ -93,10 +93,17 @@ typedef unsigned long long ulong64_t;
// Constants
// ---------------------------------------------------------------------------
#ifndef __GIT_VER__
#define __GIT_VER__ "00000000"
#endif
#ifndef __GIT_VER_HASH__
#define __GIT_VER_HASH__ "00000000"
#endif
#define __PROG_NAME__ "Digital Voice Modem Host"
#define __NET_NAME__ "DVM_DMR_P25"
#define __EXE_NAME__ "dvmhost"
#define __VER__ "R01.00.00"
#define __VER__ "R01.00.00 (" __GIT_VER__ ")"
#define __BUILD__ __DATE__ " " __TIME__
#define HOST_SW_API
@ -202,19 +209,19 @@ inline std::string __IP_FROM_ULONG(const ulong64_t& value) {
#define READ_BIT(p, i) (p[(i) >> 3] & BIT_MASK_TABLE[(i) & 7])
#define __SET_UINT32(val, buffer, offset) \
buffer[0U + offset] = val >> 24; \
buffer[1U + offset] = val >> 16; \
buffer[2U + offset] = val >> 8; \
buffer[3U + offset] = val >> 0;
buffer[0U + offset] = (val >> 24) & 0xFFU; \
buffer[1U + offset] = (val >> 16) & 0xFFU; \
buffer[2U + offset] = (val >> 8) & 0xFFU; \
buffer[3U + offset] = (val >> 0) & 0xFFU;
#define __GET_UINT32(buffer, offset) \
(buffer[offset + 0U] << 24) | \
(buffer[offset + 1U] << 16) | \
(buffer[offset + 2U] << 8) | \
(buffer[offset + 3U] << 0);
#define __SET_UINT16(val, buffer, offset) \
buffer[0U + offset] = val >> 16; \
buffer[1U + offset] = val >> 8; \
buffer[2U + offset] = val >> 0;
buffer[0U + offset] = (val >> 16) & 0xFFU; \
buffer[1U + offset] = (val >> 8) & 0xFFU; \
buffer[2U + offset] = (val >> 0) & 0xFFU;
#define __GET_UINT16(buffer, offset) \
(buffer[offset + 0U] << 16) | \
(buffer[offset + 1U] << 8) | \

@ -74,6 +74,8 @@ bool g_killed = false;
bool g_fireDMRBeacon = false;
bool g_fireP25Control = false;
uint8_t* g_gitHashBytes = NULL;
// ---------------------------------------------------------------------------
// Global Functions
// ---------------------------------------------------------------------------
@ -188,6 +190,12 @@ int checkArgs(int argc, char* argv[])
int main(int argc, char** argv)
{
g_gitHashBytes = new uint8_t[4U];
::memset(g_gitHashBytes, 0x00U, 4U);
uint32_t hash = ::strtoul(__GIT_VER_HASH__, 0, 16);
__SET_UINT32(hash, g_gitHashBytes, 0U);
if (argv[0] != NULL && *argv[0] != 0)
g_progExe = std::string(argv[0]);

@ -50,6 +50,8 @@ extern bool g_killed;
extern bool g_fireDMRBeacon;
extern bool g_fireP25Control;
extern uint8_t* g_gitHashBytes;
extern HOST_SW_API void fatal(const char* msg, ...);
#endif // __HOST_MAIN_H__

@ -19,6 +19,15 @@ ifneq ($(ARCH),)
TAR_ARCH = "_$(ARCH)"
endif
GITFLAGS=
GIT_VER =
GIT_VER_HASH =
ifneq ("$(wildcard .git)",)
GIT_VER = \"$(shell git describe --abbrev=8 --dirty --always --tags)\"
GIT_VER_HASH = \"$(shell git describe --abbrev=8 --always --tags)\"
GITFLAGS= -D__GIT_VER__=$(GIT_VER) -D__GIT_VER_HASH__=$(GIT_VER_HASH)
endif
HOST_BIN = dvmhost
HOST_OBJECTS = \
edac/AMBEFEC.o \
@ -107,13 +116,13 @@ CMD_OBJECTS = \
all: dvmhost dvmcmd
dvmhost: $(HOST_OBJECTS)
$($(ARCH)CXX) $(HOST_OBJECTS) $(CFLAGS) $(EXTFLAGS) $(LIBS) -o $(HOST_BIN)
$($(ARCH)CXX) $(HOST_OBJECTS) $(CFLAGS) $(GITFLAGS) $(EXTFLAGS) $(LIBS) -o $(HOST_BIN)
dvmcmd: $(CMD_OBJECTS)
$($(ARCH)CXX) $(CMD_OBJECTS) $(CFLAGS) $(CCFLAGS) $(EXTFLAGS) $(LIBS) -o $(CMD_BIN)
$($(ARCH)CXX) $(CMD_OBJECTS) $(GITFLAGS) $(CFLAGS) $(CCFLAGS) $(EXTFLAGS) $(LIBS) -o $(CMD_BIN)
%.o: %.cpp
$($(ARCH)CXX) $(CFLAGS) $(EXTFLAGS) -c -o $@ $<
$($(ARCH)CXX) $(CFLAGS) $(GITFLAGS) $(EXTFLAGS) -c -o $@ $<
%.cmd.o: %.cpp
$($(ARCH)CXX) $(CFLAGS) $(CCFLAGS) $(EXTFLAGS) -c -o $@ $<
$($(ARCH)CXX) $(CFLAGS) $(CCFLAGS) $(GITFLAGS) $(EXTFLAGS) -c -o $@ $<
strip:
-$($(ARCH)STRIP) $(HOST_BIN)
-$($(ARCH)STRIP) $(CMD_BIN)
@ -122,7 +131,7 @@ clean:
$(RM) -r dpkg_build tar_build
$(RM) dvmhost_1.0.0* dvmhost-dbgsym*.deb
install:all
install: all
mkdir -p /opt/dvm/bin || true
install -m 755 $(HOST_BIN) /opt/dvm/bin/
install -m 755 $(CMD_BIN) /opt/dvm/bin/

@ -80,14 +80,18 @@ public:
clear();
return false;
}
#if DEBUG_RINGBUFFER
uint32_t iPtr_BeforeWrite = m_iPtr;
#endif
for (uint32_t i = 0U; i < length; i++) {
m_buffer[m_iPtr++] = buffer[i];
if (m_iPtr == m_length)
m_iPtr = 0U;
}
#if DEBUG_RINGBUFFER
LogDebug(LOG_HOST, "RingBuffer::addData(%s): iPtr_Before = %u, iPtr_After = %u, oPtr = %u, len = %u, len_Written = %u", m_name, iPtr_BeforeWrite, m_iPtr, m_oPtr, m_length, (m_iPtr - iPtr_BeforeWrite));
#endif
return true;
}
@ -101,14 +105,18 @@ public:
LogError(LOG_HOST, "**** Underflow in %s ring buffer, %u < %u", m_name, dataSize(), length);
return false;
}
#if DEBUG_RINGBUFFER
uint32_t oPtr_BeforeRead = m_oPtr;
#endif
for (uint32_t i = 0U; i < length; i++) {
buffer[i] = m_buffer[m_oPtr++];
if (m_oPtr == m_length)
m_oPtr = 0U;
}
#if DEBUG_RINGBUFFER
LogDebug(LOG_HOST, "RingBuffer::getData(%s): iPtr = %u, oPtr_Before = %u, oPtr_After = %u, len = %u, len_Read = %u", m_name, m_iPtr, oPtr_BeforeRead, m_oPtr, m_length, (m_oPtr - oPtr_BeforeRead));
#endif
return true;
}

@ -76,6 +76,7 @@ Control::Control(uint32_t colorCode, uint32_t callHang, uint32_t queueSize, bool
m_tidLookup(tidLookup),
m_tsccSlotNo(0U),
m_ccRunning(false),
m_ccHalted(false),
m_dumpCSBKData(dumpCSBKData),
m_verbose(verbose),
m_debug(debug)
@ -176,6 +177,26 @@ void Control::setCCRunning(bool ccRunning)
}
}
/// <summary>
/// Sets a flag indicating whether the DMR control channel is halted.
/// </summary>
/// <param name="ccHalted"></param>
void Control::setCCHalted(bool ccHalted)
{
m_ccHalted = ccHalted;
switch (m_tsccSlotNo) {
case 1U:
m_slot1->setCCHalted(ccHalted);
break;
case 2U:
m_slot2->setCCHalted(ccHalted);
break;
default:
LogError(LOG_NET, "DMR, invalid slot, TSCC disabled, slotNo = %u", m_tsccSlotNo);
break;
}
}
/// <summary>
/// Helper to process wakeup frames from the RF interface.
/// </summary>

@ -68,10 +68,15 @@ namespace dmr
/// <summary>Helper to set DMR configuration options.</summary>
void setOptions(yaml::Node& conf, uint32_t netId, uint8_t siteId, uint8_t channelId, uint32_t channelNo, bool printOptions);
/// <summary>Gets a flag indicating whether the DMR control channel is running.</summary>
bool getCCRunning() { return m_ccRunning; }
/// <summary>Sets a flag indicating whether the DMR control channel is running.</summary>
void setCCRunning(bool ccRunning);
/// <summary>Gets a flag indicating whether the DMR control channel is running.</summary>
bool getCCHalted() { return m_ccHalted; }
/// <summary>Sets a flag indicating whether the DMR control channel is halted.</summary>
void setCCHalted(bool ccHalted);
/// <summary>Helper to process wakeup frames from the RF interface.</summary>
bool processWakeup(const uint8_t* data);
@ -107,6 +112,7 @@ namespace dmr
uint8_t m_tsccSlotNo;
bool m_ccRunning;
bool m_ccHalted;
bool m_dumpCSBKData;
bool m_verbose;

@ -160,6 +160,7 @@ namespace dmr
// Feature IDs
const uint8_t FID_ETSI = 0x00U; // ETSI Standard Feature Set
const uint8_t FID_DMRA = 0x10U; //
const uint8_t FID_DVM = 0xFEU; // internal DMR FID used for internal signalling
// LC Service Options
const uint8_t LC_SVC_OPT_EMERGENCY = 0x80U;
@ -248,6 +249,8 @@ namespace dmr
const uint8_t CSBKO_BSDWNACT = 0x38U; // BS DWN ACT - BS Outbound Activation
const uint8_t CSBKO_PRECCSBK = 0x3DU; // PRE CSBK - Preamble CSBK
const uint8_t CSBKO_DVM_GIT_HASH = 0xFBU; //
const uint8_t TALKER_ID_NONE = 0x00U;
const uint8_t TALKER_ID_HEADER = 0x01U;
const uint8_t TALKER_ID_BLOCK1 = 0x02U;

@ -148,6 +148,8 @@ Slot::Slot(uint32_t slotNo, uint32_t timeout, uint32_t tgHang, uint32_t queueSiz
m_silenceThreshold(DEFAULT_SILENCE_THRESHOLD),
m_ccSeq(0U),
m_ccRunning(false),
m_ccPrevRunning(false),
m_ccHalted(false),
m_enableTSCC(false),
m_dumpCSBKData(dumpCSBKData),
m_verbose(verbose),
@ -170,15 +172,6 @@ Slot::~Slot()
delete m_control;
}
/// <summary>
/// Sets a flag indicating whether the DMR control channel is running.
/// </summary>
/// <param name="ccRunning"></param>
void Slot::setCCRunning(bool ccRunning)
{
m_ccRunning = ccRunning;
}
/// <summary>
/// Process DMR data frame from the RF interface.
/// </summary>
@ -332,7 +325,6 @@ uint32_t Slot::getFrame(uint8_t* data)
uint8_t len = 0U;
m_queue.getData(&len, 1U);
m_queue.getData(data, len);
return len;
@ -404,13 +396,27 @@ void Slot::clock()
}
}
// if we have control enabled; do clocking to generate a CC data stream
if (m_enableTSCC) {
if (m_ccRunning && !m_ccPacketInterval.isRunning()) {
m_ccPacketInterval.start();
}
if (m_ccHalted) {
if (!m_ccRunning) {
m_ccHalted = false;
m_ccPrevRunning = m_ccRunning;
m_queue.clear(); // clear the frame buffer
}
}
else {
m_ccPacketInterval.clock(ms);
if (!m_ccPacketInterval.isRunning()) {
m_ccPacketInterval.start();
}
if (m_ccPacketInterval.isRunning() && m_ccPacketInterval.hasExpired()) {
if (m_ccRunning) {
// increment the TSCC counter on every slot 1 clock
m_tsccCnt++;
if (m_tsccCnt == TSCC_MAX_CNT) {
@ -421,23 +427,22 @@ void Slot::clock()
m_ccSeq = 0U;
}
if (m_dedicatedTSCC) {
setShortLC_TSCC(m_siteData, m_tsccCnt);
writeRF_ControlData(m_tsccCnt, m_ccSeq);
}
else {
if (m_ccRunning) {
setShortLC_TSCC(m_siteData, m_tsccCnt);
writeRF_ControlData(m_tsccCnt, m_ccSeq);
}
}
m_ccSeq++;
}
m_ccPacketInterval.start();
}
}
if (m_ccPrevRunning && !m_ccRunning) {
m_queue.clear(); // clear the frame buffer
m_ccPrevRunning = m_ccRunning;
}
}
m_rfTimeoutTimer.clock(ms);
if (m_rfTimeoutTimer.isRunning() && m_rfTimeoutTimer.hasExpired()) {
if (!m_rfTimeout) {
@ -872,11 +877,22 @@ void Slot::writeRF_ControlData(uint16_t frameCnt, uint8_t n)
return;
}
// loop to generate 2 control sequences
// loop to generate 3 control sequences
if (frameCnt == 511U) {
seqCnt = 3U;
}
// shuld we insert the Git Hash burst?
bool hash = (frameCnt % 256U) == 0U;
if (hash) {
m_control->writeRF_TSCC_Git_Hash();
if (seqCnt > 0U)
n++;
return;
}
do
{
if (m_debug) {

@ -72,8 +72,14 @@ namespace dmr
/// <summary>Finalizes a instance of the Slot class.</summary>
~Slot();
/// <summary>Sets a flag indicating whether the DMR control channel is running.</summary>
void setCCRunning(bool ccRunning);
/// <summary>Gets a flag indicating whether the P25 control channel is running.</summary>
bool getCCRunning() { return m_ccRunning; }
/// <summary>Sets a flag indicating whether the P25 control channel is running.</summary>
void setCCRunning(bool ccRunning) { m_ccPrevRunning = m_ccRunning; m_ccRunning = ccRunning; }
/// <summary>Gets a flag indicating whether the DMR control channel is running.</summary>
bool getCCHalted() { return m_ccHalted; }
/// <summary>Sets a flag indicating whether the DMR control channel is halted.</summary>
void setCCHalted(bool ccHalted) { m_ccHalted = ccHalted; }
/// <summary>Process a data frame from the RF interface.</summary>
bool processFrame(uint8_t* data, uint32_t len);
@ -165,6 +171,8 @@ namespace dmr
uint8_t m_ccSeq;
bool m_ccRunning;
bool m_ccPrevRunning;
bool m_ccHalted;
bool m_enableTSCC;
bool m_dedicatedTSCC;

@ -32,6 +32,7 @@
#include "dmr/lc/CSBK.h"
#include "edac/BPTC19696.h"
#include "edac/CRC.h"
#include "HostMain.h"
#include "Log.h"
#include "Utils.h"
@ -427,7 +428,6 @@ void CSBK::encode(uint8_t* bytes)
csbkValue = (csbkValue << 1) + ((m_hibernating) ? 1U : 0U); // TSCC Hibernating
csbkValue = (csbkValue << 22) + 0U; // Broadcast Parms 2 (Reserved)
}
break;
}
// split value into bytes
@ -457,12 +457,62 @@ void CSBK::encode(uint8_t* bytes)
m_data[5U] = (m_dstId >> 8) & 0xFFU;
m_data[6U] = (m_dstId >> 0) & 0xFFU;
m_data[7U] = (m_srcId >> 16) & 0xFFU; // Source ID
m_data[8U] = (m_srcId >> 8) & 0xFFU;
m_data[9U] = (m_srcId >> 0) & 0xFFU;
if ((m_FID == FID_ETSI) || (m_FID == FID_DMRA)) {
LogError(LOG_DMR, "CSBK::encode(), unknown CSBK type, csbko = $%02X", m_CSBKO);
}
break;
}
// internal DMR vendor opcodes
if (m_FID == FID_DVM) {
switch (m_CSBKO) {
case CSBKO_DVM_GIT_HASH:
{
ulong64_t csbkValue = 0U;
csbkValue = g_gitHashBytes[0]; // ...
csbkValue = (csbkValue << 8) + (g_gitHashBytes[1U]); // ...
csbkValue = (csbkValue << 8) + (g_gitHashBytes[2U]); // ...
csbkValue = (csbkValue << 8) + (g_gitHashBytes[3U]); // ...
csbkValue = (csbkValue << 16) + 0U;
csbkValue = (csbkValue << 4) + m_siteIdenEntry.channelId(); // Channel ID
csbkValue = (csbkValue << 12) + m_logicalCh1; // Channel Number
// split value into bytes
m_data[2U] = (uint8_t)((csbkValue >> 56) & 0xFFU);
m_data[3U] = (uint8_t)((csbkValue >> 48) & 0xFFU);
m_data[4U] = (uint8_t)((csbkValue >> 40) & 0xFFU);
m_data[5U] = (uint8_t)((csbkValue >> 32) & 0xFFU);
m_data[6U] = (uint8_t)((csbkValue >> 24) & 0xFFU);
m_data[7U] = (uint8_t)((csbkValue >> 16) & 0xFFU);
m_data[8U] = (uint8_t)((csbkValue >> 8) & 0xFFU);
m_data[9U] = (uint8_t)((csbkValue >> 0) & 0xFFU);
}
break;
default:
if (m_GI) {
m_data[2U] |= 0x40U; // Group or Individual
}
if (m_dataContent) {
m_data[2U] |= 0x80U; //
}
m_data[3U] = m_CBF; //
m_data[4U] = (m_dstId >> 16) & 0xFFU; // Destination ID
m_data[5U] = (m_dstId >> 8) & 0xFFU;
m_data[6U] = (m_dstId >> 0) & 0xFFU;
m_data[7U] = (m_srcId >> 16) & 0xFFU; // Source ID
m_data[8U] = (m_srcId >> 8) & 0xFFU;
m_data[9U] = (m_srcId >> 0) & 0xFFU;
LogError(LOG_DMR, "CSBK::encode(), unknown CSBK type, csbko = $%02X", m_CSBKO);
break;
}
}
m_data[10U] ^= CSBK_CRC_MASK[0U];
m_data[11U] ^= CSBK_CRC_MASK[1U];

@ -633,3 +633,42 @@ void ControlSignaling::writeRF_TSCC_Bcast_Sys_Parm()
if (m_slot->m_duplex)
m_slot->addFrame(data);
}
/// <summary>
/// Helper to write a TSCC Git Hash broadcast packet on the RF interface.
/// </summary>
void ControlSignaling::writeRF_TSCC_Git_Hash()
{
if (m_debug) {
LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_DVM_GIT_HASH (DVM Git Hash)", m_slot->m_slotNo);
}
uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
::memset(data + 2U, 0x00U, DMR_FRAME_LENGTH_BYTES);
SlotType slotType;
slotType.setColorCode(m_slot->m_colorCode);
slotType.setDataType(DT_CSBK);
lc::CSBK csbk = lc::CSBK(m_slot->m_siteData, m_slot->m_idenEntry, m_slot->m_dumpCSBKData);
csbk.setVerbose(m_dumpCSBKData);
csbk.setCSBKO(CSBKO_DVM_GIT_HASH);
csbk.setFID(FID_DVM);
// Regenerate the CSBK data
csbk.encode(data + 2U);
// Regenerate the Slot Type
slotType.encode(data + 2U);
// Convert the Data Sync to be from the BS or MS as needed
Sync::addDMRDataSync(data + 2U, m_slot->m_duplex);
m_slot->m_rfSeqNo = 0U;
data[0U] = modem::TAG_DATA;
data[1U] = 0x00U;
if (m_slot->m_duplex)
m_slot->addFrame(data);
}

@ -93,6 +93,8 @@ namespace dmr
void writeRF_TSCC_Bcast_Ann_Wd(uint32_t channelNo, bool annWd);
/// <summary>Helper to write a TSCC Sys_Parm broadcast packet on the RF interface.</summary>
void writeRF_TSCC_Bcast_Sys_Parm();
/// <summary>Helper to write a TSCC Git Hash broadcast packet on the RF interface.</summary>
void writeRF_TSCC_Git_Hash();
};
} // namespace packet
} // namespace dmr

@ -662,9 +662,11 @@ int Host::run()
#define INTERRUPT_DMR_BEACON \
if (dmr != NULL) { \
if (dmrBeaconDurationTimer.isRunning() && !dmrBeaconDurationTimer.hasExpired()) { \
if (m_dmrTSCCData && !m_dmrCtrlChannel) \
if (m_dmrTSCCData && !m_dmrCtrlChannel) { \
dmr->setCCHalted(true); \
dmr->setCCRunning(false); \
} \
} \
dmrBeaconDurationTimer.stop(); \
}
@ -727,6 +729,8 @@ int Host::run()
if (ret) {
len = dmr->getFrame(1U, data);
if (len > 0U) {
LogDebug(LOG_HOST, "found DMR frame, len = %u", len);
// if the state is idle; set to DMR, start mode timer and start DMR idle frames
if (m_state == STATE_IDLE) {
m_modeTimer.setTimeout(m_netModeHang);
@ -1274,6 +1278,7 @@ int Host::run()
}
dmr->setCCRunning(false);
dmr->setCCHalted(true);
dmrBeaconDurationTimer.stop();
dmrBeaconIntervalTimer.stop();
@ -2042,6 +2047,10 @@ void Host::setState(uint8_t state)
m_modem->setState(STATE_IDLE);
m_modem->clearDMRData1();
m_modem->clearDMRData2();
m_modem->clearP25Data();
if (m_state == HOST_STATE_ERROR) {
m_modem->sendCWId(m_cwCallsign);

@ -113,7 +113,7 @@ namespace p25
const uint8_t P25_MFG_STANDARD = 0x00U;
const uint8_t P25_MFG_MOT = 0x90U;
const uint8_t P25_MFG_DVM = 0xFEU; // internal P25 MFId used for internal signalling (shouldn't be air transmitted!)
const uint8_t P25_MFG_DVM = 0xFEU; // internal P25 MFId used for internal signalling
const uint8_t P25_MOT_CALLSIGN_LENGTH_BYTES = 8U;
@ -333,6 +333,9 @@ namespace p25
const uint8_t TSBK_OSP_MOT_CC_BSI = 0x0BU; // MOT CC BSI - Motorola / Control Channel Base Station Identifier
const uint8_t TSBK_OSP_MOT_PSH_CCH = 0x0EU; // MOT PSH CCH - Motorola / Planned Control Channel Shutdown
// TSBK Motorola Outbound Signalling Packet (OSP) Opcode(s)
const uint8_t TSBK_OSP_DVM_GIT_HASH = 0xFBU; //
// Data Unit ID(s)
const uint8_t P25_DUID_HDU = 0x00U; // Header Data Unit
const uint8_t P25_DUID_TDU = 0x03U; // Simple Terminator Data Unit

@ -28,6 +28,7 @@
#include "p25/lc/TSBK.h"
#include "p25/P25Utils.h"
#include "edac/CRC.h"
#include "HostMain.h"
#include "Log.h"
#include "Utils.h"
@ -975,7 +976,7 @@ void TSBK::encode(uint8_t* data, bool rawTSBK, bool noTrellis)
break;
case TSBK_OSP_MOT_CC_BSI:
tsbkValue = (m_siteCallsign[0] - 43U) & 0x3F; // Character 0
for (int i = 1; i < P25_MOT_CALLSIGN_LENGTH_BYTES; i++) {
for (uint8_t i = 1; i < P25_MOT_CALLSIGN_LENGTH_BYTES; i++) {
tsbkValue = (tsbkValue << 6) + ((m_siteCallsign[i] - 43U) & 0x3F); // Character 1 - 7
}
tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID
@ -1008,6 +1009,15 @@ void TSBK::encode(uint8_t* data, bool rawTSBK, bool noTrellis)
// internal P25 vendor opcodes
if (m_mfId == P25_MFG_DVM) {
switch (m_lco) {
case TSBK_OSP_DVM_GIT_HASH:
tsbkValue = g_gitHashBytes[0]; // ...
tsbkValue = (tsbkValue << 8) + (g_gitHashBytes[1U]); // ...
tsbkValue = (tsbkValue << 8) + (g_gitHashBytes[2U]); // ...
tsbkValue = (tsbkValue << 8) + (g_gitHashBytes[3U]); // ...
tsbkValue = (tsbkValue << 16) + 0U;
tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID
tsbkValue = (tsbkValue << 12) + m_siteData.channelNo(); // Channel Number
break;
case LC_CALL_TERM:
tsbkValue = (tsbkValue << 4) + m_siteData.channelId(); // Channel ID
tsbkValue = (tsbkValue << 12) + m_grpVchNo; // Channel Number

@ -1521,6 +1521,12 @@ void Trunk::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS)
queueRF_TSBK_Ctrl(TSBK_OSP_MOT_CC_BSI);
}
// shuld we insert the Git Hash burst?
bool hash = (frameCnt % 125U) == 0U;
if (hash && n > 3U) {
queueRF_TSBK_Ctrl(TSBK_OSP_DVM_GIT_HASH);
}
// add padding after the last sequence or if forced; and only
// if we're doing multiblock frames (MBF)
if ((n >= 4U || forcePad) && m_ctrlTSDUMBF)
@ -2057,6 +2063,17 @@ void Trunk::queueRF_TSBK_Ctrl(uint8_t lco)
m_rfTSBK.setLCO(TSBK_OSP_MOT_CC_BSI);
m_rfTSBK.setMFId(P25_MFG_MOT);
break;
/** DVM CC data */
case TSBK_OSP_DVM_GIT_HASH:
if (m_debug) {
LogMessage(LOG_RF, P25_TSDU_STR ", TSBK_OSP_DVM_GIT_HASH (DVM Git Hash)");
}
// transmit git hash burst
m_rfTSBK.setLCO(TSBK_OSP_DVM_GIT_HASH);
m_rfTSBK.setMFId(P25_MFG_DVM);
break;
}
m_rfTSBK.setLastBlock(true); // always set last block

Loading…
Cancel
Save

Powered by TurnKey Linux.