2016-01-27 21:01:50 +01:00
/*
2025-02-27 13:46:25 +01:00
* Copyright ( C ) 2015 - 2019 , 2021 , 2023 , 2025 Jonathan Naylor , G4KLX
2016-01-27 21:01:50 +01: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 .
*/
# include "DStarControl.h"
# include "Utils.h"
2016-02-15 22:46:57 +01:00
# include "Sync.h"
2016-01-27 21:01:50 +01:00
# include "Log.h"
2021-05-15 06:51:12 +02:00
# include "SMeter.h"
2016-01-27 21:01:50 +01:00
2016-03-07 21:21:55 +01:00
# include <cstdio>
2016-01-27 21:01:50 +01:00
# include <cassert>
# include <ctime>
2016-04-06 19:46:05 +02:00
# include <algorithm>
# include <functional>
2016-01-27 21:01:50 +01:00
const unsigned int MAX_SYNC_BIT_ERRORS = 2U ;
2016-04-06 19:46:05 +02:00
bool CallsignCompare ( const std : : string & arg , const unsigned char * my )
{
for ( unsigned int i = 0U ; i < ( DSTAR_LONG_CALLSIGN_LENGTH - 1U ) ; i + + ) {
if ( arg . at ( i ) ! = my [ i ] )
return false ;
}
return true ;
}
2016-01-27 21:01:50 +01:00
// #define DUMP_DSTAR
2025-03-13 19:16:33 +01:00
CDStarControl : : CDStarControl ( const std : : string & callsign , const std : : string & module , bool selfOnly , bool ackReply , unsigned int ackTime , DSTAR_ACK ackMessage , bool errorReply , const std : : vector < std : : string > & blackList , const std : : vector < std : : string > & whiteList , CDStarNetwork * network , CDisplay * display , unsigned int timeout , bool duplex , bool remoteGateway , CRSSIInterpolator * rssiMapper ) :
2025-03-14 13:13:11 +01:00
m_callsign ( nullptr ) ,
m_gateway ( nullptr ) ,
2016-04-04 18:40:05 +02:00
m_selfOnly ( selfOnly ) ,
2017-06-10 14:18:18 +02:00
m_ackReply ( ackReply ) ,
2018-09-14 12:30:48 +02:00
m_ackMessage ( ackMessage ) ,
2017-02-16 20:49:58 +01:00
m_errorReply ( errorReply ) ,
2017-11-09 10:21:22 +01:00
m_remoteGateway ( remoteGateway ) ,
2016-04-06 19:46:05 +02:00
m_blackList ( blackList ) ,
2019-10-06 16:15:25 +02:00
m_whiteList ( whiteList ) ,
2016-01-27 21:01:50 +01:00
m_network ( network ) ,
m_display ( display ) ,
m_duplex ( duplex ) ,
2016-05-10 19:54:35 +02:00
m_queue ( 5000U , " D-Star Control " ) ,
2016-02-25 20:54:18 +01:00
m_rfHeader ( ) ,
m_netHeader ( ) ,
2025-03-13 19:16:33 +01:00
m_rfState ( RPT_RF_STATE : : LISTENING ) ,
m_netState ( RPT_NET_STATE : : IDLE ) ,
2016-01-27 21:01:50 +01:00
m_net ( false ) ,
2023-06-16 12:35:16 +02:00
m_rfSlowData ( ) ,
m_netSlowData ( ) ,
2016-02-28 18:18:13 +01:00
m_rfN ( 0U ) ,
m_netN ( 0U ) ,
2016-01-27 21:01:50 +01:00
m_networkWatchdog ( 1000U , 0U , 1500U ) ,
2016-02-25 20:54:18 +01:00
m_rfTimeoutTimer ( 1000U , timeout ) ,
m_netTimeoutTimer ( 1000U , timeout ) ,
2016-09-27 07:45:42 +02:00
m_packetTimer ( 1000U , 0U , 300U ) ,
2017-06-16 08:58:30 +02:00
m_ackTimer ( 1000U , 0U , ackTime ) ,
m_errTimer ( 1000U , 0U , ackTime ) ,
2016-03-14 21:55:15 +01:00
m_interval ( ) ,
2016-01-27 21:01:50 +01:00
m_elapsed ( ) ,
2016-02-25 20:54:18 +01:00
m_rfFrames ( 0U ) ,
m_netFrames ( 0U ) ,
m_netLost ( 0U ) ,
2016-01-27 21:01:50 +01:00
m_fec ( ) ,
2016-07-21 18:50:13 +02:00
m_rfBits ( 1U ) ,
m_netBits ( 1U ) ,
2016-02-25 20:54:18 +01:00
m_rfErrs ( 0U ) ,
m_netErrs ( 0U ) ,
2025-03-14 13:13:11 +01:00
m_lastFrame ( nullptr ) ,
2016-07-21 19:09:29 +02:00
m_lastFrameValid ( false ) ,
2017-01-05 20:15:10 +01:00
m_rssiMapper ( rssiMapper ) ,
m_rssi ( 0U ) ,
m_maxRSSI ( 0U ) ,
m_minRSSI ( 0U ) ,
m_aveRSSI ( 0U ) ,
m_rssiCount ( 0U ) ,
2019-01-19 18:15:24 +01:00
m_enabled ( true ) ,
2025-03-14 13:13:11 +01:00
m_fp ( nullptr )
2016-01-27 21:01:50 +01:00
{
2025-03-14 13:13:11 +01:00
assert ( display ! = nullptr ) ;
assert ( rssiMapper ! = nullptr ) ;
2016-01-27 21:01:50 +01:00
m_callsign = new unsigned char [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
m_gateway = new unsigned char [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
m_lastFrame = new unsigned char [ DSTAR_FRAME_LENGTH_BYTES + 1U ] ;
std : : string call = callsign ;
call . resize ( DSTAR_LONG_CALLSIGN_LENGTH - 1U , ' ' ) ;
std : : string mod = module ;
mod . resize ( 1U , ' ' ) ;
call . append ( mod ) ;
std : : string gate = callsign ;
gate . resize ( DSTAR_LONG_CALLSIGN_LENGTH - 1U , ' ' ) ;
gate . append ( " G " ) ;
for ( unsigned int i = 0U ; i < DSTAR_LONG_CALLSIGN_LENGTH ; i + + ) {
m_callsign [ i ] = call . at ( i ) ;
m_gateway [ i ] = gate . at ( i ) ;
}
2016-03-14 21:55:15 +01:00
m_interval . start ( ) ;
2016-01-27 21:01:50 +01:00
}
CDStarControl : : ~ CDStarControl ( )
{
delete [ ] m_callsign ;
delete [ ] m_gateway ;
delete [ ] m_lastFrame ;
}
2016-08-08 22:26:18 +02:00
bool CDStarControl : : writeModem ( unsigned char * data , unsigned int len )
2016-01-27 21:01:50 +01:00
{
2025-03-14 13:13:11 +01:00
assert ( data ! = nullptr ) ;
2016-03-07 21:21:55 +01:00
2019-01-20 18:09:40 +01:00
if ( ! m_enabled )
return false ;
2016-01-27 21:01:50 +01:00
unsigned char type = data [ 0U ] ;
2025-03-13 19:16:33 +01:00
if ( type = = TAG_LOST & & ( ( m_rfState = = RPT_RF_STATE : : AUDIO ) | | ( m_rfState = = RPT_RF_STATE : : DATA ) ) ) {
2019-09-29 18:06:22 +02:00
unsigned char my1 [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
unsigned char my2 [ DSTAR_SHORT_CALLSIGN_LENGTH ] ;
unsigned char your [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
m_rfHeader . getMyCall1 ( my1 ) ;
m_rfHeader . getMyCall2 ( my2 ) ;
m_rfHeader . getYourCall ( your ) ;
2017-01-07 13:25:03 +01:00
if ( m_rssi ! = 0U )
2019-09-29 18:06:22 +02:00
LogMessage ( " D-Star, transmission lost from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm " , my1 , my2 , your , float ( m_rfFrames ) / 50.0F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , m_minRSSI , m_maxRSSI , m_aveRSSI / m_rssiCount ) ;
2017-01-07 13:25:03 +01:00
else
2019-09-29 18:06:22 +02:00
LogMessage ( " D-Star, transmission lost from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%% " , my1 , my2 , your , float ( m_rfFrames ) / 50.0F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
2016-02-25 20:54:18 +01:00
writeEndRF ( ) ;
2016-02-02 19:17:36 +01:00
return false ;
2016-01-27 21:01:50 +01:00
}
2025-03-13 19:16:33 +01:00
if ( ( type = = TAG_LOST ) & & ( m_rfState = = RPT_RF_STATE : : INVALID ) ) {
m_rfState = RPT_RF_STATE : : LISTENING ;
2017-02-16 20:49:58 +01:00
2025-03-13 19:16:33 +01:00
if ( m_netState = = RPT_NET_STATE : : IDLE ) {
2017-02-16 20:49:58 +01:00
if ( m_errorReply )
m_errTimer . start ( ) ;
2025-03-14 13:13:11 +01:00
if ( m_network ! = nullptr )
2017-02-16 20:49:58 +01:00
m_network - > reset ( ) ;
}
return false ;
}
2016-02-02 20:54:51 +01:00
if ( type = = TAG_LOST ) {
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2016-02-02 19:17:36 +01:00
return false ;
2016-01-27 21:01:50 +01:00
}
2017-01-05 20:15:10 +01:00
// Have we got RSSI bytes on the end of a D-Star header?
if ( len = = ( DSTAR_HEADER_LENGTH_BYTES + 3U ) ) {
uint16_t raw = 0U ;
raw | = ( data [ 42U ] < < 8 ) & 0xFF00U ;
raw | = ( data [ 43U ] < < 0 ) & 0x00FFU ;
// Convert the raw RSSI to dBm
int rssi = m_rssiMapper - > interpolate ( raw ) ;
2018-03-06 21:12:29 +01:00
if ( rssi ! = 0 )
LogDebug ( " D-Star, raw RSSI: %u, reported RSSI: %d dBm " , raw , rssi ) ;
2017-01-05 20:15:10 +01:00
// RSSI is always reported as positive
m_rssi = ( rssi > = 0 ) ? rssi : - rssi ;
if ( m_rssi > m_minRSSI )
m_minRSSI = m_rssi ;
if ( m_rssi < m_maxRSSI )
m_maxRSSI = m_rssi ;
m_aveRSSI + = m_rssi ;
m_rssiCount + + ;
}
// Have we got RSSI bytes on the end of D-Star data?
if ( len = = ( DSTAR_FRAME_LENGTH_BYTES + 3U ) ) {
uint16_t raw = 0U ;
raw | = ( data [ 13U ] < < 8 ) & 0xFF00U ;
raw | = ( data [ 14U ] < < 0 ) & 0x00FFU ;
// Convert the raw RSSI to dBm
int rssi = m_rssiMapper - > interpolate ( raw ) ;
2018-03-06 21:12:29 +01:00
if ( rssi ! = 0 )
LogDebug ( " D-Star, raw RSSI: %u, reported RSSI: %d dBm " , raw , rssi ) ;
2017-01-05 20:15:10 +01:00
// RSSI is always reported as positive
m_rssi = ( rssi > = 0 ) ? rssi : - rssi ;
if ( m_rssi > m_minRSSI )
m_minRSSI = m_rssi ;
if ( m_rssi < m_maxRSSI )
m_maxRSSI = m_rssi ;
m_aveRSSI + = m_rssi ;
m_rssiCount + + ;
}
2016-01-27 21:01:50 +01:00
if ( type = = TAG_HEADER ) {
CDStarHeader header ( data + 1U ) ;
2017-02-16 20:49:58 +01:00
m_rfHeader = header ;
2016-01-27 21:01:50 +01:00
2016-04-19 18:15:37 +02:00
unsigned char my1 [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
header . getMyCall1 ( my1 ) ;
2016-01-27 21:01:50 +01:00
// Is this a transmission destined for a repeater?
2016-04-19 18:15:37 +02:00
if ( ! header . isRepeater ( ) ) {
LogMessage ( " D-Star, non repeater RF header received from %8.8s " , my1 ) ;
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : INVALID ;
2021-06-25 10:58:34 +02:00
return true ;
2016-04-19 18:15:37 +02:00
}
2016-04-18 20:15:49 +02:00
unsigned char callsign [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
header . getRPTCall1 ( callsign ) ;
// Is it for us?
2016-04-19 18:15:37 +02:00
if ( : : memcmp ( callsign , m_callsign , DSTAR_LONG_CALLSIGN_LENGTH ) ! = 0 ) {
LogMessage ( " D-Star, received RF header for wrong repeater (%8.8s) from %8.8s " , callsign , my1 ) ;
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : INVALID ;
2021-06-25 10:58:34 +02:00
return true ;
2016-04-19 18:15:37 +02:00
}
2016-04-04 18:40:05 +02:00
2019-10-06 16:15:25 +02:00
if ( m_selfOnly & & : : memcmp ( my1 , m_callsign , DSTAR_LONG_CALLSIGN_LENGTH - 1U ) ! = 0 & & ! ( std : : find_if ( m_whiteList . begin ( ) , m_whiteList . end ( ) , std : : bind ( CallsignCompare , std : : placeholders : : _1 , my1 ) ) ! = m_whiteList . end ( ) ) ) {
2016-04-04 18:40:05 +02:00
LogMessage ( " D-Star, invalid access attempt from %8.8s " , my1 ) ;
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : REJECTED ;
2021-06-25 10:58:34 +02:00
return true ;
2016-04-04 18:40:05 +02:00
}
2016-04-06 19:46:05 +02:00
if ( ! m_selfOnly & & std : : find_if ( m_blackList . begin ( ) , m_blackList . end ( ) , std : : bind ( CallsignCompare , std : : placeholders : : _1 , my1 ) ) ! = m_blackList . end ( ) ) {
LogMessage ( " D-Star, invalid access attempt from %8.8s " , my1 ) ;
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : REJECTED ;
2021-06-25 10:58:34 +02:00
return true ;
2016-04-06 19:46:05 +02:00
}
2016-01-27 21:01:50 +01:00
unsigned char gateway [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
header . getRPTCall2 ( gateway ) ;
unsigned char my2 [ DSTAR_SHORT_CALLSIGN_LENGTH ] ;
header . getMyCall2 ( my2 ) ;
unsigned char your [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
header . getYourCall ( your ) ;
m_net = : : memcmp ( gateway , m_gateway , DSTAR_LONG_CALLSIGN_LENGTH ) = = 0 ;
2016-02-25 20:54:18 +01:00
// Only start the timeout if not already running
2016-02-28 18:40:15 +01:00
if ( ! m_rfTimeoutTimer . isRunning ( ) )
2016-02-25 20:54:18 +01:00
m_rfTimeoutTimer . start ( ) ;
2016-02-01 21:49:52 +01:00
2016-02-25 20:54:18 +01:00
m_ackTimer . stop ( ) ;
2017-02-16 20:49:58 +01:00
m_errTimer . stop ( ) ;
2016-01-27 21:01:50 +01:00
2016-02-28 18:40:15 +01:00
m_rfBits = 1U ;
m_rfErrs = 0U ;
2016-02-25 20:54:18 +01:00
m_rfFrames = 1U ;
2016-02-28 18:18:13 +01:00
m_rfN = 0U ;
2016-01-27 21:01:50 +01:00
2017-01-05 20:15:10 +01:00
m_minRSSI = m_rssi ;
m_maxRSSI = m_rssi ;
m_aveRSSI = m_rssi ;
m_rssiCount = 1U ;
2023-06-16 12:35:16 +02:00
m_rfSlowData . start ( ) ;
2016-02-25 20:54:18 +01:00
if ( m_duplex ) {
// Modify the header
header . setRepeater ( false ) ;
header . setRPTCall1 ( m_callsign ) ;
header . setRPTCall2 ( m_callsign ) ;
header . get ( data + 1U ) ;
2016-01-27 21:01:50 +01:00
2016-02-25 20:54:18 +01:00
writeQueueHeaderRF ( data ) ;
}
2016-01-27 21:01:50 +01:00
2016-02-25 20:54:18 +01:00
if ( m_net ) {
// Modify the header
header . setRepeater ( false ) ;
header . setRPTCall1 ( m_callsign ) ;
header . setRPTCall2 ( m_gateway ) ;
header . get ( data + 1U ) ;
2016-01-27 21:01:50 +01:00
2016-02-28 18:40:15 +01:00
writeNetworkHeaderRF ( data ) ;
2016-02-25 20:54:18 +01:00
}
2016-01-27 21:01:50 +01:00
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : AUDIO ;
2016-01-27 21:01:50 +01:00
2025-03-13 19:16:33 +01:00
if ( m_netState = = RPT_NET_STATE : : IDLE ) {
2016-04-16 22:31:49 +02:00
m_display - > writeDStar ( ( char * ) my1 , ( char * ) my2 , ( char * ) your , " R " , " " ) ;
2017-01-05 20:44:52 +01:00
m_display - > writeDStarRSSI ( m_rssi ) ;
}
2016-01-27 21:01:50 +01:00
2016-02-25 20:54:18 +01:00
LogMessage ( " D-Star, received RF header from %8.8s/%4.4s to %8.8s " , my1 , my2 , your ) ;
2016-01-27 21:01:50 +01:00
} else if ( type = = TAG_EOT ) {
2025-03-13 19:16:33 +01:00
if ( m_rfState = = RPT_RF_STATE : : REJECTED ) {
m_rfState = RPT_RF_STATE : : LISTENING ;
} else if ( m_rfState = = RPT_RF_STATE : : INVALID ) {
m_rfState = RPT_RF_STATE : : LISTENING ;
2017-02-16 20:49:58 +01:00
2025-03-13 19:16:33 +01:00
if ( m_netState = = RPT_NET_STATE : : IDLE ) {
2017-02-16 20:49:58 +01:00
if ( m_errorReply )
m_errTimer . start ( ) ;
2025-03-14 13:13:11 +01:00
if ( m_network ! = nullptr )
2017-02-16 20:49:58 +01:00
m_network - > reset ( ) ;
}
return false ;
2025-03-13 19:16:33 +01:00
} else if ( ( m_rfState = = RPT_RF_STATE : : AUDIO ) | | ( m_rfState = = RPT_RF_STATE : : DATA ) ) {
2016-02-18 20:57:11 +01:00
if ( m_net )
2016-02-25 20:54:18 +01:00
writeNetworkDataRF ( DSTAR_END_PATTERN_BYTES , 0U , true ) ;
2016-01-27 21:01:50 +01:00
2016-02-17 08:23:41 +01:00
if ( m_duplex )
2016-02-25 20:54:18 +01:00
writeQueueEOTRF ( ) ;
2016-02-01 21:49:52 +01:00
2019-09-29 18:06:22 +02:00
unsigned char my1 [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
unsigned char my2 [ DSTAR_SHORT_CALLSIGN_LENGTH ] ;
unsigned char your [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
m_rfHeader . getMyCall1 ( my1 ) ;
m_rfHeader . getMyCall2 ( my2 ) ;
m_rfHeader . getYourCall ( your ) ;
2017-01-07 13:25:03 +01:00
if ( m_rssi ! = 0U )
2019-09-29 18:06:22 +02:00
LogMessage ( " D-Star, received RF end of transmission from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm " , my1 , my2 , your , float ( m_rfFrames ) / 50.0F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , m_minRSSI , m_maxRSSI , m_aveRSSI / m_rssiCount ) ;
2017-01-07 13:25:03 +01:00
else
2019-09-29 18:06:22 +02:00
LogMessage ( " D-Star, received RF end of transmission from %8.8s/%4.4s to %8.8s, %.1f seconds, BER: %.1f%% " , my1 , my2 , your , float ( m_rfFrames ) / 50.0F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
2016-01-27 21:01:50 +01:00
2016-02-25 20:54:18 +01:00
writeEndRF ( ) ;
2016-01-27 21:01:50 +01:00
}
2016-02-02 19:17:36 +01:00
return false ;
2021-08-10 20:54:14 +02:00
} else if ( type = = TAG_DATA ) {
2025-03-13 19:16:33 +01:00
if ( m_rfState = = RPT_RF_STATE : : REJECTED )
2021-06-25 17:21:48 +02:00
return true ;
2025-02-27 13:46:25 +01:00
2025-03-13 19:16:33 +01:00
if ( m_rfState = = RPT_RF_STATE : : INVALID )
2021-06-25 17:21:48 +02:00
return true ;
2025-02-27 13:46:25 +01:00
2025-03-13 19:16:33 +01:00
if ( m_rfState = = RPT_RF_STATE : : LISTENING ) {
2016-02-02 20:54:51 +01:00
// The sync is regenerated by the modem so can do exact match
if ( : : memcmp ( data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES , DSTAR_SYNC_BYTES , DSTAR_DATA_FRAME_LENGTH_BYTES ) = = 0 ) {
2023-06-16 12:35:16 +02:00
m_rfSlowData . start ( ) ;
2025-03-12 13:55:25 +01:00
m_rfN = 0U ;
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : LATE_ENTRY ;
2016-01-27 21:01:50 +01:00
}
2016-02-02 19:17:36 +01:00
return false ;
2025-02-27 13:46:25 +01:00
}
2025-03-13 19:16:33 +01:00
if ( ( m_rfState = = RPT_RF_STATE : : AUDIO ) | | ( m_rfState = = RPT_RF_STATE : : DATA ) ) {
2018-11-12 12:57:34 +01:00
// The sync is regenerated by the modem so can do exact match
2023-06-16 12:35:16 +02:00
if ( : : memcmp ( data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES , DSTAR_SYNC_BYTES , DSTAR_DATA_FRAME_LENGTH_BYTES ) = = 0 ) {
m_rfSlowData . start ( ) ;
2018-11-12 12:57:34 +01:00
m_rfN = 0U ;
2023-06-16 12:35:16 +02:00
}
2025-03-12 13:55:25 +01:00
}
2018-11-12 12:57:34 +01:00
2025-03-13 19:16:33 +01:00
if ( m_rfState = = RPT_RF_STATE : : AUDIO ) {
2025-03-12 13:55:25 +01:00
m_rfSlowData . peakSlowData ( data + 1U , m_rfN ) ;
unsigned char type = m_rfSlowData . getType ( ) ;
if ( type = = DSTAR_SLOW_DATA_TYPE_FASTDATA_BEGIN ) {
LogMessage ( " D-Star, starting fast data mode " ) ;
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : DATA ;
2025-03-12 13:55:25 +01:00
}
}
2025-03-13 19:16:33 +01:00
if ( m_rfState = = RPT_RF_STATE : : DATA ) {
2025-03-12 13:55:25 +01:00
LogDebug ( " D-Star, fast data sequence no. %u " , m_rfN ) ;
m_rfBits + = 48U ;
m_rfFrames + + ;
if ( m_net )
writeNetworkDataRF ( data , 0U , false ) ;
if ( m_duplex )
writeQueueDataRF ( data ) ;
m_rfSlowData . peakSlowData ( data + 1U , m_rfN ) ;
bool complete = m_rfSlowData . isComplete ( ) ;
if ( complete ) {
unsigned char type = m_rfSlowData . getType ( ) ;
if ( type = = DSTAR_SLOW_DATA_TYPE_FASTDATA_END ) {
LogMessage ( " D-Star, leaving fast data mode " ) ;
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : AUDIO ;
2025-03-12 13:55:25 +01:00
}
2018-11-12 12:57:34 +01:00
}
2025-03-12 13:55:25 +01:00
m_rfN = ( m_rfN + 1U ) % 21U ;
2025-03-13 19:16:33 +01:00
} else if ( m_rfState = = RPT_RF_STATE : : AUDIO ) {
2025-03-12 13:55:25 +01:00
// Send the RSSI data to the display
if ( m_rfN = = 0U )
m_display - > writeDStarRSSI ( m_rssi ) ;
2016-10-05 08:42:41 +02:00
unsigned int errors = 0U ;
2025-03-14 13:13:11 +01:00
if ( : : memcmp ( data + 1U , DSTAR_nullptr_AMBE_DATA_BYTES_SCRAMBLED , DSTAR_VOICE_FRAME_LENGTH_BYTES ) = = 0 ) {
2025-03-12 13:55:25 +01:00
LogDebug ( " D-Star, audio sequence no. %u, null audio " , m_rfN ) ;
} else {
2025-02-27 13:46:25 +01:00
errors = m_fec . regenerateDStar ( data + 1U ) ;
2025-03-12 13:55:25 +01:00
LogDebug ( " D-Star, audio sequence no. %u, errs: %u/48 (%.1f%%) " , m_rfN , errors , float ( errors ) / 0.48F ) ;
2017-02-12 19:37:00 +01:00
m_display - > writeDStarBER ( float ( errors ) / 0.48F ) ;
}
2016-02-18 19:18:37 +01:00
2025-03-12 13:55:25 +01:00
m_rfErrs + = errors ;
2016-02-25 20:54:18 +01:00
m_rfBits + = 48U ;
m_rfFrames + + ;
2016-02-01 22:44:40 +01:00
2025-02-27 13:46:25 +01:00
if ( m_rfN ! = 0U ) {
2025-03-12 13:55:25 +01:00
const unsigned char * text = m_rfSlowData . addText ( data + 1U , m_rfN ) ;
2025-03-14 13:13:11 +01:00
if ( text ! = nullptr )
2025-02-27 13:46:25 +01:00
LogMessage ( " D-Star, RF slow data text = \" %s \" " , text ) ;
2017-01-05 20:44:52 +01:00
}
2016-01-27 21:01:50 +01:00
2025-02-27 13:46:25 +01:00
if ( m_net )
writeNetworkDataRF ( data , errors , false ) ;
2016-01-27 21:01:50 +01:00
if ( m_duplex ) {
2025-02-27 13:46:25 +01:00
blankDTMF ( data + 1U ) ;
writeQueueDataRF ( data ) ;
2016-01-27 21:01:50 +01:00
}
2016-03-21 19:13:21 +01:00
m_rfN = ( m_rfN + 1U ) % 21U ;
2025-03-12 13:55:25 +01:00
}
2025-03-13 19:16:33 +01:00
if ( m_rfState = = RPT_RF_STATE : : LATE_ENTRY ) {
2016-02-02 20:54:51 +01:00
// The sync is regenerated by the modem so can do exact match
if ( : : memcmp ( data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES , DSTAR_SYNC_BYTES , DSTAR_DATA_FRAME_LENGTH_BYTES ) = = 0 ) {
2023-06-16 12:35:16 +02:00
m_rfSlowData . reset ( ) ;
2025-03-12 13:55:25 +01:00
m_rfN = 0U ;
2016-02-02 19:17:36 +01:00
return false ;
2025-02-27 13:46:25 +01:00
} else {
2025-03-12 13:55:25 +01:00
CDStarHeader * header = m_rfSlowData . addHeader ( data + 1U , m_rfN ) ;
2025-03-14 13:13:11 +01:00
if ( header = = nullptr ) {
2025-03-12 13:55:25 +01:00
m_rfN = ( m_rfN + 1U ) % 21U ;
2025-02-27 13:46:25 +01:00
return false ;
2025-03-12 13:55:25 +01:00
}
2016-01-27 21:01:50 +01:00
2025-02-27 13:46:25 +01:00
m_rfHeader = * header ;
delete header ;
}
2017-02-16 20:49:58 +01:00
unsigned char my1 [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
2025-02-27 13:46:25 +01:00
m_rfHeader . getMyCall1 ( my1 ) ;
2017-02-16 20:49:58 +01:00
2016-01-27 21:01:50 +01:00
// Is this a transmission destined for a repeater?
2025-02-27 13:46:25 +01:00
if ( ! m_rfHeader . isRepeater ( ) ) {
2017-02-16 20:49:58 +01:00
LogMessage ( " D-Star, non repeater RF header received from %8.8s " , my1 ) ;
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : INVALID ;
2021-06-25 10:58:34 +02:00
return true ;
2016-04-18 20:15:49 +02:00
}
unsigned char callsign [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
2025-02-27 13:46:25 +01:00
m_rfHeader . getRPTCall1 ( callsign ) ;
2016-04-18 20:15:49 +02:00
// Is it for us?
if ( : : memcmp ( callsign , m_callsign , DSTAR_LONG_CALLSIGN_LENGTH ) ! = 0 ) {
2017-02-16 20:49:58 +01:00
LogMessage ( " D-Star, received RF header for wrong repeater (%8.8s) from %8.8s " , callsign , my1 ) ;
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : INVALID ;
2021-06-25 10:58:34 +02:00
return true ;
2016-04-06 19:46:05 +02:00
}
2019-10-06 16:15:25 +02:00
if ( m_selfOnly & & : : memcmp ( my1 , m_callsign , DSTAR_LONG_CALLSIGN_LENGTH - 1U ) ! = 0 & & ! ( std : : find_if ( m_whiteList . begin ( ) , m_whiteList . end ( ) , std : : bind ( CallsignCompare , std : : placeholders : : _1 , my1 ) ) ! = m_whiteList . end ( ) ) ) {
2017-02-16 20:49:58 +01:00
LogMessage ( " D-Star, invalid access attempt from %8.8s " , my1 ) ;
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : REJECTED ;
2021-06-25 10:58:34 +02:00
return true ;
2016-04-06 19:46:05 +02:00
}
if ( ! m_selfOnly & & std : : find_if ( m_blackList . begin ( ) , m_blackList . end ( ) , std : : bind ( CallsignCompare , std : : placeholders : : _1 , my1 ) ) ! = m_blackList . end ( ) ) {
2017-02-16 20:49:58 +01:00
LogMessage ( " D-Star, invalid access attempt from %8.8s " , my1 ) ;
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : REJECTED ;
2021-06-25 10:58:34 +02:00
return true ;
2016-02-03 08:20:48 +01:00
}
2016-01-27 21:01:50 +01:00
unsigned char gateway [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
2025-02-27 13:46:25 +01:00
m_rfHeader . getRPTCall2 ( gateway ) ;
2016-01-27 21:01:50 +01:00
unsigned char my2 [ DSTAR_SHORT_CALLSIGN_LENGTH ] ;
2025-02-27 13:46:25 +01:00
m_rfHeader . getMyCall2 ( my2 ) ;
2016-01-27 21:01:50 +01:00
unsigned char your [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
2025-02-27 13:46:25 +01:00
m_rfHeader . getYourCall ( your ) ;
2016-01-27 21:01:50 +01:00
m_net = : : memcmp ( gateway , m_gateway , DSTAR_LONG_CALLSIGN_LENGTH ) = = 0 ;
2016-02-02 20:54:51 +01:00
// Only reset the timeout if the timeout is not running
2016-02-28 18:40:15 +01:00
if ( ! m_rfTimeoutTimer . isRunning ( ) )
2016-02-25 20:54:18 +01:00
m_rfTimeoutTimer . start ( ) ;
2016-02-01 21:49:52 +01:00
2016-01-27 21:01:50 +01:00
// Create a dummy start frame to replace the received frame
2016-02-01 21:49:52 +01:00
m_ackTimer . stop ( ) ;
2017-02-16 20:49:58 +01:00
m_errTimer . stop ( ) ;
2016-01-27 21:01:50 +01:00
2016-02-28 18:40:15 +01:00
m_rfBits = 1U ;
m_rfErrs = 0U ;
2016-02-28 18:18:13 +01:00
m_rfN = 0U ;
2016-02-25 20:54:18 +01:00
m_rfFrames = 1U ;
2016-01-27 21:01:50 +01:00
2017-01-05 20:15:10 +01:00
m_minRSSI = m_rssi ;
m_maxRSSI = m_rssi ;
m_aveRSSI = m_rssi ;
m_rssiCount = 1U ;
2016-01-27 21:01:50 +01:00
if ( m_duplex ) {
unsigned char start [ DSTAR_HEADER_LENGTH_BYTES + 1U ] ;
start [ 0U ] = TAG_HEADER ;
// Modify the header
2025-02-27 13:46:25 +01:00
CDStarHeader header ( m_rfHeader ) ;
header . setRepeater ( false ) ;
header . setRPTCall1 ( m_callsign ) ;
header . setRPTCall2 ( m_callsign ) ;
header . get ( start + 1U ) ;
2016-01-27 21:01:50 +01:00
2016-02-25 20:54:18 +01:00
writeQueueHeaderRF ( start ) ;
2016-01-27 21:01:50 +01:00
}
if ( m_net ) {
unsigned char start [ DSTAR_HEADER_LENGTH_BYTES + 1U ] ;
start [ 0U ] = TAG_HEADER ;
// Modify the header
2025-02-27 13:46:25 +01:00
CDStarHeader header ( m_rfHeader ) ;
header . setRepeater ( false ) ;
header . setRPTCall1 ( m_callsign ) ;
header . setRPTCall2 ( m_gateway ) ;
header . get ( start + 1U ) ;
2016-01-27 21:01:50 +01:00
2016-02-28 18:40:15 +01:00
writeNetworkHeaderRF ( start ) ;
2016-01-27 21:01:50 +01:00
}
2016-10-05 08:42:41 +02:00
unsigned int errors = 0U ;
2025-03-14 13:13:11 +01:00
if ( : : memcmp ( data + 1U , DSTAR_nullptr_AMBE_DATA_BYTES_SCRAMBLED , DSTAR_VOICE_FRAME_LENGTH_BYTES ) = = 0 ) {
2025-03-12 13:55:25 +01:00
LogDebug ( " D-Star, audio sequence no. %u, null audio " , m_rfN ) ;
} else {
2025-02-27 13:46:25 +01:00
errors = m_fec . regenerateDStar ( data + 1U ) ;
LogDebug ( " D-Star, audio sequence no. %u, errs: %u/48 (%.1f%%) " , m_rfN , errors , float ( errors ) / 0.48F ) ;
2017-02-12 19:37:00 +01:00
}
2016-02-18 19:18:37 +01:00
2025-03-12 13:55:25 +01:00
m_rfErrs + = errors ;
2016-02-25 20:54:18 +01:00
m_rfBits + = 48U ;
2016-01-27 21:01:50 +01:00
if ( m_net )
2016-02-25 20:54:18 +01:00
writeNetworkDataRF ( data , errors , false ) ;
2016-01-27 21:01:50 +01:00
2025-02-27 13:46:25 +01:00
if ( m_duplex ) {
blankDTMF ( data + 1U ) ;
2016-02-25 20:54:18 +01:00
writeQueueDataRF ( data ) ;
2025-02-27 13:46:25 +01:00
}
2016-01-27 21:01:50 +01:00
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : AUDIO ;
2016-01-27 21:01:50 +01:00
2025-03-13 19:16:33 +01:00
if ( m_netState = = RPT_NET_STATE : : IDLE ) {
2016-04-16 22:31:49 +02:00
m_display - > writeDStar ( ( char * ) my1 , ( char * ) my2 , ( char * ) your , " R " , " " ) ;
2017-01-05 20:44:52 +01:00
m_display - > writeDStarRSSI ( m_rssi ) ;
2017-02-12 19:37:00 +01:00
m_display - > writeDStarBER ( float ( errors ) / 0.48F ) ;
2017-01-05 20:44:52 +01:00
}
2016-01-27 21:01:50 +01:00
2016-02-01 23:33:09 +01:00
LogMessage ( " D-Star, received RF late entry from %8.8s/%4.4s to %8.8s " , my1 , my2 , your ) ;
2025-03-12 13:55:25 +01:00
m_rfN = ( m_rfN + 1U ) % 21U ;
2016-01-27 21:01:50 +01:00
}
2016-02-03 08:20:48 +01:00
} else {
CUtils : : dump ( " D-Star, unknown data from modem " , data , DSTAR_FRAME_LENGTH_BYTES + 1U ) ;
2016-01-27 21:01:50 +01:00
}
2016-02-02 19:17:36 +01:00
return true ;
2016-01-27 21:01:50 +01:00
}
unsigned int CDStarControl : : readModem ( unsigned char * data )
{
2025-03-14 13:13:11 +01:00
assert ( data ! = nullptr ) ;
2016-03-07 21:21:55 +01:00
2016-03-07 19:08:50 +01:00
if ( m_queue . isEmpty ( ) )
2016-01-27 21:01:50 +01:00
return 0U ;
unsigned char len = 0U ;
2016-03-07 19:08:50 +01:00
m_queue . getData ( & len , 1U ) ;
2016-01-27 21:01:50 +01:00
2016-03-07 19:08:50 +01:00
m_queue . getData ( data , len ) ;
2016-01-27 21:01:50 +01:00
return len ;
}
2016-02-25 20:54:18 +01:00
void CDStarControl : : writeEndRF ( )
2016-01-27 21:01:50 +01:00
{
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2016-02-25 20:54:18 +01:00
2023-01-11 20:35:23 +01:00
m_rfTimeoutTimer . stop ( ) ;
2025-03-13 19:16:33 +01:00
if ( m_netState = = RPT_NET_STATE : : IDLE ) {
2016-02-25 20:54:18 +01:00
m_display - > clearDStar ( ) ;
2017-06-10 14:18:18 +02:00
2017-07-27 17:39:14 +02:00
m_ackTimer . start ( ) ;
2016-02-25 20:54:18 +01:00
2025-03-14 13:13:11 +01:00
if ( m_network ! = nullptr )
2016-02-25 20:54:18 +01:00
m_network - > reset ( ) ;
}
}
void CDStarControl : : writeEndNet ( )
{
2025-03-13 19:16:33 +01:00
m_netState = RPT_NET_STATE : : IDLE ;
2016-01-27 21:01:50 +01:00
2016-07-21 19:09:29 +02:00
m_lastFrameValid = false ;
2016-01-27 21:01:50 +01:00
m_display - > clearDStar ( ) ;
2016-02-25 20:54:18 +01:00
m_netTimeoutTimer . stop ( ) ;
2016-01-27 21:01:50 +01:00
m_networkWatchdog . stop ( ) ;
2016-07-15 07:32:56 +02:00
m_packetTimer . stop ( ) ;
2016-01-27 21:01:50 +01:00
2025-03-14 13:13:11 +01:00
if ( m_network ! = nullptr )
2016-02-04 23:41:38 +01:00
m_network - > reset ( ) ;
2016-01-27 21:01:50 +01:00
# if defined(DUMP_DSTAR)
closeFile ( ) ;
# endif
}
void CDStarControl : : writeNetwork ( )
{
2025-03-14 13:13:11 +01:00
assert ( m_network ! = nullptr ) ;
2016-01-27 21:01:50 +01:00
unsigned char data [ DSTAR_HEADER_LENGTH_BYTES + 2U ] ;
unsigned int length = m_network - > read ( data , DSTAR_HEADER_LENGTH_BYTES + 2U ) ;
if ( length = = 0U )
return ;
2019-01-20 18:09:40 +01:00
if ( ! m_enabled )
return ;
2025-03-13 19:16:33 +01:00
if ( ( ( m_rfState = = RPT_RF_STATE : : AUDIO ) | | ( m_rfState = = RPT_RF_STATE : : DATA ) ) & & ( m_netState = = RPT_NET_STATE : : IDLE ) )
2016-01-27 21:01:50 +01:00
return ;
m_networkWatchdog . start ( ) ;
unsigned char type = data [ 0U ] ;
if ( type = = TAG_HEADER ) {
2025-03-13 19:16:33 +01:00
if ( m_netState ! = RPT_NET_STATE : : IDLE )
2016-01-27 21:01:50 +01:00
return ;
2016-01-31 19:11:12 +01:00
CDStarHeader header ( data + 1U ) ;
2016-01-27 21:01:50 +01:00
unsigned char my1 [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
header . getMyCall1 ( my1 ) ;
unsigned char my2 [ DSTAR_SHORT_CALLSIGN_LENGTH ] ;
header . getMyCall2 ( my2 ) ;
unsigned char your [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
header . getYourCall ( your ) ;
2016-02-25 20:54:18 +01:00
m_netHeader = header ;
2016-02-01 21:49:52 +01:00
2016-02-25 20:54:18 +01:00
m_netTimeoutTimer . start ( ) ;
2016-07-15 07:32:56 +02:00
m_packetTimer . start ( ) ;
2016-02-01 21:49:52 +01:00
m_ackTimer . stop ( ) ;
2017-02-16 20:49:58 +01:00
m_errTimer . stop ( ) ;
2016-01-27 21:01:50 +01:00
2016-07-21 19:09:29 +02:00
m_lastFrameValid = false ;
2016-02-25 20:54:18 +01:00
m_netFrames = 0U ;
2016-07-13 08:19:08 +02:00
m_netLost = 0U ;
2016-01-27 21:01:50 +01:00
2016-07-13 08:19:08 +02:00
m_netN = 20U ;
2016-01-27 21:01:50 +01:00
2016-07-13 08:19:08 +02:00
m_netBits = 1U ;
m_netErrs = 0U ;
2016-01-27 21:01:50 +01:00
2017-11-09 10:21:22 +01:00
if ( m_remoteGateway ) {
header . setRepeater ( true ) ;
header . setRPTCall1 ( m_callsign ) ;
header . setRPTCall2 ( m_callsign ) ;
header . get ( data + 1U ) ;
}
2016-02-25 20:54:18 +01:00
writeQueueHeaderNet ( data ) ;
2016-01-27 21:01:50 +01:00
# if defined(DUMP_DSTAR)
openFile ( ) ;
writeFile ( data + 1U , length - 1U ) ;
# endif
2025-03-13 19:16:33 +01:00
m_netState = RPT_NET_STATE : : AUDIO ;
2016-01-27 21:01:50 +01:00
2025-03-13 19:16:33 +01:00
LINK_STATUS status = LINK_STATUS : : NONE ;
2016-04-16 21:45:49 +02:00
unsigned char reflector [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
2016-04-20 22:33:06 +02:00
m_network - > getStatus ( status , reflector ) ;
2025-03-13 19:16:33 +01:00
if ( ( status = = LINK_STATUS : : LINKED_DEXTRA ) | | ( status = = LINK_STATUS : : LINKED_DPLUS ) | | ( status = = LINK_STATUS : : LINKED_DCS ) | | ( status = = LINK_STATUS : : LINKED_CCS ) | | ( status = = LINK_STATUS : : LINKED_LOOPBACK ) ) {
2016-04-20 22:33:06 +02:00
m_display - > writeDStar ( ( char * ) my1 , ( char * ) my2 , ( char * ) your , " N " , ( char * ) reflector ) ;
2016-04-16 21:45:49 +02:00
LogMessage ( " D-Star, received network header from %8.8s/%4.4s to %8.8s via %8.8s " , my1 , my2 , your , reflector ) ;
2016-04-20 22:33:06 +02:00
} else {
m_display - > writeDStar ( ( char * ) my1 , ( char * ) my2 , ( char * ) your , " N " , ( char * ) " " ) ;
LogMessage ( " D-Star, received network header from %8.8s/%4.4s to %8.8s " , my1 , my2 , your ) ;
2016-09-28 00:09:19 +02:00
}
2016-09-29 21:23:19 +02:00
m_elapsed . start ( ) ;
2016-01-27 21:01:50 +01:00
} else if ( type = = TAG_EOT ) {
2025-03-13 19:16:33 +01:00
if ( ( m_netState ! = RPT_NET_STATE : : AUDIO ) & & ( m_netState ! = RPT_NET_STATE : : DATA ) )
2020-11-28 00:01:02 +01:00
return ;
2016-02-17 08:23:41 +01:00
2020-11-28 00:01:02 +01:00
writeQueueEOTNet ( ) ;
2016-02-17 08:23:41 +01:00
2020-11-28 00:01:02 +01:00
data [ 1U ] = TAG_EOT ;
2016-01-27 21:01:50 +01:00
# if defined(DUMP_DSTAR)
2020-11-28 00:01:02 +01:00
writeFile ( data + 1U , length - 1U ) ;
closeFile ( ) ;
2016-01-27 21:01:50 +01:00
# endif
2020-11-28 00:01:02 +01:00
unsigned char my1 [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
unsigned char my2 [ DSTAR_SHORT_CALLSIGN_LENGTH ] ;
unsigned char your [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
m_netHeader . getMyCall1 ( my1 ) ;
m_netHeader . getMyCall2 ( my2 ) ;
m_netHeader . getYourCall ( your ) ;
2016-07-12 08:05:48 +02:00
2020-11-28 00:01:02 +01:00
// We've received the header and EOT haven't we?
m_netFrames + = 2U ;
LogMessage ( " D-Star, received network end of transmission from %8.8s/%4.4s to %8.8s, %.1f seconds, %u%% packet loss, BER: %.1f%% " , my1 , my2 , your , float ( m_netFrames ) / 50.0F , ( m_netLost * 100U ) / m_netFrames , float ( m_netErrs * 100U ) / float ( m_netBits ) ) ;
2016-01-27 21:01:50 +01:00
2020-11-28 00:01:02 +01:00
writeEndNet ( ) ;
2021-08-10 20:54:14 +02:00
} else if ( type = = TAG_DATA ) {
2025-03-13 19:16:33 +01:00
if ( ( m_netState = = RPT_NET_STATE : : AUDIO ) | | ( m_netState = = RPT_NET_STATE : : DATA ) ) {
2025-03-12 13:55:25 +01:00
unsigned char n = data [ 1U ] ;
2019-01-27 21:15:14 +01:00
2025-03-12 13:55:25 +01:00
// The sync is regenerated by the modem so can do exact match
if ( : : memcmp ( data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES , DSTAR_SYNC_BYTES , DSTAR_DATA_FRAME_LENGTH_BYTES ) = = 0 ) {
m_netSlowData . start ( ) ;
} else {
m_netSlowData . peakSlowData ( data + 1U , n ) ;
2025-03-13 19:16:33 +01:00
if ( m_netState = = RPT_NET_STATE : : AUDIO ) {
2025-03-12 13:55:25 +01:00
unsigned char type = m_netSlowData . getType ( ) ;
if ( type = = DSTAR_SLOW_DATA_TYPE_FASTDATA_BEGIN ) {
LogMessage ( " D-Star, starting fast data mode " ) ;
2025-03-13 19:16:33 +01:00
m_netState = RPT_NET_STATE : : DATA ;
2025-03-12 13:55:25 +01:00
}
}
}
}
2016-01-27 21:01:50 +01:00
2025-03-13 19:16:33 +01:00
if ( m_netState = = RPT_NET_STATE : : AUDIO ) {
2025-03-12 13:55:25 +01:00
unsigned char n = data [ 1U ] ;
2025-02-27 13:46:25 +01:00
2025-03-12 13:55:25 +01:00
unsigned int errors = 0U ;
2025-03-14 13:13:11 +01:00
if ( : : memcmp ( data + 2U , DSTAR_nullptr_AMBE_DATA_BYTES_SCRAMBLED , DSTAR_VOICE_FRAME_LENGTH_BYTES ) ! = 0 ) {
2025-03-12 13:55:25 +01:00
errors = m_fec . regenerateDStar ( data + 2U ) ;
blankDTMF ( data + 2U ) ;
}
2025-02-27 13:46:25 +01:00
2025-03-12 13:55:25 +01:00
data [ 1U ] = TAG_DATA ;
2018-11-12 12:57:34 +01:00
2025-03-12 13:55:25 +01:00
// Insert silence and reject if in the past
bool ret = insertSilence ( data + 1U , n ) ;
if ( ! ret )
return ;
2018-11-12 12:57:34 +01:00
2025-03-12 13:55:25 +01:00
m_netErrs + = errors ;
m_netBits + = 48U ;
2018-11-12 12:57:34 +01:00
2025-03-12 13:55:25 +01:00
m_netN = n ;
2019-01-28 08:50:43 +01:00
2025-03-12 13:55:25 +01:00
// Regenerate the sync
if ( m_netN = = 0U ) {
CSync : : addDStarSync ( data + 2U ) ;
m_netSlowData . start ( ) ;
} else {
const unsigned char * text = m_netSlowData . addText ( data + 2U , m_netN ) ;
2025-03-14 13:13:11 +01:00
if ( text ! = nullptr )
2025-03-12 13:55:25 +01:00
LogMessage ( " D-Star, network slow data text = \" %s \" " , text ) ;
}
m_packetTimer . start ( ) ;
m_netFrames + + ;
2023-06-16 12:35:16 +02:00
2025-03-12 13:55:25 +01:00
# if defined(DUMP_DSTAR)
writeFile ( data + 1U , length - 1U ) ;
# endif
writeQueueDataNet ( data + 1U ) ;
2025-02-27 13:46:25 +01:00
}
2018-11-12 12:57:34 +01:00
2025-03-13 19:16:33 +01:00
if ( m_netState = = RPT_NET_STATE : : DATA ) {
2025-03-12 13:55:25 +01:00
m_netN = data [ 1U ] ;
data [ 1U ] = TAG_DATA ;
m_netBits + = 48U ;
// Regenerate the sync
if ( m_netN = = 0U ) {
CSync : : addDStarSync ( data + 2U ) ;
m_netSlowData . start ( ) ;
} else {
m_netSlowData . peakSlowData ( data + 2U , m_netN ) ;
bool complete = m_netSlowData . isComplete ( ) ;
if ( complete ) {
unsigned char type = m_netSlowData . getType ( ) ;
if ( type = = DSTAR_SLOW_DATA_TYPE_FASTDATA_END ) {
LogMessage ( " D-Star, leaving fast data mode " ) ;
2025-03-13 19:16:33 +01:00
m_netState = RPT_NET_STATE : : AUDIO ;
2025-03-12 13:55:25 +01:00
}
}
}
m_packetTimer . start ( ) ;
m_netFrames + + ;
2016-01-27 21:01:50 +01:00
# if defined(DUMP_DSTAR)
2025-03-12 13:55:25 +01:00
writeFile ( data + 1U , length - 1U ) ;
2016-01-27 21:01:50 +01:00
# endif
2025-03-12 13:55:25 +01:00
writeQueueDataNet ( data + 1U ) ;
}
2016-02-03 08:20:48 +01:00
} else {
CUtils : : dump ( " D-Star, unknown data from network " , data , DSTAR_FRAME_LENGTH_BYTES + 1U ) ;
2016-01-27 21:01:50 +01:00
}
}
2016-03-14 21:55:15 +01:00
void CDStarControl : : clock ( )
2016-01-27 21:01:50 +01:00
{
2016-03-14 21:55:15 +01:00
unsigned int ms = m_interval . elapsed ( ) ;
m_interval . start ( ) ;
2025-03-14 13:13:11 +01:00
if ( m_network ! = nullptr )
2016-01-27 21:01:50 +01:00
writeNetwork ( ) ;
2016-02-01 21:49:52 +01:00
m_ackTimer . clock ( ms ) ;
if ( m_ackTimer . isRunning ( ) & & m_ackTimer . hasExpired ( ) ) {
sendAck ( ) ;
m_ackTimer . stop ( ) ;
}
2017-02-16 20:49:58 +01:00
m_errTimer . clock ( ms ) ;
if ( m_errTimer . isRunning ( ) & & m_errTimer . hasExpired ( ) ) {
sendError ( ) ;
m_errTimer . stop ( ) ;
}
2016-02-25 20:54:18 +01:00
m_rfTimeoutTimer . clock ( ms ) ;
m_netTimeoutTimer . clock ( ms ) ;
2016-01-27 21:01:50 +01:00
2025-03-13 19:16:33 +01:00
if ( m_netState = = RPT_NET_STATE : : AUDIO ) {
2016-01-27 21:01:50 +01:00
m_networkWatchdog . clock ( ms ) ;
if ( m_networkWatchdog . hasExpired ( ) ) {
2016-01-27 21:14:02 +01:00
// We're received the header haven't we?
2016-02-25 20:54:18 +01:00
m_netFrames + = 1U ;
LogMessage ( " D-Star, network watchdog has expired, %.1f seconds, %u%% packet loss, BER: %.1f%% " , float ( m_netFrames ) / 50.0F , ( m_netLost * 100U ) / m_netFrames , float ( m_netErrs * 100U ) / float ( m_netBits ) ) ;
writeEndNet ( ) ;
2016-01-27 21:01:50 +01:00
# if defined(DUMP_DSTAR)
closeFile ( ) ;
# endif
}
}
2018-11-16 11:23:29 +01:00
// Only insert silence on audio data
2025-03-13 19:16:33 +01:00
if ( m_netState = = RPT_NET_STATE : : AUDIO ) {
2016-07-15 07:32:56 +02:00
m_packetTimer . clock ( ms ) ;
if ( m_packetTimer . isRunning ( ) & & m_packetTimer . hasExpired ( ) ) {
unsigned int elapsed = m_elapsed . elapsed ( ) ;
unsigned int frames = elapsed / DSTAR_FRAME_TIME ;
if ( frames > m_netFrames ) {
unsigned int count = frames - m_netFrames ;
2016-09-26 19:10:51 +02:00
if ( count > 15U ) {
LogDebug ( " D-Star, lost audio for 300ms filling in, elapsed: %ums, expected: %u, received: %u " , elapsed , frames , m_netFrames ) ;
2016-07-15 07:32:56 +02:00
insertSilence ( count - 2U ) ;
}
2016-01-27 21:01:50 +01:00
}
2016-07-15 07:32:56 +02:00
m_packetTimer . start ( ) ;
2016-01-27 21:01:50 +01:00
}
}
}
2016-02-25 20:54:18 +01:00
void CDStarControl : : writeQueueHeaderRF ( const unsigned char * data )
{
2025-03-14 13:13:11 +01:00
assert ( data ! = nullptr ) ;
2016-02-25 20:54:18 +01:00
2025-03-13 19:16:33 +01:00
if ( m_netState ! = RPT_NET_STATE : : IDLE )
2016-02-25 20:54:18 +01:00
return ;
if ( m_rfTimeoutTimer . isRunning ( ) & & m_rfTimeoutTimer . hasExpired ( ) )
return ;
unsigned char len = DSTAR_HEADER_LENGTH_BYTES + 1U ;
2016-03-07 19:08:50 +01:00
unsigned int space = m_queue . freeSpace ( ) ;
if ( space < ( len + 1U ) ) {
LogError ( " D-Star, overflow in the D-Star RF queue " ) ;
return ;
}
m_queue . addData ( & len , 1U ) ;
m_queue . addData ( data , len ) ;
2016-02-25 20:54:18 +01:00
}
void CDStarControl : : writeQueueDataRF ( const unsigned char * data )
{
2025-03-14 13:13:11 +01:00
assert ( data ! = nullptr ) ;
2016-02-25 20:54:18 +01:00
2025-03-13 19:16:33 +01:00
if ( m_netState ! = RPT_NET_STATE : : IDLE )
2016-02-25 20:54:18 +01:00
return ;
if ( m_rfTimeoutTimer . isRunning ( ) & & m_rfTimeoutTimer . hasExpired ( ) )
return ;
unsigned char len = DSTAR_FRAME_LENGTH_BYTES + 1U ;
2016-03-07 19:08:50 +01:00
unsigned int space = m_queue . freeSpace ( ) ;
if ( space < ( len + 1U ) ) {
LogError ( " D-Star, overflow in the D-Star RF queue " ) ;
return ;
}
m_queue . addData ( & len , 1U ) ;
m_queue . addData ( data , len ) ;
2016-02-25 20:54:18 +01:00
}
void CDStarControl : : writeQueueEOTRF ( )
{
2025-03-13 19:16:33 +01:00
if ( m_netState ! = RPT_NET_STATE : : IDLE )
2016-02-25 20:54:18 +01:00
return ;
if ( m_rfTimeoutTimer . isRunning ( ) & & m_rfTimeoutTimer . hasExpired ( ) )
return ;
unsigned char len = 1U ;
2016-03-07 19:08:50 +01:00
unsigned int space = m_queue . freeSpace ( ) ;
if ( space < ( len + 1U ) ) {
LogError ( " D-Star, overflow in the D-Star RF queue " ) ;
return ;
}
m_queue . addData ( & len , 1U ) ;
2016-02-25 20:54:18 +01:00
unsigned char data = TAG_EOT ;
2016-03-07 19:08:50 +01:00
m_queue . addData ( & data , len ) ;
2016-02-25 20:54:18 +01:00
}
void CDStarControl : : writeQueueHeaderNet ( const unsigned char * data )
2016-01-27 21:01:50 +01:00
{
2025-03-14 13:13:11 +01:00
assert ( data ! = nullptr ) ;
2016-01-27 21:01:50 +01:00
2016-02-25 20:54:18 +01:00
if ( m_netTimeoutTimer . isRunning ( ) & & m_netTimeoutTimer . hasExpired ( ) )
2016-01-27 21:01:50 +01:00
return ;
unsigned char len = DSTAR_HEADER_LENGTH_BYTES + 1U ;
2016-03-07 19:08:50 +01:00
unsigned int space = m_queue . freeSpace ( ) ;
if ( space < ( len + 1U ) ) {
LogError ( " D-Star, overflow in the D-Star RF queue " ) ;
return ;
}
m_queue . addData ( & len , 1U ) ;
m_queue . addData ( data , len ) ;
2016-01-27 21:01:50 +01:00
}
2016-02-25 20:54:18 +01:00
void CDStarControl : : writeQueueDataNet ( const unsigned char * data )
2016-01-27 21:01:50 +01:00
{
2025-03-14 13:13:11 +01:00
assert ( data ! = nullptr ) ;
2016-01-27 21:01:50 +01:00
2016-02-25 20:54:18 +01:00
if ( m_netTimeoutTimer . isRunning ( ) & & m_netTimeoutTimer . hasExpired ( ) )
2016-01-27 21:01:50 +01:00
return ;
unsigned char len = DSTAR_FRAME_LENGTH_BYTES + 1U ;
2016-03-07 19:08:50 +01:00
unsigned int space = m_queue . freeSpace ( ) ;
if ( space < ( len + 1U ) ) {
LogError ( " D-Star, overflow in the D-Star RF queue " ) ;
return ;
}
m_queue . addData ( & len , 1U ) ;
m_queue . addData ( data , len ) ;
2016-01-27 21:01:50 +01:00
}
2016-02-25 20:54:18 +01:00
void CDStarControl : : writeQueueEOTNet ( )
2016-02-17 08:23:41 +01:00
{
2016-02-25 20:54:18 +01:00
if ( m_netTimeoutTimer . isRunning ( ) & & m_netTimeoutTimer . hasExpired ( ) )
2016-02-17 08:23:41 +01:00
return ;
unsigned char len = 1U ;
2016-03-07 19:08:50 +01:00
unsigned int space = m_queue . freeSpace ( ) ;
if ( space < ( len + 1U ) ) {
LogError ( " D-Star, overflow in the D-Star RF queue " ) ;
return ;
}
m_queue . addData ( & len , 1U ) ;
2016-02-17 08:23:41 +01:00
unsigned char data = TAG_EOT ;
2016-03-07 19:08:50 +01:00
m_queue . addData ( & data , len ) ;
2016-02-17 08:23:41 +01:00
}
2016-02-25 20:54:18 +01:00
void CDStarControl : : writeNetworkHeaderRF ( const unsigned char * data )
2016-01-27 21:01:50 +01:00
{
2025-03-14 13:13:11 +01:00
assert ( data ! = nullptr ) ;
2016-01-27 21:01:50 +01:00
2025-03-14 13:13:11 +01:00
if ( m_network = = nullptr )
2016-01-27 21:01:50 +01:00
return ;
// Don't send to the network if the timeout has expired
2016-02-25 20:54:18 +01:00
if ( m_rfTimeoutTimer . isRunning ( ) & & m_rfTimeoutTimer . hasExpired ( ) )
2016-01-27 21:01:50 +01:00
return ;
2025-03-13 19:16:33 +01:00
m_network - > writeHeader ( data + 1U , DSTAR_HEADER_LENGTH_BYTES , m_netState ! = RPT_NET_STATE : : IDLE ) ;
2016-01-27 21:01:50 +01:00
}
2016-02-25 20:54:18 +01:00
void CDStarControl : : writeNetworkDataRF ( const unsigned char * data , unsigned int errors , bool end )
2016-01-27 21:01:50 +01:00
{
2025-03-14 13:13:11 +01:00
assert ( data ! = nullptr ) ;
2016-01-27 21:01:50 +01:00
2025-03-14 13:13:11 +01:00
if ( m_network = = nullptr )
2016-01-27 21:01:50 +01:00
return ;
// Don't send to the network if the timeout has expired
2016-02-25 20:54:18 +01:00
if ( m_rfTimeoutTimer . isRunning ( ) & & m_rfTimeoutTimer . hasExpired ( ) )
2016-01-27 21:01:50 +01:00
return ;
2025-03-13 19:16:33 +01:00
m_network - > writeData ( data + 1U , DSTAR_FRAME_LENGTH_BYTES , errors , end , m_netState ! = RPT_NET_STATE : : IDLE ) ;
2016-01-27 21:01:50 +01:00
}
bool CDStarControl : : openFile ( )
{
2025-03-14 13:13:11 +01:00
if ( m_fp ! = nullptr )
2016-01-27 21:01:50 +01:00
return true ;
time_t t ;
: : time ( & t ) ;
struct tm * tm = : : localtime ( & t ) ;
char name [ 100U ] ;
: : sprintf ( name , " DStar_%04d%02d%02d_%02d%02d%02d.ambe " , tm - > tm_year + 1900 , tm - > tm_mon + 1 , tm - > tm_mday , tm - > tm_hour , tm - > tm_min , tm - > tm_sec ) ;
m_fp = : : fopen ( name , " wb " ) ;
2025-03-14 13:13:11 +01:00
if ( m_fp = = nullptr )
2016-01-27 21:01:50 +01:00
return false ;
: : fwrite ( " DSTAR " , 1U , 4U , m_fp ) ;
return true ;
}
bool CDStarControl : : writeFile ( const unsigned char * data , unsigned int length )
{
2025-03-14 13:13:11 +01:00
if ( m_fp = = nullptr )
2016-01-27 21:01:50 +01:00
return false ;
: : fwrite ( data , 1U , length , m_fp ) ;
return true ;
}
void CDStarControl : : closeFile ( )
{
2025-03-14 13:13:11 +01:00
if ( m_fp ! = nullptr ) {
2016-01-27 21:01:50 +01:00
: : fclose ( m_fp ) ;
2025-03-14 13:13:11 +01:00
m_fp = nullptr ;
2016-01-27 21:01:50 +01:00
}
}
2016-07-12 08:05:48 +02:00
bool CDStarControl : : insertSilence ( const unsigned char * data , unsigned char seqNo )
2016-01-27 21:01:50 +01:00
{
2025-03-14 13:13:11 +01:00
assert ( data ! = nullptr ) ;
2016-01-27 21:01:50 +01:00
// Check to see if we have any spaces to fill?
2016-07-11 21:08:14 +02:00
unsigned int oldSeqNo = ( m_netN + 1U ) % 21U ;
if ( oldSeqNo = = seqNo ) {
2016-01-27 21:01:50 +01:00
// Just copy the data, nothing else to do here
2016-07-12 08:24:07 +02:00
: : memcpy ( m_lastFrame , data , DSTAR_FRAME_LENGTH_BYTES + 1U ) ;
2016-07-21 19:09:29 +02:00
m_lastFrameValid = true ;
2016-07-12 08:05:48 +02:00
return true ;
2016-01-27 21:01:50 +01:00
}
unsigned int count ;
2016-07-11 21:08:14 +02:00
if ( seqNo > oldSeqNo )
count = seqNo - oldSeqNo ;
2016-01-27 21:01:50 +01:00
else
2016-07-11 21:08:14 +02:00
count = ( 21U + seqNo ) - oldSeqNo ;
2016-01-27 21:01:50 +01:00
2016-07-12 08:05:48 +02:00
if ( count > = 10U )
return false ;
insertSilence ( count ) ;
2016-07-12 08:24:07 +02:00
: : memcpy ( m_lastFrame , data , DSTAR_FRAME_LENGTH_BYTES + 1U ) ;
2016-07-21 19:09:29 +02:00
m_lastFrameValid = true ;
2016-01-27 21:01:50 +01:00
2016-07-12 08:05:48 +02:00
return true ;
2016-01-27 21:01:50 +01:00
}
void CDStarControl : : insertSilence ( unsigned int count )
{
2016-02-28 18:18:13 +01:00
unsigned char n = ( m_netN + 1U ) % 21U ;
2016-01-27 21:01:50 +01:00
for ( unsigned int i = 0U ; i < count ; i + + ) {
2016-07-21 19:09:29 +02:00
if ( i < 3U & & m_lastFrameValid ) {
2016-07-11 21:08:14 +02:00
if ( n = = 0U ) {
2025-03-14 13:13:11 +01:00
: : memcpy ( m_lastFrame + DSTAR_VOICE_FRAME_LENGTH_BYTES + 1U , DSTAR_nullptr_SLOW_SYNC_BYTES , DSTAR_DATA_FRAME_LENGTH_BYTES ) ;
2016-07-11 21:08:14 +02:00
writeQueueDataNet ( m_lastFrame ) ;
} else {
2025-03-14 13:13:11 +01:00
: : memcpy ( m_lastFrame + DSTAR_VOICE_FRAME_LENGTH_BYTES + 1U , DSTAR_nullptr_SLOW_DATA_BYTES , DSTAR_DATA_FRAME_LENGTH_BYTES ) ;
2016-07-11 21:08:14 +02:00
writeQueueDataNet ( m_lastFrame ) ;
}
2016-01-27 21:01:50 +01:00
} else {
2016-08-09 18:20:26 +02:00
m_lastFrameValid = false ;
2016-02-01 21:49:52 +01:00
if ( n = = 0U )
2025-03-14 13:13:11 +01:00
writeQueueDataNet ( DSTAR_nullptr_FRAME_SYNC_BYTES ) ;
2016-02-01 21:49:52 +01:00
else
2025-03-14 13:13:11 +01:00
writeQueueDataNet ( DSTAR_nullptr_FRAME_DATA_BYTES ) ;
2016-01-27 21:01:50 +01:00
}
2016-02-28 18:18:13 +01:00
m_netN = n ;
2016-01-27 21:01:50 +01:00
2016-02-25 20:54:18 +01:00
m_netFrames + + ;
m_netLost + + ;
2016-01-27 21:01:50 +01:00
n = ( n + 1U ) % 21U ;
}
}
void CDStarControl : : blankDTMF ( unsigned char * data ) const
{
2025-03-14 13:13:11 +01:00
assert ( data ! = nullptr ) ;
2016-01-27 21:01:50 +01:00
// DTMF begins with these byte values
if ( ( data [ 0 ] & DSTAR_DTMF_MASK [ 0 ] ) = = DSTAR_DTMF_SIG [ 0 ] & & ( data [ 1 ] & DSTAR_DTMF_MASK [ 1 ] ) = = DSTAR_DTMF_SIG [ 1 ] & &
( data [ 2 ] & DSTAR_DTMF_MASK [ 2 ] ) = = DSTAR_DTMF_SIG [ 2 ] & & ( data [ 3 ] & DSTAR_DTMF_MASK [ 3 ] ) = = DSTAR_DTMF_SIG [ 3 ] & &
( data [ 4 ] & DSTAR_DTMF_MASK [ 4 ] ) = = DSTAR_DTMF_SIG [ 4 ] & & ( data [ 5 ] & DSTAR_DTMF_MASK [ 5 ] ) = = DSTAR_DTMF_SIG [ 5 ] & &
( data [ 6 ] & DSTAR_DTMF_MASK [ 6 ] ) = = DSTAR_DTMF_SIG [ 6 ] & & ( data [ 7 ] & DSTAR_DTMF_MASK [ 7 ] ) = = DSTAR_DTMF_SIG [ 7 ] & &
( data [ 8 ] & DSTAR_DTMF_MASK [ 8 ] ) = = DSTAR_DTMF_SIG [ 8 ] )
2025-03-14 13:13:11 +01:00
: : memcpy ( data , DSTAR_nullptr_AMBE_DATA_BYTES , DSTAR_VOICE_FRAME_LENGTH_BYTES ) ;
2016-01-27 21:01:50 +01:00
}
2016-02-01 21:49:52 +01:00
void CDStarControl : : sendAck ( )
{
2016-02-25 20:54:18 +01:00
m_rfTimeoutTimer . stop ( ) ;
2016-02-02 20:54:51 +01:00
2017-07-27 17:39:14 +02:00
if ( ! m_ackReply )
return ;
2016-02-01 21:49:52 +01:00
unsigned char user [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
2016-02-25 20:54:18 +01:00
m_rfHeader . getMyCall1 ( user ) ;
2016-02-01 21:49:52 +01:00
CDStarHeader header ;
header . setUnavailable ( true ) ;
header . setMyCall1 ( m_callsign ) ;
header . setYourCall ( user ) ;
header . setRPTCall1 ( m_gateway ) ;
header . setRPTCall2 ( m_callsign ) ;
unsigned char data [ DSTAR_HEADER_LENGTH_BYTES + 1U ] ;
header . get ( data + 1U ) ;
data [ 0U ] = TAG_HEADER ;
2016-02-25 20:54:18 +01:00
writeQueueHeaderRF ( data ) ;
2016-02-01 21:49:52 +01:00
2025-03-14 13:13:11 +01:00
writeQueueDataRF ( DSTAR_nullptr_FRAME_SYNC_BYTES ) ;
2016-02-01 21:49:52 +01:00
2025-03-13 19:16:33 +01:00
LINK_STATUS status = LINK_STATUS : : NONE ;
2016-02-01 21:49:52 +01:00
unsigned char reflector [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
2025-03-14 13:13:11 +01:00
if ( m_network ! = nullptr )
2016-02-01 21:49:52 +01:00
m_network - > getStatus ( status , reflector ) ;
2016-02-01 22:44:40 +01:00
char text [ 40U ] ;
2025-03-13 19:16:33 +01:00
if ( ( m_ackMessage = = DSTAR_ACK : : RSSI ) & & ( m_rssi ! = 0U ) ) {
if ( ( status = = LINK_STATUS : : LINKED_DEXTRA ) | | ( status = = LINK_STATUS : : LINKED_DPLUS ) | | ( status = = LINK_STATUS : : LINKED_DCS ) | | ( status = = LINK_STATUS : : LINKED_CCS ) | | ( status = = LINK_STATUS : : LINKED_LOOPBACK ) ) {
2021-05-15 07:06:37 +02:00
CUtils : : removeChar ( reflector , ' ' ) ; //remove space from reflector so all nicely fits onto 20 chars in case rssi < 99dBm
2021-05-15 06:51:12 +02:00
: : sprintf ( text , " %-8.8s %.1f%% -%udBm " , reflector , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , m_aveRSSI / m_rssiCount ) ;
2021-05-18 20:44:41 +02:00
} else {
2018-09-14 12:30:48 +02:00
: : sprintf ( text , " BER:%.1f%% -%udBm " , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , m_aveRSSI / m_rssiCount ) ;
2021-05-18 20:44:41 +02:00
}
2025-03-13 19:16:33 +01:00
} else if ( ( m_ackMessage = = DSTAR_ACK : : SMETER ) & & ( m_rssi ! = 0U ) ) {
2021-05-15 06:51:12 +02:00
unsigned int signal , plus ;
2021-05-31 21:02:35 +02:00
char signalText [ 15U ] ;
2021-05-15 06:51:12 +02:00
CSMeter : : getSignal ( m_aveRSSI / m_rssiCount , signal , plus ) ;
if ( plus ! = 0U )
2021-05-17 07:14:26 +02:00
: : sprintf ( signalText , " S%u+%02u " , signal , plus ) ;
2021-05-15 06:51:12 +02:00
else
2021-05-15 12:22:39 +02:00
: : sprintf ( signalText , " S%u " , signal ) ;
2021-05-15 06:51:12 +02:00
2025-03-13 19:16:33 +01:00
if ( ( status = = LINK_STATUS : : LINKED_DEXTRA ) | | ( status = = LINK_STATUS : : LINKED_DPLUS ) | | ( status = = LINK_STATUS : : LINKED_DCS ) | | ( status = = LINK_STATUS : : LINKED_CCS ) | | ( status = = LINK_STATUS : : LINKED_LOOPBACK ) )
2021-05-15 06:51:12 +02:00
: : sprintf ( text , " %-8.8s %.1f%% %s " , reflector , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , signalText ) ;
2018-09-14 12:30:48 +02:00
else
2021-05-15 12:22:39 +02:00
: : sprintf ( text , " BER:%.1f%% %s " , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , signalText ) ;
2021-05-18 20:44:41 +02:00
} else {
2025-03-13 19:16:33 +01:00
if ( ( status = = LINK_STATUS : : LINKED_DEXTRA ) | | ( status = = LINK_STATUS : : LINKED_DPLUS ) | | ( status = = LINK_STATUS : : LINKED_DCS ) | | ( status = = LINK_STATUS : : LINKED_CCS ) | | ( status = = LINK_STATUS : : LINKED_LOOPBACK ) )
2018-09-14 12:30:48 +02:00
: : sprintf ( text , " %-8.8s BER: %.1f%% " , reflector , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
else
: : sprintf ( text , " BER: %.1f%% " , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
}
2021-05-18 20:44:41 +02:00
2023-06-16 12:35:16 +02:00
m_rfSlowData . setText ( text ) ;
2016-02-01 21:49:52 +01:00
2025-03-14 13:13:11 +01:00
: : memcpy ( data , DSTAR_nullptr_FRAME_DATA_BYTES , DSTAR_FRAME_LENGTH_BYTES + 1U ) ;
2016-02-01 21:49:52 +01:00
for ( unsigned int i = 0U ; i < 19U ; i + + ) {
2023-06-16 12:35:16 +02:00
m_rfSlowData . getSlowData ( data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES ) ;
2016-02-25 20:54:18 +01:00
writeQueueDataRF ( data ) ;
2016-02-01 21:49:52 +01:00
}
2016-02-25 20:54:18 +01:00
writeQueueEOTRF ( ) ;
2016-02-01 21:49:52 +01:00
}
2017-02-16 20:49:58 +01:00
void CDStarControl : : sendError ( )
{
unsigned char user [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
m_rfHeader . getMyCall1 ( user ) ;
CDStarHeader header ;
header . setUnavailable ( true ) ;
header . setMyCall1 ( m_callsign ) ;
header . setYourCall ( user ) ;
header . setRPTCall1 ( m_callsign ) ;
header . setRPTCall2 ( m_callsign ) ;
unsigned char data [ DSTAR_HEADER_LENGTH_BYTES + 1U ] ;
header . get ( data + 1U ) ;
data [ 0U ] = TAG_HEADER ;
writeQueueHeaderRF ( data ) ;
2025-03-14 13:13:11 +01:00
writeQueueDataRF ( DSTAR_nullptr_FRAME_SYNC_BYTES ) ;
2017-02-16 20:49:58 +01:00
2025-03-13 19:16:33 +01:00
LINK_STATUS status = LINK_STATUS : : NONE ;
2017-02-16 20:49:58 +01:00
unsigned char reflector [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
2025-03-14 13:13:11 +01:00
if ( m_network ! = nullptr )
2017-02-16 20:49:58 +01:00
m_network - > getStatus ( status , reflector ) ;
char text [ 40U ] ;
2025-03-13 19:16:33 +01:00
if ( ( m_ackMessage = = DSTAR_ACK : : RSSI ) & & ( m_rssi ! = 0U ) ) {
if ( ( status = = LINK_STATUS : : LINKED_DEXTRA ) | | ( status = = LINK_STATUS : : LINKED_DPLUS ) | | ( status = = LINK_STATUS : : LINKED_DCS ) | | ( status = = LINK_STATUS : : LINKED_CCS ) | | ( status = = LINK_STATUS : : LINKED_LOOPBACK ) ) {
2021-05-15 07:06:37 +02:00
CUtils : : removeChar ( reflector , ' ' ) ; //remove space from reflector so all nicely fits onto 20 chars in case rssi < 99dBm
2021-05-15 06:51:12 +02:00
: : sprintf ( text , " %-8.8s %.1f%% -%udBm " , reflector , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , m_aveRSSI / m_rssiCount ) ;
2021-05-18 20:44:41 +02:00
} else {
2018-09-14 12:30:48 +02:00
: : sprintf ( text , " BER:%.1f%% -%udBm " , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , m_aveRSSI / m_rssiCount ) ;
2021-05-18 20:44:41 +02:00
}
2025-03-13 19:16:33 +01:00
} else if ( ( m_ackMessage = = DSTAR_ACK : : SMETER ) & & ( m_rssi ! = 0U ) ) {
2021-05-15 06:51:12 +02:00
unsigned int signal , plus ;
2021-05-31 21:02:35 +02:00
char signalText [ 15U ] ;
2021-05-15 06:51:12 +02:00
CSMeter : : getSignal ( m_aveRSSI / m_rssiCount , signal , plus ) ;
if ( plus ! = 0U )
2021-05-17 07:14:26 +02:00
: : sprintf ( signalText , " S%u+%02u " , signal , plus ) ;
2021-05-15 06:51:12 +02:00
else
2021-05-15 12:22:39 +02:00
: : sprintf ( signalText , " S%u " , signal ) ;
2021-05-15 06:51:12 +02:00
2025-03-13 19:16:33 +01:00
if ( ( status = = LINK_STATUS : : LINKED_DEXTRA ) | | ( status = = LINK_STATUS : : LINKED_DPLUS ) | | ( status = = LINK_STATUS : : LINKED_DCS ) | | ( status = = LINK_STATUS : : LINKED_CCS ) | | ( status = = LINK_STATUS : : LINKED_LOOPBACK ) )
2021-05-15 06:51:12 +02:00
: : sprintf ( text , " %-8.8s %.1f%% %s " , reflector , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , signalText ) ;
2018-09-14 12:30:48 +02:00
else
2021-05-15 12:22:39 +02:00
: : sprintf ( text , " BER:%.1f%% %s " , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , signalText ) ;
2021-05-18 20:44:41 +02:00
} else {
2025-03-13 19:16:33 +01:00
if ( ( status = = LINK_STATUS : : LINKED_DEXTRA ) | | ( status = = LINK_STATUS : : LINKED_DPLUS ) | | ( status = = LINK_STATUS : : LINKED_DCS ) | | ( status = = LINK_STATUS : : LINKED_CCS ) | | ( status = = LINK_STATUS : : LINKED_LOOPBACK ) )
2018-09-14 12:30:48 +02:00
: : sprintf ( text , " %-8.8s BER: %.1f%% " , reflector , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
else
: : sprintf ( text , " BER: %.1f%% " , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
}
2021-05-18 20:44:41 +02:00
2023-06-16 12:35:16 +02:00
m_rfSlowData . setText ( text ) ;
2017-02-16 20:49:58 +01:00
2025-03-14 13:13:11 +01:00
: : memcpy ( data , DSTAR_nullptr_FRAME_DATA_BYTES , DSTAR_FRAME_LENGTH_BYTES + 1U ) ;
2017-02-16 20:49:58 +01:00
for ( unsigned int i = 0U ; i < 19U ; i + + ) {
2023-06-16 12:35:16 +02:00
m_rfSlowData . getSlowData ( data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES ) ;
2017-02-16 20:49:58 +01:00
writeQueueDataRF ( data ) ;
}
writeQueueEOTRF ( ) ;
}
2018-11-12 12:57:34 +01:00
2019-01-10 10:05:15 +01:00
bool CDStarControl : : isBusy ( ) const
{
2025-03-13 19:16:33 +01:00
return ( m_rfState ! = RPT_RF_STATE : : LISTENING ) | | ( m_netState ! = RPT_NET_STATE : : IDLE ) ;
2019-01-10 10:05:15 +01:00
}
2019-01-19 18:15:24 +01:00
void CDStarControl : : enable ( bool enabled )
{
if ( ! enabled & & m_enabled ) {
m_queue . clear ( ) ;
// Reset the RF section
2025-03-13 19:16:33 +01:00
m_rfState = RPT_RF_STATE : : LISTENING ;
2019-01-19 18:15:24 +01:00
m_rfTimeoutTimer . stop ( ) ;
// Reset the networking section
2025-03-13 19:16:33 +01:00
m_netState = RPT_NET_STATE : : IDLE ;
2019-01-19 18:15:24 +01:00
m_lastFrameValid = false ;
m_netTimeoutTimer . stop ( ) ;
m_networkWatchdog . stop ( ) ;
m_packetTimer . stop ( ) ;
}
m_enabled = enabled ;
}