diff --git a/DVMHost.vcxproj b/DVMHost.vcxproj
index aa6f0524..8fb674d4 100644
--- a/DVMHost.vcxproj
+++ b/DVMHost.vcxproj
@@ -162,6 +162,7 @@
+
diff --git a/DVMHost.vcxproj.filters b/DVMHost.vcxproj.filters
index c34140d2..1e1614a2 100644
--- a/DVMHost.vcxproj.filters
+++ b/DVMHost.vcxproj.filters
@@ -329,6 +329,9 @@
Header Files\p25\data
+
+ Header Files\dmr
+
diff --git a/HostMain.cpp b/HostMain.cpp
index 894e9e07..b015578a 100644
--- a/HostMain.cpp
+++ b/HostMain.cpp
@@ -87,11 +87,12 @@ static void sigHandler(int signum)
void fatal(const char* msg, ...)
{
char buffer[400U];
+ ::memset(buffer, 0x20U, 400U);
va_list vl;
va_start(vl, msg);
- ::vsprintf(buffer + ::strlen(buffer), msg, vl);
+ ::vsprintf(buffer, msg, vl);
va_end(vl);
diff --git a/dmr/Control.cpp b/dmr/Control.cpp
index cbf21c5c..7260117f 100644
--- a/dmr/Control.cpp
+++ b/dmr/Control.cpp
@@ -81,7 +81,7 @@ Control::Control(uint32_t colorCode, uint32_t callHang, uint32_t queueSize, bool
assert(rssi != NULL);
acl::AccessControl::init(m_ridLookup, m_tidLookup);
- Slot::init(colorCode, embeddedLCOnly, dumpTAData, callHang, modem, network, duplex, m_ridLookup, m_tidLookup, rssi, jitter);
+ Slot::init(colorCode, SiteData(), embeddedLCOnly, dumpTAData, callHang, modem, network, duplex, m_ridLookup, m_tidLookup, rssi, jitter);
m_slot1 = new Slot(1U, timeout, tgHang, queueSize, dumpDataPacket, repeatDataPacket, dumpCSBKData, debug, verbose);
m_slot2 = new Slot(2U, timeout, tgHang, queueSize, dumpDataPacket, repeatDataPacket, dumpCSBKData, debug, verbose);
diff --git a/dmr/DMRDefines.h b/dmr/DMRDefines.h
index cf069c1a..a83787bc 100644
--- a/dmr/DMRDefines.h
+++ b/dmr/DMRDefines.h
@@ -12,7 +12,7 @@
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
-* Copyright (C) 2019 by Bryan Biedenkapp N2PLL
+* Copyright (C) 2019-2021 by Bryan Biedenkapp N2PLL
*
* 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
@@ -130,6 +130,8 @@ namespace dmr
const uint32_t DMR_EXT_FNCT_UNINHIBIT_ACK = 0x00FEU; // Radio Uninhibit Ack
const uint32_t DMR_EXT_FNCT_INHIBIT_ACK = 0x00FFU; // Radio Inhibit Ack
+ const uint8_t DMR_ALOHA_VER_151 = 0x00U;
+
// Data Type(s)
const uint8_t DT_VOICE_PI_HEADER = 0x00U;
const uint8_t DT_VOICE_LC_HEADER = 0x01U;
@@ -145,6 +147,31 @@ namespace dmr
const uint8_t DT_VOICE_SYNC = 0xF0U;
const uint8_t DT_VOICE = 0xF1U;
+ // Site Models
+ const uint8_t SITE_MODEL_TINY = 0x00U;
+ const uint8_t SITE_MODEL_SMALL = 0x01U;
+ const uint8_t SITE_MODEL_LARGE = 0x02U;
+ const uint8_t SITE_MODEL_HUGE = 0x03U;
+
+ // Target Address
+ const uint8_t TGT_ADRS_SYSCODE = 0x00U;
+ const uint8_t TGT_ADRS_TGID = 0x01U;
+
+ // Short-Link Control Opcode(s)
+ const uint8_t SLCO_NULL = 0x00U;
+ const uint8_t SLCO_ACT = 0x01U;
+ const uint8_t SLCO_TSCC = 0x02U;
+
+ // Broadcast Announcement Type(s)
+ const uint8_t BCAST_ANNC_ANN_WD_TSCC = 0x00U; // Announce/Withdraw TSCC
+ const uint8_t BCAST_ANNC_CALL_TIMER_PARMS = 0x01U; // Specify Call Timer Parameters
+ const uint8_t BCAST_ANNC_VOTE_NOW = 0x02U; // Vote Now Advice
+ const uint8_t BCAST_ANNC_LOCAL_TIME = 0x03U; // Broadcast Local Time
+ const uint8_t BCAST_ANNC_MASS_REG = 0x04U; // Mass Registration
+ const uint8_t BCAST_ANNC_CHAN_FREQ = 0x05U; // Announce a logical channel/frequency relationship
+ const uint8_t BCAST_ANNC_ADJ_SITE = 0x06U; // Adjacent Site information
+ const uint8_t BCAST_ANNC_SITE_PARMS = 0x07U; // General Site Parameters information
+
// Full-Link Control Opcode(s)
const uint8_t FLCO_GROUP = 0x00U; // GRP VCH USER - Group Voice Channel User
const uint8_t FLCO_PRIVATE = 0x03U; // UU VCH USER - Unit-to-Unit Voice Channel User
@@ -159,12 +186,15 @@ namespace dmr
const uint8_t CSBKO_UU_V_REQ = 0x04U; // UU VCH REQ - Unit-to-Unit Voice Channel Request
const uint8_t CSBKO_UU_ANS_RSP = 0x05U; // UU ANS RSP - Unit to Unit Answer Response
const uint8_t CSBKO_CTCSBK = 0x07U; // CT CSBK - Channel Timing CSBK
- const uint8_t CSBKO_CALL_ALRT = 0x1FU; // CALL ALRT - Call Alert
+ const uint8_t CSBKO_ALOHA = 0x19U; // ALOHA - Aloha PDUs for the random access protocol
+ const uint8_t CSBKO_RAND = 0x1FU; // (ETSI) RAND - Random Access / (DMRA) CALL ALRT - Call Alert
+ const uint8_t CSBKO_CALL_ALRT = 0x1FU; // (ETSI) RAND - Random Access / (DMRA) CALL ALRT - Call Alert
const uint8_t CSBKO_ACK_RSP = 0x20U; // ACK RSP - Acknowledge Response
- const uint8_t CSBKO_EXT_FNCT = 0x24U; // EXT FNCT - Extended Function
+ const uint8_t CSBKO_EXT_FNCT = 0x24U; // (DMRA) EXT FNCT - Extended Function
const uint8_t CSBKO_NACK_RSP = 0x26U; // NACK RSP - Negative Acknowledgement Response
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_BROADCAST = 0x40U; // BCAST - Announcement PDUs
const uint8_t TALKER_ID_NONE = 0x00U;
const uint8_t TALKER_ID_HEADER = 0x01U;
diff --git a/dmr/SiteData.h b/dmr/SiteData.h
new file mode 100644
index 00000000..92499bee
--- /dev/null
+++ b/dmr/SiteData.h
@@ -0,0 +1,219 @@
+/**
+* Digital Voice Modem - Host Software
+* GPLv2 Open Source. Use is subject to license terms.
+* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+*
+* @package DVM / Host Server
+*
+*/
+//
+// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost)
+// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
+//
+/*
+* Copyright (C) 2021 by Bryan Biedenkapp N2PLL
+*
+* 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(__DMR_SITE_DATA_H__)
+#define __DMR_SITE_DATA_H__
+
+#include "Defines.h"
+#include "dmr/DMRDefines.h"
+
+namespace dmr
+{
+ // ---------------------------------------------------------------------------
+ // Class Declaration
+ // Represents site data for DMR.
+ // ---------------------------------------------------------------------------
+
+ class HOST_SW_API SiteData {
+ public:
+ /// Initializes a new instance of the SiteData class.
+ SiteData() :
+ m_siteModel(SITE_MODEL_SMALL),
+ m_netId(1U),
+ m_siteId(1U),
+ m_parId(3U),
+ m_requireReg(false)
+ {
+ /* stub */
+ }
+ /// Initializes a new instance of the SiteData class.
+ /// DMR site model.
+ /// DMR Network ID.
+ /// DMR Site ID.
+ /// DMR partition ID.
+ ///
+ SiteData(uint8_t siteModel, uint16_t netId, uint16_t siteId, uint8_t parId, bool requireReq) :
+ m_siteModel(siteModel),
+ m_netId(netId),
+ m_siteId(siteId),
+ m_parId(parId),
+ m_requireReg(requireReq)
+ {
+ // siteModel clamping
+ if (siteModel > SITE_MODEL_HUGE)
+ siteModel = SITE_MODEL_SMALL;
+
+ // netId clamping
+ if (netId == 0U) // clamp to 1
+ netId = 1U;
+
+ switch (siteModel) {
+ case SITE_MODEL_TINY:
+ {
+ if (netId > 0x1FFU) // clamp to $1FF
+ netId = 0x1FFU;
+ }
+ break;
+ case SITE_MODEL_SMALL:
+ {
+ if (netId > 0x7FU) // clamp to $7F
+ netId = 0x7FU;
+ }
+ break;
+ case SITE_MODEL_LARGE:
+ {
+ if (netId > 0x1FU) // clamp to $1F
+ netId = 0x1FU;
+ }
+ break;
+ case SITE_MODEL_HUGE:
+ {
+ if (netId > 0x03U) // clamp to $3
+ netId = 0x03U;
+ }
+ break;
+ }
+
+ m_netId = netId;
+
+ // siteId clamping
+ if (siteId == 0U) // clamp to 1
+ siteId = 1U;
+
+ switch (siteModel)
+ {
+ case SITE_MODEL_TINY:
+ {
+ if (siteId > 0x07U) // clamp to $7
+ siteId = 0x07U;
+ }
+ break;
+ case SITE_MODEL_SMALL:
+ {
+ if (siteId > 0x1FU) // clamp to $1F
+ siteId = 0x1FU;
+ }
+ break;
+ case SITE_MODEL_LARGE:
+ {
+ if (siteId > 0xFFU) // clamp to $FF
+ siteId = 0xFFU;
+ }
+ break;
+ case SITE_MODEL_HUGE:
+ {
+ if (siteId > 0x7FFU) // clamp to $7FF
+ siteId = 0x7FFU;
+ }
+ break;
+ }
+
+ m_siteId = siteId;
+
+ // parId clamping
+ if (parId == 0U)
+ parId = 3U;
+ if (parId > 3U)
+ parId = 3U;
+ }
+
+ /// Returns the DMR system identity value.
+ ///
+ ///
+ const uint32_t systemIdentity(bool msb = false)
+ {
+ uint32_t value = m_siteModel;
+
+ switch (m_siteModel)
+ {
+ case SITE_MODEL_TINY:
+ {
+ value = (value << 9) + m_netId;
+ value = (value << 3) + m_siteId;
+ }
+ break;
+ case SITE_MODEL_SMALL:
+ {
+ value = (value << 7) + m_netId;
+ value = (value << 5) + m_siteId;
+ }
+ break;
+ case SITE_MODEL_LARGE:
+ {
+ value = (value << 5) + m_netId;
+ value = (value << 7) + m_siteId;
+ }
+ break;
+ case SITE_MODEL_HUGE:
+ {
+ value = (value << 2) + m_netId;
+ value = (value << 10) + m_siteId;
+ }
+ break;
+ }
+
+ if (!msb) {
+ value = (value << 2) + m_parId;
+ }
+
+ return value & 0xFFFFU;
+ }
+
+ /// Equals operator.
+ ///
+ ///
+ SiteData & operator=(const SiteData & data)
+ {
+ if (this != &data) {
+ m_siteModel = data.m_siteModel;
+
+ m_netId = data.m_netId;
+ m_siteId = data.m_siteId;
+
+ m_requireReg = data.m_requireReg;
+ }
+
+ return *this;
+ }
+
+ public:
+ /// DMR site model type.
+ __READONLY_PROPERTY_PLAIN(uint8_t, siteModel, siteModel);
+ /// DMR site network ID.
+ __READONLY_PROPERTY_PLAIN(uint16_t, netId, netId);
+ /// DMR site ID.
+ __READONLY_PROPERTY_PLAIN(uint16_t, siteId, siteId);
+ /// DMR partition ID.
+ __READONLY_PROPERTY_PLAIN(uint8_t, parId, parId);
+ /// DMR require registration.
+ __READONLY_PROPERTY_PLAIN(bool, requireReg, requireReg);
+ };
+} // namespace dmr
+
+#endif // __DMR_SITE_DATA_H__
diff --git a/dmr/Slot.cpp b/dmr/Slot.cpp
index 2494b417..454bfdd9 100644
--- a/dmr/Slot.cpp
+++ b/dmr/Slot.cpp
@@ -12,7 +12,7 @@
//
/*
* Copyright (C) 2015,2016,2017,2018 Jonathan Naylor, G4KLX
-* Copyright (C) 2017-2020 by Bryan Biedenkapp N2PLL
+* Copyright (C) 2017-2021 by Bryan Biedenkapp N2PLL
*
* 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
@@ -49,6 +49,8 @@ using namespace dmr;
uint32_t Slot::m_colorCode = 0U;
+SiteData Slot::m_siteData = SiteData();
+
bool Slot::m_embeddedLCOnly = false;
bool Slot::m_dumpTAData = true;
@@ -385,6 +387,7 @@ void Slot::clock()
/// Helper to initialize the DMR slot processor.
///
/// DMR access color code.
+/// DMR site data.
///
///
/// Amount of hangtime for a DMR call.
@@ -395,7 +398,7 @@ void Slot::clock()
/// Instance of the TalkgroupIdLookup class.
/// Instance of the CRSSIInterpolator class.
///
-void Slot::init(uint32_t colorCode, bool embeddedLCOnly, bool dumpTAData, uint32_t callHang, modem::Modem* modem,
+void Slot::init(uint32_t colorCode, SiteData siteData, bool embeddedLCOnly, bool dumpTAData, uint32_t callHang, modem::Modem* modem,
network::BaseNetwork* network, bool duplex, lookups::RadioIdLookup* ridLookup, lookups::TalkgroupIdLookup* tidLookup,
lookups::RSSIInterpolator* rssiMapper, uint32_t jitter)
{
@@ -405,6 +408,7 @@ void Slot::init(uint32_t colorCode, bool embeddedLCOnly, bool dumpTAData, uint32
assert(rssiMapper != NULL);
m_colorCode = colorCode;
+ m_siteData = siteData;
m_embeddedLCOnly = embeddedLCOnly;
m_dumpTAData = dumpTAData;
m_modem = modem;
@@ -650,6 +654,50 @@ void Slot::writeRF_Call_Alrt(uint32_t srcId, uint32_t dstId)
writeQueueRF(data);
}
+///
+/// Helper to write a TSCC broadcast packet on the RF interface.
+///
+/// Broadcast announcement type.
+void Slot::writeRF_TSCC_Broadcast(uint8_t anncType)
+{
+ if (m_verbose) {
+ LogMessage(LOG_RF, "DMR Slot %u, DT_CSBK, CSBKO_BROADCAST (Broadcast), anncType = %u",
+ m_slotNo, anncType);
+ }
+
+ uint8_t data[DMR_FRAME_LENGTH_BYTES + 2U];
+ ::memset(data + 2U, 0x00U, DMR_FRAME_LENGTH_BYTES);
+
+ SlotType slotType;
+ slotType.setColorCode(m_colorCode);
+ slotType.setDataType(DT_CSBK);
+
+ lc::CSBK csbk = lc::CSBK();
+ csbk.setVerbose(m_dumpCSBKData);
+ csbk.setCSBKO(CSBKO_BROADCAST);
+ csbk.setFID(FID_ETSI);
+
+ csbk.setAnncType(anncType);
+ csbk.setSiteData(m_siteData);
+
+ // 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_duplex);
+
+ m_rfSeqNo = 0U;
+
+ data[0U] = TAG_DATA;
+ data[1U] = 0x00U;
+
+ if (m_duplex)
+ writeQueueRF(data);
+}
+
///
///
///
@@ -696,7 +744,7 @@ void Slot::setShortLC(uint32_t slotNo, uint32_t id, uint8_t flco, bool voice)
return;
uint8_t lc[5U];
- lc[0U] = 0x01U;
+ lc[0U] = SLCO_ACT;
lc[1U] = 0x00U;
lc[2U] = 0x00U;
lc[3U] = 0x00U;
@@ -742,3 +790,64 @@ void Slot::setShortLC(uint32_t slotNo, uint32_t id, uint8_t flco, bool voice)
m_modem->writeDMRShortLC(sLC);
}
+
+///
+///
+///
+///
+///
+///
+void Slot::setShortLC_TSCC(SiteData siteData, uint16_t counter)
+{
+ assert(m_modem != NULL);
+
+ uint8_t lc[5U];
+ uint32_t lcValue = 0U;
+ lcValue = SLCO_TSCC;
+ lcValue = (lcValue << 2) + siteData.siteModel();
+
+ switch (siteData.siteModel())
+ {
+ case SITE_MODEL_TINY:
+ {
+ lcValue = (lcValue << 9) + siteData.netId();
+ lcValue = (lcValue << 3) + siteData.siteId();
+ }
+ break;
+ case SITE_MODEL_SMALL:
+ {
+ lcValue = (lcValue << 7) + siteData.netId();
+ lcValue = (lcValue << 5) + siteData.siteId();
+ }
+ break;
+ case SITE_MODEL_LARGE:
+ {
+ lcValue = (lcValue << 5) + siteData.netId();
+ lcValue = (lcValue << 7) + siteData.siteId();
+ }
+ break;
+ case SITE_MODEL_HUGE:
+ {
+ lcValue = (lcValue << 2) + siteData.netId();
+ lcValue = (lcValue << 10) + siteData.siteId();
+ }
+ break;
+ }
+
+ lcValue = (lcValue << 1) + ((siteData.requireReg()) ? 1U : 0U);
+ lcValue = (lcValue << 9) + (counter & 0x1FFU);
+
+ // split value into bytes
+ lc[0U] = (uint8_t)((lcValue >> 24) & 0xFFU);
+ lc[1U] = (uint8_t)((lcValue >> 16) & 0xFFU);
+ lc[2U] = (uint8_t)((lcValue >> 8) & 0xFFU);
+ lc[3U] = (uint8_t)((lcValue >> 0) & 0xFFU);
+ lc[4U] = edac::CRC::crc8(lc, 4U);
+
+ uint8_t sLC[9U];
+
+ lc::ShortLC shortLC;
+ shortLC.encode(lc, sLC);
+
+ m_modem->writeDMRShortLC(sLC);
+}
diff --git a/dmr/Slot.h b/dmr/Slot.h
index 69de0c68..88364484 100644
--- a/dmr/Slot.h
+++ b/dmr/Slot.h
@@ -33,6 +33,7 @@
#include "Defines.h"
#include "dmr/Control.h"
+#include "dmr/SiteData.h"
#include "dmr/DataPacket.h"
#include "dmr/VoicePacket.h"
#include "modem/Modem.h"
@@ -80,7 +81,7 @@ namespace dmr
void clock();
/// Helper to initialize the slot processor.
- static void init(uint32_t colorCode, bool embeddedLCOnly, bool dumpTAData, uint32_t callHang, modem::Modem* modem,
+ static void init(uint32_t colorCode, SiteData siteData, bool embeddedLCOnly, bool dumpTAData, uint32_t callHang, modem::Modem* modem,
network::BaseNetwork* network, bool duplex, lookups::RadioIdLookup* ridLookup, lookups::TalkgroupIdLookup* tidLookup,
lookups::RSSIInterpolator* rssiMapper, uint32_t jitter);
@@ -136,6 +137,8 @@ namespace dmr
static uint32_t m_colorCode;
+ static SiteData m_siteData;
+
static bool m_embeddedLCOnly;
static bool m_dumpTAData;
@@ -181,9 +184,13 @@ namespace dmr
void writeRF_Ext_Func(uint32_t func, uint32_t arg, uint32_t dstId);
/// Helper to write a call alert packet on the RF interface.
void writeRF_Call_Alrt(uint32_t srcId, uint32_t dstId);
+ /// Helper to write a TSCC broadcast packet on the RF interface.
+ void writeRF_TSCC_Broadcast(uint8_t anncType);
///
static void setShortLC(uint32_t slotNo, uint32_t id, uint8_t flco = FLCO_GROUP, bool voice = true);
+ ///
+ static void setShortLC_TSCC(SiteData siteData, uint16_t counter);
};
} // namespace dmr
diff --git a/dmr/lc/CSBK.cpp b/dmr/lc/CSBK.cpp
index 7eb29851..e4f92379 100644
--- a/dmr/lc/CSBK.cpp
+++ b/dmr/lc/CSBK.cpp
@@ -12,7 +12,7 @@
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
-* Copyright (C) 2019 by Bryan Biedenkapp N2PLL
+* Copyright (C) 2019-2021 by Bryan Biedenkapp N2PLL
*
* 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
@@ -51,15 +51,20 @@ CSBK::CSBK() :
m_verbose(false),
m_CSBKO(CSBKO_NONE),
m_FID(0x00U),
+ m_lastBlock(true),
m_bsId(0U),
m_GI(false),
m_srcId(0U),
m_dstId(0U),
m_dataContent(false),
m_CBF(0U),
- m_data(NULL)
+ m_data(NULL),
+ m_siteData(),
+ m_siteNetActive(false)
{
m_data = new uint8_t[12U];
+
+ reset();
}
///
@@ -99,72 +104,86 @@ bool CSBK::decode(const uint8_t* bytes)
Utils::dump(2U, "Decoded CSBK", m_data, DMR_LC_HEADER_LENGTH_BYTES);
}
- m_CSBKO = m_data[0U] & 0x3FU;
- m_FID = m_data[1U];
+ m_CSBKO = m_data[0U] & 0x3FU; // CSBKO
+ m_lastBlock = (m_data[0U] & 0x80U) == 0x80U; // Last Block Marker
+ m_FID = m_data[1U]; // Feature ID
switch (m_CSBKO) {
case CSBKO_BSDWNACT:
m_GI = false;
- m_bsId = 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_bsId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U]; // Base Station ID
+ m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; // Source ID
m_dataContent = false;
m_CBF = 0U;
break;
case CSBKO_UU_V_REQ:
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_dstId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U]; // Destination ID
+ m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; // Source ID
m_dataContent = false;
m_CBF = 0U;
break;
case CSBKO_UU_ANS_RSP:
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_dstId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U]; // Destination ID
+ m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; // Source ID
m_dataContent = false;
m_CBF = 0U;
break;
case CSBKO_PRECCSBK:
m_GI = (m_data[2U] & 0x40U) == 0x40U;
- 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_dstId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U]; // Destination ID
+ m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; // Source ID
m_dataContent = (m_data[2U] & 0x80U) == 0x80U;
m_CBF = m_data[3U];
break;
- case CSBKO_CALL_ALRT:
- m_GI = (m_data[2U] & 0x40U) == 0x40U;
- 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 = (m_data[2U] & 0x80U) == 0x80U;
- m_CBF = m_data[3U];
- break;
+ case CSBKO_RAND: // CSBKO_CALL_ALRT when FID == FID_DMRA
+ switch (m_FID)
+ {
+ case FID_DMRA:
+ m_GI = (m_data[2U] & 0x40U) == 0x40U; // Group or Individual
+ m_dstId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U]; // Destination ID
+ m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; // Source ID
+ m_dataContent = (m_data[2U] & 0x80U) == 0x80U; //
+ m_CBF = m_data[3U]; //
+ break;
+
+ /* Tier III */
+ case FID_ETSI:
+ default:
+ m_serviceOptions = m_data[2U]; // Service Options
+ m_targetAddress = (m_data[3U] >> 6 & 0x03U); // Target Address
+ m_dstId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U]; // Destination ID
+ m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; // Source ID
+ break;
+ }
case CSBKO_ACK_RSP:
m_GI = (m_data[2U] & 0x40U) == 0x40U;
- 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 = (m_data[2U] & 0x80U) == 0x80U;
- m_CBF = m_data[3U];
+ m_dstId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U]; // Destination ID
+ m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; // Source ID
+ m_response = m_data[3U]; // Response
+ m_dataContent = false;
break;
case CSBKO_EXT_FNCT:
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 = (m_data[2U] & 0x80U) == 0x80U;
- m_CBF = m_data[3U];
+ m_dstId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U]; // Destination ID
+ m_srcId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; // Source ID
+ m_dataContent = (m_data[2U] & 0x80U) == 0x80U; //
+ m_CBF = m_data[3U]; //
break;
case CSBKO_NACK_RSP:
m_GI = false;
- m_srcId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U];
- m_dstId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U];
+ m_srcId = m_data[4U] << 16 | m_data[5U] << 8 | m_data[6U]; // Source ID
+ m_dstId = m_data[7U] << 16 | m_data[8U] << 8 | m_data[9U]; // Destination ID
+ m_response = m_data[3U]; // Response
m_dataContent = false;
- m_CBF = 0U;
break;
default:
@@ -184,40 +203,146 @@ bool CSBK::decode(const uint8_t* bytes)
/// Encodes a DMR CSBK.
///
///
-void CSBK::encode(uint8_t* bytes) const
+void CSBK::encode(uint8_t* bytes)
{
assert(bytes != NULL);
- m_data[0U] = m_CSBKO;
- m_data[1U] = m_FID;
+ m_data[0U] = m_CSBKO; // CSBKO
+ m_data[0U] |= (m_lastBlock) ? 0x80U : 0x00U; // Last Block Marker
+ m_data[1U] = m_FID; // Feature ID
- if (m_GI) {
- m_data[2U] |= 0x40U;
- }
+ switch (m_CSBKO) {
+ case CSBKO_ACK_RSP:
+ m_data[2U] = (uint8_t)(m_serviceType & 0x3FU); // Service Type
+ m_data[2U] |= 0x80U; // Additional Information Field (always 1)
+ if (m_GI) {
+ m_data[2U] |= 0x40U; // Source Type
+ }
+ m_data[3U] = m_response; // Reason Code
+
+ m_data[4U] = (m_srcId >> 16) & 0xFFU; // Source ID
+ m_data[5U] = (m_srcId >> 8) & 0xFFU;
+ m_data[6U] = (m_srcId >> 0) & 0xFFU;
- if (m_dataContent) {
- m_data[2U] |= 0x80U;
- }
+ m_data[7U] = (m_dstId >> 16) & 0xFFU; // Destination ID
+ m_data[8U] = (m_dstId >> 8) & 0xFFU;
+ m_data[9U] = (m_dstId >> 0) & 0xFFU;
+ break;
+
+ case CSBKO_EXT_FNCT:
+ if (m_GI) {
+ m_data[2U] |= 0x40U; // Group or Individual
+ }
- m_data[3U] = m_CBF;
+ if (m_dataContent) {
+ m_data[2U] |= 0x80U; //
+ }
- if (m_CSBKO == CSBKO_EXT_FNCT) {
- m_data[4U] = (m_srcId >> 16) & 0xFFU;
+ m_data[3U] = m_CBF; //
+
+ m_data[4U] = (m_srcId >> 16) & 0xFFU; // Source ID
m_data[5U] = (m_srcId >> 8) & 0xFFU;
m_data[6U] = (m_srcId >> 0) & 0xFFU;
- m_data[7U] = (m_dstId >> 16) & 0xFFU;
+ m_data[7U] = (m_dstId >> 16) & 0xFFU; // Destination ID
m_data[8U] = (m_dstId >> 8) & 0xFFU;
m_data[9U] = (m_dstId >> 0) & 0xFFU;
- }
- else {
- m_data[4U] = (m_dstId >> 16) & 0xFFU;
+ break;
+
+ case CSBKO_NACK_RSP:
+ m_data[2U] = (uint8_t)(m_serviceType & 0x3FU); // Service Type
+ m_data[2U] |= 0x80U; // Additional Information Field (always 1)
+ if (m_GI) {
+ m_data[2U] |= 0x40U; // Source Type
+ }
+ m_data[3U] = m_response; // Reason Code
+
+ m_data[4U] = (m_srcId >> 16) & 0xFFU; // Source ID
+ m_data[5U] = (m_srcId >> 8) & 0xFFU;
+ m_data[6U] = (m_srcId >> 0) & 0xFFU;
+
+ m_data[7U] = (m_dstId >> 16) & 0xFFU; // Destination ID
+ m_data[8U] = (m_dstId >> 8) & 0xFFU;
+ m_data[9U] = (m_dstId >> 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;
+ m_data[7U] = (m_srcId >> 16) & 0xFFU; // Source ID
m_data[8U] = (m_srcId >> 8) & 0xFFU;
m_data[9U] = (m_srcId >> 0) & 0xFFU;
+ break;
+
+ /* Tier III */
+ case CSBKO_ALOHA:
+ {
+ ulong64_t csbkValue = 0U;
+ csbkValue = (csbkValue << 2) + 0U; // Reserved
+ csbkValue = (csbkValue << 1) + ((m_siteTSSync) ? 1U : 0U); // Site Time Slot Synchronization
+ csbkValue = (csbkValue << 3) + DMR_ALOHA_VER_151; // DMR Spec. Version (1.5.1)
+ csbkValue = (csbkValue << 1) + ((m_siteOffsetTiming) ? 1U : 0U); // Site Timing: Aligned or Offset
+ csbkValue = (csbkValue << 1) + ((m_siteNetActive) ? 1U : 0U); // Site Networked
+ csbkValue = (csbkValue << 5) + (m_alohaMask & 0x1FU); // MS Mask
+ csbkValue = (csbkValue << 2) + 0U; // Service Function
+ csbkValue = (csbkValue << 4) + 0U; //
+ csbkValue = (csbkValue << 1) + ((m_siteData.requireReg()) ? 1U : 0U); // Require Registration
+ csbkValue = (csbkValue << 4) + (m_backoffNo & 0x0FU); // Backoff Number
+ csbkValue = (csbkValue << 16) + m_siteData.systemIdentity(); // Site Identity
+ csbkValue = (csbkValue << 24) + m_srcId; // Source ID
+
+ // 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;
+ }
+ break;
+ case CSBKO_BROADCAST:
+ {
+ ulong64_t csbkValue = 0U;
+ csbkValue = m_anncType; // Announcement Type
+
+ switch (m_anncType)
+ {
+ case BCAST_ANNC_SITE_PARMS:
+ csbkValue = (csbkValue << 14) + m_siteData.systemIdentity(true); // Site Identity (Broadcast Parms 1)
+ csbkValue = (csbkValue << 4) + (m_backoffNo & 0x0FU); // Backoff Number
+ csbkValue = (csbkValue << 16) + m_siteData.systemIdentity(); // Site Identity
+ csbkValue = (csbkValue << 1) + 0U; // Roaming TG Subscription/Attach
+ csbkValue = (csbkValue << 1) + ((m_hibernating) ? 1U : 0U); // TSCC Hibernating
+ csbkValue = (csbkValue << 22) + 0U; // Broadcast Parms 2 (Reserved)
+ break;
+ }
+
+ // 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;
}
m_data[10U] ^= CSBK_CRC_MASK[0U];
@@ -236,3 +361,39 @@ void CSBK::encode(uint8_t* bytes) const
edac::BPTC19696 bptc;
bptc.encode(m_data, bytes);
}
+
+///
+/// Helper to reset data values to defaults.
+///
+void CSBK::reset()
+{
+ m_backoffNo = 1U;
+ m_serviceType = 0U;
+ m_serviceOptions = 0U;
+ m_targetAddress = TGT_ADRS_TGID;
+
+ m_response = 0U;
+
+ /* Broadcast */
+ m_anncType = BCAST_ANNC_SITE_PARMS;
+ m_hibernating = false;
+
+ /* Aloha */
+ m_siteTSSync = false;
+ m_siteOffsetTiming = false;
+}
+
+/** Local Site data */
+/// Sets local configured site data.
+///
+void CSBK::setSiteData(SiteData siteData)
+{
+ m_siteData = siteData;
+}
+
+/// Sets a flag indicating whether or not networking is active.
+///
+void CSBK::setNetActive(bool netActive)
+{
+ m_siteNetActive = netActive;
+}
diff --git a/dmr/lc/CSBK.h b/dmr/lc/CSBK.h
index 1e0f9c78..dc7d136a 100644
--- a/dmr/lc/CSBK.h
+++ b/dmr/lc/CSBK.h
@@ -12,7 +12,7 @@
//
/*
* Copyright (C) 2015,2016 by Jonathan Naylor G4KLX
-* Copyright (C) 2019 by Bryan Biedenkapp N2PLL
+* Copyright (C) 2019-2021 by Bryan Biedenkapp N2PLL
*
* 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
@@ -33,6 +33,7 @@
#include "Defines.h"
#include "dmr/DMRDefines.h"
+#include "dmr/SiteData.h"
namespace dmr
{
@@ -53,7 +54,16 @@ namespace dmr
/// Decodes a DMR CSBK.
bool decode(const uint8_t* bytes);
/// Encodes a DMR CSBK.
- void encode(uint8_t* bytes) const;
+ void encode(uint8_t* bytes);
+
+ /// Helper to reset data values to defaults.
+ void reset();
+
+ /** Local Site data */
+ /// Sets local configured site data.
+ void setSiteData(SiteData siteData);
+ /// Sets a flag indicating whether or not networking is active.
+ void setNetActive(bool netActive);
public:
///
@@ -65,6 +75,9 @@ namespace dmr
/// CSBK feature ID.
__PROPERTY(uint8_t, FID, FID);
+ /// Flag indicating this is the last TSBK in a sequence of TSBKs.
+ __PROPERTY(bool, lastBlock, LastBlock);
+
// For BS Dwn Act
__READONLY_PROPERTY(uint32_t, bsId, BSId);
@@ -83,8 +96,38 @@ namespace dmr
/// Sets the number of blocks to follow.
__PROPERTY(uint8_t, CBF, CBF);
+ // Tier III
+ /// Backoff Number.
+ __PROPERTY(uint8_t, backoffNo, BackoffNo);
+
+ /// Service Type.
+ __PROPERTY(uint8_t, serviceType, serviceType);
+ /// Service type.
+ __PROPERTY(uint8_t, serviceOptions, ServiceOptions);
+ /// Destination/Target address type.
+ __PROPERTY(uint8_t, targetAddress, TargetAddress);
+
+ /// Response type.
+ __PROPERTY(uint8_t, response, Response);
+
+ /// Broadcast Announcment Type.
+ __PROPERTY(uint8_t, anncType, AnncType);
+ /// Broadcast Hibernation Flag.
+ __PROPERTY(bool, hibernating, Hibernating);
+
+ /// Aloha Site Time Slot Synchronization.
+ __PROPERTY(bool, siteTSSync, SiteTSSync);
+ /// Aloha site users offset timing.
+ __PROPERTY(bool, siteOffsetTiming, SiteOffsetTiming);
+ /// Aloha MS mask.
+ __PROPERTY(uint8_t, alohaMask, AlohaMask);
+
private:
uint8_t* m_data;
+
+ /** Local Site data */
+ SiteData m_siteData;
+ bool m_siteNetActive;
};
} // namespace lc
} // namespace dmr
diff --git a/p25/lc/TSBK.cpp b/p25/lc/TSBK.cpp
index 12e7a1e3..ca2679c1 100644
--- a/p25/lc/TSBK.cpp
+++ b/p25/lc/TSBK.cpp
@@ -130,7 +130,7 @@ bool TSBK::decode(const uint8_t* data)
}
m_lco = tsbk[0U] & 0x3F; // LCO
- m_lastBlock = (tsbk[0U] & 0x80U) == 0x80U; // Protect Flag
+ m_lastBlock = (tsbk[0U] & 0x80U) == 0x80U; // Last Block Marker
m_mfId = tsbk[1U]; // Mfg Id.
ulong64_t tsbkValue = 0U;