2016-01-27 20:01:50 +00:00
/*
* Copyright ( C ) 2015 , 2016 Jonathan Naylor , G4KLX
*
* 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 21:46:57 +00:00
# include "Sync.h"
2016-01-27 20:01:50 +00:00
# include "Log.h"
2016-03-07 20:21:55 +00:00
# include <cstdio>
2016-01-27 20:01:50 +00:00
# include <cassert>
# include <ctime>
2016-04-06 17:46:05 +00:00
# include <algorithm>
# include <functional>
2016-01-27 20:01:50 +00:00
const unsigned int MAX_SYNC_BIT_ERRORS = 2U ;
2016-04-06 17:46:05 +00: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 20:01:50 +00:00
// #define DUMP_DSTAR
2016-09-27 18:55:36 +00:00
CDStarControl : : CDStarControl ( const std : : string & callsign , const std : : string & module , bool selfOnly , const std : : vector < std : : string > & blackList , CDStarNetwork * network , CDisplay * display , unsigned int timeout , bool duplex ) :
2016-01-27 20:01:50 +00:00
m_callsign ( NULL ) ,
m_gateway ( NULL ) ,
2016-04-04 16:40:05 +00:00
m_selfOnly ( selfOnly ) ,
2016-04-06 17:46:05 +00:00
m_blackList ( blackList ) ,
2016-01-27 20:01:50 +00:00
m_network ( network ) ,
m_display ( display ) ,
m_duplex ( duplex ) ,
2016-05-10 17:54:35 +00:00
m_queue ( 5000U , " D-Star Control " ) ,
2016-02-25 19:54:18 +00:00
m_rfHeader ( ) ,
m_netHeader ( ) ,
m_rfState ( RS_RF_LISTENING ) ,
m_netState ( RS_NET_IDLE ) ,
2016-01-27 20:01:50 +00:00
m_net ( false ) ,
m_slowData ( ) ,
2016-02-28 17:18:13 +00:00
m_rfN ( 0U ) ,
m_netN ( 0U ) ,
2016-01-27 20:01:50 +00:00
m_networkWatchdog ( 1000U , 0U , 1500U ) ,
2016-02-25 19:54:18 +00:00
m_rfTimeoutTimer ( 1000U , timeout ) ,
m_netTimeoutTimer ( 1000U , timeout ) ,
2016-09-27 05:45:42 +00:00
m_packetTimer ( 1000U , 0U , 300U ) ,
2016-02-01 20:49:52 +00:00
m_ackTimer ( 1000U , 0U , 750U ) ,
2016-03-14 20:55:15 +00:00
m_interval ( ) ,
2016-01-27 20:01:50 +00:00
m_elapsed ( ) ,
2016-02-25 19:54:18 +00:00
m_rfFrames ( 0U ) ,
m_netFrames ( 0U ) ,
m_netLost ( 0U ) ,
2016-01-27 20:01:50 +00:00
m_fec ( ) ,
2016-07-21 16:50:13 +00:00
m_rfBits ( 1U ) ,
m_netBits ( 1U ) ,
2016-02-25 19:54:18 +00:00
m_rfErrs ( 0U ) ,
m_netErrs ( 0U ) ,
2016-01-27 20:01:50 +00:00
m_lastFrame ( NULL ) ,
2016-07-21 17:09:29 +00:00
m_lastFrameValid ( false ) ,
2016-01-27 20:01:50 +00:00
m_fp ( NULL )
{
assert ( display ! = NULL ) ;
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 20:55:15 +00:00
m_interval . start ( ) ;
2016-01-27 20:01:50 +00:00
}
CDStarControl : : ~ CDStarControl ( )
{
delete [ ] m_callsign ;
delete [ ] m_gateway ;
delete [ ] m_lastFrame ;
}
2016-08-08 20:26:18 +00:00
bool CDStarControl : : writeModem ( unsigned char * data , unsigned int len )
2016-01-27 20:01:50 +00:00
{
2016-03-07 20:21:55 +00:00
assert ( data ! = NULL ) ;
2016-01-27 20:01:50 +00:00
unsigned char type = data [ 0U ] ;
2016-02-25 19:54:18 +00:00
if ( type = = TAG_LOST & & m_rfState = = RS_RF_AUDIO ) {
LogMessage ( " D-Star, transmission lost, %.1f seconds, BER: %.1f%% " , float ( m_rfFrames ) / 50.0F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
writeEndRF ( ) ;
2016-02-02 18:17:36 +00:00
return false ;
2016-01-27 20:01:50 +00:00
}
2016-02-02 19:54:51 +00:00
if ( type = = TAG_LOST ) {
2016-07-12 06:50:01 +00:00
m_rfState = RS_RF_LISTENING ;
2016-02-02 18:17:36 +00:00
return false ;
2016-01-27 20:01:50 +00:00
}
if ( type = = TAG_HEADER ) {
CDStarHeader header ( data + 1U ) ;
2016-04-19 16:15:37 +00:00
unsigned char my1 [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
header . getMyCall1 ( my1 ) ;
2016-01-27 20:01:50 +00:00
// Is this a transmission destined for a repeater?
2016-04-19 16:15:37 +00:00
if ( ! header . isRepeater ( ) ) {
LogMessage ( " D-Star, non repeater RF header received from %8.8s " , my1 ) ;
2016-07-12 06:50:01 +00:00
m_rfState = RS_RF_REJECTED ;
2016-04-18 18:15:49 +00:00
return false ;
2016-04-19 16:15:37 +00:00
}
2016-04-18 18:15:49 +00:00
unsigned char callsign [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
header . getRPTCall1 ( callsign ) ;
// Is it for us?
2016-04-19 16:15:37 +00: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 ) ;
2016-07-12 06:50:01 +00:00
m_rfState = RS_RF_REJECTED ;
2016-02-02 18:17:36 +00:00
return false ;
2016-04-19 16:15:37 +00:00
}
2016-04-04 16:40:05 +00:00
if ( m_selfOnly & & : : memcmp ( my1 , m_callsign , DSTAR_LONG_CALLSIGN_LENGTH - 1U ) ! = 0 ) {
LogMessage ( " D-Star, invalid access attempt from %8.8s " , my1 ) ;
2016-07-12 06:50:01 +00:00
m_rfState = RS_RF_REJECTED ;
2016-04-04 16:40:05 +00:00
return false ;
}
2016-04-06 17:46:05 +00: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 ) ;
2016-07-12 06:50:01 +00:00
m_rfState = RS_RF_REJECTED ;
2016-04-06 17:46:05 +00:00
return false ;
}
2016-01-27 20:01:50 +00: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 19:54:18 +00:00
// Only start the timeout if not already running
2016-02-28 17:40:15 +00:00
if ( ! m_rfTimeoutTimer . isRunning ( ) )
2016-02-25 19:54:18 +00:00
m_rfTimeoutTimer . start ( ) ;
2016-02-01 20:49:52 +00:00
2016-02-25 19:54:18 +00:00
m_rfHeader = header ;
2016-01-27 20:01:50 +00:00
2016-02-25 19:54:18 +00:00
m_ackTimer . stop ( ) ;
2016-01-27 20:01:50 +00:00
2016-02-28 17:40:15 +00:00
m_rfBits = 1U ;
m_rfErrs = 0U ;
2016-02-25 19:54:18 +00:00
m_rfFrames = 1U ;
2016-02-28 17:18:13 +00:00
m_rfN = 0U ;
2016-01-27 20:01:50 +00:00
2016-02-25 19:54:18 +00: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 20:01:50 +00:00
2016-02-25 19:54:18 +00:00
writeQueueHeaderRF ( data ) ;
}
2016-01-27 20:01:50 +00:00
2016-02-25 19:54:18 +00: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 20:01:50 +00:00
2016-02-28 17:40:15 +00:00
writeNetworkHeaderRF ( data ) ;
2016-02-25 19:54:18 +00:00
}
2016-01-27 20:01:50 +00:00
2016-02-25 19:54:18 +00:00
m_rfState = RS_RF_AUDIO ;
2016-01-27 20:01:50 +00:00
2016-02-25 19:54:18 +00:00
if ( m_netState = = RS_NET_IDLE )
2016-04-16 20:31:49 +00:00
m_display - > writeDStar ( ( char * ) my1 , ( char * ) my2 , ( char * ) your , " R " , " " ) ;
2016-01-27 20:01:50 +00:00
2016-02-25 19:54:18 +00:00
LogMessage ( " D-Star, received RF header from %8.8s/%4.4s to %8.8s " , my1 , my2 , your ) ;
2016-01-27 20:01:50 +00:00
} else if ( type = = TAG_EOT ) {
2016-07-12 06:50:01 +00:00
if ( m_rfState = = RS_RF_REJECTED ) {
m_rfState = RS_RF_LISTENING ;
} else if ( m_rfState = = RS_RF_AUDIO ) {
2016-02-18 19:57:11 +00:00
if ( m_net )
2016-02-25 19:54:18 +00:00
writeNetworkDataRF ( DSTAR_END_PATTERN_BYTES , 0U , true ) ;
2016-01-27 20:01:50 +00:00
2016-02-17 07:23:41 +00:00
if ( m_duplex )
2016-02-25 19:54:18 +00:00
writeQueueEOTRF ( ) ;
2016-02-01 20:49:52 +00:00
2016-02-25 19:54:18 +00:00
LogMessage ( " D-Star, received RF end of transmission, %.1f seconds, BER: %.1f%% " , float ( m_rfFrames ) / 50.0F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
2016-01-27 20:01:50 +00:00
2016-02-25 19:54:18 +00:00
writeEndRF ( ) ;
2016-01-27 20:01:50 +00:00
}
2016-02-02 18:17:36 +00:00
return false ;
2016-02-03 07:20:48 +00:00
} else if ( type = = TAG_DATA ) {
2016-07-12 06:50:01 +00:00
if ( m_rfState = = RS_RF_REJECTED ) {
return false ;
} else if ( m_rfState = = RS_RF_LISTENING ) {
2016-02-02 19:54:51 +00: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 ) {
2016-01-27 20:01:50 +00:00
m_slowData . start ( ) ;
2016-02-25 19:54:18 +00:00
m_rfState = RS_RF_LATE_ENTRY ;
2016-01-27 20:01:50 +00:00
}
2016-02-02 18:17:36 +00:00
return false ;
2016-02-25 19:54:18 +00:00
} else if ( m_rfState = = RS_RF_AUDIO ) {
2016-10-05 06:42:41 +00:00
unsigned int errors = 0U ;
if ( ! m_rfHeader . isDataPacket ( ) )
errors = m_fec . regenerateDStar ( data + 1U ) ;
2016-02-18 18:18:37 +00:00
2016-02-25 19:54:18 +00:00
m_rfErrs + = errors ;
m_rfBits + = 48U ;
2016-01-27 20:01:50 +00:00
2016-02-25 19:54:18 +00:00
m_rfFrames + + ;
2016-02-01 21:44:40 +00:00
2016-02-02 19:54:51 +00: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 )
2016-02-28 17:18:13 +00:00
m_rfN = 0U ;
2016-01-27 20:01:50 +00:00
// Regenerate the sync
2016-02-28 17:18:13 +00:00
if ( m_rfN = = 0U )
2016-02-15 21:46:57 +00:00
CSync : : addDStarSync ( data + 1U ) ;
2016-01-27 20:01:50 +00:00
2016-06-01 08:43:39 +00:00
LogDebug ( " D-Star, audio sequence no. %u, errs: %u/48 " , m_rfN , errors ) ;
2016-01-27 20:01:50 +00:00
if ( m_net )
2016-02-25 19:54:18 +00:00
writeNetworkDataRF ( data , errors , false ) ;
2016-01-27 20:01:50 +00:00
if ( m_duplex ) {
blankDTMF ( data + 1U ) ;
2016-02-25 19:54:18 +00:00
writeQueueDataRF ( data ) ;
2016-01-27 20:01:50 +00:00
}
2016-03-21 18:13:21 +00:00
m_rfN = ( m_rfN + 1U ) % 21U ;
2016-02-25 19:54:18 +00:00
} else if ( m_rfState = = RS_RF_LATE_ENTRY ) {
2016-02-02 19:54:51 +00: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 ) {
2016-01-27 20:01:50 +00:00
m_slowData . reset ( ) ;
2016-02-02 18:17:36 +00:00
return false ;
2016-01-27 20:01:50 +00:00
}
CDStarHeader * header = m_slowData . add ( data + 1U ) ;
if ( header = = NULL )
2016-02-02 18:17:36 +00:00
return false ;
2016-01-27 20:01:50 +00:00
// Is this a transmission destined for a repeater?
2016-02-03 07:20:48 +00:00
if ( ! header - > isRepeater ( ) ) {
2016-04-18 18:15:49 +00:00
delete header ;
return false ;
}
unsigned char callsign [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
header - > getRPTCall1 ( callsign ) ;
// Is it for us?
if ( : : memcmp ( callsign , m_callsign , DSTAR_LONG_CALLSIGN_LENGTH ) ! = 0 ) {
2016-04-06 17:46:05 +00:00
delete header ;
return false ;
}
unsigned char my1 [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
header - > getMyCall1 ( my1 ) ;
if ( m_selfOnly & & : : memcmp ( my1 , m_callsign , DSTAR_LONG_CALLSIGN_LENGTH - 1U ) ! = 0 ) {
delete header ;
return false ;
}
if ( ! m_selfOnly & & std : : find_if ( m_blackList . begin ( ) , m_blackList . end ( ) , std : : bind ( CallsignCompare , std : : placeholders : : _1 , my1 ) ) ! = m_blackList . end ( ) ) {
delete header ;
2016-02-02 18:17:36 +00:00
return false ;
2016-02-03 07:20:48 +00:00
}
2016-01-27 20:01:50 +00: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-02 19:54:51 +00:00
// Only reset the timeout if the timeout is not running
2016-02-28 17:40:15 +00:00
if ( ! m_rfTimeoutTimer . isRunning ( ) )
2016-02-25 19:54:18 +00:00
m_rfTimeoutTimer . start ( ) ;
2016-02-01 20:49:52 +00:00
2016-01-27 20:01:50 +00:00
// Create a dummy start frame to replace the received frame
2016-02-01 20:49:52 +00:00
m_ackTimer . stop ( ) ;
2016-02-25 19:54:18 +00:00
m_rfHeader = * header ;
2016-01-27 20:01:50 +00:00
2016-02-28 17:40:15 +00:00
m_rfBits = 1U ;
m_rfErrs = 0U ;
2016-02-28 17:18:13 +00:00
m_rfN = 0U ;
2016-02-25 19:54:18 +00:00
m_rfFrames = 1U ;
2016-01-27 20:01:50 +00:00
if ( m_duplex ) {
unsigned char start [ DSTAR_HEADER_LENGTH_BYTES + 1U ] ;
start [ 0U ] = TAG_HEADER ;
// Modify the header
header - > setRepeater ( false ) ;
header - > setRPTCall1 ( m_callsign ) ;
header - > setRPTCall2 ( m_callsign ) ;
header - > get ( start + 1U ) ;
2016-02-25 19:54:18 +00:00
writeQueueHeaderRF ( start ) ;
2016-01-27 20:01:50 +00:00
}
if ( m_net ) {
unsigned char start [ DSTAR_HEADER_LENGTH_BYTES + 1U ] ;
start [ 0U ] = TAG_HEADER ;
// Modify the header
header - > setRepeater ( false ) ;
2016-02-18 13:44:05 +00:00
header - > setRPTCall1 ( m_callsign ) ;
header - > setRPTCall2 ( m_gateway ) ;
2016-01-27 20:01:50 +00:00
header - > get ( start + 1U ) ;
2016-02-28 17:40:15 +00:00
writeNetworkHeaderRF ( start ) ;
2016-01-27 20:01:50 +00:00
}
delete header ;
2016-10-05 06:42:41 +00:00
unsigned int errors = 0U ;
if ( ! m_rfHeader . isDataPacket ( ) )
errors = m_fec . regenerateDStar ( data + 1U ) ;
2016-02-18 18:18:37 +00:00
2016-02-25 19:54:18 +00:00
m_rfErrs + = errors ;
m_rfBits + = 48U ;
2016-01-27 20:01:50 +00:00
2016-06-01 08:43:39 +00:00
LogDebug ( " D-Star, audio sequence no. %u, errs: %u/48 " , m_rfN , errors ) ;
2016-02-28 17:18:13 +00:00
2016-01-27 20:01:50 +00:00
if ( m_net )
2016-02-25 19:54:18 +00:00
writeNetworkDataRF ( data , errors , false ) ;
2016-01-27 20:01:50 +00:00
if ( m_duplex ) {
blankDTMF ( data + 1U ) ;
2016-02-25 19:54:18 +00:00
writeQueueDataRF ( data ) ;
2016-01-27 20:01:50 +00:00
}
2016-02-25 19:54:18 +00:00
m_rfState = RS_RF_AUDIO ;
2016-01-27 20:01:50 +00:00
2016-03-21 18:13:21 +00:00
m_rfN = ( m_rfN + 1U ) % 21U ;
2016-02-25 19:54:18 +00:00
if ( m_netState = = RS_NET_IDLE )
2016-04-16 20:31:49 +00:00
m_display - > writeDStar ( ( char * ) my1 , ( char * ) my2 , ( char * ) your , " R " , " " ) ;
2016-01-27 20:01:50 +00:00
2016-02-01 22:33:09 +00:00
LogMessage ( " D-Star, received RF late entry from %8.8s/%4.4s to %8.8s " , my1 , my2 , your ) ;
2016-01-27 20:01:50 +00:00
}
2016-02-03 07:20:48 +00:00
} else {
CUtils : : dump ( " D-Star, unknown data from modem " , data , DSTAR_FRAME_LENGTH_BYTES + 1U ) ;
2016-01-27 20:01:50 +00:00
}
2016-02-02 18:17:36 +00:00
return true ;
2016-01-27 20:01:50 +00:00
}
unsigned int CDStarControl : : readModem ( unsigned char * data )
{
2016-03-07 20:21:55 +00:00
assert ( data ! = NULL ) ;
2016-03-07 18:08:50 +00:00
if ( m_queue . isEmpty ( ) )
2016-01-27 20:01:50 +00:00
return 0U ;
unsigned char len = 0U ;
2016-03-07 18:08:50 +00:00
m_queue . getData ( & len , 1U ) ;
2016-01-27 20:01:50 +00:00
2016-03-07 18:08:50 +00:00
m_queue . getData ( data , len ) ;
2016-01-27 20:01:50 +00:00
return len ;
}
2016-02-25 19:54:18 +00:00
void CDStarControl : : writeEndRF ( )
2016-01-27 20:01:50 +00:00
{
2016-02-25 19:54:18 +00:00
m_rfState = RS_RF_LISTENING ;
if ( m_netState = = RS_NET_IDLE ) {
m_display - > clearDStar ( ) ;
m_ackTimer . start ( ) ;
if ( m_network ! = NULL )
m_network - > reset ( ) ;
} else {
m_rfTimeoutTimer . stop ( ) ;
}
}
void CDStarControl : : writeEndNet ( )
{
m_netState = RS_NET_IDLE ;
2016-01-27 20:01:50 +00:00
2016-07-21 17:09:29 +00:00
m_lastFrameValid = false ;
2016-01-27 20:01:50 +00:00
m_display - > clearDStar ( ) ;
2016-02-25 19:54:18 +00:00
m_netTimeoutTimer . stop ( ) ;
2016-01-27 20:01:50 +00:00
m_networkWatchdog . stop ( ) ;
2016-07-15 05:32:56 +00:00
m_packetTimer . stop ( ) ;
2016-01-27 20:01:50 +00:00
2016-02-04 22:41:38 +00:00
if ( m_network ! = NULL )
m_network - > reset ( ) ;
2016-01-27 20:01:50 +00:00
# if defined(DUMP_DSTAR)
closeFile ( ) ;
# endif
}
void CDStarControl : : writeNetwork ( )
{
assert ( m_network ! = NULL ) ;
unsigned char data [ DSTAR_HEADER_LENGTH_BYTES + 2U ] ;
unsigned int length = m_network - > read ( data , DSTAR_HEADER_LENGTH_BYTES + 2U ) ;
if ( length = = 0U )
return ;
2016-07-12 06:50:01 +00:00
if ( m_rfState = = RS_RF_AUDIO & & m_netState = = RS_NET_IDLE )
2016-01-27 20:01:50 +00:00
return ;
m_networkWatchdog . start ( ) ;
unsigned char type = data [ 0U ] ;
if ( type = = TAG_HEADER ) {
2016-02-25 19:54:18 +00:00
if ( m_netState ! = RS_NET_IDLE )
2016-01-27 20:01:50 +00:00
return ;
2016-01-31 18:11:12 +00:00
CDStarHeader header ( data + 1U ) ;
2016-01-27 20:01:50 +00: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 19:54:18 +00:00
m_netHeader = header ;
2016-02-01 20:49:52 +00:00
2016-02-25 19:54:18 +00:00
m_netTimeoutTimer . start ( ) ;
2016-07-15 05:32:56 +00:00
m_packetTimer . start ( ) ;
2016-09-29 19:23:19 +00:00
//m_elapsed.start(); // commented out and placed lower down due to delay introduced somewhere below here.
2016-02-01 20:49:52 +00:00
m_ackTimer . stop ( ) ;
2016-01-27 20:01:50 +00:00
2016-07-21 17:09:29 +00:00
m_lastFrameValid = false ;
2016-02-25 19:54:18 +00:00
m_netFrames = 0U ;
2016-07-13 06:19:08 +00:00
m_netLost = 0U ;
2016-01-27 20:01:50 +00:00
2016-07-13 06:19:08 +00:00
m_netN = 20U ;
2016-01-27 20:01:50 +00:00
2016-07-13 06:19:08 +00:00
m_netBits = 1U ;
m_netErrs = 0U ;
2016-01-27 20:01:50 +00:00
2016-02-25 19:54:18 +00:00
writeQueueHeaderNet ( data ) ;
2016-01-27 20:01:50 +00:00
# if defined(DUMP_DSTAR)
openFile ( ) ;
writeFile ( data + 1U , length - 1U ) ;
# endif
2016-02-25 19:54:18 +00:00
m_netState = RS_NET_AUDIO ;
2016-01-27 20:01:50 +00:00
2016-04-16 19:45:49 +00:00
LINK_STATUS status = LS_NONE ;
unsigned char reflector [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
2016-04-20 20:33:06 +00:00
m_network - > getStatus ( status , reflector ) ;
if ( status = = LS_LINKED_DEXTRA | | status = = LS_LINKED_DPLUS | | status = = LS_LINKED_DCS | | status = = LS_LINKED_CCS | | status = = LS_LINKED_LOOPBACK ) {
m_display - > writeDStar ( ( char * ) my1 , ( char * ) my2 , ( char * ) your , " N " , ( char * ) reflector ) ;
2016-04-16 19:45:49 +00: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 20:33:06 +00: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-27 22:09:19 +00:00
}
2016-09-29 19:23:19 +00:00
// Something just above here introduces a large delay forcing erroneous(?) insertion of silence packets.
// Starting the elapsed timer here instead of the commented out position above solves that.
m_elapsed . start ( ) ;
2016-01-27 20:01:50 +00:00
} else if ( type = = TAG_EOT ) {
2016-02-25 19:54:18 +00:00
if ( m_netState ! = RS_NET_AUDIO )
2016-01-27 20:01:50 +00:00
return ;
2016-02-25 19:54:18 +00:00
writeQueueEOTNet ( ) ;
2016-02-17 07:23:41 +00:00
2016-01-27 20:01:50 +00:00
data [ 1U ] = TAG_EOT ;
# if defined(DUMP_DSTAR)
writeFile ( data + 1U , length - 1U ) ;
closeFile ( ) ;
# endif
// We've received the header and EOT haven't we?
2016-02-25 19:54:18 +00:00
m_netFrames + = 2U ;
LogMessage ( " D-Star, received network end of transmission, %.1f seconds, %u%% packet loss, BER: %.1f%% " , float ( m_netFrames ) / 50.0F , ( m_netLost * 100U ) / m_netFrames , float ( m_netErrs * 100U ) / float ( m_netBits ) ) ;
2016-01-27 20:01:50 +00:00
2016-02-25 19:54:18 +00:00
writeEndNet ( ) ;
2016-02-03 07:20:48 +00:00
} else if ( type = = TAG_DATA ) {
2016-02-25 19:54:18 +00:00
if ( m_netState ! = RS_NET_AUDIO )
2016-01-27 20:01:50 +00:00
return ;
2016-02-02 19:54:51 +00:00
unsigned char n = data [ 1U ] ;
2016-10-05 06:42:41 +00:00
unsigned int errors = 0U ;
if ( ! m_netHeader . isDataPacket ( ) )
errors = m_fec . regenerateDStar ( data + 2U ) ;
2016-02-18 18:18:37 +00:00
2016-07-12 06:05:48 +00:00
blankDTMF ( data + 2U ) ;
2016-07-12 16:11:55 +00:00
data [ 1U ] = TAG_DATA ;
2016-07-12 06:05:48 +00:00
// Insert silence and reject if in the past
2016-07-12 06:24:07 +00:00
bool ret = insertSilence ( data + 1U , n ) ;
2016-07-12 06:05:48 +00:00
if ( ! ret )
return ;
2016-02-25 19:54:18 +00:00
m_netErrs + = errors ;
m_netBits + = 48U ;
2016-01-27 20:01:50 +00:00
2016-07-13 06:19:08 +00:00
m_netN = n ;
LogDebug ( " D-Star, audio sequence no. %u, errs: %u/48 " , m_netN , errors ) ;
2016-01-27 20:01:50 +00:00
// Regenerate the sync
if ( n = = 0U )
2016-02-15 21:46:57 +00:00
CSync : : addDStarSync ( data + 2U ) ;
2016-01-27 20:01:50 +00:00
2016-07-15 05:32:56 +00:00
m_packetTimer . start ( ) ;
2016-02-25 19:54:18 +00:00
m_netFrames + + ;
2016-01-27 20:01:50 +00:00
# if defined(DUMP_DSTAR)
writeFile ( data + 1U , length - 1U ) ;
# endif
2016-02-25 19:54:18 +00:00
writeQueueDataNet ( data + 1U ) ;
2016-02-03 07:20:48 +00:00
} else {
CUtils : : dump ( " D-Star, unknown data from network " , data , DSTAR_FRAME_LENGTH_BYTES + 1U ) ;
2016-01-27 20:01:50 +00:00
}
}
2016-03-14 20:55:15 +00:00
void CDStarControl : : clock ( )
2016-01-27 20:01:50 +00:00
{
2016-03-14 20:55:15 +00:00
unsigned int ms = m_interval . elapsed ( ) ;
m_interval . start ( ) ;
2016-01-27 20:01:50 +00:00
if ( m_network ! = NULL )
writeNetwork ( ) ;
2016-02-01 20:49:52 +00:00
m_ackTimer . clock ( ms ) ;
if ( m_ackTimer . isRunning ( ) & & m_ackTimer . hasExpired ( ) ) {
sendAck ( ) ;
m_ackTimer . stop ( ) ;
}
2016-02-25 19:54:18 +00:00
m_rfTimeoutTimer . clock ( ms ) ;
m_netTimeoutTimer . clock ( ms ) ;
2016-01-27 20:01:50 +00:00
2016-02-25 19:54:18 +00:00
if ( m_netState = = RS_NET_AUDIO ) {
2016-01-27 20:01:50 +00:00
m_networkWatchdog . clock ( ms ) ;
if ( m_networkWatchdog . hasExpired ( ) ) {
2016-01-27 20:14:02 +00:00
// We're received the header haven't we?
2016-02-25 19:54:18 +00: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 20:01:50 +00:00
# if defined(DUMP_DSTAR)
closeFile ( ) ;
# endif
}
}
2016-02-25 19:54:18 +00:00
if ( m_netState = = RS_NET_AUDIO ) {
2016-07-15 05:32:56 +00: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 17:10:51 +00: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 05:32:56 +00:00
insertSilence ( count - 2U ) ;
}
2016-01-27 20:01:50 +00:00
}
2016-07-15 05:32:56 +00:00
m_packetTimer . start ( ) ;
2016-01-27 20:01:50 +00:00
}
}
}
2016-02-25 19:54:18 +00:00
void CDStarControl : : writeQueueHeaderRF ( const unsigned char * data )
{
assert ( data ! = NULL ) ;
if ( m_netState ! = RS_NET_IDLE )
return ;
if ( m_rfTimeoutTimer . isRunning ( ) & & m_rfTimeoutTimer . hasExpired ( ) )
return ;
unsigned char len = DSTAR_HEADER_LENGTH_BYTES + 1U ;
2016-03-07 18:08:50 +00: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 19:54:18 +00:00
}
void CDStarControl : : writeQueueDataRF ( const unsigned char * data )
{
assert ( data ! = NULL ) ;
if ( m_netState ! = RS_NET_IDLE )
return ;
if ( m_rfTimeoutTimer . isRunning ( ) & & m_rfTimeoutTimer . hasExpired ( ) )
return ;
unsigned char len = DSTAR_FRAME_LENGTH_BYTES + 1U ;
2016-03-07 18:08:50 +00: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 19:54:18 +00:00
}
void CDStarControl : : writeQueueEOTRF ( )
{
if ( m_netState ! = RS_NET_IDLE )
return ;
if ( m_rfTimeoutTimer . isRunning ( ) & & m_rfTimeoutTimer . hasExpired ( ) )
return ;
unsigned char len = 1U ;
2016-03-07 18:08:50 +00: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 19:54:18 +00:00
unsigned char data = TAG_EOT ;
2016-03-07 18:08:50 +00:00
m_queue . addData ( & data , len ) ;
2016-02-25 19:54:18 +00:00
}
void CDStarControl : : writeQueueHeaderNet ( const unsigned char * data )
2016-01-27 20:01:50 +00:00
{
assert ( data ! = NULL ) ;
2016-02-25 19:54:18 +00:00
if ( m_netTimeoutTimer . isRunning ( ) & & m_netTimeoutTimer . hasExpired ( ) )
2016-01-27 20:01:50 +00:00
return ;
unsigned char len = DSTAR_HEADER_LENGTH_BYTES + 1U ;
2016-03-07 18:08:50 +00: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 20:01:50 +00:00
}
2016-02-25 19:54:18 +00:00
void CDStarControl : : writeQueueDataNet ( const unsigned char * data )
2016-01-27 20:01:50 +00:00
{
assert ( data ! = NULL ) ;
2016-02-25 19:54:18 +00:00
if ( m_netTimeoutTimer . isRunning ( ) & & m_netTimeoutTimer . hasExpired ( ) )
2016-01-27 20:01:50 +00:00
return ;
unsigned char len = DSTAR_FRAME_LENGTH_BYTES + 1U ;
2016-03-07 18:08:50 +00: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 20:01:50 +00:00
}
2016-02-25 19:54:18 +00:00
void CDStarControl : : writeQueueEOTNet ( )
2016-02-17 07:23:41 +00:00
{
2016-02-25 19:54:18 +00:00
if ( m_netTimeoutTimer . isRunning ( ) & & m_netTimeoutTimer . hasExpired ( ) )
2016-02-17 07:23:41 +00:00
return ;
unsigned char len = 1U ;
2016-03-07 18:08:50 +00: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 07:23:41 +00:00
unsigned char data = TAG_EOT ;
2016-03-07 18:08:50 +00:00
m_queue . addData ( & data , len ) ;
2016-02-17 07:23:41 +00:00
}
2016-02-25 19:54:18 +00:00
void CDStarControl : : writeNetworkHeaderRF ( const unsigned char * data )
2016-01-27 20:01:50 +00:00
{
assert ( data ! = NULL ) ;
if ( m_network = = NULL )
return ;
// Don't send to the network if the timeout has expired
2016-02-25 19:54:18 +00:00
if ( m_rfTimeoutTimer . isRunning ( ) & & m_rfTimeoutTimer . hasExpired ( ) )
2016-01-27 20:01:50 +00:00
return ;
2016-02-25 19:54:18 +00:00
m_network - > writeHeader ( data + 1U , DSTAR_HEADER_LENGTH_BYTES , m_netState ! = RS_NET_IDLE ) ;
2016-01-27 20:01:50 +00:00
}
2016-02-25 19:54:18 +00:00
void CDStarControl : : writeNetworkDataRF ( const unsigned char * data , unsigned int errors , bool end )
2016-01-27 20:01:50 +00:00
{
assert ( data ! = NULL ) ;
if ( m_network = = NULL )
return ;
// Don't send to the network if the timeout has expired
2016-02-25 19:54:18 +00:00
if ( m_rfTimeoutTimer . isRunning ( ) & & m_rfTimeoutTimer . hasExpired ( ) )
2016-01-27 20:01:50 +00:00
return ;
2016-02-25 19:54:18 +00:00
m_network - > writeData ( data + 1U , DSTAR_FRAME_LENGTH_BYTES , errors , end , m_netState ! = RS_NET_IDLE ) ;
2016-01-27 20:01:50 +00:00
}
bool CDStarControl : : openFile ( )
{
if ( m_fp ! = NULL )
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 " ) ;
if ( m_fp = = NULL )
return false ;
: : fwrite ( " DSTAR " , 1U , 4U , m_fp ) ;
return true ;
}
bool CDStarControl : : writeFile ( const unsigned char * data , unsigned int length )
{
if ( m_fp = = NULL )
return false ;
: : fwrite ( data , 1U , length , m_fp ) ;
return true ;
}
void CDStarControl : : closeFile ( )
{
if ( m_fp ! = NULL ) {
: : fclose ( m_fp ) ;
m_fp = NULL ;
}
}
2016-07-12 06:05:48 +00:00
bool CDStarControl : : insertSilence ( const unsigned char * data , unsigned char seqNo )
2016-01-27 20:01:50 +00:00
{
assert ( data ! = NULL ) ;
// Check to see if we have any spaces to fill?
2016-07-11 19:08:14 +00:00
unsigned int oldSeqNo = ( m_netN + 1U ) % 21U ;
if ( oldSeqNo = = seqNo ) {
2016-01-27 20:01:50 +00:00
// Just copy the data, nothing else to do here
2016-07-12 06:24:07 +00:00
: : memcpy ( m_lastFrame , data , DSTAR_FRAME_LENGTH_BYTES + 1U ) ;
2016-07-21 17:09:29 +00:00
m_lastFrameValid = true ;
2016-07-12 06:05:48 +00:00
return true ;
2016-01-27 20:01:50 +00:00
}
unsigned int count ;
2016-07-11 19:08:14 +00:00
if ( seqNo > oldSeqNo )
count = seqNo - oldSeqNo ;
2016-01-27 20:01:50 +00:00
else
2016-07-11 19:08:14 +00:00
count = ( 21U + seqNo ) - oldSeqNo ;
2016-01-27 20:01:50 +00:00
2016-07-12 06:05:48 +00:00
if ( count > = 10U )
return false ;
insertSilence ( count ) ;
2016-07-12 06:24:07 +00:00
: : memcpy ( m_lastFrame , data , DSTAR_FRAME_LENGTH_BYTES + 1U ) ;
2016-07-21 17:09:29 +00:00
m_lastFrameValid = true ;
2016-01-27 20:01:50 +00:00
2016-07-12 06:05:48 +00:00
return true ;
2016-01-27 20:01:50 +00:00
}
void CDStarControl : : insertSilence ( unsigned int count )
{
2016-02-28 17:18:13 +00:00
unsigned char n = ( m_netN + 1U ) % 21U ;
2016-01-27 20:01:50 +00:00
for ( unsigned int i = 0U ; i < count ; i + + ) {
2016-07-21 17:09:29 +00:00
if ( i < 3U & & m_lastFrameValid ) {
2016-07-11 19:08:14 +00:00
if ( n = = 0U ) {
: : memcpy ( m_lastFrame + DSTAR_VOICE_FRAME_LENGTH_BYTES + 1U , DSTAR_NULL_SLOW_SYNC_BYTES , DSTAR_DATA_FRAME_LENGTH_BYTES ) ;
writeQueueDataNet ( m_lastFrame ) ;
} else {
: : memcpy ( m_lastFrame + DSTAR_VOICE_FRAME_LENGTH_BYTES + 1U , DSTAR_NULL_SLOW_DATA_BYTES , DSTAR_DATA_FRAME_LENGTH_BYTES ) ;
writeQueueDataNet ( m_lastFrame ) ;
}
2016-01-27 20:01:50 +00:00
} else {
2016-08-09 16:20:26 +00:00
m_lastFrameValid = false ;
2016-02-01 20:49:52 +00:00
if ( n = = 0U )
2016-02-25 19:54:18 +00:00
writeQueueDataNet ( DSTAR_NULL_FRAME_SYNC_BYTES ) ;
2016-02-01 20:49:52 +00:00
else
2016-02-25 19:54:18 +00:00
writeQueueDataNet ( DSTAR_NULL_FRAME_DATA_BYTES ) ;
2016-01-27 20:01:50 +00:00
}
2016-02-28 17:18:13 +00:00
m_netN = n ;
2016-01-27 20:01:50 +00:00
2016-02-25 19:54:18 +00:00
m_netFrames + + ;
m_netLost + + ;
2016-01-27 20:01:50 +00:00
n = ( n + 1U ) % 21U ;
}
}
void CDStarControl : : blankDTMF ( unsigned char * data ) const
{
assert ( data ! = NULL ) ;
// 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 ] )
: : memcpy ( data , DSTAR_NULL_AMBE_DATA_BYTES , DSTAR_VOICE_FRAME_LENGTH_BYTES ) ;
}
2016-02-01 20:49:52 +00:00
void CDStarControl : : sendAck ( )
{
2016-02-25 19:54:18 +00:00
m_rfTimeoutTimer . stop ( ) ;
2016-02-02 19:54:51 +00:00
2016-02-01 20:49:52 +00:00
unsigned char user [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
2016-02-25 19:54:18 +00:00
m_rfHeader . getMyCall1 ( user ) ;
2016-02-01 20:49:52 +00: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 19:54:18 +00:00
writeQueueHeaderRF ( data ) ;
2016-02-01 20:49:52 +00:00
2016-02-25 19:54:18 +00:00
writeQueueDataRF ( DSTAR_NULL_FRAME_SYNC_BYTES ) ;
2016-02-01 20:49:52 +00:00
LINK_STATUS status = LS_NONE ;
unsigned char reflector [ DSTAR_LONG_CALLSIGN_LENGTH ] ;
if ( m_network ! = NULL )
m_network - > getStatus ( status , reflector ) ;
2016-02-01 21:44:40 +00:00
char text [ 40U ] ;
2016-02-01 20:49:52 +00:00
if ( status = = LS_LINKED_DEXTRA | | status = = LS_LINKED_DPLUS | | status = = LS_LINKED_DCS | | status = = LS_LINKED_CCS | | status = = LS_LINKED_LOOPBACK )
2016-02-25 19:54:18 +00:00
: : sprintf ( text , " %-8.8s BER: %.1f%% " , reflector , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
2016-02-01 20:49:52 +00:00
else
2016-02-25 19:54:18 +00:00
: : sprintf ( text , " BER: %.1f%% " , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
2016-02-01 20:49:52 +00:00
m_slowData . setText ( text ) ;
: : memcpy ( data , DSTAR_NULL_FRAME_DATA_BYTES , DSTAR_FRAME_LENGTH_BYTES + 1U ) ;
for ( unsigned int i = 0U ; i < 19U ; i + + ) {
m_slowData . get ( data + 1U + DSTAR_VOICE_FRAME_LENGTH_BYTES ) ;
2016-02-25 19:54:18 +00:00
writeQueueDataRF ( data ) ;
2016-02-01 20:49:52 +00:00
}
2016-02-25 19:54:18 +00:00
writeQueueEOTRF ( ) ;
2016-02-01 20:49:52 +00:00
}