2016-01-14 18:45:04 +00:00
/*
2025-03-13 18:16:33 +00:00
* Copyright ( C ) 2015 - 2021 , 2023 , 2025 Jonathan Naylor , G4KLX
2016-01-14 18:45:04 +00:00
*
* 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 ; version 2 of the License .
*
* 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 .
*/
2017-01-05 19:15:10 +00:00
# include "DMRAccessControl.h"
2017-05-21 11:52:26 +01:00
# include "DMRDataHeader.h"
2016-02-15 18:45:57 +00:00
# include "DMRSlotType.h"
# include "DMRShortLC.h"
2024-10-04 17:14:36 +01:00
# include "DMRTrellis.h"
2016-02-15 18:45:57 +00:00
# include "DMRFullLC.h"
2016-02-24 07:37:58 +00:00
# include "BPTC19696.h"
2016-01-14 18:45:04 +00:00
# include "DMRSlot.h"
2016-02-15 18:45:57 +00:00
# include "DMRCSBK.h"
2017-01-05 19:15:10 +00:00
# include "DMREMB.h"
2016-01-14 18:45:04 +00:00
# include "Utils.h"
2016-02-15 18:45:57 +00:00
# include "Sync.h"
2016-01-14 18:45:04 +00:00
# include "CRC.h"
# include "Log.h"
2023-07-03 16:21:35 +01:00
# if defined(USE_DMR)
2016-01-14 18:45:04 +00:00
# include <cassert>
# include <ctime>
2016-04-06 17:24:14 +01:00
# include <algorithm>
2017-11-26 17:56:20 +00:00
# include <cstdint>
2018-02-09 14:49:18 -03:00
# include <cmath>
2016-01-14 18:45:04 +00:00
2016-02-15 18:45:57 +00:00
unsigned int CDMRSlot : : m_colorCode = 0U ;
2016-07-10 21:37:11 +01:00
2017-01-16 13:10:23 +01:00
bool CDMRSlot : : m_embeddedLCOnly = false ;
2017-02-27 20:17:55 +00:00
bool CDMRSlot : : m_dumpTAData = true ;
2017-01-16 13:10:23 +01:00
2025-03-14 16:11:04 +00:00
CModem * CDMRSlot : : m_modem = nullptr ;
CDMRNetwork * CDMRSlot : : m_network = nullptr ;
2016-02-15 18:45:57 +00:00
bool CDMRSlot : : m_duplex = true ;
2025-03-14 12:13:11 +00:00
CDMRLookup * CDMRSlot : : m_lookup = nullptr ;
2016-06-16 21:40:05 +01:00
unsigned int CDMRSlot : : m_hangCount = 3U * 17U ;
2025-03-13 18:16:33 +00:00
DMR_OVCM CDMRSlot : : m_ovcm = DMR_OVCM : : OFF ;
2025-03-12 12:55:25 +00:00
bool CDMRSlot : : m_protect = false ;
2016-01-14 18:45:04 +00:00
2025-03-14 12:13:11 +00:00
CRSSIInterpolator * CDMRSlot : : m_rssiMapper = nullptr ;
2016-08-08 21:26:18 +01:00
2018-02-09 14:32:52 -03:00
unsigned int CDMRSlot : : m_jitterTime = 360U ;
unsigned int CDMRSlot : : m_jitterSlots = 6U ;
2018-02-08 20:25:03 +00:00
2025-03-14 12:13:11 +00:00
unsigned char * CDMRSlot : : m_idle = nullptr ;
2016-01-14 18:45:04 +00:00
2016-02-15 18:45:57 +00:00
FLCO CDMRSlot : : m_flco1 ;
unsigned char CDMRSlot : : m_id1 = 0U ;
2025-03-13 18:16:33 +00:00
ACTIVITY_TYPE CDMRSlot : : m_activity1 = ACTIVITY_TYPE : : NONE ;
2016-02-15 18:45:57 +00:00
FLCO CDMRSlot : : m_flco2 ;
unsigned char CDMRSlot : : m_id2 = 0U ;
2025-03-13 18:16:33 +00:00
ACTIVITY_TYPE CDMRSlot : : m_activity2 = ACTIVITY_TYPE : : NONE ;
2016-01-14 18:45:04 +00:00
2017-02-12 18:37:00 +00:00
const unsigned char TALKER_ID_NONE = 0x00U ;
const unsigned char TALKER_ID_HEADER = 0x01U ;
const unsigned char TALKER_ID_BLOCK1 = 0x02U ;
const unsigned char TALKER_ID_BLOCK2 = 0x04U ;
const unsigned char TALKER_ID_BLOCK3 = 0x08U ;
2017-04-02 13:39:28 +01:00
const unsigned int NO_HEADERS_SIMPLEX = 8U ;
const unsigned int NO_HEADERS_DUPLEX = 3U ;
2018-09-23 11:11:44 -03:00
const unsigned int NO_PREAMBLE_CSBK = 15U ;
2017-04-02 13:39:28 +01:00
2023-08-05 16:51:55 +01:00
const unsigned int RSSI_COUNT = 3U ; // 3 * 360ms = 1080ms
const unsigned int BER_COUNT = 18U * 141U ; // 18 * 60ms = 1080ms
2023-06-11 11:38:59 +01:00
2016-01-14 18:45:04 +00:00
CDMRSlot : : CDMRSlot ( unsigned int slotNo , unsigned int timeout ) :
m_slotNo ( slotNo ) ,
2016-05-10 18:54:35 +01:00
m_queue ( 5000U , " DMR Slot " ) ,
2025-03-13 18:16:33 +00:00
m_rfState ( RPT_RF_STATE : : LISTENING ) ,
m_netState ( RPT_NET_STATE : : IDLE ) ,
2017-01-10 19:27:12 +00:00
m_rfEmbeddedLC ( ) ,
2025-03-14 12:13:11 +00:00
m_rfEmbeddedData ( nullptr ) ,
2017-01-10 19:27:12 +00:00
m_rfEmbeddedReadN ( 0U ) ,
m_rfEmbeddedWriteN ( 1U ) ,
2017-02-12 18:37:00 +00:00
m_rfTalkerId ( TALKER_ID_NONE ) ,
2023-06-06 20:33:25 +01:00
m_rfTalkerAlias ( slotNo ) ,
2017-01-10 19:27:12 +00:00
m_netEmbeddedLC ( ) ,
2025-03-14 12:13:11 +00:00
m_netEmbeddedData ( nullptr ) ,
2017-01-10 19:27:12 +00:00
m_netEmbeddedReadN ( 0U ) ,
m_netEmbeddedWriteN ( 1U ) ,
2017-02-12 18:37:00 +00:00
m_netTalkerId ( TALKER_ID_NONE ) ,
2023-06-06 20:33:25 +01:00
m_netTalkerAlias ( slotNo ) ,
2025-03-14 12:13:11 +00:00
m_rfLC ( nullptr ) ,
m_netLC ( nullptr ) ,
2016-02-28 17:40:15 +00:00
m_rfSeqNo ( 0U ) ,
2016-02-28 17:18:13 +00:00
m_rfN ( 0U ) ,
2018-07-21 21:01:55 -04:00
m_lastrfN ( 0U ) ,
2016-02-28 17:18:13 +00:00
m_netN ( 0U ) ,
2016-01-21 18:38:45 +00:00
m_networkWatchdog ( 1000U , 0U , 1500U ) ,
2016-02-25 19:54:18 +00:00
m_rfTimeoutTimer ( 1000U , timeout ) ,
m_netTimeoutTimer ( 1000U , timeout ) ,
2018-02-08 20:25:03 +00:00
m_packetTimer ( 1000U , 0U , 50U ) ,
2016-03-14 20:55:15 +00:00
m_interval ( ) ,
2018-02-08 20:25:03 +00:00
m_elapsed ( ) ,
2016-02-25 19:54:18 +00:00
m_rfFrames ( 0U ) ,
m_netFrames ( 0U ) ,
m_netLost ( 0U ) ,
2016-01-19 20:13:20 +00:00
m_fec ( ) ,
2016-07-21 17:46:37 +01:00
m_rfBits ( 1U ) ,
m_netBits ( 1U ) ,
2016-02-25 19:54:18 +00:00
m_rfErrs ( 0U ) ,
m_netErrs ( 0U ) ,
2017-03-12 19:06:47 +00:00
m_rfTimeout ( false ) ,
m_netTimeout ( false ) ,
2025-03-14 12:13:11 +00:00
m_lastFrame ( nullptr ) ,
2018-02-08 20:25:03 +00:00
m_lastFrameValid ( false ) ,
2023-08-05 16:51:55 +01:00
m_rssi ( 0 ) ,
m_maxRSSI ( 0 ) ,
m_minRSSI ( 0 ) ,
m_aveRSSI ( 0 ) ,
2023-06-11 11:38:59 +01:00
m_rssiCountTotal ( 0U ) ,
2023-08-05 16:51:55 +01:00
m_rssiAccum ( 0 ) ,
2017-01-05 19:15:10 +00:00
m_rssiCount ( 0U ) ,
2023-06-11 11:38:59 +01:00
m_bitErrsAccum ( 0U ) ,
m_bitsCount ( 0U ) ,
2023-07-03 16:21:35 +01:00
m_enabled ( true )
2016-01-14 18:45:04 +00:00
{
2018-02-08 20:25:03 +00:00
m_lastFrame = new unsigned char [ DMR_FRAME_LENGTH_BYTES + 2U ] ;
2017-01-10 19:27:12 +00:00
m_rfEmbeddedData = new CDMREmbeddedData [ 2U ] ;
m_netEmbeddedData = new CDMREmbeddedData [ 2U ] ;
2016-03-14 20:55:15 +00:00
m_interval . start ( ) ;
2016-01-14 18:45:04 +00:00
}
CDMRSlot : : ~ CDMRSlot ( )
{
2017-01-10 19:27:12 +00:00
delete [ ] m_rfEmbeddedData ;
delete [ ] m_netEmbeddedData ;
2018-02-08 20:25:03 +00:00
delete [ ] m_lastFrame ;
2016-01-14 18:45:04 +00:00
}
2017-03-12 19:06:47 +00:00
bool CDMRSlot : : writeModem ( unsigned char * data , unsigned int len )
2016-01-14 18:45:04 +00:00
{
2025-03-14 12:13:11 +00:00
assert ( data ! = nullptr ) ;
2016-03-07 20:21:55 +00:00
2019-01-20 17:09:40 +00:00
if ( ! m_enabled )
return false ;
2025-03-14 16:11:04 +00:00
if ( ( data [ 0U ] = = TAG_LOST ) & & ( m_rfState = = RPT_RF_STATE : : AUDIO ) ) {
2023-01-18 17:58:53 +00:00
unsigned int srcId = m_rfLC - > getSrcId ( ) ;
unsigned int dstId = m_rfLC - > getDstId ( ) ;
std : : string src = m_lookup - > find ( srcId ) ;
std : : string dst = m_lookup - > find ( dstId ) ;
2019-09-29 17:30:25 +01:00
FLCO flco = m_rfLC - > getFLCO ( ) ;
2023-08-05 16:51:55 +01:00
if ( m_rssi ! = 0 ) {
2025-03-14 16:11:04 +00:00
LogMessage ( " DMR Slot %u, RF voice transmission lost from %s to %s%s, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm " , m_slotNo , src . c_str ( ) , flco = = FLCO : : GROUP ? " TG " : " " , dst . c_str ( ) , float ( m_rfFrames ) / 16.667F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , m_minRSSI , m_maxRSSI , m_aveRSSI / m_rssiCountTotal ) ;
2023-08-05 16:51:55 +01:00
writeJSONRF ( " lost " , float ( m_rfFrames ) / 16.667F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , m_minRSSI , m_maxRSSI , m_aveRSSI / int ( m_rssiCountTotal ) ) ;
2023-01-18 17:58:53 +00:00
} else {
2025-03-14 16:11:04 +00:00
LogMessage ( " DMR Slot %u, RF voice transmission lost from %s to %s%s, %.1f seconds, BER: %.1f%% " , m_slotNo , src . c_str ( ) , flco = = FLCO : : GROUP ? " TG " : " " , dst . c_str ( ) , float ( m_rfFrames ) / 16.667F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
2023-06-09 15:46:29 +01:00
writeJSONRF ( " lost " , float ( m_rfFrames ) / 16.667F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
2023-01-18 17:58:53 +00:00
}
2017-03-12 19:25:01 +00:00
if ( m_rfTimeout ) {
writeEndRF ( ) ;
return false ;
} else {
writeEndRF ( true ) ;
return true ;
}
2016-01-20 07:12:28 +00:00
}
2025-03-14 16:11:04 +00:00
if ( ( data [ 0U ] = = TAG_LOST ) & & ( m_rfState = = RPT_RF_STATE : : DATA ) ) {
2023-01-18 17:58:53 +00:00
unsigned int srcId = m_rfLC - > getSrcId ( ) ;
unsigned int dstId = m_rfLC - > getDstId ( ) ;
std : : string src = m_lookup - > find ( srcId ) ;
std : : string dst = m_lookup - > find ( dstId ) ;
2019-09-29 17:30:25 +01:00
FLCO flco = m_rfLC - > getFLCO ( ) ;
2025-03-14 16:11:04 +00:00
LogMessage ( " DMR Slot %u, RF data transmission lost from %s to %s%s " , m_slotNo , src . c_str ( ) , flco = = FLCO : : GROUP ? " TG " : " " , dst . c_str ( ) ) ;
2023-06-09 15:46:29 +01:00
writeJSONRF ( " lost " ) ;
2016-02-25 19:54:18 +00:00
writeEndRF ( ) ;
2017-03-13 08:28:38 +00:00
return false ;
2016-01-14 18:45:04 +00:00
}
2016-02-02 18:17:36 +00:00
if ( data [ 0U ] = = TAG_LOST ) {
2025-03-13 18:16:33 +00:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2017-03-12 19:06:47 +00:00
return false ;
2016-01-14 18:45:04 +00:00
}
2016-08-08 21:26:18 +01:00
// Have we got RSSI bytes on the end?
2016-12-21 19:58:46 +00:00
if ( len = = ( DMR_FRAME_LENGTH_BYTES + 4U ) ) {
2016-08-11 17:28:47 +01:00
uint16_t raw = 0U ;
raw | = ( data [ 35U ] < < 8 ) & 0xFF00U ;
raw | = ( data [ 36U ] < < 0 ) & 0x00FFU ;
2018-09-22 12:53:52 -03:00
2016-12-22 11:10:08 +00:00
// Convert the raw RSSI to dBm
2023-08-05 16:51:55 +01:00
m_rssi = m_rssiMapper - > interpolate ( raw ) ;
if ( m_rssi ! = 0 )
LogDebug ( " DMR Slot %u, raw RSSI: %u, reported RSSI: %d dBm " , m_slotNo , raw , m_rssi ) ;
2016-12-22 11:10:08 +00:00
2023-08-05 16:51:55 +01:00
if ( m_rssi < m_minRSSI )
2017-01-05 19:15:10 +00:00
m_minRSSI = m_rssi ;
2023-08-05 16:51:55 +01:00
if ( m_rssi > m_maxRSSI )
2017-01-05 19:15:10 +00:00
m_maxRSSI = m_rssi ;
m_aveRSSI + = m_rssi ;
2023-06-11 11:38:59 +01:00
m_rssiCountTotal + + ;
2023-06-16 12:58:29 +01:00
m_rssiAccum + = m_rssi ;
2017-01-05 19:15:10 +00:00
m_rssiCount + + ;
2016-08-08 21:26:18 +01:00
}
2016-01-14 18:45:04 +00:00
bool dataSync = ( data [ 1U ] & DMR_SYNC_DATA ) = = DMR_SYNC_DATA ;
bool audioSync = ( data [ 1U ] & DMR_SYNC_AUDIO ) = = DMR_SYNC_AUDIO ;
if ( dataSync ) {
2016-12-21 08:55:30 +00:00
// Get the type from the packet metadata
unsigned char dataType = data [ 1U ] & 0x0FU ;
2016-01-14 18:45:04 +00:00
2016-12-21 08:55:30 +00:00
CDMRSlotType slotType ;
slotType . setColorCode ( m_colorCode ) ;
slotType . setDataType ( dataType ) ;
2016-01-14 18:45:04 +00:00
if ( dataType = = DT_VOICE_LC_HEADER ) {
2025-03-13 18:16:33 +00:00
if ( m_rfState = = RPT_RF_STATE : : AUDIO )
2017-03-12 19:06:47 +00:00
return true ;
2016-01-14 18:45:04 +00:00
2016-02-15 18:45:57 +00:00
CDMRFullLC fullLC ;
2016-04-04 17:40:05 +01:00
CDMRLC * lc = fullLC . decode ( data + 2U , DT_VOICE_LC_HEADER ) ;
2025-03-14 12:13:11 +00:00
if ( lc = = nullptr )
2017-03-12 19:06:47 +00:00
return false ;
2016-01-14 18:45:04 +00:00
2016-11-01 08:00:13 +00:00
unsigned int srcId = lc - > getSrcId ( ) ;
unsigned int dstId = lc - > getDstId ( ) ;
2023-01-18 17:58:53 +00:00
std : : string src = m_lookup - > find ( srcId ) ;
std : : string dst = m_lookup - > find ( dstId ) ;
FLCO flco = lc - > getFLCO ( ) ;
2016-10-10 19:27:08 +01:00
2025-03-12 12:55:25 +00:00
if ( ! m_protect ) {
if ( lc - > getPF ( ) ) {
LogMessage ( " DMR Slot %u, RF user %u rejected " , m_slotNo , srcId ) ;
delete lc ;
2025-03-13 18:16:33 +00:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2025-03-12 12:55:25 +00:00
return false ;
}
}
2017-01-05 19:15:10 +00:00
if ( ! CDMRAccessControl : : validateSrcId ( srcId ) ) {
2016-12-21 19:06:29 +00:00
LogMessage ( " DMR Slot %u, RF user %u rejected " , m_slotNo , srcId ) ;
2025-03-14 16:11:04 +00:00
writeJSONRF ( " rejected " , srcId , src , flco = = FLCO : : GROUP , dstId ) ;
2018-09-22 12:53:52 -03:00
delete lc ;
2025-03-13 18:16:33 +00:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2018-09-22 12:53:52 -03:00
return false ;
2016-06-07 15:34:16 +01:00
}
2016-10-10 19:27:08 +01:00
2025-03-13 18:16:33 +00:00
if ( ! CDMRAccessControl : : validateTGId ( m_slotNo , flco = = FLCO : : GROUP , dstId ) ) {
2017-01-05 19:15:10 +00:00
LogMessage ( " DMR Slot %u, RF user %u rejected for using TG %u " , m_slotNo , srcId , dstId ) ;
2025-03-14 16:11:04 +00:00
writeJSONRF ( " rejected " , srcId , src , flco = = FLCO : : GROUP , dstId ) ;
2017-01-05 19:15:10 +00:00
delete lc ;
2025-03-13 18:16:33 +00:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2017-03-12 19:06:47 +00:00
return false ;
2017-01-05 19:15:10 +00:00
}
2025-03-13 18:16:33 +00:00
if ( ( m_ovcm = = DMR_OVCM : : TX_ON ) | | ( m_ovcm = = DMR_OVCM : : ON ) )
2022-05-23 22:35:14 +01:00
lc - > setOVCM ( true ) ;
2025-03-13 18:16:33 +00:00
else if ( m_ovcm = = DMR_OVCM : : FORCE_OFF )
2022-05-23 22:35:14 +01:00
lc - > setOVCM ( false ) ;
2016-04-04 17:40:05 +01:00
2022-05-23 22:35:14 +01:00
m_rfLC = lc ;
2021-03-07 17:42:55 +00:00
2017-01-10 19:27:12 +00:00
// The standby LC data
m_rfEmbeddedLC . setLC ( * m_rfLC ) ;
m_rfEmbeddedData [ 0U ] . setLC ( * m_rfLC ) ;
m_rfEmbeddedData [ 1U ] . setLC ( * m_rfLC ) ;
2016-02-22 18:12:24 +00:00
// Regenerate the LC data
2016-02-25 19:54:18 +00:00
fullLC . encode ( * m_rfLC , data + 2U , DT_VOICE_LC_HEADER ) ;
2016-02-22 18:12:24 +00:00
2016-01-18 18:36:14 +00:00
// Regenerate the Slot Type
slotType . getData ( data + 2U ) ;
2016-01-14 18:45:04 +00:00
2016-09-05 18:03:23 +01:00
// Convert the Data Sync to be from the BS or MS as needed
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( data + 2U , m_duplex ) ;
2016-01-14 18:45:04 +00:00
2021-08-10 19:54:14 +01:00
data [ 0U ] = TAG_DATA ;
2016-01-18 18:36:14 +00:00
data [ 1U ] = 0x00U ;
2016-01-14 18:45:04 +00:00
2016-02-25 19:54:18 +00:00
m_rfTimeoutTimer . start ( ) ;
2017-03-12 19:06:47 +00:00
m_rfTimeout = false ;
2016-01-14 18:45:04 +00:00
2016-02-25 19:54:18 +00:00
m_rfFrames = 0U ;
2016-02-28 17:40:15 +00:00
m_rfSeqNo = 0U ;
2016-02-25 19:54:18 +00:00
m_rfBits = 1U ;
m_rfErrs = 0U ;
2017-02-12 18:37:00 +00:00
2017-01-10 19:27:12 +00:00
m_rfEmbeddedReadN = 0U ;
m_rfEmbeddedWriteN = 1U ;
2017-02-12 18:37:00 +00:00
m_rfTalkerId = TALKER_ID_NONE ;
2016-01-19 20:13:20 +00:00
2023-06-11 11:38:59 +01:00
m_minRSSI = m_rssi ;
m_maxRSSI = m_rssi ;
m_aveRSSI = m_rssi ;
m_rssiCountTotal = 1U ;
m_rssiAccum = m_rssi ;
2017-01-05 19:15:10 +00:00
m_rssiCount = 1U ;
2023-06-11 11:38:59 +01:00
m_bitsCount = 0U ;
m_bitErrsAccum = 0U ;
2016-02-15 18:01:21 +00:00
if ( m_duplex ) {
2016-06-20 10:01:09 +01:00
m_queue . clear ( ) ;
m_modem - > writeDMRAbort ( m_slotNo ) ;
2017-04-02 13:39:28 +01:00
for ( unsigned int i = 0U ; i < NO_HEADERS_DUPLEX ; i + + )
writeQueueRF ( data ) ;
2016-01-18 18:36:14 +00:00
}
2016-01-14 18:45:04 +00:00
2016-02-28 17:40:15 +00:00
writeNetworkRF ( data , DT_VOICE_LC_HEADER ) ;
2016-02-15 18:01:21 +00:00
2025-03-13 18:16:33 +00:00
m_rfState = RPT_RF_STATE : : AUDIO ;
2016-01-26 18:28:39 +00:00
2025-03-14 16:11:04 +00:00
if ( m_netState = = RPT_NET_STATE : : IDLE ) {
setShortLC ( m_slotNo , dstId , flco , ACTIVITY_TYPE : : VOICE ) ;
2023-06-16 12:58:29 +01:00
writeJSONRSSI ( ) ;
writeJSONBER ( ) ;
2016-02-25 19:54:18 +00:00
}
2016-01-18 18:36:14 +00:00
2025-03-14 16:11:04 +00:00
LogMessage ( " DMR Slot %u, received RF voice header from %s to %s%s " , m_slotNo , src . c_str ( ) , flco = = FLCO : : GROUP ? " TG " : " " , dst . c_str ( ) ) ;
writeJSONRF ( " start " , srcId , src , flco = = FLCO : : GROUP , dstId ) ;
2017-03-12 19:06:47 +00:00
return true ;
2016-01-14 18:45:04 +00:00
} else if ( dataType = = DT_VOICE_PI_HEADER ) {
2025-03-13 18:16:33 +00:00
if ( m_rfState ! = RPT_RF_STATE : : AUDIO )
2017-03-12 19:06:47 +00:00
return false ;
2016-01-14 18:45:04 +00:00
2016-01-18 18:36:14 +00:00
// Regenerate the Slot Type
slotType . getData ( data + 2U ) ;
2016-01-14 18:45:04 +00:00
2016-09-05 18:03:23 +01:00
// Convert the Data Sync to be from the BS or MS as needed
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( data + 2U , m_duplex ) ;
2016-01-14 18:45:04 +00:00
2016-02-25 19:59:09 +00:00
// Regenerate the payload
CBPTC19696 bptc ;
unsigned char payload [ 12U ] ;
bptc . decode ( data + 2U , payload ) ;
bptc . encode ( payload , data + 2U ) ;
2021-08-10 19:54:14 +01:00
data [ 0U ] = TAG_DATA ;
2016-01-18 18:36:14 +00:00
data [ 1U ] = 0x00U ;
2016-01-14 18:45:04 +00:00
2025-03-12 12:55:25 +00:00
if ( m_protect ) {
if ( m_duplex )
writeQueueRF ( data ) ;
2016-02-15 18:01:21 +00:00
2025-03-12 12:55:25 +00:00
writeNetworkRF ( data , DT_VOICE_PI_HEADER ) ;
}
2017-03-12 19:06:47 +00:00
return true ;
2016-01-18 18:36:14 +00:00
} else if ( dataType = = DT_TERMINATOR_WITH_LC ) {
2025-03-13 18:16:33 +00:00
if ( m_rfState ! = RPT_RF_STATE : : AUDIO )
2017-03-12 19:06:47 +00:00
return false ;
2016-01-14 18:45:04 +00:00
2016-02-22 18:12:24 +00:00
// Regenerate the LC data
CDMRFullLC fullLC ;
2016-02-25 19:54:18 +00:00
fullLC . encode ( * m_rfLC , data + 2U , DT_TERMINATOR_WITH_LC ) ;
2016-02-22 18:12:24 +00:00
2016-01-18 18:36:14 +00:00
// Regenerate the Slot Type
slotType . getData ( data + 2U ) ;
2016-01-14 18:45:04 +00:00
2016-09-05 18:03:23 +01:00
// Convert the Data Sync to be from the BS or MS as needed
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( data + 2U , m_duplex ) ;
2016-01-18 18:36:14 +00:00
2017-03-12 19:06:47 +00:00
if ( ! m_rfTimeout ) {
data [ 0U ] = TAG_EOT ;
data [ 1U ] = 0x00U ;
2016-01-14 18:45:04 +00:00
2017-03-12 19:06:47 +00:00
writeNetworkRF ( data , DT_TERMINATOR_WITH_LC ) ;
2016-01-14 18:45:04 +00:00
2017-03-12 19:06:47 +00:00
if ( m_duplex ) {
for ( unsigned int i = 0U ; i < m_hangCount ; i + + )
writeQueueRF ( data ) ;
}
2016-02-15 18:01:21 +00:00
}
2016-01-21 18:38:45 +00:00
2023-01-18 17:58:53 +00:00
unsigned int srcId = m_rfLC - > getSrcId ( ) ;
unsigned int dstId = m_rfLC - > getDstId ( ) ;
std : : string src = m_lookup - > find ( srcId ) ;
std : : string dst = m_lookup - > find ( dstId ) ;
2019-09-29 17:30:25 +01:00
FLCO flco = m_rfLC - > getFLCO ( ) ;
2023-08-05 16:51:55 +01:00
if ( m_rssi ! = 0 ) {
2025-03-14 16:11:04 +00:00
LogMessage ( " DMR Slot %u, received RF end of voice transmission from %s to %s%s, %.1f seconds, BER: %.1f%%, RSSI: %d/%d/%d dBm " , m_slotNo , src . c_str ( ) , flco = = FLCO : : GROUP ? " TG " : " " , dst . c_str ( ) , float ( m_rfFrames ) / 16.667F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , m_minRSSI , m_maxRSSI , m_aveRSSI / int ( m_rssiCountTotal ) ) ;
2023-08-05 16:51:55 +01:00
writeJSONRF ( " end " , float ( m_rfFrames ) / 16.667F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , m_minRSSI , m_maxRSSI , m_aveRSSI / int ( m_rssiCountTotal ) ) ;
2023-01-18 17:58:53 +00:00
} else {
2025-03-14 16:11:04 +00:00
LogMessage ( " DMR Slot %u, received RF end of voice transmission from %s to %s%s, %.1f seconds, BER: %.1f%% " , m_slotNo , src . c_str ( ) , flco = = FLCO : : GROUP ? " TG " : " " , dst . c_str ( ) , float ( m_rfFrames ) / 16.667F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
2023-06-09 15:46:29 +01:00
writeJSONRF ( " end " , float ( m_rfFrames ) / 16.667F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
2023-01-18 17:58:53 +00:00
}
2017-11-15 10:47:56 +00:00
2017-03-12 19:25:01 +00:00
if ( m_rfTimeout ) {
writeEndRF ( ) ;
return false ;
} else {
writeEndRF ( ) ;
return true ;
}
2016-01-18 18:36:14 +00:00
} else if ( dataType = = DT_DATA_HEADER ) {
2025-03-13 18:16:33 +00:00
if ( m_rfState = = RPT_RF_STATE : : DATA )
2017-03-12 19:06:47 +00:00
return true ;
2016-04-26 18:22:47 +01:00
2016-02-22 18:12:24 +00:00
CDMRDataHeader dataHeader ;
bool valid = dataHeader . put ( data + 2U ) ;
2016-04-28 09:58:45 +01:00
if ( ! valid )
2017-03-12 19:06:47 +00:00
return false ;
2016-01-26 18:06:31 +00:00
2016-04-04 17:40:05 +01:00
bool gi = dataHeader . getGI ( ) ;
unsigned int srcId = dataHeader . getSrcId ( ) ;
unsigned int dstId = dataHeader . getDstId ( ) ;
2023-01-18 17:58:53 +00:00
std : : string src = m_lookup - > find ( srcId ) ;
std : : string dst = m_lookup - > find ( dstId ) ;
2016-11-01 08:00:13 +00:00
2017-01-05 19:15:10 +00:00
if ( ! CDMRAccessControl : : validateSrcId ( srcId ) ) {
2016-12-21 19:06:29 +00:00
LogMessage ( " DMR Slot %u, RF user %u rejected " , m_slotNo , srcId ) ;
2023-01-19 10:08:50 +00:00
writeJSONRF ( " rejected " , srcId , src , gi , dstId ) ;
2025-03-14 16:11:04 +00:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2017-03-12 19:06:47 +00:00
return false ;
2016-12-21 19:06:29 +00:00
}
2016-04-04 17:40:05 +01:00
2017-01-05 19:15:10 +00:00
if ( ! CDMRAccessControl : : validateTGId ( m_slotNo , gi , dstId ) ) {
LogMessage ( " DMR Slot %u, RF user %u rejected for using TG %u " , m_slotNo , srcId , dstId ) ;
2023-01-19 10:08:50 +00:00
writeJSONRF ( " rejected " , srcId , src , gi , dstId ) ;
2025-03-14 16:11:04 +00:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2017-03-12 19:06:47 +00:00
return false ;
2017-01-05 19:15:10 +00:00
}
2016-03-15 22:46:36 +00:00
m_rfFrames = dataHeader . getBlocks ( ) ;
2025-03-13 18:16:33 +00:00
m_rfLC = new CDMRLC ( gi ? FLCO : : GROUP : FLCO : : USER_USER , srcId , dstId ) ;
2016-01-26 18:06:31 +00:00
2016-02-22 18:12:24 +00:00
// Regenerate the data header
2016-03-14 17:33:05 +00:00
dataHeader . get ( data + 2U ) ;
2016-02-22 18:12:24 +00:00
2016-01-18 18:36:14 +00:00
// Regenerate the Slot Type
slotType . getData ( data + 2U ) ;
2016-09-05 18:03:23 +01:00
// Convert the Data Sync to be from the BS or MS as needed
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( data + 2U , m_duplex ) ;
2016-01-18 18:36:14 +00:00
2021-08-10 19:54:14 +01:00
data [ 0U ] = m_rfFrames = = 0U ? TAG_EOT : TAG_DATA ;
2016-01-18 18:36:14 +00:00
data [ 1U ] = 0x00U ;
2016-03-11 06:50:06 +00:00
if ( m_duplex )
writeQueueRF ( data ) ;
2016-01-14 18:45:04 +00:00
2016-02-28 17:40:15 +00:00
writeNetworkRF ( data , DT_DATA_HEADER ) ;
2016-01-18 18:36:14 +00:00
2025-03-13 18:16:33 +00:00
m_rfState = RPT_RF_STATE : : DATA ;
2016-01-26 18:06:31 +00:00
2025-03-14 16:11:04 +00:00
if ( m_netState = = RPT_NET_STATE : : IDLE ) {
setShortLC ( m_slotNo , dstId , gi ? FLCO : : GROUP : FLCO : : USER_USER , ACTIVITY_TYPE : : DATA ) ;
2023-06-16 12:58:29 +01:00
writeJSONRSSI ( ) ;
2016-02-25 19:54:18 +00:00
}
2016-01-18 18:36:14 +00:00
2016-04-15 15:24:57 +02:00
LogMessage ( " DMR Slot %u, received RF data header from %s to %s%s, %u blocks " , m_slotNo , src . c_str ( ) , gi ? " TG " : " " , dst . c_str ( ) , m_rfFrames ) ;
2023-01-19 10:08:50 +00:00
writeJSONRF ( " start " , srcId , src , gi , dstId , m_rfFrames ) ;
2016-04-26 18:22:47 +01:00
2017-03-05 19:33:03 +00:00
if ( m_rfFrames = = 0U ) {
2019-09-29 17:30:25 +01:00
LogMessage ( " DMR Slot %u, ended RF data transmission from %s to %s%s " , m_slotNo , src . c_str ( ) , gi ? " TG " : " " , dst . c_str ( ) ) ;
2023-06-09 15:46:29 +01:00
writeJSONRF ( " end " ) ;
2017-03-05 19:33:03 +00:00
writeEndRF ( ) ;
}
2017-03-12 19:06:47 +00:00
return true ;
2016-01-25 21:56:57 +00:00
} else if ( dataType = = DT_CSBK ) {
2016-02-22 18:12:24 +00:00
CDMRCSBK csbk ;
bool valid = csbk . put ( data + 2U ) ;
2016-04-28 09:58:45 +01:00
if ( ! valid )
2017-03-12 19:06:47 +00:00
return false ;
2016-01-25 21:56:57 +00:00
CSBKO csbko = csbk . getCSBKO ( ) ;
2025-03-13 18:16:33 +00:00
if ( csbko = = CSBKO : : BSDWNACT )
2017-03-12 19:06:47 +00:00
return false ;
2016-01-25 21:56:57 +00:00
2019-10-18 19:15:08 +02:00
// set the OVCM bit for the supported csbk
2025-03-13 18:16:33 +00:00
if ( ( m_ovcm = = DMR_OVCM : : TX_ON ) | | ( m_ovcm = = DMR_OVCM : : ON ) )
2022-05-23 22:35:14 +01:00
csbk . setOVCM ( true ) ;
2025-03-13 18:16:33 +00:00
else if ( m_ovcm = = DMR_OVCM : : FORCE_OFF )
2022-05-23 22:35:14 +01:00
csbk . setOVCM ( false ) ;
2021-03-07 17:42:55 +00:00
2016-03-14 21:22:09 +00:00
bool gi = csbk . getGI ( ) ;
unsigned int srcId = csbk . getSrcId ( ) ;
unsigned int dstId = csbk . getDstId ( ) ;
2023-01-18 17:58:53 +00:00
std : : string src = m_lookup - > find ( srcId ) ;
std : : string dst = m_lookup - > find ( dstId ) ;
2016-11-01 08:00:13 +00:00
2016-11-11 07:20:45 +00:00
if ( srcId ! = 0U | | dstId ! = 0U ) {
2017-01-05 19:15:10 +00:00
if ( ! CDMRAccessControl : : validateSrcId ( srcId ) ) {
2016-12-21 19:06:29 +00:00
LogMessage ( " DMR Slot %u, RF user %u rejected " , m_slotNo , srcId ) ;
2023-01-19 10:08:50 +00:00
writeJSONRF ( " rejected " , srcId , src , gi , dstId ) ;
2025-03-14 16:11:04 +00:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2017-03-12 19:06:47 +00:00
return false ;
2016-12-21 19:06:29 +00:00
}
2017-01-05 19:15:10 +00:00
if ( ! CDMRAccessControl : : validateTGId ( m_slotNo , gi , dstId ) ) {
LogMessage ( " DMR Slot %u, RF user %u rejected for using TG %u " , m_slotNo , srcId , dstId ) ;
2023-01-19 10:08:50 +00:00
writeJSONRF ( " rejected " , srcId , src , gi , dstId ) ;
2025-03-14 16:11:04 +00:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2017-03-12 19:06:47 +00:00
return false ;
2017-01-05 19:15:10 +00:00
}
2016-11-11 07:20:45 +00:00
}
2016-09-11 13:01:05 +01:00
2016-03-14 17:33:05 +00:00
// Regenerate the CSBK data
csbk . get ( data + 2U ) ;
2016-02-22 18:12:24 +00:00
2016-03-14 17:33:05 +00:00
// Regenerate the Slot Type
slotType . getData ( data + 2U ) ;
2016-01-26 18:06:31 +00:00
2016-09-05 18:03:23 +01:00
// Convert the Data Sync to be from the BS or MS as needed
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( data + 2U , m_duplex ) ;
2016-01-25 21:56:57 +00:00
2021-08-10 19:54:14 +01:00
data [ 0U ] = TAG_DATA ;
2016-03-14 17:33:05 +00:00
data [ 1U ] = 0x00U ;
2016-01-25 21:56:57 +00:00
2016-03-14 17:33:05 +00:00
if ( m_duplex )
writeQueueRF ( data ) ;
2016-02-15 18:01:21 +00:00
2025-03-13 18:16:33 +00:00
writeNetworkRF ( data , DT_CSBK , gi ? FLCO : : GROUP : FLCO : : USER_USER , srcId , dstId ) ;
2016-01-25 22:36:16 +00:00
2016-03-14 17:33:05 +00:00
switch ( csbko ) {
2025-03-13 18:16:33 +00:00
case CSBKO : : UUVREQ :
2016-04-28 10:32:11 +01:00
LogMessage ( " DMR Slot %u, received RF Unit to Unit Voice Service Request CSBK from %s to %s%s " , m_slotNo , src . c_str ( ) , gi ? " TG " : " " , dst . c_str ( ) ) ;
2023-01-19 10:08:50 +00:00
writeJSONRF ( " csbk " , " Unit to Unit Voice Service Request " , srcId , src , gi , dstId ) ;
2016-03-14 17:33:05 +00:00
break ;
2025-03-13 18:16:33 +00:00
case CSBKO : : UUANSRSP :
2016-04-28 10:32:11 +01:00
LogMessage ( " DMR Slot %u, received RF Unit to Unit Voice Service Answer Response CSBK from %s to %s%s " , m_slotNo , src . c_str ( ) , gi ? " TG " : " " , dst . c_str ( ) ) ;
2023-01-19 10:08:50 +00:00
writeJSONRF ( " csbk " , " Unit to Unit Voice Answer Response " , srcId , src , gi , dstId ) ;
2016-03-14 17:33:05 +00:00
break ;
2025-03-13 18:16:33 +00:00
case CSBKO : : NACKRSP :
2016-04-28 10:32:11 +01:00
LogMessage ( " DMR Slot %u, received RF Negative Acknowledgment Response CSBK from %s to %s%s " , m_slotNo , src . c_str ( ) , gi ? " TG " : " " , dst . c_str ( ) ) ;
2023-01-19 10:08:50 +00:00
writeJSONRF ( " csbk " , " Negative Acknowledgment Response " , srcId , src , gi , dstId ) ;
2016-03-14 17:33:05 +00:00
break ;
2025-03-13 18:16:33 +00:00
case CSBKO : : PRECCSBK :
2016-12-11 16:01:30 +00:00
LogMessage ( " DMR Slot %u, received RF %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 ( ) ) ;
2023-01-19 10:08:50 +00:00
writeJSONRF ( " csbk " , " Preamble " , srcId , src , gi , dstId ) ;
2016-01-25 21:56:57 +00:00
break ;
2025-03-13 18:16:33 +00:00
case CSBKO : : CALL_ALERT :
2020-03-02 12:10:44 +00:00
LogMessage ( " DMR Slot %u, received RF Call Alert CSBK from %s to %s%s " , m_slotNo , src . c_str ( ) , gi ? " TG " : " " , dst . c_str ( ) ) ;
2023-01-19 10:08:50 +00:00
writeJSONRF ( " csbk " , " Call Alert " , srcId , src , gi , dstId ) ;
2020-03-02 12:10:44 +00:00
break ;
2025-03-13 18:16:33 +00:00
case CSBKO : : CALL_ALERT_ACK :
2020-03-02 12:10:44 +00:00
LogMessage ( " DMR Slot %u, received RF Call Alert Ack CSBK from %s to %s%s " , m_slotNo , src . c_str ( ) , gi ? " TG " : " " , dst . c_str ( ) ) ;
2023-01-19 10:08:50 +00:00
writeJSONRF ( " csbk " , " Call Alert Ack " , srcId , src , gi , dstId ) ;
2020-03-02 12:10:44 +00:00
break ;
2025-03-13 18:16:33 +00:00
case CSBKO : : RADIO_CHECK :
2022-03-04 10:52:57 +01:00
LogMessage ( " DMR Slot %u, received RF Radio Check %s CSBK from %s to %s%s " , m_slotNo , /* TBD */ 1 ? " Req " : " Ack " , src . c_str ( ) , gi ? " TG " : " " , dst . c_str ( ) ) ;
2023-01-19 10:08:50 +00:00
writeJSONRF ( " csbk " , " Radio Check " , srcId , src , gi , dstId ) ;
2022-03-04 10:52:57 +01:00
break ;
2025-03-13 18:16:33 +00:00
case CSBKO : : CALL_EMERGENCY :
2022-12-15 14:11:40 +03:00
LogMessage ( " DMR Slot %u, received RF Call Emergency CSBK from %s to %s%s " , m_slotNo , src . c_str ( ) , gi ? " TG " : " " , dst . c_str ( ) ) ;
2023-01-19 10:08:50 +00:00
writeJSONRF ( " csbk " , " Call Emergency " , srcId , src , gi , dstId ) ;
2022-12-15 14:11:40 +03:00
break ;
2016-01-25 21:56:57 +00:00
default :
2016-01-25 22:36:16 +00:00
LogWarning ( " DMR Slot %u, unhandled RF CSBK type - 0x%02X " , m_slotNo , csbko ) ;
2016-01-25 21:56:57 +00:00
break ;
}
2017-03-12 19:06:47 +00:00
2017-10-04 21:56:40 +01:00
// If data preamble, signal its existence
2025-03-14 16:11:04 +00:00
if ( ( m_netState = = RPT_NET_STATE : : IDLE ) & & ( csbko = = CSBKO : : PRECCSBK ) & & csbk . getDataContent ( ) ) {
setShortLC ( m_slotNo , dstId , gi ? FLCO : : GROUP : FLCO : : USER_USER , ACTIVITY_TYPE : : DATA ) ;
2023-06-16 12:58:29 +01:00
writeJSONRSSI ( ) ;
2017-10-04 21:56:40 +01:00
}
2017-10-03 10:03:13 +01:00
2018-09-22 12:53:52 -03:00
return true ;
2025-03-13 18:16:33 +00:00
} else if ( ( dataType = = DT_RATE_12_DATA ) | | ( dataType = = DT_RATE_34_DATA ) | | ( dataType = = DT_RATE_1_DATA ) ) {
if ( ( m_rfState ! = RPT_RF_STATE : : DATA ) | | ( m_rfFrames = = 0U ) )
2017-03-12 19:06:47 +00:00
return false ;
2016-01-26 18:06:31 +00:00
2023-09-18 13:28:42 +02:00
char title [ 80U ] ;
2016-04-19 19:46:16 +01:00
// Regenerate the rate 1/2 payload
2016-03-10 17:18:40 +00:00
if ( dataType = = DT_RATE_12_DATA ) {
CBPTC19696 bptc ;
unsigned char payload [ 12U ] ;
bptc . decode ( data + 2U , payload ) ;
2023-09-18 13:28:42 +02:00
: : sprintf ( title , " DMR Slot %u, Data 1/2 " , m_slotNo ) ;
CUtils : : dump ( 1U , title , payload , 12U ) ;
2016-03-10 17:18:40 +00:00
bptc . encode ( payload , data + 2U ) ;
2016-07-07 14:05:38 +01:00
} else if ( dataType = = DT_RATE_34_DATA ) {
2024-10-04 17:14:36 +01:00
CDMRTrellis trellis ;
2016-07-07 20:23:30 +01:00
unsigned char payload [ 18U ] ;
2024-10-04 17:14:36 +01:00
bool ret = trellis . decode ( data + 2U , payload ) ;
2016-08-08 18:05:14 +01:00
if ( ret ) {
2023-09-18 13:28:42 +02:00
: : sprintf ( title , " DMR Slot %u, Data 3/4 " , m_slotNo ) ;
CUtils : : dump ( 1U , title , payload , 18U ) ;
2024-10-04 17:14:36 +01:00
trellis . encode ( payload , data + 2U ) ;
2016-08-08 18:05:14 +01:00
} else {
2017-02-13 08:50:23 +00:00
LogMessage ( " DMR Slot %u, unfixable RF rate 3/4 data " , m_slotNo ) ;
2016-08-08 18:05:14 +01:00
CUtils : : dump ( 1U , " Data " , data + 2U , DMR_FRAME_LENGTH_BYTES ) ;
}
2023-09-27 14:29:34 +01:00
} else {
2023-09-18 13:28:42 +02:00
: : sprintf ( title , " DMR Slot %u, Data 1/1 " , m_slotNo ) ;
CUtils : : dump ( 1U , title , data + 2U , 24U ) ;
2016-03-10 17:18:40 +00:00
}
2016-01-14 18:45:04 +00:00
// Regenerate the Slot Type
slotType . getData ( data + 2U ) ;
2016-09-05 18:03:23 +01:00
// Convert the Data Sync to be from the BS or MS as needed
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( data + 2U , m_duplex ) ;
2016-01-14 18:45:04 +00:00
2016-02-25 19:54:18 +00:00
m_rfFrames - - ;
2016-01-26 18:06:31 +00:00
2021-08-10 19:54:14 +01:00
data [ 0U ] = m_rfFrames = = 0U ? TAG_EOT : TAG_DATA ;
2016-01-14 18:45:04 +00:00
data [ 1U ] = 0x00U ;
2016-02-15 18:01:21 +00:00
if ( m_duplex )
2016-02-25 19:54:18 +00:00
writeQueueRF ( data ) ;
2016-02-15 18:01:21 +00:00
2016-02-25 19:54:18 +00:00
writeNetworkRF ( data , dataType ) ;
2016-01-26 18:06:31 +00:00
2017-03-05 19:33:03 +00:00
if ( m_rfFrames = = 0U ) {
2019-10-10 13:06:28 +01:00
LogMessage ( " DMR Slot %u, ended RF data transmission " , m_slotNo ) ;
2023-06-09 15:46:29 +01:00
writeJSONRF ( " end " ) ;
2017-03-05 19:33:03 +00:00
writeEndRF ( ) ;
}
2017-03-12 19:06:47 +00:00
return true ;
2016-01-14 18:45:04 +00:00
}
} else if ( audioSync ) {
2025-03-13 18:16:33 +00:00
if ( m_rfState = = RPT_RF_STATE : : AUDIO ) {
2018-07-21 21:01:55 -04:00
m_lastrfN = 0 ;
2016-09-05 18:03:23 +01:00
// Convert the Audio Sync to be from the BS or MS as needed
2016-09-06 08:06:19 +01:00
CSync : : addDMRAudioSync ( data + 2U , m_duplex ) ;
2016-01-14 18:45:04 +00:00
2016-03-23 17:30:06 +00:00
unsigned int errors = 0U ;
2016-02-25 19:54:18 +00:00
unsigned char fid = m_rfLC - > getFID ( ) ;
2016-02-18 18:18:37 +00:00
if ( fid = = FID_ETSI | | fid = = FID_DMRA ) {
2016-03-23 17:30:06 +00:00
errors = m_fec . regenerateDMR ( data + 2U ) ;
2017-02-12 18:37:00 +00:00
LogDebug ( " DMR Slot %u, audio sequence no. 0, errs: %u/141 (%.1f%%) " , m_slotNo , errors , float ( errors ) / 1.41F ) ;
2023-06-11 11:38:59 +01:00
m_rfErrs + = errors ;
m_bitErrsAccum + = errors ;
2023-06-16 12:58:29 +01:00
writeJSONBER ( ) ;
2016-02-18 18:18:37 +00:00
}
2023-06-11 11:38:59 +01:00
m_bitsCount + = 141U ;
m_rfBits + = 141U ;
2016-04-26 19:10:45 +01:00
m_rfFrames + + ;
2017-01-10 19:27:12 +00:00
m_rfEmbeddedReadN = ( m_rfEmbeddedReadN + 1U ) % 2U ;
m_rfEmbeddedWriteN = ( m_rfEmbeddedWriteN + 1U ) % 2U ;
m_rfEmbeddedData [ m_rfEmbeddedWriteN ] . reset ( ) ;
2017-01-05 19:00:01 +00:00
2023-06-16 12:58:29 +01:00
writeJSONRSSI ( ) ;
2017-03-12 19:25:01 +00:00
2017-03-12 19:06:47 +00:00
if ( ! m_rfTimeout ) {
2021-08-10 19:54:14 +01:00
data [ 0U ] = TAG_DATA ;
2017-03-12 19:06:47 +00:00
data [ 1U ] = 0x00U ;
2016-02-15 18:01:21 +00:00
2017-03-12 19:06:47 +00:00
if ( m_duplex )
writeQueueRF ( data ) ;
writeNetworkRF ( data , DT_VOICE_SYNC , errors ) ;
2017-01-05 19:44:52 +00:00
2017-03-12 19:25:01 +00:00
return true ;
}
2017-03-12 19:06:47 +00:00
2017-03-12 19:25:01 +00:00
return false ;
2025-03-13 18:16:33 +00:00
} else if ( m_rfState = = RPT_RF_STATE : : LISTENING ) {
2017-01-05 19:00:01 +00:00
m_rfEmbeddedLC . reset ( ) ;
2025-03-13 18:16:33 +00:00
m_rfState = RPT_RF_STATE : : LATE_ENTRY ;
2017-03-12 19:06:47 +00:00
return false ;
2016-01-14 18:45:04 +00:00
}
} else {
2025-03-13 18:16:33 +00:00
if ( m_rfState = = RPT_RF_STATE : : AUDIO ) {
2016-02-29 06:51:34 +00:00
m_rfN = data [ 1U ] & 0x0FU ;
2018-02-08 20:34:18 +00:00
if ( m_rfN > 5U )
2018-02-04 14:46:09 -03:00
return false ;
2018-07-21 21:01:55 -04:00
if ( m_rfN = = m_lastrfN )
return false ;
if ( m_rfN ! = ( m_lastrfN + 1U ) )
return false ;
m_lastrfN = m_rfN ;
2018-02-04 14:46:09 -03:00
2016-03-23 17:30:06 +00:00
unsigned int errors = 0U ;
2016-02-25 19:54:18 +00:00
unsigned char fid = m_rfLC - > getFID ( ) ;
2016-02-18 18:18:37 +00:00
if ( fid = = FID_ETSI | | fid = = FID_DMRA ) {
2016-03-23 17:30:06 +00:00
errors = m_fec . regenerateDMR ( data + 2U ) ;
2017-02-12 18:37:00 +00:00
LogDebug ( " DMR Slot %u, audio sequence no. %u, errs: %u/141 (%.1f%%) " , m_slotNo , m_rfN , errors , float ( errors ) / 1.41F ) ;
2023-06-11 11:38:59 +01:00
m_rfErrs + = errors ;
m_bitErrsAccum + = errors ;
2023-06-16 12:58:29 +01:00
writeJSONBER ( ) ;
2016-02-18 18:18:37 +00:00
}
2023-06-11 11:38:59 +01:00
m_bitsCount + = 141U ;
m_rfBits + = 141U ;
2016-04-26 19:10:45 +01:00
m_rfFrames + + ;
2017-01-10 19:27:12 +00:00
// Get the LCSS from the EMB
CDMREMB emb ;
emb . putData ( data + 2U ) ;
unsigned char lcss = emb . getLCSS ( ) ;
// Dump any interesting Embedded Data
bool ret = m_rfEmbeddedData [ m_rfEmbeddedWriteN ] . addData ( data + 2U , lcss ) ;
if ( ret ) {
FLCO flco = m_rfEmbeddedData [ m_rfEmbeddedWriteN ] . getFLCO ( ) ;
2017-10-24 21:55:58 +01:00
2017-01-10 19:27:12 +00:00
unsigned char data [ 9U ] ;
m_rfEmbeddedData [ m_rfEmbeddedWriteN ] . getRawData ( data ) ;
char text [ 80U ] ;
switch ( flco ) {
2025-03-13 18:16:33 +00:00
case FLCO : : GROUP :
case FLCO : : USER_USER :
2017-01-10 19:27:12 +00:00
// ::sprintf(text, "DMR Slot %u, Embedded LC", m_slotNo);
// CUtils::dump(1U, text, data, 9U);
break ;
2017-05-18 12:58:56 +01:00
2025-03-14 16:11:04 +00:00
case FLCO : : GPS_INFO :
2017-02-27 20:17:55 +00:00
if ( m_dumpTAData ) {
: : sprintf ( text , " DMR Slot %u, Embedded GPS Info " , m_slotNo ) ;
2023-06-06 20:33:25 +01:00
CUtils : : dump ( 1U , text , data , 9U ) ;
2017-11-26 17:56:20 +00:00
logGPSPosition ( data ) ;
2017-02-27 20:17:55 +00:00
}
2025-03-14 12:13:11 +00:00
if ( m_network ! = nullptr )
2018-10-30 07:53:11 +00:00
m_network - > writeRadioPosition ( m_rfLC - > getSrcId ( ) , data ) ;
2017-01-10 19:27:12 +00:00
break ;
2017-05-18 12:58:56 +01:00
2025-03-13 18:16:33 +00:00
case FLCO : : TALKER_ALIAS_HEADER :
2025-03-14 12:13:11 +00:00
if ( m_network ! = nullptr )
2017-05-18 12:58:56 +01:00
m_network - > writeTalkerAlias ( m_rfLC - > getSrcId ( ) , 0U , data ) ;
2017-02-12 18:37:00 +00:00
if ( ! ( m_rfTalkerId & TALKER_ID_HEADER ) ) {
2017-11-15 10:47:56 +00:00
if ( m_rfTalkerId = = TALKER_ID_NONE )
2018-12-08 22:09:59 +08:00
m_rfTalkerAlias . reset ( ) ;
2023-06-11 11:38:59 +01:00
bool complete = m_rfTalkerAlias . add ( 0U , data + 2U , 7U ) ;
2023-06-16 17:18:43 +01:00
if ( complete )
2023-06-11 11:38:59 +01:00
writeJSONText ( m_rfTalkerAlias . get ( ) ) ;
2017-11-15 10:47:56 +00:00
2017-02-27 20:17:55 +00:00
if ( m_dumpTAData ) {
: : sprintf ( text , " DMR Slot %u, Embedded Talker Alias Header " , m_slotNo ) ;
2023-06-06 20:33:25 +01:00
CUtils : : dump ( 1U , text , data , 9U ) ;
2017-02-27 20:17:55 +00:00
}
2017-02-28 08:27:56 +00:00
m_rfTalkerId | = TALKER_ID_HEADER ;
2017-02-12 18:37:00 +00:00
}
2017-01-10 19:27:12 +00:00
break ;
2017-05-18 12:58:56 +01:00
2025-03-13 18:16:33 +00:00
case FLCO : : TALKER_ALIAS_BLOCK1 :
2025-03-14 12:13:11 +00:00
if ( m_network ! = nullptr )
2017-05-18 12:58:56 +01:00
m_network - > writeTalkerAlias ( m_rfLC - > getSrcId ( ) , 1U , data ) ;
2017-02-12 18:37:00 +00:00
if ( ! ( m_rfTalkerId & TALKER_ID_BLOCK1 ) ) {
2017-11-15 10:47:56 +00:00
if ( m_rfTalkerId = = TALKER_ID_NONE )
2018-12-08 22:09:59 +08:00
m_rfTalkerAlias . reset ( ) ;
2023-06-11 11:38:59 +01:00
bool complete = m_rfTalkerAlias . add ( 1U , data + 2U , 7U ) ;
2023-06-16 17:18:43 +01:00
if ( complete )
2023-06-11 11:38:59 +01:00
writeJSONText ( m_rfTalkerAlias . get ( ) ) ;
2017-11-15 10:47:56 +00:00
2017-02-27 20:17:55 +00:00
if ( m_dumpTAData ) {
: : sprintf ( text , " DMR Slot %u, Embedded Talker Alias Block 1 " , m_slotNo ) ;
2023-06-06 20:33:25 +01:00
CUtils : : dump ( 1U , text , data , 9U ) ;
2017-02-27 20:17:55 +00:00
}
2017-02-28 08:27:56 +00:00
m_rfTalkerId | = TALKER_ID_BLOCK1 ;
2017-02-12 18:37:00 +00:00
}
2017-01-10 19:27:12 +00:00
break ;
2017-05-18 12:58:56 +01:00
2025-03-13 18:16:33 +00:00
case FLCO : : TALKER_ALIAS_BLOCK2 :
2025-03-14 12:13:11 +00:00
if ( m_network ! = nullptr )
2017-05-18 12:58:56 +01:00
m_network - > writeTalkerAlias ( m_rfLC - > getSrcId ( ) , 2U , data ) ;
2017-02-12 18:37:00 +00:00
if ( ! ( m_rfTalkerId & TALKER_ID_BLOCK2 ) ) {
2017-11-15 10:47:56 +00:00
if ( m_rfTalkerId = = TALKER_ID_NONE )
2018-12-08 22:09:59 +08:00
m_rfTalkerAlias . reset ( ) ;
2023-06-11 11:38:59 +01:00
bool complete = m_rfTalkerAlias . add ( 2U , data + 2U , 7U ) ;
2023-06-16 17:18:43 +01:00
if ( complete )
2023-06-11 11:38:59 +01:00
writeJSONText ( m_rfTalkerAlias . get ( ) ) ;
2017-11-15 10:47:56 +00:00
2017-02-27 20:17:55 +00:00
if ( m_dumpTAData ) {
: : sprintf ( text , " DMR Slot %u, Embedded Talker Alias Block 2 " , m_slotNo ) ;
2023-06-06 20:33:25 +01:00
CUtils : : dump ( 1U , text , data , 9U ) ;
2017-02-27 20:17:55 +00:00
}
2017-02-28 08:27:56 +00:00
2017-10-24 21:55:58 +01:00
m_rfTalkerId | = TALKER_ID_BLOCK2 ;
2017-02-12 18:37:00 +00:00
}
2017-01-10 19:27:12 +00:00
break ;
2017-05-18 12:58:56 +01:00
2025-03-13 18:16:33 +00:00
case FLCO : : TALKER_ALIAS_BLOCK3 :
2025-03-14 12:13:11 +00:00
if ( m_network ! = nullptr )
2017-05-18 12:58:56 +01:00
m_network - > writeTalkerAlias ( m_rfLC - > getSrcId ( ) , 3U , data ) ;
2017-02-12 18:37:00 +00:00
if ( ! ( m_rfTalkerId & TALKER_ID_BLOCK3 ) ) {
2017-11-15 10:47:56 +00:00
if ( m_rfTalkerId = = TALKER_ID_NONE )
2018-12-08 22:09:59 +08:00
m_rfTalkerAlias . reset ( ) ;
2023-06-11 11:38:59 +01:00
bool complete = m_rfTalkerAlias . add ( 3U , data + 2U , 7U ) ;
2023-06-16 17:18:43 +01:00
if ( complete )
2023-06-11 11:38:59 +01:00
writeJSONText ( m_rfTalkerAlias . get ( ) ) ;
2017-11-15 10:47:56 +00:00
2017-02-27 20:17:55 +00:00
if ( m_dumpTAData ) {
: : sprintf ( text , " DMR Slot %u, Embedded Talker Alias Block 3 " , m_slotNo ) ;
2023-06-06 20:33:25 +01:00
CUtils : : dump ( 1U , text , data , 9U ) ;
2017-02-27 20:17:55 +00:00
}
2017-02-28 08:27:56 +00:00
m_rfTalkerId | = TALKER_ID_BLOCK3 ;
2017-02-12 18:37:00 +00:00
}
2017-01-10 19:27:12 +00:00
break ;
2017-05-18 12:58:56 +01:00
2017-01-10 19:27:12 +00:00
default :
: : sprintf ( text , " DMR Slot %u, Unknown Embedded Data " , m_slotNo ) ;
CUtils : : dump ( 1U , text , data , 9U ) ;
break ;
}
}
// Regenerate the previous super blocks Embedded Data or substitude the LC for it
if ( m_rfEmbeddedData [ m_rfEmbeddedReadN ] . isValid ( ) )
lcss = m_rfEmbeddedData [ m_rfEmbeddedReadN ] . getData ( data + 2U , m_rfN ) ;
else
lcss = m_rfEmbeddedLC . getData ( data + 2U , m_rfN ) ;
// Regenerate the EMB
emb . setColorCode ( m_colorCode ) ;
emb . setLCSS ( lcss ) ;
emb . getData ( data + 2U ) ;
2017-03-12 19:25:01 +00:00
if ( ! m_rfTimeout ) {
2021-08-10 19:54:14 +01:00
data [ 0U ] = TAG_DATA ;
2017-03-12 19:25:01 +00:00
data [ 1U ] = 0x00U ;
2016-01-18 18:36:14 +00:00
2017-03-12 19:06:47 +00:00
writeNetworkRF ( data , DT_VOICE , errors ) ;
2017-01-16 13:10:23 +01:00
2017-03-12 19:25:01 +00:00
if ( m_embeddedLCOnly ) {
// Only send the previously received LC
lcss = m_rfEmbeddedLC . getData ( data + 2U , m_rfN ) ;
2017-01-16 13:10:23 +01:00
2017-03-12 19:25:01 +00:00
// Regenerate the EMB
emb . setColorCode ( m_colorCode ) ;
emb . setLCSS ( lcss ) ;
emb . getData ( data + 2U ) ;
}
2017-01-16 13:10:23 +01:00
2017-03-12 19:06:47 +00:00
if ( m_duplex )
writeQueueRF ( data ) ;
2017-03-12 19:25:01 +00:00
return true ;
2017-03-12 19:06:47 +00:00
}
2017-03-12 19:25:01 +00:00
return false ;
2025-03-13 18:16:33 +00:00
} else if ( m_rfState = = RPT_RF_STATE : : LATE_ENTRY ) {
2016-02-23 21:01:14 +00:00
CDMREMB emb ;
emb . putData ( data + 2U ) ;
2016-01-14 18:45:04 +00:00
// If we haven't received an LC yet, then be strict on the color code
unsigned char colorCode = emb . getColorCode ( ) ;
if ( colorCode ! = m_colorCode )
2017-03-12 19:06:47 +00:00
return false ;
2016-01-14 18:45:04 +00:00
2017-01-05 19:00:01 +00:00
m_rfEmbeddedLC . addData ( data + 2U , emb . getLCSS ( ) ) ;
CDMRLC * lc = m_rfEmbeddedLC . getLC ( ) ;
2025-03-14 12:13:11 +00:00
if ( lc ! = nullptr ) {
2016-11-01 08:00:13 +00:00
unsigned int srcId = lc - > getSrcId ( ) ;
unsigned int dstId = lc - > getDstId ( ) ;
2023-01-18 17:58:53 +00:00
std : : string src = m_lookup - > find ( srcId ) ;
std : : string dst = m_lookup - > find ( dstId ) ;
FLCO flco = lc - > getFLCO ( ) ;
2016-10-10 19:27:08 +01:00
2025-03-12 12:55:25 +00:00
if ( ! m_protect ) {
if ( lc - > getPF ( ) ) {
LogMessage ( " DMR Slot %u, RF user %u rejected " , m_slotNo , srcId ) ;
delete lc ;
2025-03-13 18:16:33 +00:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2025-03-12 12:55:25 +00:00
return false ;
}
}
2017-01-05 19:15:10 +00:00
if ( ! CDMRAccessControl : : validateSrcId ( srcId ) ) {
2016-12-21 19:06:29 +00:00
LogMessage ( " DMR Slot %u, RF user %u rejected " , m_slotNo , srcId ) ;
2025-03-14 16:11:04 +00:00
writeJSONRF ( " rejected " , srcId , src , flco = = FLCO : : GROUP , dstId ) ;
2016-12-21 19:06:29 +00:00
delete lc ;
2025-03-13 18:16:33 +00:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2018-09-22 12:53:52 -03:00
return false ;
2016-06-16 21:40:05 +01:00
}
2016-10-10 19:27:08 +01:00
2025-03-13 18:16:33 +00:00
if ( ! CDMRAccessControl : : validateTGId ( m_slotNo , flco = = FLCO : : GROUP , dstId ) ) {
2017-01-05 19:15:10 +00:00
LogMessage ( " DMR Slot %u, RF user %u rejected for using TG %u " , m_slotNo , srcId , dstId ) ;
2025-03-14 16:11:04 +00:00
writeJSONRF ( " rejected " , srcId , src , flco = = FLCO : : GROUP , dstId ) ;
2017-01-05 19:15:10 +00:00
delete lc ;
2025-03-13 18:16:33 +00:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2017-03-12 19:06:47 +00:00
return false ;
2017-01-05 19:15:10 +00:00
}
2025-03-13 18:16:33 +00:00
if ( ( m_ovcm = = DMR_OVCM : : TX_ON ) | | ( m_ovcm = = DMR_OVCM : : ON ) )
2022-05-23 22:35:14 +01:00
lc - > setOVCM ( true ) ;
2025-03-13 18:16:33 +00:00
else if ( m_ovcm = = DMR_OVCM : : FORCE_OFF )
2022-05-23 22:35:14 +01:00
lc - > setOVCM ( false ) ;
2016-04-04 17:40:05 +01:00
2022-05-23 22:35:14 +01:00
m_rfLC = lc ;
2021-03-07 17:42:55 +00:00
2017-01-10 19:27:12 +00:00
// The standby LC data
m_rfEmbeddedLC . setLC ( * m_rfLC ) ;
m_rfEmbeddedData [ 0U ] . setLC ( * m_rfLC ) ;
m_rfEmbeddedData [ 1U ] . setLC ( * m_rfLC ) ;
2016-01-14 18:45:04 +00:00
// Create a dummy start frame to replace the received frame
unsigned char start [ DMR_FRAME_LENGTH_BYTES + 2U ] ;
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( start + 2U , m_duplex ) ;
2016-01-14 18:45:04 +00:00
2016-02-15 18:45:57 +00:00
CDMRFullLC fullLC ;
2016-02-25 19:54:18 +00:00
fullLC . encode ( * m_rfLC , start + 2U , DT_VOICE_LC_HEADER ) ;
2016-01-14 18:45:04 +00:00
2016-02-15 18:45:57 +00:00
CDMRSlotType slotType ;
2016-01-14 18:45:04 +00:00
slotType . setColorCode ( m_colorCode ) ;
slotType . setDataType ( DT_VOICE_LC_HEADER ) ;
slotType . getData ( start + 2U ) ;
2021-08-10 19:54:14 +01:00
start [ 0U ] = TAG_DATA ;
2016-01-14 18:45:04 +00:00
start [ 1U ] = 0x00U ;
2016-02-25 19:54:18 +00:00
m_rfTimeoutTimer . start ( ) ;
2017-03-12 19:06:47 +00:00
m_rfTimeout = false ;
2016-01-14 18:45:04 +00:00
2016-02-25 19:54:18 +00:00
m_rfFrames = 0U ;
2016-02-28 17:40:15 +00:00
m_rfSeqNo = 0U ;
m_rfBits = 1U ;
m_rfErrs = 0U ;
2017-02-12 18:37:00 +00:00
2017-01-10 19:27:12 +00:00
m_rfEmbeddedReadN = 0U ;
m_rfEmbeddedWriteN = 1U ;
2017-02-12 18:37:00 +00:00
m_rfTalkerId = TALKER_ID_NONE ;
2016-01-19 20:13:20 +00:00
2023-06-11 11:38:59 +01:00
m_minRSSI = m_rssi ;
m_maxRSSI = m_rssi ;
m_aveRSSI = m_rssi ;
m_rssiCountTotal = 1U ;
m_rssiAccum = m_rssi ;
2017-01-05 19:15:10 +00:00
m_rssiCount = 1U ;
2023-06-11 11:38:59 +01:00
m_bitErrsAccum = 0U ;
m_bitsCount = 0U ;
2016-02-15 18:01:21 +00:00
if ( m_duplex ) {
2016-06-20 10:01:09 +01:00
m_queue . clear ( ) ;
m_modem - > writeDMRAbort ( m_slotNo ) ;
2017-04-02 13:39:28 +01:00
for ( unsigned int i = 0U ; i < NO_HEADERS_DUPLEX ; i + + )
writeQueueRF ( start ) ;
2016-01-14 18:45:04 +00:00
}
2016-02-28 17:40:15 +00:00
writeNetworkRF ( start , DT_VOICE_LC_HEADER ) ;
2016-02-15 18:01:21 +00:00
2016-02-29 06:51:34 +00:00
m_rfN = data [ 1U ] & 0x0FU ;
2018-02-08 20:34:18 +00:00
if ( m_rfN > 5U )
2018-02-04 14:46:09 -03:00
return false ;
2018-07-21 21:01:55 -04:00
if ( m_rfN = = m_lastrfN )
return false ;
m_lastrfN = m_rfN ;
2018-02-04 14:46:09 -03:00
2016-01-19 17:43:33 +00:00
// Regenerate the EMB
emb . getData ( data + 2U ) ;
2016-01-14 18:45:04 +00:00
// Send the original audio frame out
2016-03-23 17:30:06 +00:00
unsigned int errors = 0U ;
2016-02-25 19:54:18 +00:00
unsigned char fid = m_rfLC - > getFID ( ) ;
2016-02-18 18:18:37 +00:00
if ( fid = = FID_ETSI | | fid = = FID_DMRA ) {
2016-03-23 17:30:06 +00:00
errors = m_fec . regenerateDMR ( data + 2U ) ;
2017-02-12 18:37:00 +00:00
LogDebug ( " DMR Slot %u, audio sequence no. %u, errs: %u/141 (%.1f%%) " , m_slotNo , m_rfN , errors , float ( errors ) / 1.41F ) ;
2023-06-11 11:38:59 +01:00
m_bitErrsAccum + = errors ;
m_rfErrs + = errors ;
2016-02-18 18:18:37 +00:00
}
2023-06-11 11:38:59 +01:00
m_bitsCount + = 141U ;
m_rfBits + = 141U ;
2016-04-26 19:10:45 +01:00
m_rfFrames + + ;
2021-08-10 19:54:14 +01:00
data [ 0U ] = TAG_DATA ;
2016-01-14 18:45:04 +00:00
data [ 1U ] = 0x00U ;
2016-01-18 18:36:14 +00:00
2016-02-15 18:01:21 +00:00
if ( m_duplex )
2016-02-25 19:54:18 +00:00
writeQueueRF ( data ) ;
2016-01-14 18:45:04 +00:00
2016-03-23 17:30:06 +00:00
writeNetworkRF ( data , DT_VOICE , errors ) ;
2016-01-14 18:45:04 +00:00
2025-03-13 18:16:33 +00:00
m_rfState = RPT_RF_STATE : : AUDIO ;
2016-01-14 18:45:04 +00:00
2025-03-14 16:11:04 +00:00
if ( m_netState = = RPT_NET_STATE : : IDLE ) {
setShortLC ( m_slotNo , dstId , flco , ACTIVITY_TYPE : : VOICE ) ;
2023-06-16 12:58:29 +01:00
writeJSONRSSI ( ) ;
writeJSONBER ( ) ;
2016-02-25 19:54:18 +00:00
}
2016-01-14 18:45:04 +00:00
2025-03-14 16:11:04 +00:00
LogMessage ( " DMR Slot %u, received RF late entry from %s to %s%s " , m_slotNo , src . c_str ( ) , flco = = FLCO : : GROUP ? " TG " : " " , dst . c_str ( ) ) ;
writeJSONRF ( " late_entry " , srcId , src , flco = = FLCO : : GROUP , dstId ) ;
2017-03-12 19:06:47 +00:00
return true ;
2016-01-14 18:45:04 +00:00
}
}
}
2017-03-12 19:06:47 +00:00
return false ;
2016-01-14 18:45:04 +00:00
}
unsigned int CDMRSlot : : readModem ( unsigned char * data )
{
2025-03-14 12:13:11 +00:00
assert ( data ! = nullptr ) ;
2016-03-07 20:21:55 +00:00
2016-03-07 18:08:50 +00:00
if ( m_queue . isEmpty ( ) )
2016-01-14 18:45:04 +00:00
return 0U ;
unsigned char len = 0U ;
2016-03-07 18:08:50 +00:00
m_queue . getData ( & len , 1U ) ;
2016-01-14 18:45:04 +00:00
2016-03-07 18:08:50 +00:00
m_queue . getData ( data , len ) ;
2016-01-14 18:45:04 +00:00
return len ;
}
2016-02-25 19:54:18 +00:00
void CDMRSlot : : writeEndRF ( bool writeEnd )
2016-01-14 18:45:04 +00:00
{
2025-03-13 18:16:33 +00:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2016-01-14 18:45:04 +00:00
2025-03-14 16:11:04 +00:00
if ( m_netState = = RPT_NET_STATE : : IDLE )
2016-02-25 19:54:18 +00:00
setShortLC ( m_slotNo , 0U ) ;
2016-01-14 18:45:04 +00:00
2016-02-07 20:12:24 +00:00
if ( writeEnd ) {
2025-03-13 18:16:33 +00:00
if ( ( m_netState = = RPT_NET_STATE : : IDLE ) & & m_duplex & & ! m_rfTimeout ) {
2016-02-15 18:01:21 +00:00
// Create a dummy start end frame
unsigned char data [ DMR_FRAME_LENGTH_BYTES + 2U ] ;
2016-02-07 20:12:24 +00:00
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( data + 2U , m_duplex ) ;
2016-02-07 20:12:24 +00:00
2016-02-15 18:45:57 +00:00
CDMRFullLC fullLC ;
2016-02-25 19:54:18 +00:00
fullLC . encode ( * m_rfLC , data + 2U , DT_TERMINATOR_WITH_LC ) ;
2016-02-07 20:12:24 +00:00
2016-02-15 18:45:57 +00:00
CDMRSlotType slotType ;
2016-02-15 18:01:21 +00:00
slotType . setColorCode ( m_colorCode ) ;
slotType . setDataType ( DT_TERMINATOR_WITH_LC ) ;
slotType . getData ( data + 2U ) ;
2016-02-07 20:12:24 +00:00
2016-05-03 21:18:44 +01:00
data [ 0U ] = TAG_EOT ;
2016-02-15 18:01:21 +00:00
data [ 1U ] = 0x00U ;
2016-10-10 19:27:08 +01:00
2016-06-16 21:40:05 +01:00
for ( unsigned int i = 0U ; i < m_hangCount ; i + + )
writeQueueRF ( data ) ;
2016-02-15 18:01:21 +00:00
}
2016-02-07 20:12:24 +00:00
}
2017-03-12 19:06:47 +00:00
m_rfTimeoutTimer . stop ( ) ;
m_rfTimeout = false ;
m_rfFrames = 0U ;
m_rfErrs = 0U ;
m_rfBits = 1U ;
2023-06-11 11:38:59 +01:00
m_bitErrsAccum = 0U ;
m_bitsCount = 1U ;
2018-01-11 21:17:25 +00:00
m_rfSeqNo = 0U ;
m_rfN = 0U ;
2016-02-25 19:54:18 +00:00
delete m_rfLC ;
2025-03-14 12:13:11 +00:00
m_rfLC = nullptr ;
2016-02-25 19:54:18 +00:00
}
void CDMRSlot : : writeEndNet ( bool writeEnd )
{
2025-03-13 18:16:33 +00:00
m_netState = RPT_NET_STATE : : IDLE ;
2016-02-25 19:54:18 +00:00
setShortLC ( m_slotNo , 0U ) ;
2018-02-08 20:25:03 +00:00
m_lastFrameValid = false ;
2017-03-12 19:06:47 +00:00
if ( writeEnd & & ! m_netTimeout ) {
2016-02-25 19:54:18 +00:00
// Create a dummy start end frame
unsigned char data [ DMR_FRAME_LENGTH_BYTES + 2U ] ;
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( data + 2U , m_duplex ) ;
2016-02-25 19:54:18 +00:00
CDMRFullLC fullLC ;
fullLC . encode ( * m_netLC , data + 2U , DT_TERMINATOR_WITH_LC ) ;
CDMRSlotType slotType ;
slotType . setColorCode ( m_colorCode ) ;
slotType . setDataType ( DT_TERMINATOR_WITH_LC ) ;
slotType . getData ( data + 2U ) ;
2016-05-03 21:18:44 +01:00
data [ 0U ] = TAG_EOT ;
2016-02-25 19:54:18 +00:00
data [ 1U ] = 0x00U ;
2016-06-20 10:01:09 +01:00
if ( m_duplex ) {
for ( unsigned int i = 0U ; i < m_hangCount ; i + + )
writeQueueNet ( data ) ;
} else {
for ( unsigned int i = 0U ; i < 3U ; i + + )
writeQueueNet ( data ) ;
}
2016-02-25 19:54:18 +00:00
}
2017-03-12 19:06:47 +00:00
m_networkWatchdog . stop ( ) ;
m_netTimeoutTimer . stop ( ) ;
2018-02-08 20:25:03 +00:00
m_packetTimer . stop ( ) ;
2017-03-12 19:06:47 +00:00
m_netTimeout = false ;
m_netFrames = 0U ;
m_netLost = 0U ;
m_netErrs = 0U ;
m_netBits = 1U ;
2018-01-11 21:17:25 +00:00
m_netN = 0U ;
2016-02-25 19:54:18 +00:00
delete m_netLC ;
2025-03-14 16:11:04 +00:00
m_netLC = nullptr ;
2016-01-14 18:45:04 +00:00
}
2016-06-16 21:40:05 +01:00
void CDMRSlot : : writeNetwork ( const CDMRData & dmrData )
2016-01-14 18:45:04 +00:00
{
2019-01-20 17:09:40 +00:00
if ( ! m_enabled )
return ;
2025-03-13 18:16:33 +00:00
if ( ( m_rfState ! = RPT_RF_STATE : : LISTENING ) & & ( m_netState = = RPT_NET_STATE : : IDLE ) )
2016-01-14 18:45:04 +00:00
return ;
2018-02-08 20:25:03 +00:00
m_networkWatchdog . start ( ) ;
2017-11-28 10:09:29 +00:00
2018-02-08 20:25:03 +00:00
unsigned char dataType = dmrData . getDataType ( ) ;
2018-01-10 19:05:59 +00:00
2018-02-08 20:25:03 +00:00
unsigned char data [ DMR_FRAME_LENGTH_BYTES + 2U ] ;
dmrData . getData ( data + 2U ) ;
2018-01-10 19:05:59 +00:00
2016-01-14 18:45:04 +00:00
if ( dataType = = DT_VOICE_LC_HEADER ) {
2025-03-13 18:16:33 +00:00
if ( m_netState = = RPT_NET_STATE : : AUDIO )
2016-01-18 18:36:14 +00:00
return ;
2016-01-14 18:45:04 +00:00
2016-02-15 18:45:57 +00:00
CDMRFullLC fullLC ;
2016-11-01 08:00:13 +00:00
CDMRLC * lc = fullLC . decode ( data + 2U , DT_VOICE_LC_HEADER ) ;
2025-03-14 12:13:11 +00:00
if ( lc = = nullptr ) {
2016-10-31 17:55:38 +00:00
LogMessage ( " DMR Slot %u, bad LC received from the network, replacing " , m_slotNo ) ;
2016-11-01 08:00:13 +00:00
lc = new CDMRLC ( dmrData . getFLCO ( ) , dmrData . getSrcId ( ) , dmrData . getDstId ( ) ) ;
2016-01-18 18:36:14 +00:00
}
2016-01-14 18:45:04 +00:00
2016-11-01 08:00:13 +00:00
unsigned int dstId = lc - > getDstId ( ) ;
unsigned int srcId = lc - > getSrcId ( ) ;
2017-05-12 09:26:34 +01:00
FLCO flco = lc - > getFLCO ( ) ;
if ( dstId ! = dmrData . getDstId ( ) | | srcId ! = dmrData . getSrcId ( ) | | flco ! = dmrData . getFLCO ( ) )
LogWarning ( " DMR Slot %u, DMRD header doesn't match the DMR RF header: %u->%s%u %u->%s%u " , m_slotNo ,
2025-03-13 18:16:33 +00:00
dmrData . getSrcId ( ) , dmrData . getFLCO ( ) = = FLCO : : GROUP ? " TG " : " " , dmrData . getDstId ( ) ,
srcId , flco = = FLCO : : GROUP ? " TG " : " " , dstId ) ;
2016-10-10 19:27:08 +01:00
2025-03-13 18:16:33 +00:00
if ( ( m_ovcm = = DMR_OVCM : : RX_ON ) | | ( m_ovcm = = DMR_OVCM : : ON ) )
2022-05-23 22:35:14 +01:00
lc - > setOVCM ( true ) ;
2025-03-13 18:16:33 +00:00
else if ( m_ovcm = = DMR_OVCM : : FORCE_OFF )
2022-05-23 22:35:14 +01:00
lc - > setOVCM ( false ) ;
2016-10-10 19:27:08 +01:00
2022-05-23 22:35:14 +01:00
m_netLC = lc ;
2021-03-07 17:42:55 +00:00
2017-01-10 19:27:12 +00:00
// The standby LC data
m_netEmbeddedLC . setLC ( * m_netLC ) ;
m_netEmbeddedData [ 0U ] . setLC ( * m_netLC ) ;
m_netEmbeddedData [ 1U ] . setLC ( * m_netLC ) ;
2016-02-22 18:12:24 +00:00
// Regenerate the LC data
2016-02-25 19:54:18 +00:00
fullLC . encode ( * m_netLC , data + 2U , DT_VOICE_LC_HEADER ) ;
2016-06-16 21:40:05 +01:00
2016-01-18 18:36:14 +00:00
// Regenerate the Slot Type
2016-02-15 18:45:57 +00:00
CDMRSlotType slotType ;
2016-01-18 18:36:14 +00:00
slotType . setColorCode ( m_colorCode ) ;
slotType . setDataType ( DT_VOICE_LC_HEADER ) ;
slotType . getData ( data + 2U ) ;
2016-01-14 18:45:04 +00:00
2016-09-05 18:03:23 +01:00
// Convert the Data Sync to be from the BS or MS as needed
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( data + 2U , m_duplex ) ;
2016-01-14 18:45:04 +00:00
2021-08-10 19:54:14 +01:00
data [ 0U ] = TAG_DATA ;
2016-01-18 18:36:14 +00:00
data [ 1U ] = 0x00U ;
2016-01-14 18:45:04 +00:00
2018-02-08 20:25:03 +00:00
m_lastFrameValid = false ;
2016-02-25 19:54:18 +00:00
m_netTimeoutTimer . start ( ) ;
2017-03-12 19:06:47 +00:00
m_netTimeout = false ;
2016-01-14 18:45:04 +00:00
2016-02-25 19:54:18 +00:00
m_netFrames = 0U ;
m_netLost = 0U ;
m_netBits = 1U ;
m_netErrs = 0U ;
2017-02-12 18:37:00 +00:00
2017-01-10 19:27:12 +00:00
m_netEmbeddedReadN = 0U ;
m_netEmbeddedWriteN = 1U ;
2017-02-12 18:37:00 +00:00
m_netTalkerId = TALKER_ID_NONE ;
2016-01-20 07:12:28 +00:00
2016-06-20 10:01:09 +01:00
if ( m_duplex ) {
m_queue . clear ( ) ;
m_modem - > writeDMRAbort ( m_slotNo ) ;
}
2018-02-08 20:25:03 +00:00
for ( unsigned int i = 0U ; i < m_jitterSlots ; i + + )
writeQueueNet ( m_idle ) ;
2017-04-02 13:39:28 +01:00
if ( m_duplex ) {
for ( unsigned int i = 0U ; i < NO_HEADERS_DUPLEX ; i + + )
writeQueueNet ( data ) ;
} else {
for ( unsigned int i = 0U ; i < NO_HEADERS_SIMPLEX ; i + + )
writeQueueNet ( data ) ;
}
2016-01-14 18:45:04 +00:00
2025-03-13 18:16:33 +00:00
m_netState = RPT_NET_STATE : : AUDIO ;
2016-01-14 18:45:04 +00:00
2025-03-13 18:16:33 +00:00
setShortLC ( m_slotNo , dstId , flco , ACTIVITY_TYPE : : VOICE ) ;
2016-11-01 08:00:13 +00:00
std : : string src = m_lookup - > find ( srcId ) ;
std : : string dst = m_lookup - > find ( dstId ) ;
2020-03-08 08:06:27 +09:00
class CUserDBentry cn ;
m_lookup - > findWithName ( srcId , & cn ) ;
2016-01-14 18:45:04 +00:00
2025-03-14 16:11:04 +00:00
LogMessage ( " DMR Slot %u, received network voice header from %s to %s%s " , m_slotNo , src . c_str ( ) , flco = = FLCO : : GROUP ? " TG " : " " , dst . c_str ( ) ) ;
writeJSONNet ( " start " , srcId , src , flco = = FLCO : : GROUP , dstId ) ;
2016-01-14 18:45:04 +00:00
} else if ( dataType = = DT_VOICE_PI_HEADER ) {
2025-03-13 18:16:33 +00:00
if ( m_netState ! = RPT_NET_STATE : : AUDIO ) {
2016-11-01 18:07:10 +00:00
CDMRLC * lc = new CDMRLC ( dmrData . getFLCO ( ) , dmrData . getSrcId ( ) , dmrData . getDstId ( ) ) ;
unsigned int dstId = lc - > getDstId ( ) ;
unsigned int srcId = lc - > getSrcId ( ) ;
2025-03-13 18:16:33 +00:00
if ( ( m_ovcm = = DMR_OVCM : : RX_ON ) | | ( m_ovcm = = DMR_OVCM : : ON ) )
2022-05-23 22:35:14 +01:00
lc - > setOVCM ( true ) ;
2025-03-13 18:16:33 +00:00
else if ( m_ovcm = = DMR_OVCM : : FORCE_OFF )
2022-05-23 22:35:14 +01:00
lc - > setOVCM ( false ) ;
2016-11-01 18:07:10 +00:00
2022-05-23 22:35:14 +01:00
m_netLC = lc ;
2021-03-07 17:42:55 +00:00
2018-02-08 20:25:03 +00:00
m_lastFrameValid = false ;
2016-11-01 18:07:10 +00:00
m_netTimeoutTimer . start ( ) ;
2017-03-12 19:06:47 +00:00
m_netTimeout = false ;
2016-11-01 18:07:10 +00:00
if ( m_duplex ) {
m_queue . clear ( ) ;
m_modem - > writeDMRAbort ( m_slotNo ) ;
}
2018-02-08 20:25:03 +00:00
for ( unsigned int i = 0U ; i < m_jitterSlots ; i + + )
writeQueueNet ( m_idle ) ;
2016-11-01 18:07:10 +00:00
// Create a dummy start frame
unsigned char start [ DMR_FRAME_LENGTH_BYTES + 2U ] ;
CSync : : addDMRDataSync ( start + 2U , m_duplex ) ;
CDMRFullLC fullLC ;
fullLC . encode ( * m_netLC , start + 2U , DT_VOICE_LC_HEADER ) ;
CDMRSlotType slotType ;
slotType . setColorCode ( m_colorCode ) ;
slotType . setDataType ( DT_VOICE_LC_HEADER ) ;
slotType . getData ( start + 2U ) ;
2021-08-10 19:54:14 +01:00
start [ 0U ] = TAG_DATA ;
2016-11-01 18:07:10 +00:00
start [ 1U ] = 0x00U ;
2017-04-02 13:39:28 +01:00
if ( m_duplex ) {
for ( unsigned int i = 0U ; i < NO_HEADERS_DUPLEX ; i + + )
writeQueueRF ( start ) ;
} else {
for ( unsigned int i = 0U ; i < NO_HEADERS_SIMPLEX ; i + + )
writeQueueRF ( start ) ;
}
2016-11-01 18:07:10 +00:00
m_netFrames = 0U ;
m_netLost = 0U ;
m_netBits = 1U ;
m_netErrs = 0U ;
2025-03-13 18:16:33 +00:00
m_netState = RPT_NET_STATE : : AUDIO ;
2016-11-01 18:07:10 +00:00
2025-03-13 18:16:33 +00:00
setShortLC ( m_slotNo , dstId , m_netLC - > getFLCO ( ) , ACTIVITY_TYPE : : VOICE ) ;
2016-11-01 18:07:10 +00:00
std : : string src = m_lookup - > find ( srcId ) ;
std : : string dst = m_lookup - > find ( dstId ) ;
2020-03-08 08:06:27 +09:00
class CUserDBentry cn ;
m_lookup - > findWithName ( srcId , & cn ) ;
2016-11-01 18:07:10 +00:00
2025-03-14 16:11:04 +00:00
LogMessage ( " DMR Slot %u, received network late entry from %s to %s%s " , m_slotNo , src . c_str ( ) , m_netLC - > getFLCO ( ) = = FLCO : : GROUP ? " TG " : " " , dst . c_str ( ) ) ;
writeJSONNet ( " late_entry " , srcId , src , m_netLC - > getFLCO ( ) = = FLCO : : GROUP , dstId ) ;
2016-11-01 18:07:10 +00:00
}
2016-01-14 18:45:04 +00:00
// Regenerate the Slot Type
2016-02-15 18:45:57 +00:00
CDMRSlotType slotType ;
2016-01-14 18:45:04 +00:00
slotType . setColorCode ( m_colorCode ) ;
slotType . setDataType ( DT_VOICE_PI_HEADER ) ;
slotType . getData ( data + 2U ) ;
2016-09-05 18:03:23 +01:00
// Convert the Data Sync to be from the BS or MS as needed
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( data + 2U , m_duplex ) ;
2016-01-14 18:45:04 +00:00
2016-02-25 19:59:09 +00:00
// Regenerate the payload
CBPTC19696 bptc ;
unsigned char payload [ 12U ] ;
bptc . decode ( data + 2U , payload ) ;
bptc . encode ( payload , data + 2U ) ;
2021-08-10 19:54:14 +01:00
data [ 0U ] = TAG_DATA ;
2016-01-14 18:45:04 +00:00
data [ 1U ] = 0x00U ;
2016-06-16 21:40:05 +01:00
2016-02-25 19:54:18 +00:00
writeQueueNet ( data ) ;
2016-01-14 18:45:04 +00:00
} else if ( dataType = = DT_TERMINATOR_WITH_LC ) {
2025-03-13 18:16:33 +00:00
if ( m_netState ! = RPT_NET_STATE : : AUDIO )
2016-01-14 18:45:04 +00:00
return ;
2016-06-17 07:05:37 +01:00
// Regenerate the LC data
CDMRFullLC fullLC ;
fullLC . encode ( * m_netLC , data + 2U , DT_TERMINATOR_WITH_LC ) ;
2016-01-14 18:45:04 +00:00
// Regenerate the Slot Type
2016-02-15 18:45:57 +00:00
CDMRSlotType slotType ;
2016-01-14 18:45:04 +00:00
slotType . setColorCode ( m_colorCode ) ;
slotType . setDataType ( DT_TERMINATOR_WITH_LC ) ;
slotType . getData ( data + 2U ) ;
2016-09-05 18:03:23 +01:00
// Convert the Data Sync to be from the BS or MS as needed
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( data + 2U , m_duplex ) ;
2016-01-14 18:45:04 +00:00
2017-03-12 19:06:47 +00:00
if ( ! m_netTimeout ) {
data [ 0U ] = TAG_EOT ;
data [ 1U ] = 0x00U ;
2016-06-20 10:01:09 +01:00
2017-03-12 19:06:47 +00:00
if ( m_duplex ) {
for ( unsigned int i = 0U ; i < m_hangCount ; i + + )
writeQueueNet ( data ) ;
2017-04-02 13:39:28 +01:00
} else {
2017-03-12 19:06:47 +00:00
for ( unsigned int i = 0U ; i < 3U ; i + + )
writeQueueNet ( data ) ;
}
2016-06-20 10:01:09 +01:00
}
2016-01-21 18:38:45 +00:00
2023-01-18 17:58:53 +00:00
unsigned int srcId = m_netLC - > getSrcId ( ) ;
unsigned int dstId = m_netLC - > getDstId ( ) ;
std : : string src = m_lookup - > find ( srcId ) ;
std : : string dst = m_lookup - > find ( dstId ) ;
2019-09-29 17:30:25 +01:00
FLCO flco = m_netLC - > getFLCO ( ) ;
2016-01-19 19:34:49 +00:00
// We've received the voice header and terminator haven't we?
2016-02-25 19:54:18 +00:00
m_netFrames + = 2U ;
2025-03-14 16:11:04 +00:00
LogMessage ( " DMR Slot %u, received network end of voice transmission from %s to %s%s, %.1f seconds, %u%% packet loss, BER: %.1f%% " , m_slotNo , src . c_str ( ) , flco = = FLCO : : GROUP ? " TG " : " " , dst . c_str ( ) , float ( m_netFrames ) / 16.667F , ( m_netLost * 100U ) / m_netFrames , float ( m_netErrs * 100U ) / float ( m_netBits ) ) ;
2024-05-30 14:53:55 +01:00
writeJSONNet ( " end " , float ( m_netFrames ) / 16.667F , float ( m_netLost * 100U ) / float ( m_netFrames ) , float ( m_netErrs * 100U ) / float ( m_netBits ) ) ;
2016-04-26 19:10:45 +01:00
writeEndNet ( ) ;
2016-01-18 18:36:14 +00:00
} else if ( dataType = = DT_DATA_HEADER ) {
2025-03-13 18:16:33 +00:00
if ( m_netState = = RPT_NET_STATE : : DATA )
2016-04-26 18:22:47 +01:00
return ;
2016-02-22 18:12:24 +00:00
CDMRDataHeader dataHeader ;
bool valid = dataHeader . put ( data + 2U ) ;
if ( ! valid ) {
2016-03-14 21:22:09 +00:00
LogMessage ( " DMR Slot %u, unable to decode the network data header " , m_slotNo ) ;
2016-02-18 21:56:26 +00:00
return ;
}
2016-01-26 18:06:31 +00:00
bool gi = dataHeader . getGI ( ) ;
unsigned int srcId = dataHeader . getSrcId ( ) ;
unsigned int dstId = dataHeader . getDstId ( ) ;
2016-11-01 08:00:13 +00:00
2016-06-17 07:05:37 +01:00
m_netFrames = dataHeader . getBlocks ( ) ;
2016-02-22 18:12:24 +00:00
// Regenerate the data header
2016-03-14 17:33:05 +00:00
dataHeader . get ( data + 2U ) ;
2016-02-22 18:12:24 +00:00
2016-01-18 18:36:14 +00:00
// Regenerate the Slot Type
2016-02-15 18:45:57 +00:00
CDMRSlotType slotType ;
2016-01-18 18:36:14 +00:00
slotType . setColorCode ( m_colorCode ) ;
slotType . setDataType ( DT_DATA_HEADER ) ;
slotType . getData ( data + 2U ) ;
2016-09-05 18:03:23 +01:00
// Convert the Data Sync to be from the BS or MS as needed
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( data + 2U , m_duplex ) ;
2016-01-18 18:36:14 +00:00
2021-08-10 19:54:14 +01:00
data [ 0U ] = m_netFrames = = 0U ? TAG_EOT : TAG_DATA ;
2016-01-18 18:36:14 +00:00
data [ 1U ] = 0x00U ;
2018-02-08 20:25:03 +00:00
// Put a small delay into starting transmission
writeQueueNet ( m_idle ) ;
writeQueueNet ( m_idle ) ;
2016-03-11 06:50:06 +00:00
writeQueueNet ( data ) ;
2016-01-18 18:36:14 +00:00
2025-03-13 18:16:33 +00:00
m_netState = RPT_NET_STATE : : DATA ;
2016-01-19 19:34:49 +00:00
2025-03-13 18:16:33 +00:00
setShortLC ( m_slotNo , dstId , gi ? FLCO : : GROUP : FLCO : : USER_USER , ACTIVITY_TYPE : : DATA ) ;
2016-01-18 18:36:14 +00:00
2016-04-19 19:46:16 +01:00
std : : string src = m_lookup - > find ( srcId ) ;
std : : string dst = m_lookup - > find ( dstId ) ;
2016-04-12 18:26:13 +01:00
2016-04-15 15:24:57 +02:00
LogMessage ( " DMR Slot %u, received network data header from %s to %s%s, %u blocks " , m_slotNo , src . c_str ( ) , gi ? " TG " : " " , dst . c_str ( ) , m_netFrames ) ;
2023-01-19 10:08:50 +00:00
writeJSONNet ( " start " , srcId , src , gi , dstId , m_netFrames ) ;
2016-04-26 18:22:47 +01:00
2017-03-05 19:33:03 +00:00
if ( m_netFrames = = 0U ) {
2019-09-29 17:30:25 +01:00
LogMessage ( " DMR Slot %u, ended network data transmission from %s to %s%s " , m_slotNo , src . c_str ( ) , gi ? " TG " : " " , dst . c_str ( ) ) ;
2023-06-09 15:46:29 +01:00
writeJSONNet ( " end " ) ;
2017-03-05 19:33:03 +00:00
writeEndNet ( ) ;
}
2016-01-14 18:45:04 +00:00
} else if ( dataType = = DT_VOICE_SYNC ) {
2025-03-13 18:16:33 +00:00
if ( m_netState = = RPT_NET_STATE : : IDLE ) {
2016-11-01 08:00:13 +00:00
CDMRLC * lc = new CDMRLC ( dmrData . getFLCO ( ) , dmrData . getSrcId ( ) , dmrData . getDstId ( ) ) ;
unsigned int dstId = lc - > getDstId ( ) ;
unsigned int srcId = lc - > getSrcId ( ) ;
2025-03-13 18:16:33 +00:00
if ( ( m_ovcm = = DMR_OVCM : : RX_ON ) | | ( m_ovcm = = DMR_OVCM : : ON ) )
2022-05-23 22:35:14 +01:00
lc - > setOVCM ( true ) ;
2025-03-13 18:16:33 +00:00
else if ( m_ovcm = = DMR_OVCM : : FORCE_OFF )
2022-05-23 22:35:14 +01:00
lc - > setOVCM ( false ) ;
2016-11-01 08:00:13 +00:00
2022-05-23 22:35:14 +01:00
m_netLC = lc ;
2021-03-07 17:42:55 +00:00
2017-01-10 19:27:12 +00:00
// The standby LC data
m_netEmbeddedLC . setLC ( * m_netLC ) ;
m_netEmbeddedData [ 0U ] . setLC ( * m_netLC ) ;
m_netEmbeddedData [ 1U ] . setLC ( * m_netLC ) ;
2018-02-08 20:25:03 +00:00
m_lastFrameValid = false ;
2016-02-25 19:54:18 +00:00
m_netTimeoutTimer . start ( ) ;
2017-03-12 19:06:47 +00:00
m_netTimeout = false ;
2016-01-14 18:45:04 +00:00
2016-06-20 10:01:09 +01:00
if ( m_duplex ) {
m_queue . clear ( ) ;
m_modem - > writeDMRAbort ( m_slotNo ) ;
}
2018-02-08 20:25:03 +00:00
for ( unsigned int i = 0U ; i < m_jitterSlots ; i + + )
writeQueueNet ( m_idle ) ;
2016-05-03 21:18:44 +01:00
// Create a dummy start frame
unsigned char start [ DMR_FRAME_LENGTH_BYTES + 2U ] ;
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( start + 2U , m_duplex ) ;
2016-05-03 21:18:44 +01:00
CDMRFullLC fullLC ;
fullLC . encode ( * m_netLC , start + 2U , DT_VOICE_LC_HEADER ) ;
CDMRSlotType slotType ;
slotType . setColorCode ( m_colorCode ) ;
slotType . setDataType ( DT_VOICE_LC_HEADER ) ;
slotType . getData ( start + 2U ) ;
2021-08-10 19:54:14 +01:00
start [ 0U ] = TAG_DATA ;
2016-05-03 21:18:44 +01:00
start [ 1U ] = 0x00U ;
2017-04-02 13:39:28 +01:00
if ( m_duplex ) {
for ( unsigned int i = 0U ; i < NO_HEADERS_DUPLEX ; i + + )
writeQueueRF ( start ) ;
} else {
for ( unsigned int i = 0U ; i < NO_HEADERS_SIMPLEX ; i + + )
writeQueueRF ( start ) ;
}
2016-01-21 21:08:10 +00:00
2016-02-25 19:54:18 +00:00
m_netFrames = 0U ;
m_netLost = 0U ;
m_netBits = 1U ;
m_netErrs = 0U ;
2017-02-12 18:37:00 +00:00
2017-01-10 19:27:12 +00:00
m_netEmbeddedReadN = 0U ;
m_netEmbeddedWriteN = 1U ;
2017-02-12 18:37:00 +00:00
m_netTalkerId = TALKER_ID_NONE ;
2016-01-21 21:08:10 +00:00
2025-03-13 18:16:33 +00:00
m_netState = RPT_NET_STATE : : AUDIO ;
2016-01-21 21:08:10 +00:00
2025-03-13 18:16:33 +00:00
setShortLC ( m_slotNo , dstId , m_netLC - > getFLCO ( ) , ACTIVITY_TYPE : : VOICE ) ;
2016-06-09 19:33:00 +01:00
2016-11-01 08:00:13 +00:00
std : : string src = m_lookup - > find ( srcId ) ;
std : : string dst = m_lookup - > find ( dstId ) ;
2020-03-08 08:06:27 +09:00
class CUserDBentry cn ;
m_lookup - > findWithName ( srcId , & cn ) ;
2016-04-12 18:26:13 +01:00
2025-03-14 16:11:04 +00:00
LogMessage ( " DMR Slot %u, received network late entry from %s to %s%s " , m_slotNo , src . c_str ( ) , m_netLC - > getFLCO ( ) = = FLCO : : GROUP ? " TG " : " " , dst . c_str ( ) ) ;
writeJSONNet ( " late_entry " , srcId , src , m_netLC - > getFLCO ( ) = = FLCO : : GROUP , dstId ) ;
2016-01-19 19:34:49 +00:00
}
2025-03-13 18:16:33 +00:00
if ( m_netState = = RPT_NET_STATE : : AUDIO ) {
2016-02-25 19:54:18 +00:00
unsigned char fid = m_netLC - > getFID ( ) ;
2016-06-01 09:43:39 +01:00
if ( fid = = FID_ETSI | | fid = = FID_DMRA )
m_netErrs + = m_fec . regenerateDMR ( data + 2U ) ;
2016-02-25 19:54:18 +00:00
m_netBits + = 141U ;
2016-01-25 21:56:57 +00:00
2021-08-10 19:54:14 +01:00
data [ 0U ] = TAG_DATA ;
2016-01-25 21:56:57 +00:00
data [ 1U ] = 0x00U ;
2016-09-05 18:03:23 +01:00
// Convert the Audio Sync to be from the BS or MS as needed
2016-09-06 08:06:19 +01:00
CSync : : addDMRAudioSync ( data + 2U , m_duplex ) ;
2016-01-22 06:43:08 +00:00
2018-02-08 20:25:03 +00:00
// Initialise the lost packet data
if ( m_netFrames = = 0U ) {
: : memcpy ( m_lastFrame , data , DMR_FRAME_LENGTH_BYTES + 2U ) ;
m_lastFrameValid = true ;
2018-02-09 14:32:52 -03:00
m_netN = 5U ;
2018-02-08 20:25:03 +00:00
m_netLost = 0U ;
}
2018-02-09 17:30:53 -03:00
if ( insertSilence ( data , dmrData . getN ( ) ) ) {
if ( ! m_netTimeout )
writeQueueNet ( data ) ;
}
2016-01-21 21:08:10 +00:00
2017-01-10 19:27:12 +00:00
m_netEmbeddedReadN = ( m_netEmbeddedReadN + 1U ) % 2U ;
m_netEmbeddedWriteN = ( m_netEmbeddedWriteN + 1U ) % 2U ;
m_netEmbeddedData [ m_netEmbeddedWriteN ] . reset ( ) ;
2018-02-08 20:25:03 +00:00
m_packetTimer . start ( ) ;
m_elapsed . start ( ) ;
2017-11-28 10:09:29 +00:00
2016-02-25 19:54:18 +00:00
m_netFrames + + ;
2016-01-19 19:34:49 +00:00
2018-02-08 20:25:03 +00:00
// Save details in case we need to infill data
m_netN = dmrData . getN ( ) ;
2016-01-22 06:43:08 +00:00
}
} else if ( dataType = = DT_VOICE ) {
2025-03-13 18:16:33 +00:00
if ( m_netState ! = RPT_NET_STATE : : AUDIO )
2016-01-22 06:43:08 +00:00
return ;
2016-06-16 21:40:05 +01:00
2016-02-25 19:54:18 +00:00
unsigned char fid = m_netLC - > getFID ( ) ;
2016-06-01 09:43:39 +01:00
if ( fid = = FID_ETSI | | fid = = FID_DMRA )
m_netErrs + = m_fec . regenerateDMR ( data + 2U ) ;
2016-02-25 19:54:18 +00:00
m_netBits + = 141U ;
2016-01-25 21:56:57 +00:00
2017-01-10 19:27:12 +00:00
// Get the LCSS from the EMB
2017-01-05 19:15:10 +00:00
CDMREMB emb ;
emb . putData ( data + 2U ) ;
2017-01-10 19:27:12 +00:00
unsigned char lcss = emb . getLCSS ( ) ;
// Dump any interesting Embedded Data
bool ret = m_netEmbeddedData [ m_netEmbeddedWriteN ] . addData ( data + 2U , lcss ) ;
if ( ret ) {
FLCO flco = m_netEmbeddedData [ m_netEmbeddedWriteN ] . getFLCO ( ) ;
unsigned char data [ 9U ] ;
m_netEmbeddedData [ m_netEmbeddedWriteN ] . getRawData ( data ) ;
char text [ 80U ] ;
switch ( flco ) {
2025-03-13 18:16:33 +00:00
case FLCO : : GROUP :
case FLCO : : USER_USER :
2017-01-10 19:27:12 +00:00
// ::sprintf(text, "DMR Slot %u, Embedded LC", m_slotNo);
// CUtils::dump(1U, text, data, 9U);
break ;
2025-03-14 16:11:04 +00:00
case FLCO : : GPS_INFO :
2017-02-27 20:17:55 +00:00
if ( m_dumpTAData ) {
: : sprintf ( text , " DMR Slot %u, Embedded GPS Info " , m_slotNo ) ;
2023-06-06 20:33:25 +01:00
CUtils : : dump ( 1U , text , data , 9U ) ;
2017-11-26 17:56:20 +00:00
logGPSPosition ( data ) ;
2017-02-27 20:17:55 +00:00
}
2017-01-10 19:27:12 +00:00
break ;
2025-03-13 18:16:33 +00:00
case FLCO : : TALKER_ALIAS_HEADER :
2017-02-12 18:37:00 +00:00
if ( ! ( m_netTalkerId & TALKER_ID_HEADER ) ) {
2017-11-15 10:47:56 +00:00
if ( ! m_netTalkerId )
2023-06-06 20:33:25 +01:00
m_netTalkerAlias . reset ( ) ;
2023-06-11 11:38:59 +01:00
bool complete = m_netTalkerAlias . add ( 0U , data + 2U , 7U ) ;
2023-06-16 17:18:43 +01:00
if ( complete )
2023-06-11 11:38:59 +01:00
writeJSONText ( m_netTalkerAlias . get ( ) ) ;
2017-11-15 10:47:56 +00:00
2017-02-27 20:17:55 +00:00
if ( m_dumpTAData ) {
: : sprintf ( text , " DMR Slot %u, Embedded Talker Alias Header " , m_slotNo ) ;
2023-06-06 20:33:25 +01:00
CUtils : : dump ( 1U , text , data , 9U ) ;
2017-02-27 20:17:55 +00:00
}
2017-02-28 08:27:56 +00:00
m_netTalkerId | = TALKER_ID_HEADER ;
2017-02-12 18:37:00 +00:00
}
2017-01-10 19:27:12 +00:00
break ;
2025-03-13 18:16:33 +00:00
case FLCO : : TALKER_ALIAS_BLOCK1 :
2017-02-12 18:37:00 +00:00
if ( ! ( m_netTalkerId & TALKER_ID_BLOCK1 ) ) {
2017-11-15 10:47:56 +00:00
if ( ! m_netTalkerId )
2023-06-06 20:33:25 +01:00
m_netTalkerAlias . reset ( ) ;
2023-06-11 11:38:59 +01:00
bool complete = m_netTalkerAlias . add ( 1U , data + 2U , 7U ) ;
2023-06-16 17:18:43 +01:00
if ( complete )
2023-06-11 11:38:59 +01:00
writeJSONText ( m_netTalkerAlias . get ( ) ) ;
2017-11-15 10:47:56 +00:00
2017-02-27 20:17:55 +00:00
if ( m_dumpTAData ) {
: : sprintf ( text , " DMR Slot %u, Embedded Talker Alias Block 1 " , m_slotNo ) ;
2023-06-06 20:33:25 +01:00
CUtils : : dump ( 1U , text , data , 9U ) ;
2017-02-27 20:17:55 +00:00
}
2017-02-28 08:27:56 +00:00
m_netTalkerId | = TALKER_ID_BLOCK1 ;
2017-02-12 18:37:00 +00:00
}
2017-01-10 19:27:12 +00:00
break ;
2025-03-13 18:16:33 +00:00
case FLCO : : TALKER_ALIAS_BLOCK2 :
2017-02-12 18:37:00 +00:00
if ( ! ( m_netTalkerId & TALKER_ID_BLOCK2 ) ) {
2017-11-15 10:47:56 +00:00
if ( ! m_netTalkerId )
2023-06-06 20:33:25 +01:00
m_netTalkerAlias . reset ( ) ;
2023-06-11 11:38:59 +01:00
bool complete = m_netTalkerAlias . add ( 2U , data + 2U , 7U ) ;
2023-06-16 17:18:43 +01:00
if ( complete )
2023-06-11 11:38:59 +01:00
writeJSONText ( m_netTalkerAlias . get ( ) ) ;
2017-11-15 10:47:56 +00:00
2017-02-27 20:17:55 +00:00
if ( m_dumpTAData ) {
: : sprintf ( text , " DMR Slot %u, Embedded Talker Alias Block 2 " , m_slotNo ) ;
2023-06-06 20:33:25 +01:00
CUtils : : dump ( 1U , text , data , 9U ) ;
2017-02-27 20:17:55 +00:00
}
2017-02-28 08:27:56 +00:00
m_netTalkerId | = TALKER_ID_BLOCK2 ;
2017-02-12 18:37:00 +00:00
}
2017-01-10 19:27:12 +00:00
break ;
2025-03-13 18:16:33 +00:00
case FLCO : : TALKER_ALIAS_BLOCK3 :
2017-02-12 18:37:00 +00:00
if ( ! ( m_netTalkerId & TALKER_ID_BLOCK3 ) ) {
2017-11-15 10:47:56 +00:00
if ( ! m_netTalkerId )
2023-06-06 20:33:25 +01:00
m_netTalkerAlias . reset ( ) ;
2023-06-11 11:38:59 +01:00
bool complete = m_netTalkerAlias . add ( 3U , data + 2U , 7U ) ;
2023-06-16 17:18:43 +01:00
if ( complete )
2023-06-11 11:38:59 +01:00
writeJSONText ( m_netTalkerAlias . get ( ) ) ;
2017-11-15 10:47:56 +00:00
2017-02-27 20:17:55 +00:00
if ( m_dumpTAData ) {
: : sprintf ( text , " DMR Slot %u, Embedded Talker Alias Block 3 " , m_slotNo ) ;
2023-06-06 20:33:25 +01:00
CUtils : : dump ( 1U , text , data , 9U ) ;
2017-02-27 20:17:55 +00:00
}
2017-02-28 08:27:56 +00:00
m_netTalkerId | = TALKER_ID_BLOCK3 ;
2017-02-12 18:37:00 +00:00
}
2017-01-10 19:27:12 +00:00
break ;
default :
: : sprintf ( text , " DMR Slot %u, Unknown Embedded Data " , m_slotNo ) ;
CUtils : : dump ( 1U , text , data , 9U ) ;
break ;
}
}
2017-01-16 13:10:23 +01:00
if ( m_embeddedLCOnly ) {
// Only send the previously received LC
2017-01-10 19:27:12 +00:00
lcss = m_netEmbeddedLC . getData ( data + 2U , dmrData . getN ( ) ) ;
2017-01-16 13:10:23 +01:00
} else {
// Regenerate the previous super blocks Embedded Data or substitude the LC for it
if ( m_netEmbeddedData [ m_netEmbeddedReadN ] . isValid ( ) )
lcss = m_netEmbeddedData [ m_netEmbeddedReadN ] . getData ( data + 2U , dmrData . getN ( ) ) ;
else
lcss = m_netEmbeddedLC . getData ( data + 2U , dmrData . getN ( ) ) ;
}
2017-01-10 19:27:12 +00:00
// Regenerate the EMB
2017-01-05 19:15:10 +00:00
emb . setColorCode ( m_colorCode ) ;
2017-01-10 19:27:12 +00:00
emb . setLCSS ( lcss ) ;
2017-01-05 19:15:10 +00:00
emb . getData ( data + 2U ) ;
2016-01-25 21:56:57 +00:00
2021-08-10 19:54:14 +01:00
data [ 0U ] = TAG_DATA ;
2016-01-25 21:56:57 +00:00
data [ 1U ] = 0x00U ;
2018-02-08 20:25:03 +00:00
// Initialise the lost packet data
if ( m_netFrames = = 0U ) {
: : memcpy ( m_lastFrame , data , DMR_FRAME_LENGTH_BYTES + 2U ) ;
m_lastFrameValid = true ;
2018-02-09 14:32:52 -03:00
m_netN = 5U ;
2018-02-08 20:25:03 +00:00
m_netLost = 0U ;
}
2018-02-09 17:30:53 -03:00
if ( insertSilence ( data , dmrData . getN ( ) ) ) {
if ( ! m_netTimeout )
writeQueueNet ( data ) ;
}
2016-01-21 21:08:10 +00:00
2018-02-08 20:25:03 +00:00
m_packetTimer . start ( ) ;
m_elapsed . start ( ) ;
2017-11-28 10:09:29 +00:00
2016-02-25 19:54:18 +00:00
m_netFrames + + ;
2016-01-21 21:08:10 +00:00
2018-02-08 20:25:03 +00:00
// Save details in case we need to infill data
m_netN = dmrData . getN ( ) ;
2016-01-26 18:06:31 +00:00
} else if ( dataType = = DT_CSBK ) {
2016-02-22 18:12:24 +00:00
CDMRCSBK csbk ;
bool valid = csbk . put ( data + 2U ) ;
if ( ! valid ) {
2016-03-14 21:22:09 +00:00
LogMessage ( " DMR Slot %u, unable to decode the network CSBK " , m_slotNo ) ;
2016-02-18 21:56:26 +00:00
return ;
}
2016-01-26 18:06:31 +00:00
CSBKO csbko = csbk . getCSBKO ( ) ;
2025-03-13 18:16:33 +00:00
if ( csbko = = CSBKO : : BSDWNACT )
2016-01-26 18:06:31 +00:00
return ;
2019-10-18 19:15:08 +02:00
// set the OVCM bit for the supported csbk
2025-03-13 18:16:33 +00:00
if ( ( m_ovcm = = DMR_OVCM : : RX_ON ) | | ( m_ovcm = = DMR_OVCM : : ON ) )
2022-05-23 22:35:14 +01:00
csbk . setOVCM ( true ) ;
2025-03-13 18:16:33 +00:00
else if ( m_ovcm = = DMR_OVCM : : FORCE_OFF )
2022-05-23 22:35:14 +01:00
csbk . setOVCM ( false ) ;
2021-03-07 17:42:55 +00:00
2016-03-14 21:22:09 +00:00
bool gi = csbk . getGI ( ) ;
unsigned int srcId = csbk . getSrcId ( ) ;
unsigned int dstId = csbk . getDstId ( ) ;
2016-11-01 08:00:13 +00:00
2016-03-14 17:33:05 +00:00
// Regenerate the CSBK data
csbk . get ( data + 2U ) ;
2016-02-22 18:12:24 +00:00
2016-03-14 17:33:05 +00:00
// Regenerate the Slot Type
CDMRSlotType slotType ;
slotType . putData ( data + 2U ) ;
slotType . setColorCode ( m_colorCode ) ;
slotType . getData ( data + 2U ) ;
2016-01-26 18:06:31 +00:00
2016-09-05 18:03:23 +01:00
// Convert the Data Sync to be from the BS or MS as needed
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( data + 2U , m_duplex ) ;
2016-01-26 18:06:31 +00:00
2021-08-10 19:54:14 +01:00
data [ 0U ] = TAG_DATA ;
2016-03-14 17:33:05 +00:00
data [ 1U ] = 0x00U ;
2016-01-26 18:06:31 +00:00
2025-03-13 18:16:33 +00:00
if ( ( csbko = = CSBKO : : PRECCSBK ) & & csbk . getDataContent ( ) ) {
2018-09-23 11:11:44 -03:00
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 ) ;
2016-01-26 18:06:31 +00:00
2016-04-28 10:32:11 +01:00
std : : string src = m_lookup - > find ( srcId ) ;
std : : string dst = m_lookup - > find ( dstId ) ;
2016-03-14 17:33:05 +00:00
switch ( csbko ) {
2025-03-13 18:16:33 +00:00
case CSBKO : : UUVREQ :
2016-04-28 10:32:11 +01:00
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 ( ) ) ;
2023-01-19 10:08:50 +00:00
writeJSONNet ( " csbk " , " Unit to Unit Voice Service Request " , srcId , src , gi , dstId ) ;
2016-03-14 17:33:05 +00:00
break ;
2025-03-13 18:16:33 +00:00
case CSBKO : : UUANSRSP :
2016-04-28 10:32:11 +01:00
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 ( ) ) ;
2023-01-19 10:08:50 +00:00
writeJSONNet ( " csbk " , " Unit to Unit Voice Service Answer Response " , srcId , src , gi , dstId ) ;
2016-03-14 17:33:05 +00:00
break ;
2025-03-13 18:16:33 +00:00
case CSBKO : : NACKRSP :
2016-04-28 10:32:11 +01:00
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 ( ) ) ;
2023-01-19 10:08:50 +00:00
writeJSONNet ( " csbk " , " UNegative Acknowledgment Response " , srcId , src , gi , dstId ) ;
2016-03-14 17:33:05 +00:00
break ;
2025-03-13 18:16:33 +00:00
case CSBKO : : PRECCSBK :
2016-12-11 16:01:30 +00:00
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 ( ) ) ;
2023-01-19 10:08:50 +00:00
writeJSONNet ( " csbk " , " Preamble " , srcId , src , gi , dstId ) ;
2016-03-14 17:33:05 +00:00
break ;
2025-03-13 18:16:33 +00:00
case CSBKO : : CALL_ALERT :
2020-03-02 12:10:44 +00:00
LogMessage ( " DMR Slot %u, received network Call Alert CSBK from %s to %s%s " , m_slotNo , src . c_str ( ) , gi ? " TG " : " " , dst . c_str ( ) ) ;
2023-01-19 10:08:50 +00:00
writeJSONNet ( " csbk " , " Call Alert " , srcId , src , gi , dstId ) ;
2020-03-02 12:10:44 +00:00
break ;
2025-03-13 18:16:33 +00:00
case CSBKO : : CALL_ALERT_ACK :
2020-03-02 12:10:44 +00:00
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 ( ) ) ;
2023-01-19 10:08:50 +00:00
writeJSONNet ( " csbk " , " Call Alert Ack " , srcId , src , gi , dstId ) ;
2020-03-02 12:10:44 +00:00
break ;
2025-03-13 18:16:33 +00:00
case CSBKO : : RADIO_CHECK :
2022-03-04 10:52:57 +01:00
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 ( ) ) ;
2023-01-19 10:08:50 +00:00
writeJSONNet ( " csbk " , " Radio Check " , srcId , src , gi , dstId ) ;
2022-03-04 10:52:57 +01:00
break ;
2016-01-26 18:06:31 +00:00
default :
LogWarning ( " DMR Slot %u, unhandled network CSBK type - 0x%02X " , m_slotNo , csbko ) ;
break ;
}
2017-10-03 10:03:13 +01:00
2017-10-04 21:56:40 +01:00
// If data preamble, signal its existence
2025-03-14 16:11:04 +00:00
if ( ( csbko = = CSBKO : : PRECCSBK ) & & csbk . getDataContent ( ) )
setShortLC ( m_slotNo , dstId , gi ? FLCO : : GROUP : FLCO : : USER_USER , ACTIVITY_TYPE : : DATA ) ;
2018-09-22 12:53:52 -03:00
} else if ( dataType = = DT_RATE_12_DATA | | dataType = = DT_RATE_34_DATA | | dataType = = DT_RATE_1_DATA ) {
2025-03-14 16:11:04 +00:00
if ( ( m_netState ! = RPT_NET_STATE : : DATA ) | | ( m_netFrames = = 0U ) ) {
2018-02-03 11:07:13 -03:00
writeEndNet ( ) ;
2016-01-26 18:06:31 +00:00
return ;
2018-02-03 11:07:13 -03:00
}
2016-01-26 18:06:31 +00:00
2023-09-18 13:28:42 +02:00
char title [ 80U ] ;
2016-04-19 19:46:16 +01:00
// Regenerate the rate 1/2 payload
2016-03-10 17:18:40 +00:00
if ( dataType = = DT_RATE_12_DATA ) {
CBPTC19696 bptc ;
unsigned char payload [ 12U ] ;
bptc . decode ( data + 2U , payload ) ;
2023-09-18 13:28:42 +02:00
: : sprintf ( title , " DMR Slot %u, Data 1/2 " , m_slotNo ) ;
CUtils : : dump ( 1U , title , payload , 12U ) ;
2016-03-10 17:18:40 +00:00
bptc . encode ( payload , data + 2U ) ;
2016-07-07 14:05:38 +01:00
} else if ( dataType = = DT_RATE_34_DATA ) {
2024-10-04 17:14:36 +01:00
CDMRTrellis trellis ;
2016-07-07 20:23:30 +01:00
unsigned char payload [ 18U ] ;
2024-10-04 17:14:36 +01:00
bool ret = trellis . decode ( data + 2U , payload ) ;
2017-02-13 08:50:23 +00:00
if ( ret ) {
2023-09-18 13:28:42 +02:00
: : sprintf ( title , " DMR Slot %u, Data 3/4 " , m_slotNo ) ;
CUtils : : dump ( 1U , title , payload , 18U ) ;
2024-10-04 17:14:36 +01:00
trellis . encode ( payload , data + 2U ) ;
2017-02-13 08:50:23 +00:00
} else {
LogMessage ( " DMR Slot %u, unfixable network rate 3/4 data " , m_slotNo ) ;
CUtils : : dump ( 1U , " Data " , data + 2U , DMR_FRAME_LENGTH_BYTES ) ;
}
2023-09-27 14:29:34 +01:00
} else {
2023-09-18 13:28:42 +02:00
: : sprintf ( title , " DMR Slot %u, Data 1/1 " , m_slotNo ) ;
CUtils : : dump ( 1U , title , data + 2U , 24U ) ;
2016-03-10 17:18:40 +00:00
}
2016-01-26 18:06:31 +00:00
// Regenerate the Slot Type
2016-02-15 18:45:57 +00:00
CDMRSlotType slotType ;
2016-01-14 18:45:04 +00:00
slotType . putData ( data + 2U ) ;
slotType . setColorCode ( m_colorCode ) ;
slotType . getData ( data + 2U ) ;
2016-09-05 18:03:23 +01:00
// Convert the Data Sync to be from the BS or MS as needed
2016-09-06 08:06:19 +01:00
CSync : : addDMRDataSync ( data + 2U , m_duplex ) ;
2016-01-14 18:45:04 +00:00
2016-02-25 19:54:18 +00:00
m_netFrames - - ;
2016-01-14 18:45:04 +00:00
2021-08-10 19:54:14 +01:00
data [ 0U ] = m_netFrames = = 0U ? TAG_EOT : TAG_DATA ;
2016-01-26 18:06:31 +00:00
data [ 1U ] = 0x00U ;
2016-01-14 18:45:04 +00:00
2016-02-25 19:54:18 +00:00
writeQueueNet ( data ) ;
2016-01-26 18:06:31 +00:00
2017-03-05 19:33:03 +00:00
if ( m_netFrames = = 0U ) {
2019-10-10 11:53:08 +01:00
LogMessage ( " DMR Slot %u, ended network data transmission " , m_slotNo ) ;
2023-06-09 15:46:29 +01:00
writeJSONNet ( " end " ) ;
2017-03-05 19:33:03 +00:00
writeEndNet ( ) ;
}
2016-01-26 18:06:31 +00:00
} else {
// Unhandled data type
LogWarning ( " DMR Slot %u, unhandled network data type - 0x%02X " , m_slotNo , dataType ) ;
2016-01-14 18:45:04 +00:00
}
}
2017-11-26 17:56:20 +00:00
void CDMRSlot : : logGPSPosition ( const unsigned char * data )
2017-11-13 21:48:47 +01:00
{
2017-11-15 10:47:56 +00:00
unsigned int errorI = ( data [ 2U ] & 0x0E ) > > 1U ;
2017-11-13 21:48:47 +01:00
2017-11-28 21:35:25 +00:00
const char * error ;
2017-11-28 13:45:22 +00:00
switch ( errorI ) {
2017-11-15 10:47:56 +00:00
case 0U :
2017-11-28 13:45:22 +00:00
error = " < 2m " ;
2017-11-15 10:47:56 +00:00
break ;
case 1U :
2017-11-28 13:45:22 +00:00
error = " < 20m " ;
2017-11-15 10:47:56 +00:00
break ;
case 2U :
2017-11-28 13:45:22 +00:00
error = " < 200m " ;
2017-11-15 10:47:56 +00:00
break ;
case 3U :
2017-11-28 13:45:22 +00:00
error = " < 2km " ;
2017-11-15 10:47:56 +00:00
break ;
case 4U :
2017-11-28 13:45:22 +00:00
error = " < 20km " ;
2017-11-15 10:47:56 +00:00
break ;
case 5U :
2017-11-28 13:45:22 +00:00
error = " < 200km " ;
2017-11-15 10:47:56 +00:00
break ;
case 6U :
2017-11-28 13:45:22 +00:00
error = " > 200km " ;
2017-11-15 10:47:56 +00:00
break ;
default :
2017-11-28 21:35:25 +00:00
error = " not known " ;
break ;
2017-11-28 13:45:22 +00:00
}
2017-11-13 21:48:47 +01:00
2017-11-26 17:56:20 +00:00
int32_t longitudeI = ( ( data [ 2U ] & 0x01U ) < < 31 ) | ( data [ 3U ] < < 23 ) | ( data [ 4U ] < < 15 ) | ( data [ 5U ] < < 7 ) ;
longitudeI > > = 7 ;
2017-11-15 10:47:56 +00:00
2017-11-26 17:56:20 +00:00
int32_t latitudeI = ( data [ 6U ] < < 24 ) | ( data [ 7U ] < < 16 ) | ( data [ 8U ] < < 8 ) ;
latitudeI > > = 8 ;
2017-11-13 21:48:47 +01:00
2017-11-15 10:47:56 +00:00
float longitude = 360.0F / 33554432.0F ; // 360/2^25 steps
float latitude = 180.0F / 16777216.0F ; // 180/2^24 steps
2017-11-13 21:48:47 +01:00
2017-11-26 17:56:20 +00:00
longitude * = float ( longitudeI ) ;
latitude * = float ( latitudeI ) ;
2017-11-13 21:48:47 +01:00
2017-11-28 13:45:22 +00:00
LogMessage ( " GPS position [%f,%f] (Position error %s) " , latitude , longitude , error ) ;
2017-11-13 21:48:47 +01:00
}
2016-03-14 20:55:15 +00:00
void CDMRSlot : : clock ( )
2016-01-14 18:45:04 +00:00
{
2016-03-14 20:55:15 +00:00
unsigned int ms = m_interval . elapsed ( ) ;
m_interval . start ( ) ;
2016-02-25 19:54:18 +00:00
m_rfTimeoutTimer . clock ( ms ) ;
2017-03-12 19:06:47 +00:00
if ( m_rfTimeoutTimer . isRunning ( ) & & m_rfTimeoutTimer . hasExpired ( ) ) {
if ( ! m_rfTimeout ) {
LogMessage ( " DMR Slot %u, RF user has timed out " , m_slotNo ) ;
2023-06-09 15:46:29 +01:00
writeJSONRF ( " timeout " ) ;
2017-03-12 19:06:47 +00:00
m_rfTimeout = true ;
}
}
2016-02-25 19:54:18 +00:00
m_netTimeoutTimer . clock ( ms ) ;
2017-03-12 19:06:47 +00:00
if ( m_netTimeoutTimer . isRunning ( ) & & m_netTimeoutTimer . hasExpired ( ) ) {
if ( ! m_netTimeout ) {
LogMessage ( " DMR Slot %u, network user has timed out " , m_slotNo ) ;
2023-06-09 15:46:29 +01:00
writeJSONNet ( " timeout " ) ;
2017-03-12 19:06:47 +00:00
m_netTimeout = true ;
}
}
2016-01-14 18:45:04 +00:00
CDMRSlot::clock(),
CDStarControl::clock(),
CM17Control::clock(),
CNXDNControl::clock(),
CP25Control::clock(),
CYSFControl::clock():
- Leave ASAP when it's disabled.
CDMRSlot::enable(),
CDStarControl::enable(),
CM17Control::enable(),
CNXDNControl::enable(),
CP25Control::enable(),
CYSFControl::enable():
- Log a message when Controller get disabled while running, "<MODE>, RF user has timed out" or "<MODE>, network user has timed out", depending of the RF/Net state.
CMMDVMHost::run():
- After getting the modem data, don't handle it (read/write<Controler>, etc) if the given mode is not enabled.
MMDVMHost:
- Rename CMMDVMHost::processEnableCommand() to CMMDVMHost::enableModemMode().
- Add CMMDVMHost::enableMode() and CMMDVMHost::disableMode(), called now from CMMDVMHost::remoteControl(), which clarify the code.
- CMMDVMHost::remoteControl():
* Fix indentation,
* Simplify ENABLE_x/DISABLE_x cases code.
- CMMDVMHost::setMode():
* Enable pocsag network only if m_pocsagEnabled is true,
* Enable ax25 network only if m_ax25Enabled is true (not m_fmEnabled).
2025-05-29 18:29:42 +02:00
if ( ! m_enabled )
return ;
2025-03-13 18:16:33 +00:00
if ( ( m_netState = = RPT_NET_STATE : : AUDIO ) | | ( m_netState = = RPT_NET_STATE : : DATA ) ) {
2016-01-14 18:45:04 +00:00
m_networkWatchdog . clock ( ms ) ;
if ( m_networkWatchdog . hasExpired ( ) ) {
2025-03-13 18:16:33 +00:00
if ( m_netState = = RPT_NET_STATE : : AUDIO ) {
2016-01-21 18:38:45 +00:00
// We've received the voice header haven't we?
2016-02-25 19:54:18 +00:00
m_netFrames + = 1U ;
LogMessage ( " DMR Slot %u, network watchdog has expired, %.1f seconds, %u%% packet loss, BER: %.1f%% " , m_slotNo , float ( m_netFrames ) / 16.667F , ( m_netLost * 100U ) / m_netFrames , float ( m_netErrs * 100U ) / float ( m_netBits ) ) ;
2024-05-30 14:53:55 +01:00
writeJSONNet ( " lost " , float ( m_netFrames ) / 16.667F , float ( m_netLost * 100U ) / float ( m_netFrames ) , float ( m_netErrs * 100U ) / float ( m_netBits ) ) ;
2016-02-25 19:54:18 +00:00
writeEndNet ( true ) ;
2016-01-22 06:43:08 +00:00
} else {
2016-01-20 07:12:28 +00:00
LogMessage ( " DMR Slot %u, network watchdog has expired " , m_slotNo ) ;
2023-06-09 15:46:29 +01:00
writeJSONNet ( " lost " ) ;
2016-02-25 19:54:18 +00:00
writeEndNet ( ) ;
2016-01-21 21:08:10 +00:00
}
2016-01-14 18:45:04 +00:00
}
}
2018-02-08 20:25:03 +00:00
2025-03-13 18:16:33 +00:00
if ( m_netState = = RPT_NET_STATE : : AUDIO ) {
2018-02-08 20:25:03 +00:00
m_packetTimer . clock ( ms ) ;
if ( m_packetTimer . isRunning ( ) & & m_packetTimer . hasExpired ( ) ) {
unsigned int elapsed = m_elapsed . elapsed ( ) ;
if ( elapsed > = m_jitterTime ) {
LogDebug ( " DMR Slot %u, lost audio for %ums filling in " , m_slotNo , elapsed ) ;
insertSilence ( m_jitterSlots ) ;
m_elapsed . start ( ) ;
}
m_packetTimer . start ( ) ;
}
}
2016-01-14 18:45:04 +00:00
}
2016-02-25 19:54:18 +00:00
void CDMRSlot : : writeQueueRF ( const unsigned char * data )
2016-01-14 18:45:04 +00:00
{
2025-03-14 12:13:11 +00:00
assert ( data ! = nullptr ) ;
2016-03-07 20:21:55 +00:00
2025-03-13 18:16:33 +00:00
if ( m_netState ! = RPT_NET_STATE : : IDLE )
2016-02-25 19:54:18 +00:00
return ;
2016-01-14 18:45:04 +00:00
unsigned char len = DMR_FRAME_LENGTH_BYTES + 2U ;
2016-03-07 18:08:50 +00:00
unsigned int space = m_queue . freeSpace ( ) ;
if ( space < ( len + 1U ) ) {
LogError ( " DMR Slot %u, overflow in the DMR slot RF queue " , m_slotNo ) ;
return ;
}
m_queue . addData ( & len , 1U ) ;
2017-03-12 19:06:47 +00:00
m_queue . addData ( data , len ) ;
2016-01-14 18:45:04 +00:00
}
2016-03-23 17:30:06 +00:00
void CDMRSlot : : writeNetworkRF ( const unsigned char * data , unsigned char dataType , FLCO flco , unsigned int srcId , unsigned int dstId , unsigned char errors )
2016-01-14 18:45:04 +00:00
{
2025-03-14 12:13:11 +00:00
assert ( data ! = nullptr ) ;
2016-01-14 18:45:04 +00:00
2025-03-13 18:16:33 +00:00
if ( m_netState ! = RPT_NET_STATE : : IDLE )
2016-02-25 19:54:18 +00:00
return ;
2025-03-14 12:13:11 +00:00
if ( m_network = = nullptr )
2016-01-14 18:45:04 +00:00
return ;
CDMRData dmrData ;
dmrData . setSlotNo ( m_slotNo ) ;
dmrData . setDataType ( dataType ) ;
2016-01-25 21:56:57 +00:00
dmrData . setSrcId ( srcId ) ;
dmrData . setDstId ( dstId ) ;
dmrData . setFLCO ( flco ) ;
2016-02-28 17:40:15 +00:00
dmrData . setN ( m_rfN ) ;
dmrData . setSeqNo ( m_rfSeqNo ) ;
2016-03-23 17:30:06 +00:00
dmrData . setBER ( errors ) ;
2023-08-05 16:51:55 +01:00
dmrData . setRSSI ( - m_rssi ) ; // Always report as positive
2016-01-14 18:45:04 +00:00
2016-02-28 17:40:15 +00:00
m_rfSeqNo + + ;
2016-01-14 18:45:04 +00:00
dmrData . setData ( data + 2U ) ;
m_network - > write ( dmrData ) ;
}
2016-03-23 17:30:06 +00:00
void CDMRSlot : : writeNetworkRF ( const unsigned char * data , unsigned char dataType , unsigned char errors )
2016-01-25 21:56:57 +00:00
{
2025-03-14 12:13:11 +00:00
assert ( data ! = nullptr ) ;
assert ( m_rfLC ! = nullptr ) ;
2016-02-25 19:54:18 +00:00
2016-03-23 17:30:06 +00:00
writeNetworkRF ( data , dataType , m_rfLC - > getFLCO ( ) , m_rfLC - > getSrcId ( ) , m_rfLC - > getDstId ( ) , errors ) ;
2016-02-25 19:54:18 +00:00
}
2016-01-25 21:56:57 +00:00
2016-02-25 19:54:18 +00:00
void CDMRSlot : : writeQueueNet ( const unsigned char * data )
{
2025-03-14 12:13:11 +00:00
assert ( data ! = nullptr ) ;
2016-03-07 20:21:55 +00:00
2016-02-25 19:54:18 +00:00
unsigned char len = DMR_FRAME_LENGTH_BYTES + 2U ;
2016-03-07 18:08:50 +00:00
unsigned int space = m_queue . freeSpace ( ) ;
if ( space < ( len + 1U ) ) {
LogError ( " DMR Slot %u, overflow in the DMR slot RF queue " , m_slotNo ) ;
return ;
}
m_queue . addData ( & len , 1U ) ;
2017-03-12 19:06:47 +00:00
m_queue . addData ( data , len ) ;
2016-01-25 21:56:57 +00:00
}
2025-03-14 16:11:04 +00:00
void CDMRSlot : : 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 )
2016-01-14 18:45:04 +00:00
{
2025-03-14 16:11:04 +00:00
assert ( modem ! = nullptr ) ;
assert ( lookup ! = nullptr ) ;
assert ( rssiMapper ! = nullptr ) ;
2016-01-14 18:45:04 +00:00
2017-01-16 13:10:23 +01:00
m_colorCode = colorCode ;
m_embeddedLCOnly = embeddedLCOnly ;
2017-02-27 20:17:55 +00:00
m_dumpTAData = dumpTAData ;
2017-01-16 13:10:23 +01:00
m_modem = modem ;
m_network = network ;
m_duplex = duplex ;
m_lookup = lookup ;
m_hangCount = callHang * 17U ;
2019-10-11 18:24:25 +01:00
m_ovcm = ovcm ;
2025-03-12 12:55:25 +00:00
m_protect = protect ;
2016-08-08 21:26:18 +01:00
2017-01-16 13:10:23 +01:00
m_rssiMapper = rssiMapper ;
2016-01-14 18:45:04 +00:00
2018-02-08 20:25:03 +00:00
m_jitterTime = jitter ;
2018-02-09 14:49:18 -03:00
float jitter_tmp = float ( jitter ) / 360.0F ;
m_jitterSlots = ( unsigned int ) ( std : : ceil ( jitter_tmp ) * 6.0F ) ;
2018-02-08 20:25:03 +00:00
2016-09-06 19:42:15 +01:00
m_idle = new unsigned char [ DMR_FRAME_LENGTH_BYTES + 2U ] ;
2016-01-25 21:56:57 +00:00
: : memcpy ( m_idle , DMR_IDLE_DATA , DMR_FRAME_LENGTH_BYTES + 2U ) ;
2016-01-21 18:38:45 +00:00
// Generate the Slot Type for the Idle frame
2016-02-15 18:45:57 +00:00
CDMRSlotType slotType ;
2016-01-14 18:45:04 +00:00
slotType . setColorCode ( colorCode ) ;
slotType . setDataType ( DT_IDLE ) ;
slotType . getData ( m_idle + 2U ) ;
2016-04-04 19:03:38 +01:00
}
2017-10-04 11:20:37 +01:00
void CDMRSlot : : setShortLC ( unsigned int slotNo , unsigned int id , FLCO flco , ACTIVITY_TYPE type )
2016-01-14 18:45:04 +00:00
{
2025-03-14 12:13:11 +00:00
assert ( m_modem ! = nullptr ) ;
2016-01-14 18:45:04 +00:00
switch ( slotNo ) {
case 1U :
2017-10-03 10:03:13 +01:00
m_id1 = 0U ;
m_flco1 = flco ;
m_activity1 = type ;
2016-01-14 18:45:04 +00:00
if ( id ! = 0U ) {
unsigned char buffer [ 3U ] ;
buffer [ 0U ] = ( id < < 16 ) & 0xFFU ;
buffer [ 1U ] = ( id < < 8 ) & 0xFFU ;
buffer [ 2U ] = ( id < < 0 ) & 0xFFU ;
m_id1 = CCRC : : crc8 ( buffer , 3U ) ;
}
break ;
case 2U :
2017-10-03 10:03:13 +01:00
m_id2 = 0U ;
m_flco2 = flco ;
m_activity2 = type ;
2016-01-14 18:45:04 +00:00
if ( id ! = 0U ) {
unsigned char buffer [ 3U ] ;
buffer [ 0U ] = ( id < < 16 ) & 0xFFU ;
buffer [ 1U ] = ( id < < 8 ) & 0xFFU ;
buffer [ 2U ] = ( id < < 0 ) & 0xFFU ;
m_id2 = CCRC : : crc8 ( buffer , 3U ) ;
}
break ;
default :
LogError ( " Invalid slot number passed to setShortLC - %u " , slotNo ) ;
return ;
}
2016-06-20 18:33:04 +01:00
// If we have no activity to report, let the modem send the null Short LC when it's ready
if ( m_id1 = = 0U & & m_id2 = = 0U )
return ;
2016-01-14 18:45:04 +00:00
unsigned char lc [ 5U ] ;
lc [ 0U ] = 0x01U ;
lc [ 1U ] = 0x00U ;
lc [ 2U ] = 0x00U ;
lc [ 3U ] = 0x00U ;
if ( m_id1 ! = 0U ) {
2017-10-04 11:20:37 +01:00
lc [ 2U ] = m_id1 ;
2025-03-13 18:16:33 +00:00
if ( ( m_activity1 = = ACTIVITY_TYPE : : VOICE ) & & ( m_flco1 = = FLCO : : GROUP ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0x08U ;
2025-03-13 18:16:33 +00:00
else if ( ( m_activity1 = = ACTIVITY_TYPE : : VOICE ) & & ( m_flco1 = = FLCO : : USER_USER ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0x09U ;
2025-03-13 18:16:33 +00:00
else if ( ( m_activity1 = = ACTIVITY_TYPE : : DATA ) & & ( m_flco1 = = FLCO : : GROUP ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0x0BU ;
2025-03-13 18:16:33 +00:00
else if ( ( m_activity1 = = ACTIVITY_TYPE : : DATA ) & & ( m_flco1 = = FLCO : : USER_USER ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0x0AU ;
2025-03-13 18:16:33 +00:00
else if ( ( m_activity1 = = ACTIVITY_TYPE : : CSBK ) & & ( m_flco1 = = FLCO : : GROUP ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0x02U ;
2025-03-13 18:16:33 +00:00
else if ( ( m_activity1 = = ACTIVITY_TYPE : : CSBK ) & & ( m_flco1 = = FLCO : : USER_USER ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0x03U ;
2025-03-13 18:16:33 +00:00
else if ( ( m_activity1 = = ACTIVITY_TYPE : : EMERG ) & & ( m_flco1 = = FLCO : : GROUP ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0x0CU ;
2025-03-13 18:16:33 +00:00
else if ( ( m_activity1 = = ACTIVITY_TYPE : : EMERG ) & & ( m_flco1 = = FLCO : : USER_USER ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0x0DU ;
}
if ( m_id2 ! = 0U ) {
lc [ 3U ] = m_id2 ;
2025-03-13 18:16:33 +00:00
if ( ( m_activity2 = = ACTIVITY_TYPE : : VOICE ) & & ( m_flco2 = = FLCO : : GROUP ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0x80U ;
2025-03-13 18:16:33 +00:00
else if ( ( m_activity2 = = ACTIVITY_TYPE : : VOICE ) & & ( m_flco2 = = FLCO : : USER_USER ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0x90U ;
2025-03-13 18:16:33 +00:00
else if ( ( m_activity2 = = ACTIVITY_TYPE : : DATA ) & & ( m_flco2 = = FLCO : : GROUP ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0xB0U ;
2025-03-13 18:16:33 +00:00
else if ( ( m_activity2 = = ACTIVITY_TYPE : : DATA ) & & ( m_flco2 = = FLCO : : USER_USER ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0xA0U ;
2025-03-13 18:16:33 +00:00
else if ( ( m_activity2 = = ACTIVITY_TYPE : : CSBK ) & & ( m_flco2 = = FLCO : : GROUP ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0x20U ;
2025-03-13 18:16:33 +00:00
else if ( ( m_activity2 = = ACTIVITY_TYPE : : CSBK ) & & ( m_flco2 = = FLCO : : USER_USER ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0x30U ;
2025-03-13 18:16:33 +00:00
else if ( ( m_activity2 = = ACTIVITY_TYPE : : EMERG ) & & ( m_flco2 = = FLCO : : GROUP ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0xC0U ;
2025-03-13 18:16:33 +00:00
else if ( ( m_activity2 = = ACTIVITY_TYPE : : EMERG ) & & ( m_flco2 = = FLCO : : USER_USER ) )
2017-10-04 21:56:40 +01:00
lc [ 1U ] | = 0xD0U ;
}
lc [ 4U ] = CCRC : : crc8 ( lc , 4U ) ;
2016-01-14 18:45:04 +00:00
unsigned char sLC [ 9U ] ;
2016-02-15 18:45:57 +00:00
CDMRShortLC shortLC ;
2016-01-14 18:45:04 +00:00
shortLC . encode ( lc , sLC ) ;
m_modem - > writeDMRShortLC ( sLC ) ;
}
2018-02-08 20:25:03 +00:00
bool CDMRSlot : : insertSilence ( const unsigned char * data , unsigned char seqNo )
2017-11-28 10:09:29 +00:00
{
2025-03-14 12:13:11 +00:00
assert ( data ! = nullptr ) ;
2018-02-08 20:25:03 +00:00
2018-02-09 17:30:53 -03:00
// Do not send duplicate
if ( seqNo = = m_netN )
return false ;
2018-02-08 20:25:03 +00:00
// Check to see if we have any spaces to fill?
2018-02-09 14:32:52 -03:00
unsigned char seq = ( m_netN + 1U ) % 6U ;
2018-02-08 20:25:03 +00:00
if ( seq = = seqNo ) {
// Just copy the data, nothing else to do here
: : memcpy ( m_lastFrame , data , DMR_FRAME_LENGTH_BYTES + 2U ) ;
m_lastFrameValid = true ;
return true ;
2017-11-28 10:09:29 +00:00
}
2016-01-19 19:34:49 +00:00
2018-02-09 14:32:52 -03:00
unsigned int count = ( seqNo - seq + 6U ) % 6U ;
2018-02-08 20:25:03 +00:00
insertSilence ( count ) ;
: : memcpy ( m_lastFrame , data , DMR_FRAME_LENGTH_BYTES + 2U ) ;
m_lastFrameValid = true ;
return true ;
}
void CDMRSlot : : insertSilence ( unsigned int count )
{
unsigned char data [ DMR_FRAME_LENGTH_BYTES + 2U ] ;
2017-11-28 10:09:29 +00:00
2018-02-08 20:25:03 +00:00
if ( m_lastFrameValid ) {
2018-09-22 12:53:52 -03:00
: : memcpy ( data , m_lastFrame , 2U ) ; // The control data
2018-02-08 20:25:03 +00:00
: : memcpy ( data + 2U , m_lastFrame + 24U + 2U , 9U ) ; // Copy the last audio block to the first
2018-09-22 12:53:52 -03:00
: : memcpy ( data + 24U + 2U , data + 2U , 9U ) ; // Copy the last audio block to the last
: : memcpy ( data + 9U + 2U , data + 2U , 5U ) ; // Copy the last audio block to the middle (1/2)
2018-02-08 20:25:03 +00:00
: : memcpy ( data + 19U + 2U , data + 4U + 2U , 5U ) ; // Copy the last audio block to the middle (2/2)
2018-01-10 19:05:59 +00:00
} else {
2018-02-08 20:25:03 +00:00
// Not sure what to do if this isn't AMBE audio
: : memcpy ( data , DMR_SILENCE_DATA , DMR_FRAME_LENGTH_BYTES + 2U ) ;
}
2017-11-28 10:09:29 +00:00
2018-02-08 20:25:03 +00:00
unsigned char n = ( m_netN + 1U ) % 6U ;
unsigned char fid = m_netLC - > getFID ( ) ;
CDMREMB emb ;
emb . setColorCode ( m_colorCode ) ;
2017-11-28 10:09:29 +00:00
2018-02-08 20:25:03 +00:00
for ( unsigned int i = 0U ; i < count ; i + + ) {
// Only use our silence frame if its AMBE audio data
if ( fid = = FID_ETSI | | fid = = FID_DMRA ) {
if ( i > 0U ) {
: : memcpy ( data , DMR_SILENCE_DATA , DMR_FRAME_LENGTH_BYTES + 2U ) ;
m_lastFrameValid = false ;
}
}
if ( n = = 0U ) {
CSync : : addDMRAudioSync ( data + 2U , m_duplex ) ;
} else {
2018-02-09 17:43:26 -03:00
unsigned char lcss = m_netEmbeddedLC . getData ( data + 2U , n ) ;
emb . setLCSS ( lcss ) ;
2018-02-08 20:25:03 +00:00
emb . getData ( data + 2U ) ;
}
writeQueueNet ( data ) ;
m_netN = n ;
m_netFrames + + ;
m_netLost + + ;
2017-11-28 10:09:29 +00:00
2018-02-08 20:25:03 +00:00
n = ( n + 1U ) % 6U ;
2016-01-19 19:34:49 +00:00
}
}
2019-01-10 09:05:15 +00:00
bool CDMRSlot : : isBusy ( ) const
{
2025-03-13 18:16:33 +00:00
return ( m_rfState ! = RPT_RF_STATE : : LISTENING ) | | ( m_netState ! = RPT_NET_STATE : : IDLE ) ;
2019-01-10 09:05:15 +00:00
}
2019-01-19 17:15:24 +00:00
void CDMRSlot : : enable ( bool enabled )
{
if ( ! enabled & & m_enabled ) {
m_queue . clear ( ) ;
// Reset the RF section
CDMRSlot::clock(),
CDStarControl::clock(),
CM17Control::clock(),
CNXDNControl::clock(),
CP25Control::clock(),
CYSFControl::clock():
- Leave ASAP when it's disabled.
CDMRSlot::enable(),
CDStarControl::enable(),
CM17Control::enable(),
CNXDNControl::enable(),
CP25Control::enable(),
CYSFControl::enable():
- Log a message when Controller get disabled while running, "<MODE>, RF user has timed out" or "<MODE>, network user has timed out", depending of the RF/Net state.
CMMDVMHost::run():
- After getting the modem data, don't handle it (read/write<Controler>, etc) if the given mode is not enabled.
MMDVMHost:
- Rename CMMDVMHost::processEnableCommand() to CMMDVMHost::enableModemMode().
- Add CMMDVMHost::enableMode() and CMMDVMHost::disableMode(), called now from CMMDVMHost::remoteControl(), which clarify the code.
- CMMDVMHost::remoteControl():
* Fix indentation,
* Simplify ENABLE_x/DISABLE_x cases code.
- CMMDVMHost::setMode():
* Enable pocsag network only if m_pocsagEnabled is true,
* Enable ax25 network only if m_ax25Enabled is true (not m_fmEnabled).
2025-05-29 18:29:42 +02:00
switch ( m_rfState ) {
case RPT_RF_STATE : : LISTENING :
case RPT_RF_STATE : : REJECTED :
case RPT_RF_STATE : : INVALID :
break ;
default :
if ( m_rfTimeoutTimer . isRunning ( ) ) {
if ( ! m_rfTimeout )
LogMessage ( " DMR Slot %u, RF user has timed out " , m_slotNo ) ;
}
break ;
}
2025-03-13 18:16:33 +00:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2019-01-19 17:15:24 +00:00
m_rfTimeoutTimer . stop ( ) ;
m_rfTimeout = false ;
m_rfFrames = 0U ;
m_rfErrs = 0U ;
m_rfBits = 1U ;
2023-06-11 11:38:59 +01:00
m_bitErrsAccum = 0U ;
m_bitsCount = 1U ;
2019-01-19 17:15:24 +00:00
m_rfSeqNo = 0U ;
m_rfN = 0U ;
delete m_rfLC ;
2025-03-14 12:13:11 +00:00
m_rfLC = nullptr ;
2019-01-19 17:15:24 +00:00
// Reset the networking section
CDMRSlot::clock(),
CDStarControl::clock(),
CM17Control::clock(),
CNXDNControl::clock(),
CP25Control::clock(),
CYSFControl::clock():
- Leave ASAP when it's disabled.
CDMRSlot::enable(),
CDStarControl::enable(),
CM17Control::enable(),
CNXDNControl::enable(),
CP25Control::enable(),
CYSFControl::enable():
- Log a message when Controller get disabled while running, "<MODE>, RF user has timed out" or "<MODE>, network user has timed out", depending of the RF/Net state.
CMMDVMHost::run():
- After getting the modem data, don't handle it (read/write<Controler>, etc) if the given mode is not enabled.
MMDVMHost:
- Rename CMMDVMHost::processEnableCommand() to CMMDVMHost::enableModemMode().
- Add CMMDVMHost::enableMode() and CMMDVMHost::disableMode(), called now from CMMDVMHost::remoteControl(), which clarify the code.
- CMMDVMHost::remoteControl():
* Fix indentation,
* Simplify ENABLE_x/DISABLE_x cases code.
- CMMDVMHost::setMode():
* Enable pocsag network only if m_pocsagEnabled is true,
* Enable ax25 network only if m_ax25Enabled is true (not m_fmEnabled).
2025-05-29 18:29:42 +02:00
switch ( m_netState ) {
case RPT_NET_STATE : : IDLE :
break ;
default :
if ( m_netTimeoutTimer . isRunning ( ) ) {
if ( ! m_netTimeout )
LogMessage ( " DMR Slot %u, network user has timed out " , m_slotNo ) ;
}
break ;
}
2025-03-13 18:16:33 +00:00
m_netState = RPT_NET_STATE : : IDLE ;
2019-01-19 17:15:24 +00:00
m_lastFrameValid = false ;
m_networkWatchdog . stop ( ) ;
m_netTimeoutTimer . stop ( ) ;
m_packetTimer . stop ( ) ;
m_netTimeout = false ;
m_netFrames = 0U ;
m_netLost = 0U ;
m_netErrs = 0U ;
m_netBits = 1U ;
m_netN = 0U ;
delete m_netLC ;
2025-03-14 12:13:11 +00:00
m_netLC = nullptr ;
2019-01-19 17:15:24 +00:00
}
m_enabled = enabled ;
}
2023-01-18 17:58:53 +00:00
2023-06-11 11:38:59 +01:00
void CDMRSlot : : writeJSONRSSI ( )
{
2023-06-19 13:58:10 +01:00
if ( m_rssi = = 0U )
return ;
2023-06-11 11:38:59 +01:00
if ( m_rssiCount > = RSSI_COUNT ) {
nlohmann : : json json ;
json [ " timestamp " ] = CUtils : : createTimestamp ( ) ;
json [ " mode " ] = " DMR " ;
json [ " slot " ] = int ( m_slotNo ) ;
2023-08-05 16:51:55 +01:00
json [ " value " ] = m_rssiAccum / int ( m_rssiCount ) ;
2023-06-11 11:38:59 +01:00
WriteJSON ( " RSSI " , json ) ;
2023-08-05 16:51:55 +01:00
m_rssiAccum = 0 ;
2023-06-11 11:38:59 +01:00
m_rssiCount = 0U ;
}
}
void CDMRSlot : : writeJSONBER ( )
{
if ( m_bitsCount > = BER_COUNT ) {
nlohmann : : json json ;
json [ " timestamp " ] = CUtils : : createTimestamp ( ) ;
json [ " mode " ] = " DMR " ;
json [ " slot " ] = int ( m_slotNo ) ;
json [ " value " ] = float ( m_bitErrsAccum * 100U ) / float ( m_bitsCount ) ;
WriteJSON ( " BER " , json ) ;
m_bitErrsAccum = 0U ;
m_bitsCount = 1U ;
}
}
void CDMRSlot : : writeJSONText ( const unsigned char * text )
{
2025-03-14 16:11:04 +00:00
assert ( text ! = nullptr ) ;
2023-06-11 11:38:59 +01:00
nlohmann : : json json ;
json [ " timestamp " ] = CUtils : : createTimestamp ( ) ;
json [ " mode " ] = " DMR " ;
json [ " slot " ] = int ( m_slotNo ) ;
json [ " value " ] = std : : string ( ( char * ) text ) ;
WriteJSON ( " Text " , json ) ;
}
2023-06-09 15:46:29 +01:00
void CDMRSlot : : writeJSONRF ( const char * action )
{
2025-03-14 16:11:04 +00:00
assert ( action ! = nullptr ) ;
2023-06-09 15:46:29 +01:00
nlohmann : : json json ;
2023-06-09 17:24:07 +01:00
writeJSON ( json , action ) ;
2023-06-09 15:46:29 +01:00
WriteJSON ( " DMR " , json ) ;
}
2023-01-19 10:08:50 +00:00
void CDMRSlot : : writeJSONRF ( const char * action , unsigned int srcId , const std : : string & srcInfo , bool grp , unsigned int dstId )
2023-01-18 17:58:53 +00:00
{
2025-03-14 16:11:04 +00:00
assert ( action ! = nullptr ) ;
2023-01-18 17:58:53 +00:00
nlohmann : : json json ;
2023-01-19 10:08:50 +00:00
writeJSON ( json , " rf " , action , srcId , srcInfo , grp , dstId ) ;
2023-01-18 17:58:53 +00:00
WriteJSON ( " DMR " , json ) ;
}
2023-01-19 10:08:50 +00:00
void CDMRSlot : : writeJSONRF ( const char * action , const char * desc , unsigned int srcId , const std : : string & srcInfo , bool grp , unsigned int dstId )
2023-01-18 17:58:53 +00:00
{
2025-03-14 16:11:04 +00:00
assert ( action ! = nullptr ) ;
assert ( desc ! = nullptr ) ;
2023-01-18 17:58:53 +00:00
nlohmann : : json json ;
2023-01-19 10:08:50 +00:00
writeJSON ( json , " rf " , action , srcId , srcInfo , grp , dstId ) ;
2023-01-18 17:58:53 +00:00
json [ " csbk_desc " ] = desc ;
WriteJSON ( " DMR " , json ) ;
}
2023-01-19 10:08:50 +00:00
void CDMRSlot : : writeJSONRF ( const char * action , unsigned int srcId , const std : : string & srcInfo , bool grp , unsigned int dstId , unsigned int frames )
2023-01-18 17:58:53 +00:00
{
2025-03-14 16:11:04 +00:00
assert ( action ! = nullptr ) ;
2023-01-18 17:58:53 +00:00
nlohmann : : json json ;
2023-01-19 10:08:50 +00:00
writeJSON ( json , " rf " , action , srcId , srcInfo , grp , dstId ) ;
2023-01-18 17:58:53 +00:00
json [ " frames " ] = int ( frames ) ;
WriteJSON ( " DMR " , json ) ;
}
2023-06-09 15:46:29 +01:00
void CDMRSlot : : writeJSONRF ( const char * action , float duration , float ber )
2023-01-18 17:58:53 +00:00
{
2025-03-14 16:11:04 +00:00
assert ( action ! = nullptr ) ;
2023-01-18 17:58:53 +00:00
nlohmann : : json json ;
2023-06-09 17:24:07 +01:00
writeJSON ( json , action ) ;
2023-01-18 17:58:53 +00:00
json [ " duration " ] = duration ;
json [ " ber " ] = ber ;
WriteJSON ( " DMR " , json ) ;
}
2023-08-05 16:51:55 +01:00
void CDMRSlot : : writeJSONRF ( const char * action , float duration , float ber , int minRSSI , int maxRSSI , int aveRSSI )
2023-01-18 17:58:53 +00:00
{
2025-03-14 16:11:04 +00:00
assert ( action ! = nullptr ) ;
2023-01-18 17:58:53 +00:00
nlohmann : : json json ;
2023-06-09 17:24:07 +01:00
writeJSON ( json , action ) ;
2023-01-18 17:58:53 +00:00
json [ " duration " ] = duration ;
json [ " ber " ] = ber ;
nlohmann : : json rssi ;
2023-08-05 16:51:55 +01:00
rssi [ " min " ] = minRSSI ;
rssi [ " max " ] = maxRSSI ;
rssi [ " ave " ] = aveRSSI ;
2023-01-18 17:58:53 +00:00
json [ " rssi " ] = rssi ;
WriteJSON ( " DMR " , json ) ;
}
2023-06-09 15:46:29 +01:00
void CDMRSlot : : writeJSONNet ( const char * action )
{
2025-03-14 16:11:04 +00:00
assert ( action ! = nullptr ) ;
2023-06-09 15:46:29 +01:00
nlohmann : : json json ;
2023-06-09 17:24:07 +01:00
writeJSON ( json , action ) ;
2023-06-09 15:46:29 +01:00
WriteJSON ( " DMR " , json ) ;
}
2023-01-19 10:08:50 +00:00
void CDMRSlot : : writeJSONNet ( const char * action , unsigned int srcId , const std : : string & srcInfo , bool grp , unsigned int dstId )
2023-01-18 17:58:53 +00:00
{
2025-03-14 16:11:04 +00:00
assert ( action ! = nullptr ) ;
2023-01-18 17:58:53 +00:00
nlohmann : : json json ;
2023-01-19 10:08:50 +00:00
writeJSON ( json , " network " , action , srcId , srcInfo , grp , dstId ) ;
2023-01-18 17:58:53 +00:00
WriteJSON ( " DMR " , json ) ;
}
2023-01-19 10:08:50 +00:00
void CDMRSlot : : writeJSONNet ( const char * action , const char * desc , unsigned int srcId , const std : : string & srcInfo , bool grp , unsigned int dstId )
2023-01-18 17:58:53 +00:00
{
2025-03-14 16:11:04 +00:00
assert ( action ! = nullptr ) ;
assert ( desc ! = nullptr ) ;
2023-01-18 17:58:53 +00:00
nlohmann : : json json ;
2023-01-19 10:08:50 +00:00
writeJSON ( json , " network " , action , srcId , srcInfo , grp , dstId ) ;
2023-01-18 17:58:53 +00:00
json [ " csbk_desc " ] = desc ;
WriteJSON ( " DMR " , json ) ;
}
2023-01-19 10:08:50 +00:00
void CDMRSlot : : writeJSONNet ( const char * action , unsigned int srcId , const std : : string & srcInfo , bool grp , unsigned int dstId , unsigned int frames )
2023-01-18 17:58:53 +00:00
{
2025-03-14 16:11:04 +00:00
assert ( action ! = nullptr ) ;
2023-01-18 17:58:53 +00:00
nlohmann : : json json ;
2023-01-19 10:08:50 +00:00
writeJSON ( json , " network " , action , srcId , srcInfo , grp , dstId ) ;
2023-01-18 17:58:53 +00:00
json [ " frames " ] = int ( frames ) ;
WriteJSON ( " DMR " , json ) ;
}
2023-06-09 15:46:29 +01:00
void CDMRSlot : : writeJSONNet ( const char * action , float duration , float loss , float ber )
2023-01-18 17:58:53 +00:00
{
2025-03-14 16:11:04 +00:00
assert ( action ! = nullptr ) ;
2023-01-18 17:58:53 +00:00
nlohmann : : json json ;
2023-06-09 17:24:07 +01:00
writeJSON ( json , action ) ;
2023-01-18 17:58:53 +00:00
json [ " duration " ] = duration ;
json [ " loss " ] = loss ;
json [ " ber " ] = ber ;
WriteJSON ( " DMR " , json ) ;
}
2023-06-09 17:24:07 +01:00
void CDMRSlot : : writeJSON ( nlohmann : : json & json , const char * action )
2023-06-09 15:46:29 +01:00
{
2025-03-14 16:11:04 +00:00
assert ( action ! = nullptr ) ;
2023-06-09 15:46:29 +01:00
json [ " timestamp " ] = CUtils : : createTimestamp ( ) ;
json [ " action " ] = action ;
json [ " slot " ] = int ( m_slotNo ) ;
}
2023-01-19 10:08:50 +00:00
void CDMRSlot : : writeJSON ( nlohmann : : json & json , const char * source , const char * action , unsigned int srcId , const std : : string & srcInfo , bool grp , unsigned int dstId )
2023-01-18 17:58:53 +00:00
{
2025-03-14 16:11:04 +00:00
assert ( source ! = nullptr ) ;
assert ( action ! = nullptr ) ;
2023-01-18 17:58:53 +00:00
json [ " timestamp " ] = CUtils : : createTimestamp ( ) ;
json [ " source " ] = source ;
json [ " action " ] = action ;
2023-01-19 10:08:50 +00:00
json [ " slot " ] = int ( m_slotNo ) ;
2023-01-18 17:58:53 +00:00
json [ " source_id " ] = int ( srcId ) ;
json [ " destination_id " ] = int ( dstId ) ;
json [ " destination_type " ] = grp ? " group " : " individual " ;
2026-03-17 15:02:38 +00:00
json [ " source_info " ] = srcInfo ;
2023-01-18 17:58:53 +00:00
}
2023-07-03 16:21:35 +01:00
# endif