2018-01-11 19:35:33 +00:00
/*
2019-01-10 09:05:15 +00:00
* Copyright ( C ) 2015 - 2019 Jonathan Naylor , G4KLX
2018-01-11 19:35:33 +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 .
*/
# include "NXDNControl.h"
2018-01-24 21:10:49 +00:00
# include "NXDNFACCH1.h"
2018-01-22 23:22:32 +00:00
# include "NXDNSACCH.h"
2018-02-19 20:51:43 +00:00
# include "NXDNAudio.h"
2018-01-24 21:10:49 +00:00
# include "NXDNUDCH.h"
# include "AMBEFEC.h"
2018-01-11 19:35:33 +00:00
# include "Utils.h"
# include "Sync.h"
# include "Log.h"
# include <cstdio>
# include <cassert>
# include <cstring>
# include <ctime>
2018-01-15 23:10:54 +00:00
const unsigned char SCRAMBLER [ ] = {
2018-01-25 20:19:31 +00:00
0x00U , 0x00U , 0x00U , 0x82U , 0xA0U , 0x88U , 0x8AU , 0x00U , 0xA2U , 0xA8U , 0x82U , 0x8AU , 0x82U , 0x02U ,
0x20U , 0x08U , 0x8AU , 0x20U , 0xAAU , 0xA2U , 0x82U , 0x08U , 0x22U , 0x8AU , 0xAAU , 0x08U , 0x28U , 0x88U ,
0x28U , 0x28U , 0x00U , 0x0AU , 0x02U , 0x82U , 0x20U , 0x28U , 0x82U , 0x2AU , 0xAAU , 0x20U , 0x22U , 0x80U ,
0xA8U , 0x8AU , 0x08U , 0xA0U , 0xAAU , 0x02U } ;
2018-01-15 23:10:54 +00:00
2018-01-11 19:35:33 +00:00
// #define DUMP_NXDN
2018-01-18 21:26:31 +00:00
const unsigned char BIT_MASK_TABLE [ ] = { 0x80U , 0x40U , 0x20U , 0x10U , 0x08U , 0x04U , 0x02U , 0x01U } ;
# define WRITE_BIT1(p,i,b) p[(i)>>3] = (b) ? (p[(i)>>3] | BIT_MASK_TABLE[(i)&7]) : (p[(i)>>3] & ~BIT_MASK_TABLE[(i)&7])
# define READ_BIT1(p,i) (p[(i)>>3] & BIT_MASK_TABLE[(i)&7])
2018-01-17 21:21:25 +00:00
CNXDNControl : : CNXDNControl ( unsigned int ran , unsigned int id , bool selfOnly , CNXDNNetwork * network , CDisplay * display , unsigned int timeout , bool duplex , bool remoteGateway , CNXDNLookup * lookup , CRSSIInterpolator * rssiMapper ) :
2018-01-15 21:03:34 +00:00
m_ran ( ran ) ,
m_id ( id ) ,
2018-01-11 19:35:33 +00:00
m_selfOnly ( selfOnly ) ,
m_network ( network ) ,
m_display ( display ) ,
m_duplex ( duplex ) ,
m_remoteGateway ( remoteGateway ) ,
2018-01-17 21:21:25 +00:00
m_lookup ( lookup ) ,
2018-01-11 19:35:33 +00:00
m_queue ( 5000U , " NXDN Control " ) ,
m_rfState ( RS_RF_LISTENING ) ,
m_netState ( RS_NET_IDLE ) ,
m_rfTimeoutTimer ( 1000U , timeout ) ,
m_netTimeoutTimer ( 1000U , timeout ) ,
m_packetTimer ( 1000U , 0U , 200U ) ,
m_networkWatchdog ( 1000U , 0U , 1500U ) ,
m_elapsed ( ) ,
m_rfFrames ( 0U ) ,
m_netFrames ( 0U ) ,
m_rfErrs ( 0U ) ,
m_rfBits ( 1U ) ,
2018-01-30 19:51:47 +00:00
m_rfLastLICH ( ) ,
2018-01-31 21:05:34 +00:00
m_rfLayer3 ( ) ,
2018-02-15 20:46:22 +00:00
m_netLayer3 ( ) ,
2018-01-30 19:51:47 +00:00
m_rfMask ( 0x00U ) ,
2018-02-15 20:46:22 +00:00
m_netMask ( 0x00U ) ,
2018-01-11 19:35:33 +00:00
m_rssiMapper ( rssiMapper ) ,
m_rssi ( 0U ) ,
m_maxRSSI ( 0U ) ,
m_minRSSI ( 0U ) ,
m_aveRSSI ( 0U ) ,
m_rssiCount ( 0U ) ,
2019-01-19 17:15:24 +00:00
m_enabled ( true ) ,
2018-01-11 19:35:33 +00:00
m_fp ( NULL )
{
assert ( display ! = NULL ) ;
2018-01-17 21:21:25 +00:00
assert ( lookup ! = NULL ) ;
2018-01-11 19:35:33 +00:00
assert ( rssiMapper ! = NULL ) ;
}
CNXDNControl : : ~ CNXDNControl ( )
{
}
bool CNXDNControl : : writeModem ( unsigned char * data , unsigned int len )
{
assert ( data ! = NULL ) ;
2019-01-20 17:09:40 +00:00
if ( ! m_enabled )
return false ;
2018-01-11 19:35:33 +00:00
unsigned char type = data [ 0U ] ;
if ( type = = TAG_LOST & & m_rfState = = RS_RF_AUDIO ) {
if ( m_rssi ! = 0U )
2018-02-07 19:49:53 +00:00
LogMessage ( " NXDN, transmission lost, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm " , float ( m_rfFrames ) / 12.5F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , m_minRSSI , m_maxRSSI , m_aveRSSI / m_rssiCount ) ;
2018-01-11 19:35:33 +00:00
else
2018-02-07 19:49:53 +00:00
LogMessage ( " NXDN, transmission lost, %.1f seconds, BER: %.1f%% " , float ( m_rfFrames ) / 12.5F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
2018-01-11 19:35:33 +00:00
writeEndRF ( ) ;
return false ;
}
2018-01-30 21:12:21 +00:00
if ( type = = TAG_LOST & & m_rfState = = RS_RF_DATA ) {
writeEndRF ( ) ;
return false ;
}
2018-01-11 19:35:33 +00:00
if ( type = = TAG_LOST ) {
m_rfState = RS_RF_LISTENING ;
2018-01-31 21:05:34 +00:00
m_rfMask = 0x00U ;
2018-02-02 07:34:59 +00:00
m_rfLayer3 . reset ( ) ;
2018-01-11 19:35:33 +00:00
return false ;
}
// Have we got RSSI bytes on the end?
if ( len = = ( NXDN_FRAME_LENGTH_BYTES + 4U ) ) {
uint16_t raw = 0U ;
2018-01-15 21:29:59 +00:00
raw | = ( data [ 50U ] < < 8 ) & 0xFF00U ;
raw | = ( data [ 51U ] < < 0 ) & 0x00FFU ;
2018-01-11 19:35:33 +00:00
// Convert the raw RSSI to dBm
int rssi = m_rssiMapper - > interpolate ( raw ) ;
2018-03-06 20:12:29 +00:00
if ( rssi ! = 0 )
LogDebug ( " NXDN, raw RSSI: %u, reported RSSI: %d dBm " , raw , rssi ) ;
2018-01-11 19:35:33 +00: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 + + ;
}
2018-01-15 23:10:54 +00:00
scrambler ( data + 2U ) ;
2018-01-16 20:10:35 +00:00
CNXDNLICH lich ;
bool valid = lich . decode ( data + 2U ) ;
2018-01-11 19:35:33 +00:00
if ( valid )
2018-01-30 19:51:47 +00:00
m_rfLastLICH = lich ;
2018-01-11 19:35:33 +00:00
// Stop repeater packets coming through, unless we're acting as a remote gateway
if ( m_remoteGateway ) {
2018-01-30 19:51:47 +00:00
unsigned char direction = m_rfLastLICH . getDirection ( ) ;
2018-01-18 21:26:31 +00:00
if ( direction = = NXDN_LICH_DIRECTION_INBOUND )
2018-01-11 19:35:33 +00:00
return false ;
} else {
2018-01-30 19:51:47 +00:00
unsigned char direction = m_rfLastLICH . getDirection ( ) ;
2018-01-16 20:10:35 +00:00
if ( direction = = NXDN_LICH_DIRECTION_OUTBOUND )
2018-01-11 19:35:33 +00:00
return false ;
}
2018-01-30 19:51:47 +00:00
unsigned char usc = m_rfLastLICH . getFCT ( ) ;
unsigned char option = m_rfLastLICH . getOption ( ) ;
2018-01-11 19:35:33 +00:00
2018-01-22 23:22:32 +00:00
bool ret ;
if ( usc = = NXDN_LICH_USC_UDCH )
2018-01-18 21:26:31 +00:00
ret = processData ( option , data ) ;
else
2018-02-05 21:39:50 +00:00
ret = processVoice ( usc , option , data ) ;
2018-01-22 23:22:32 +00:00
2018-01-11 19:35:33 +00:00
return ret ;
}
2018-02-05 21:39:50 +00:00
bool CNXDNControl : : processVoice ( unsigned char usc , unsigned char option , unsigned char * data )
2018-01-11 19:35:33 +00:00
{
2018-01-22 23:22:32 +00:00
CNXDNSACCH sacch ;
2018-02-05 21:39:50 +00:00
bool valid = sacch . decode ( data + 2U ) ;
if ( valid ) {
2018-01-24 21:10:49 +00:00
unsigned char ran = sacch . getRAN ( ) ;
if ( ran ! = m_ran & & ran ! = 0U )
return false ;
2018-02-01 18:32:23 +00:00
} else if ( m_rfState = = RS_RF_LISTENING ) {
2018-02-01 21:28:48 +00:00
return false ;
2018-01-24 21:10:49 +00:00
}
2018-01-11 19:35:33 +00:00
2018-02-27 20:05:49 +00:00
unsigned char netData [ 40U ] ;
: : memset ( netData , 0x00U , 40U ) ;
2018-01-31 21:05:34 +00:00
if ( usc = = NXDN_LICH_USC_SACCH_NS ) {
// The SACCH on a non-superblock frame is usually an idle and not interesting apart from the RAN.
2018-02-01 06:35:16 +00:00
CNXDNFACCH1 facch ;
bool valid = facch . decode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS ) ;
if ( ! valid )
valid = facch . decode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS ) ;
2018-02-01 21:28:48 +00:00
if ( ! valid )
return false ;
2018-01-31 21:05:34 +00:00
2018-02-01 18:32:23 +00:00
unsigned char buffer [ 10U ] ;
facch . getData ( buffer ) ;
2018-01-30 19:51:47 +00:00
2018-02-01 18:32:23 +00:00
CNXDNLayer3 layer3 ;
layer3 . decode ( buffer , NXDN_FACCH1_LENGTH_BITS ) ;
2018-01-31 21:05:34 +00:00
2018-02-01 18:32:23 +00:00
unsigned char type = layer3 . getMessageType ( ) ;
if ( type = = NXDN_MESSAGE_TYPE_TX_REL ) {
if ( m_rfState ! = RS_RF_AUDIO ) {
m_rfState = RS_RF_LISTENING ;
m_rfMask = 0x00U ;
2018-02-02 07:34:59 +00:00
m_rfLayer3 . reset ( ) ;
2018-02-01 18:32:23 +00:00
return false ;
}
2018-02-05 20:42:43 +00:00
} else if ( type = = NXDN_MESSAGE_TYPE_VCALL ) {
if ( m_rfState = = RS_RF_LISTENING & & m_selfOnly ) {
2018-02-01 18:32:23 +00:00
unsigned short srcId = layer3 . getSourceUnitId ( ) ;
if ( srcId ! = m_id ) {
m_rfState = RS_RF_REJECTED ;
2018-01-31 21:05:34 +00:00
return false ;
}
}
2018-02-05 20:42:43 +00:00
} else {
return false ;
2018-02-01 18:32:23 +00:00
}
2018-01-31 21:05:34 +00:00
2018-02-13 19:07:30 +00:00
m_rfLayer3 = layer3 ;
2018-02-01 18:32:23 +00:00
data [ 0U ] = type = = NXDN_MESSAGE_TYPE_TX_REL ? TAG_EOT : TAG_DATA ;
data [ 1U ] = 0x00U ;
2018-01-31 21:05:34 +00:00
2018-02-01 18:32:23 +00:00
CSync : : addNXDNSync ( data + 2U ) ;
2018-01-31 21:05:34 +00:00
2018-02-28 21:07:43 +00:00
CNXDNLICH lich ;
lich . setRFCT ( NXDN_LICH_RFCT_RDCH ) ;
lich . setFCT ( NXDN_LICH_USC_SACCH_NS ) ;
lich . setOption ( NXDN_LICH_STEAL_FACCH ) ;
2018-03-08 07:12:35 +00:00
lich . setDirection ( m_remoteGateway | | ! m_duplex ? NXDN_LICH_DIRECTION_INBOUND : NXDN_LICH_DIRECTION_OUTBOUND ) ;
2018-02-01 18:32:23 +00:00
lich . encode ( data + 2U ) ;
2018-01-31 21:05:34 +00:00
2018-05-23 17:30:40 +00:00
lich . setDirection ( NXDN_LICH_DIRECTION_INBOUND ) ;
2018-02-27 20:05:49 +00:00
netData [ 0U ] = lich . getRaw ( ) ;
2018-02-01 18:32:23 +00:00
CNXDNSACCH sacch ;
sacch . setRAN ( m_ran ) ;
sacch . setStructure ( NXDN_SR_SINGLE ) ;
sacch . setData ( SACCH_IDLE ) ;
sacch . encode ( data + 2U ) ;
2018-01-31 21:05:34 +00:00
2018-02-27 20:05:49 +00:00
sacch . getRaw ( netData + 1U ) ;
2018-02-01 18:32:23 +00:00
facch . encode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS ) ;
facch . encode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS ) ;
2018-01-31 21:05:34 +00:00
2018-02-27 20:05:49 +00:00
facch . getRaw ( netData + 5U + 0U ) ;
facch . getRaw ( netData + 5U + 14U ) ;
2018-02-01 18:32:23 +00:00
scrambler ( data + 2U ) ;
2018-01-31 21:05:34 +00:00
2018-05-17 18:23:01 +00:00
writeNetwork ( netData , data [ 0U ] = = TAG_EOT ? NNMT_VOICE_TRAILER : NNMT_VOICE_HEADER ) ;
2018-01-31 21:05:34 +00:00
# if defined(DUMP_NXDN)
2018-02-01 18:32:23 +00:00
writeFile ( data + 2U ) ;
2018-01-31 21:05:34 +00:00
# endif
2018-02-01 18:32:23 +00:00
if ( m_duplex )
writeQueueRF ( data ) ;
2018-02-06 20:40:29 +00:00
if ( data [ 0U ] = = TAG_EOT ) {
2018-02-01 18:32:23 +00:00
m_rfFrames + + ;
if ( m_rssi ! = 0U )
2018-02-07 19:49:53 +00:00
LogMessage ( " NXDN, received RF end of transmission, %.1f seconds, BER: %.1f%%, RSSI: -%u/-%u/-%u dBm " , float ( m_rfFrames ) / 12.5F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) , m_minRSSI , m_maxRSSI , m_aveRSSI / m_rssiCount ) ;
2018-02-01 18:32:23 +00:00
else
2018-02-07 19:49:53 +00:00
LogMessage ( " NXDN, received RF end of transmission, %.1f seconds, BER: %.1f%% " , float ( m_rfFrames ) / 12.5F , float ( m_rfErrs * 100U ) / float ( m_rfBits ) ) ;
2018-02-01 18:32:23 +00:00
writeEndRF ( ) ;
} else {
m_rfFrames = 0U ;
m_rfErrs = 0U ;
m_rfBits = 1U ;
m_rfTimeoutTimer . start ( ) ;
m_rfState = RS_RF_AUDIO ;
m_minRSSI = m_rssi ;
m_maxRSSI = m_rssi ;
m_aveRSSI = m_rssi ;
m_rssiCount = 1U ;
2018-01-31 21:05:34 +00:00
# if defined(DUMP_NXDN)
2018-02-01 18:32:23 +00:00
openFile ( ) ;
2018-01-31 21:05:34 +00:00
# endif
2018-02-01 18:32:23 +00:00
unsigned short srcId = m_rfLayer3 . getSourceUnitId ( ) ;
unsigned short dstId = m_rfLayer3 . getDestinationGroupId ( ) ;
bool grp = m_rfLayer3 . getIsGroup ( ) ;
2018-01-31 21:05:34 +00:00
2018-02-01 18:32:23 +00:00
std : : string source = m_lookup - > find ( srcId ) ;
2018-02-01 18:49:03 +00:00
LogMessage ( " NXDN, received RF header from %s to %s%u " , source . c_str ( ) , grp ? " TG " : " " , dstId ) ;
2018-02-01 18:32:23 +00:00
m_display - > writeNXDN ( source . c_str ( ) , grp , dstId , " R " ) ;
2018-01-31 21:05:34 +00:00
}
2018-02-01 18:32:23 +00:00
return true ;
2018-02-01 21:28:48 +00:00
} else {
2018-02-01 22:05:17 +00:00
if ( m_rfState = = RS_RF_LISTENING ) {
2018-02-02 07:03:10 +00:00
CNXDNFACCH1 facch ;
bool valid = false ;
switch ( option ) {
case NXDN_LICH_STEAL_FACCH :
valid = facch . decode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS ) ;
if ( ! valid )
valid = facch . decode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS ) ;
2018-02-01 18:32:23 +00:00
break ;
2018-02-02 07:03:10 +00:00
case NXDN_LICH_STEAL_FACCH1_1 :
valid = facch . decode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS ) ;
2018-02-01 18:32:23 +00:00
break ;
2018-02-02 07:03:10 +00:00
case NXDN_LICH_STEAL_FACCH1_2 :
valid = facch . decode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS ) ;
2018-02-01 18:32:23 +00:00
break ;
default :
break ;
}
2018-01-30 19:51:47 +00:00
2018-02-02 07:03:10 +00:00
bool hasInfo = false ;
if ( valid ) {
unsigned char buffer [ 10U ] ;
facch . getData ( buffer ) ;
2018-01-30 19:51:47 +00:00
2018-02-02 07:03:10 +00:00
CNXDNLayer3 layer3 ;
layer3 . decode ( buffer , NXDN_FACCH1_LENGTH_BITS ) ;
2018-02-05 20:42:43 +00:00
hasInfo = layer3 . getMessageType ( ) = = NXDN_MESSAGE_TYPE_VCALL ;
2018-02-02 07:03:10 +00:00
if ( ! hasInfo )
return false ;
m_rfLayer3 = layer3 ;
}
if ( ! hasInfo ) {
unsigned char message [ 3U ] ;
sacch . getData ( message ) ;
unsigned char structure = sacch . getStructure ( ) ;
switch ( structure ) {
case NXDN_SR_1_4 :
m_rfLayer3 . decode ( message , 18U , 0U ) ;
2018-02-12 04:38:39 +00:00
if ( m_rfLayer3 . getMessageType ( ) = = NXDN_MESSAGE_TYPE_VCALL )
m_rfMask = 0x01U ;
else
m_rfMask = 0x00U ;
2018-02-02 07:03:10 +00:00
break ;
case NXDN_SR_2_4 :
m_rfMask | = 0x02U ;
m_rfLayer3 . decode ( message , 18U , 18U ) ;
break ;
case NXDN_SR_3_4 :
m_rfMask | = 0x04U ;
m_rfLayer3 . decode ( message , 18U , 36U ) ;
break ;
case NXDN_SR_4_4 :
m_rfMask | = 0x08U ;
m_rfLayer3 . decode ( message , 18U , 54U ) ;
break ;
default :
break ;
}
if ( m_rfMask ! = 0x0FU )
return false ;
2018-02-05 20:42:43 +00:00
unsigned char type = m_rfLayer3 . getMessageType ( ) ;
if ( type ! = NXDN_MESSAGE_TYPE_VCALL )
2018-02-02 07:03:10 +00:00
return false ;
}
2018-01-30 19:51:47 +00:00
2018-02-01 18:32:23 +00:00
unsigned short srcId = m_rfLayer3 . getSourceUnitId ( ) ;
unsigned short dstId = m_rfLayer3 . getDestinationGroupId ( ) ;
bool grp = m_rfLayer3 . getIsGroup ( ) ;
2018-01-30 19:51:47 +00:00
2018-02-01 18:32:23 +00:00
if ( m_selfOnly ) {
if ( srcId ! = m_id ) {
m_rfState = RS_RF_REJECTED ;
return false ;
}
2018-01-30 19:51:47 +00:00
}
2018-02-01 18:32:23 +00:00
m_rfFrames = 0U ;
m_rfErrs = 0U ;
m_rfBits = 1U ;
m_rfTimeoutTimer . start ( ) ;
m_rfState = RS_RF_AUDIO ;
2018-01-30 19:51:47 +00:00
2018-02-01 18:32:23 +00:00
m_minRSSI = m_rssi ;
m_maxRSSI = m_rssi ;
m_aveRSSI = m_rssi ;
m_rssiCount = 1U ;
2018-01-30 19:51:47 +00:00
# if defined(DUMP_NXDN)
2018-02-01 18:32:23 +00:00
openFile ( ) ;
2018-01-30 19:51:47 +00:00
# endif
2018-02-01 18:32:23 +00:00
std : : string source = m_lookup - > find ( srcId ) ;
2018-02-01 18:49:03 +00:00
LogMessage ( " NXDN, received RF late entry from %s to %s%u " , source . c_str ( ) , grp ? " TG " : " " , dstId ) ;
2018-02-01 18:32:23 +00:00
m_display - > writeNXDN ( source . c_str ( ) , grp , dstId , " R " ) ;
2018-01-30 19:51:47 +00:00
2018-02-01 18:32:23 +00:00
m_rfState = RS_RF_AUDIO ;
2018-02-02 07:03:10 +00:00
// Create a dummy start message
unsigned char start [ NXDN_FRAME_LENGTH_BYTES + 2U ] ;
start [ 0U ] = TAG_DATA ;
start [ 1U ] = 0x00U ;
// Generate the sync
CSync : : addNXDNSync ( start + 2U ) ;
// Generate the LICH
CNXDNLICH lich ;
lich . setRFCT ( NXDN_LICH_RFCT_RDCH ) ;
lich . setFCT ( NXDN_LICH_USC_SACCH_NS ) ;
lich . setOption ( NXDN_LICH_STEAL_FACCH ) ;
2018-03-08 07:12:35 +00:00
lich . setDirection ( m_remoteGateway | | ! m_duplex ? NXDN_LICH_DIRECTION_INBOUND : NXDN_LICH_DIRECTION_OUTBOUND ) ;
2018-02-02 07:03:10 +00:00
lich . encode ( start + 2U ) ;
2018-05-23 17:30:40 +00:00
lich . setDirection ( NXDN_LICH_DIRECTION_INBOUND ) ;
2018-02-27 20:05:49 +00:00
netData [ 0U ] = lich . getRaw ( ) ;
2018-02-02 07:03:10 +00:00
CNXDNSACCH sacch ;
sacch . setRAN ( m_ran ) ;
sacch . setStructure ( NXDN_SR_SINGLE ) ;
sacch . setData ( SACCH_IDLE ) ;
sacch . encode ( start + 2U ) ;
2018-02-27 20:05:49 +00:00
sacch . getRaw ( netData + 1U ) ;
2018-02-02 07:03:10 +00:00
unsigned char message [ 22U ] ;
m_rfLayer3 . getData ( message ) ;
facch . setData ( message ) ;
facch . encode ( start + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS ) ;
facch . encode ( start + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS ) ;
2018-02-27 20:05:49 +00:00
facch . getRaw ( netData + 5U + 0U ) ;
facch . getRaw ( netData + 5U + 14U ) ;
2018-02-02 07:03:10 +00:00
scrambler ( start + 2U ) ;
2018-05-17 18:23:01 +00:00
writeNetwork ( netData , NNMT_VOICE_HEADER ) ;
2018-02-02 07:03:10 +00:00
# if defined(DUMP_NXDN)
writeFile ( start + 2U ) ;
# endif
if ( m_duplex )
writeQueueRF ( start ) ;
2018-02-01 18:32:23 +00:00
}
2018-02-01 22:05:17 +00:00
}
2018-01-30 19:51:47 +00:00
2018-02-01 22:05:17 +00:00
if ( m_rfState = = RS_RF_AUDIO ) {
2018-01-30 21:12:21 +00:00
// Regenerate the sync
CSync : : addNXDNSync ( data + 2U ) ;
2018-01-11 19:35:33 +00:00
2018-01-30 21:12:21 +00:00
// Regenerate the LICH
CNXDNLICH lich ;
lich . setRFCT ( NXDN_LICH_RFCT_RDCH ) ;
2018-02-28 21:07:43 +00:00
lich . setFCT ( NXDN_LICH_USC_SACCH_SS ) ;
2018-01-30 21:12:21 +00:00
lich . setOption ( option ) ;
2018-03-08 07:12:35 +00:00
lich . setDirection ( m_remoteGateway | | ! m_duplex ? NXDN_LICH_DIRECTION_INBOUND : NXDN_LICH_DIRECTION_OUTBOUND ) ;
2018-01-30 21:12:21 +00:00
lich . encode ( data + 2U ) ;
2018-01-11 19:35:33 +00:00
2018-05-23 17:30:40 +00:00
lich . setDirection ( NXDN_LICH_DIRECTION_INBOUND ) ;
2018-02-27 20:05:49 +00:00
netData [ 0U ] = lich . getRaw ( ) ;
2018-02-05 21:07:06 +00:00
// Regenerate SACCH if it's valid
CNXDNSACCH sacch ;
bool validSACCH = sacch . decode ( data + 2U ) ;
if ( validSACCH ) {
sacch . setRAN ( m_ran ) ;
sacch . encode ( data + 2U ) ;
}
2018-01-30 21:12:21 +00:00
2018-02-27 20:05:49 +00:00
sacch . getRaw ( netData + 1U ) ;
2018-01-30 21:12:21 +00:00
// Regenerate the audio and interpret the FACCH1 data
if ( option = = NXDN_LICH_STEAL_NONE ) {
CAMBEFEC ambe ;
unsigned int errors = 0U ;
2018-02-27 20:05:49 +00:00
errors + = ambe . regenerateYSFDN ( data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 0U ) ;
2018-02-07 18:26:26 +00:00
errors + = ambe . regenerateYSFDN ( data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 9U ) ;
errors + = ambe . regenerateYSFDN ( data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 18U ) ;
errors + = ambe . regenerateYSFDN ( data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 27U ) ;
m_rfErrs + = errors ;
m_rfBits + = 188U ;
m_display - > writeNXDNBER ( float ( errors ) / 1.88F ) ;
LogDebug ( " NXDN, AMBE FEC %u/188 (%.1f%%) " , errors , float ( errors ) / 1.88F ) ;
2018-02-27 20:05:49 +00:00
CNXDNAudio audio ;
2018-02-28 21:46:18 +00:00
audio . decode ( data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 0U , netData + 5U + 0U ) ;
audio . decode ( data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 18U , netData + 5U + 14U ) ;
2018-01-30 21:12:21 +00:00
} else if ( option = = NXDN_LICH_STEAL_FACCH1_1 ) {
CNXDNFACCH1 facch1 ;
bool valid = facch1 . decode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS ) ;
if ( valid )
facch1 . encode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS ) ;
2018-02-27 20:05:49 +00:00
facch1 . getRaw ( netData + 5U + 0U ) ;
2018-01-30 21:12:21 +00:00
CAMBEFEC ambe ;
unsigned int errors = 0U ;
2018-02-07 18:26:26 +00:00
errors + = ambe . regenerateYSFDN ( data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 18U ) ;
errors + = ambe . regenerateYSFDN ( data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 27U ) ;
m_rfErrs + = errors ;
m_rfBits + = 94U ;
m_display - > writeNXDNBER ( float ( errors ) / 0.94F ) ;
LogDebug ( " NXDN, AMBE FEC %u/94 (%.1f%%) " , errors , float ( errors ) / 0.94F ) ;
2018-02-27 20:05:49 +00:00
CNXDNAudio audio ;
2018-02-28 21:46:18 +00:00
audio . decode ( data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 18U , netData + 5U + 14U ) ;
2018-01-30 21:12:21 +00:00
} else if ( option = = NXDN_LICH_STEAL_FACCH1_2 ) {
CAMBEFEC ambe ;
unsigned int errors = 0U ;
2018-02-07 18:26:26 +00:00
errors + = ambe . regenerateYSFDN ( data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES ) ;
errors + = ambe . regenerateYSFDN ( data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 9U ) ;
m_rfErrs + = errors ;
m_rfBits + = 94U ;
m_display - > writeNXDNBER ( float ( errors ) / 0.94F ) ;
LogDebug ( " NXDN, AMBE FEC %u/94 (%.1f%%) " , errors , float ( errors ) / 0.94F ) ;
2018-01-11 19:35:33 +00:00
2018-02-27 20:05:49 +00:00
CNXDNAudio audio ;
2018-02-28 21:46:18 +00:00
audio . decode ( data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 0U , netData + 5U + 0U ) ;
2018-02-27 20:05:49 +00:00
2018-01-30 21:12:21 +00:00
CNXDNFACCH1 facch1 ;
bool valid = facch1 . decode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS ) ;
if ( valid )
facch1 . encode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS ) ;
2018-02-27 20:05:49 +00:00
facch1 . getRaw ( netData + 5U + 14U ) ;
2018-01-30 21:12:21 +00:00
} else {
CNXDNFACCH1 facch11 ;
bool valid1 = facch11 . decode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS ) ;
if ( valid1 )
facch11 . encode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS ) ;
2018-02-27 20:05:49 +00:00
facch11 . getRaw ( netData + 5U + 0U ) ;
2018-01-30 21:12:21 +00:00
CNXDNFACCH1 facch12 ;
bool valid2 = facch12 . decode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS ) ;
if ( valid2 )
facch12 . encode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS ) ;
2018-02-27 20:05:49 +00:00
facch12 . getRaw ( netData + 5U + 14U ) ;
2018-01-11 19:35:33 +00:00
}
2018-01-30 21:12:21 +00:00
data [ 0U ] = TAG_DATA ;
data [ 1U ] = 0x00U ;
2018-01-11 19:35:33 +00:00
2018-01-30 22:02:14 +00:00
scrambler ( data + 2U ) ;
2018-05-17 18:23:01 +00:00
writeNetwork ( netData , NNMT_VOICE_BODY ) ;
2018-01-11 19:35:33 +00:00
2018-01-15 23:10:54 +00:00
# if defined(DUMP_NXDN)
2018-01-30 21:12:21 +00:00
writeFile ( data + 2U ) ;
2018-01-11 19:35:33 +00:00
# endif
2018-01-30 21:12:21 +00:00
if ( m_duplex )
writeQueueRF ( data ) ;
2018-01-11 19:35:33 +00:00
2018-01-30 21:12:21 +00:00
m_rfFrames + + ;
2018-01-11 19:35:33 +00:00
2018-01-30 21:12:21 +00:00
m_display - > writeNXDNRSSI ( m_rssi ) ;
2018-02-01 22:05:17 +00:00
}
2018-01-30 21:12:21 +00:00
return true ;
2018-01-11 19:35:33 +00:00
}
2018-01-24 21:10:49 +00:00
bool CNXDNControl : : processData ( unsigned char option , unsigned char * data )
2018-01-11 19:35:33 +00:00
{
2018-01-30 20:41:43 +00:00
CNXDNUDCH udch ;
2018-02-05 21:07:06 +00:00
bool validUDCH = udch . decode ( data + 2U ) ;
if ( m_rfState = = RS_RF_LISTENING & & ! validUDCH )
2018-02-05 20:42:43 +00:00
return false ;
2018-02-05 21:07:06 +00:00
if ( validUDCH ) {
2018-01-30 20:41:43 +00:00
unsigned char ran = udch . getRAN ( ) ;
if ( ran ! = m_ran & & ran ! = 0U )
return false ;
2018-01-24 21:10:49 +00:00
}
2018-02-27 20:05:49 +00:00
unsigned char netData [ 40U ] ;
: : memset ( netData , 0x00U , 40U ) ;
2018-02-05 20:42:43 +00:00
// The layer3 data will only be correct if valid is true
unsigned char buffer [ 23U ] ;
udch . getData ( buffer ) ;
2018-01-11 19:35:33 +00:00
2018-02-05 20:42:43 +00:00
CNXDNLayer3 layer3 ;
layer3 . decode ( buffer , 184U ) ;
2018-01-11 19:35:33 +00:00
2018-02-05 20:42:43 +00:00
if ( m_rfState = = RS_RF_LISTENING ) {
unsigned char type = layer3 . getMessageType ( ) ;
if ( type ! = NXDN_MESSAGE_TYPE_DCALL_HDR )
return false ;
2018-01-11 19:35:33 +00:00
2018-02-05 20:42:43 +00:00
unsigned short srcId = layer3 . getSourceUnitId ( ) ;
unsigned short dstId = layer3 . getDestinationGroupId ( ) ;
bool grp = layer3 . getIsGroup ( ) ;
2018-01-11 19:35:33 +00:00
2018-02-05 20:42:43 +00:00
if ( m_selfOnly ) {
if ( srcId ! = m_id )
return false ;
2018-01-11 19:35:33 +00:00
}
2018-02-05 20:42:43 +00:00
unsigned char frames = layer3 . getDataBlocks ( ) ;
2018-01-11 19:35:33 +00:00
2018-02-05 20:42:43 +00:00
std : : string source = m_lookup - > find ( srcId ) ;
2018-01-11 19:35:33 +00:00
2018-02-05 20:42:43 +00:00
m_display - > writeNXDN ( source . c_str ( ) , grp , dstId , " R " ) ;
m_display - > writeNXDNRSSI ( m_rssi ) ;
2018-01-11 19:35:33 +00:00
2018-02-05 20:42:43 +00:00
LogMessage ( " NXDN, received RF data header from %s to %s%u, %u blocks " , source . c_str ( ) , grp ? " TG " : " " , dstId , frames ) ;
2018-01-11 19:35:33 +00:00
2018-02-06 19:00:01 +00:00
m_rfLayer3 = layer3 ;
m_rfFrames = 0U ;
2018-02-05 20:42:43 +00:00
m_rfState = RS_RF_DATA ;
2018-01-11 19:35:33 +00:00
2018-01-15 23:10:54 +00:00
# if defined(DUMP_NXDN)
2018-02-05 20:42:43 +00:00
openFile ( ) ;
2018-01-11 19:35:33 +00:00
# endif
2018-02-05 20:42:43 +00:00
}
2018-01-11 19:35:33 +00:00
2018-02-05 20:42:43 +00:00
if ( m_rfState ! = RS_RF_DATA )
return false ;
2018-01-11 19:35:33 +00:00
2018-02-05 20:42:43 +00:00
CSync : : addNXDNSync ( data + 2U ) ;
2018-01-11 19:35:33 +00:00
2018-02-05 20:42:43 +00:00
CNXDNLICH lich ;
lich . setRFCT ( NXDN_LICH_RFCT_RDCH ) ;
lich . setFCT ( NXDN_LICH_USC_UDCH ) ;
lich . setOption ( option ) ;
2018-03-08 07:12:35 +00:00
lich . setDirection ( m_remoteGateway | | ! m_duplex ? NXDN_LICH_DIRECTION_INBOUND : NXDN_LICH_DIRECTION_OUTBOUND ) ;
2018-02-05 20:42:43 +00:00
lich . encode ( data + 2U ) ;
2018-01-11 19:35:33 +00:00
2018-05-23 17:30:40 +00:00
lich . setDirection ( NXDN_LICH_DIRECTION_INBOUND ) ;
2018-02-27 20:05:49 +00:00
netData [ 0U ] = lich . getRaw ( ) ;
udch . getRaw ( netData + 1U ) ;
2018-05-17 18:23:01 +00:00
unsigned char type = NXDN_MESSAGE_TYPE_DCALL_DATA ;
2018-02-05 21:07:06 +00:00
if ( validUDCH ) {
2018-05-17 18:23:01 +00:00
type = layer3 . getMessageType ( ) ;
2018-02-05 21:33:36 +00:00
data [ 0U ] = type = = NXDN_MESSAGE_TYPE_TX_REL ? TAG_EOT : TAG_DATA ;
2018-02-05 20:42:43 +00:00
udch . setRAN ( m_ran ) ;
udch . encode ( data + 2U ) ;
2018-02-05 21:33:36 +00:00
} else {
data [ 0U ] = TAG_DATA ;
data [ 1U ] = 0x00U ;
2018-02-05 20:42:43 +00:00
}
2018-01-11 19:35:33 +00:00
2018-02-05 20:42:43 +00:00
scrambler ( data + 2U ) ;
2018-01-11 19:35:33 +00:00
2018-05-17 18:23:01 +00:00
switch ( type ) {
case NXDN_MESSAGE_TYPE_DCALL_HDR :
writeNetwork ( netData , NNMT_DATA_HEADER ) ;
break ;
case NXDN_MESSAGE_TYPE_TX_REL :
writeNetwork ( netData , NNMT_DATA_TRAILER ) ;
break ;
default :
writeNetwork ( netData , NNMT_DATA_BODY ) ;
break ;
}
2018-01-11 19:35:33 +00:00
2018-02-05 20:42:43 +00:00
if ( m_duplex )
writeQueueRF ( data ) ;
2018-01-11 19:35:33 +00:00
2018-02-06 19:00:01 +00:00
m_rfFrames + + ;
2018-01-15 23:10:54 +00:00
# if defined(DUMP_NXDN)
2018-02-05 20:42:43 +00:00
writeFile ( data + 2U ) ;
2018-01-11 19:35:33 +00:00
# endif
2018-02-06 19:00:01 +00:00
if ( data [ 0U ] = = TAG_EOT ) {
LogMessage ( " NXDN, ended RF data transmission " ) ;
writeEndRF ( ) ;
2018-01-11 19:35:33 +00:00
}
2018-02-05 20:42:43 +00:00
return true ;
2018-01-11 19:35:33 +00:00
}
unsigned int CNXDNControl : : readModem ( unsigned char * data )
{
assert ( data ! = NULL ) ;
if ( m_queue . isEmpty ( ) )
return 0U ;
unsigned char len = 0U ;
m_queue . getData ( & len , 1U ) ;
m_queue . getData ( data , len ) ;
return len ;
}
void CNXDNControl : : writeEndRF ( )
{
m_rfState = RS_RF_LISTENING ;
2018-01-30 19:51:47 +00:00
m_rfMask = 0x00U ;
2018-02-02 07:34:59 +00:00
m_rfLayer3 . reset ( ) ;
2018-01-30 19:51:47 +00:00
2018-01-11 19:35:33 +00:00
m_rfTimeoutTimer . stop ( ) ;
if ( m_netState = = RS_NET_IDLE ) {
2018-01-22 23:22:32 +00:00
m_display - > clearNXDN ( ) ;
2018-01-11 19:35:33 +00:00
if ( m_network ! = NULL )
m_network - > reset ( ) ;
}
2018-01-15 23:10:54 +00:00
# if defined(DUMP_NXDN)
2018-01-11 19:35:33 +00:00
closeFile ( ) ;
# endif
}
void CNXDNControl : : writeEndNet ( )
{
m_netState = RS_NET_IDLE ;
2018-02-15 20:46:22 +00:00
m_netMask = 0x00U ;
m_netLayer3 . reset ( ) ;
2018-01-11 19:35:33 +00:00
m_netTimeoutTimer . stop ( ) ;
m_networkWatchdog . stop ( ) ;
m_packetTimer . stop ( ) ;
2018-01-22 23:22:32 +00:00
m_display - > clearNXDN ( ) ;
2018-01-11 19:35:33 +00:00
if ( m_network ! = NULL )
m_network - > reset ( ) ;
}
void CNXDNControl : : writeNetwork ( )
{
2018-02-15 20:20:07 +00:00
unsigned char netData [ 40U ] ;
bool exists = m_network - > read ( netData ) ;
if ( ! exists )
2018-01-11 19:35:33 +00:00
return ;
2019-01-20 17:09:40 +00:00
if ( ! m_enabled )
return ;
2018-01-11 19:35:33 +00:00
if ( m_rfState ! = RS_RF_LISTENING & & m_netState = = RS_NET_IDLE )
return ;
m_networkWatchdog . start ( ) ;
2018-02-06 20:40:29 +00:00
2018-02-15 20:20:07 +00:00
unsigned char data [ NXDN_FRAME_LENGTH_BYTES + 2U ] ;
2018-02-06 20:40:29 +00:00
2018-02-15 20:20:07 +00:00
CSync : : addNXDNSync ( data + 2U ) ;
2018-02-06 20:40:29 +00:00
2018-02-15 20:20:07 +00:00
CNXDNLICH lich ;
2018-02-21 07:48:24 +00:00
lich . setRaw ( netData [ 0U ] ) ;
2018-02-15 20:20:07 +00:00
unsigned char usc = lich . getFCT ( ) ;
unsigned char option = lich . getOption ( ) ;
2018-03-08 07:16:41 +00:00
lich . setDirection ( m_remoteGateway | | ! m_duplex ? NXDN_LICH_DIRECTION_INBOUND : NXDN_LICH_DIRECTION_OUTBOUND ) ;
2018-02-15 20:20:07 +00:00
lich . encode ( data + 2U ) ;
2018-02-06 20:40:29 +00:00
2018-02-15 20:20:07 +00:00
if ( usc = = NXDN_LICH_USC_UDCH ) {
CNXDNLayer3 layer3 ;
layer3 . setData ( netData + 2U , 23U ) ;
unsigned char type = layer3 . getMessageType ( ) ;
2018-02-06 20:40:29 +00:00
2018-02-15 20:20:07 +00:00
if ( m_netState = = RS_NET_IDLE ) {
if ( type = = NXDN_MESSAGE_TYPE_DCALL_HDR ) {
unsigned short srcId = layer3 . getSourceUnitId ( ) ;
unsigned short dstId = layer3 . getDestinationGroupId ( ) ;
2018-02-15 21:32:28 +00:00
bool grp = layer3 . getIsGroup ( ) ;
2018-02-06 20:40:29 +00:00
2018-02-15 20:20:07 +00:00
unsigned char frames = layer3 . getDataBlocks ( ) ;
2018-02-06 20:40:29 +00:00
2018-02-15 20:20:07 +00:00
std : : string source = m_lookup - > find ( srcId ) ;
m_display - > writeNXDN ( source . c_str ( ) , grp , dstId , " N " ) ;
LogMessage ( " NXDN, received network data header from %s to %s%u, %u blocks " , source . c_str ( ) , grp ? " TG " : " " , dstId , frames ) ;
2018-02-06 20:40:29 +00:00
2018-02-15 20:20:07 +00:00
m_netState = RS_NET_DATA ;
} else {
return ;
}
}
2018-02-06 20:40:29 +00:00
2018-02-15 20:20:07 +00:00
if ( m_netState = = RS_NET_DATA ) {
data [ 0U ] = type = = NXDN_MESSAGE_TYPE_TX_REL ? TAG_EOT : TAG_DATA ;
data [ 1U ] = 0x00U ;
CNXDNUDCH udch ;
udch . setRAN ( m_ran ) ;
udch . setData ( netData + 2U ) ;
udch . encode ( data + 2U ) ;
scrambler ( data + 2U ) ;
writeQueueNet ( data ) ;
if ( type = = NXDN_MESSAGE_TYPE_TX_REL ) {
LogMessage ( " NXDN, ended network data transmission " ) ;
writeEndNet ( ) ;
2018-02-06 20:40:29 +00:00
}
2018-02-15 20:20:07 +00:00
}
} else if ( usc = = NXDN_LICH_USC_SACCH_NS ) {
2018-02-15 21:32:28 +00:00
m_netLayer3 . setData ( netData + 5U + 0U , 10U ) ;
2018-02-15 20:20:07 +00:00
2018-02-15 21:32:28 +00:00
unsigned char type = m_netLayer3 . getMessageType ( ) ;
2018-03-01 07:34:41 +00:00
if ( type = = NXDN_MESSAGE_TYPE_TX_REL & & m_netState = = RS_NET_IDLE )
2018-02-15 20:20:07 +00:00
return ;
2018-03-01 07:34:41 +00:00
if ( type = = NXDN_MESSAGE_TYPE_VCALL & & m_netState ! = RS_NET_IDLE )
2018-02-15 20:20:07 +00:00
return ;
2018-02-06 20:40:29 +00:00
2018-02-15 20:20:07 +00:00
CNXDNSACCH sacch ;
sacch . setRAN ( m_ran ) ;
sacch . setStructure ( NXDN_SR_SINGLE ) ;
sacch . setData ( SACCH_IDLE ) ;
sacch . encode ( data + 2U ) ;
2018-02-06 20:40:29 +00:00
2018-02-15 20:20:07 +00:00
CNXDNFACCH1 facch ;
2018-02-27 20:05:49 +00:00
facch . setRaw ( netData + 5U + 0U ) ;
2018-02-15 20:20:07 +00:00
facch . encode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS ) ;
facch . encode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS ) ;
2018-02-06 20:40:29 +00:00
2018-02-15 20:20:07 +00:00
data [ 0U ] = type = = NXDN_MESSAGE_TYPE_TX_REL ? TAG_EOT : TAG_DATA ;
data [ 1U ] = 0x00U ;
2018-02-06 20:40:29 +00:00
2018-02-15 20:20:07 +00:00
scrambler ( data + 2U ) ;
2018-02-06 20:40:29 +00:00
2018-02-15 20:20:07 +00:00
writeQueueNet ( data ) ;
2018-02-06 20:40:29 +00:00
2018-02-15 20:20:07 +00:00
if ( type = = NXDN_MESSAGE_TYPE_TX_REL ) {
m_netFrames + + ;
2018-02-21 08:10:59 +00:00
LogMessage ( " NXDN, received network end of transmission, %.1f seconds " , float ( m_netFrames ) / 12.5F ) ;
2018-02-15 20:20:07 +00:00
writeEndNet ( ) ;
} else if ( type = = NXDN_MESSAGE_TYPE_VCALL ) {
2018-02-15 21:32:28 +00:00
unsigned short srcId = m_netLayer3 . getSourceUnitId ( ) ;
unsigned short dstId = m_netLayer3 . getDestinationGroupId ( ) ;
bool grp = m_netLayer3 . getIsGroup ( ) ;
2018-02-15 20:20:07 +00:00
std : : string source = m_lookup - > find ( srcId ) ;
LogMessage ( " NXDN, received network transmission from %s to %s%u " , source . c_str ( ) , grp ? " TG " : " " , dstId ) ;
m_display - > writeNXDN ( source . c_str ( ) , grp , dstId , " N " ) ;
m_netTimeoutTimer . start ( ) ;
m_packetTimer . start ( ) ;
m_elapsed . start ( ) ;
m_netState = RS_NET_AUDIO ;
m_netFrames = 1U ;
} else {
CUtils : : dump ( 2U , " NXDN, interesting non superblock network frame " , netData , 33U ) ;
2018-02-20 20:31:26 +00:00
return ;
2018-02-06 20:40:29 +00:00
}
2018-02-15 20:46:22 +00:00
} else {
2018-02-06 20:40:29 +00:00
if ( m_netState = = RS_NET_IDLE ) {
2018-02-15 20:46:22 +00:00
unsigned char structure = ( netData [ 1U ] > > 6 ) & 0x03U ;
switch ( structure ) {
case NXDN_SR_1_4 :
m_netLayer3 . decode ( netData + 2U , 18U , 0U ) ;
if ( m_netLayer3 . getMessageType ( ) = = NXDN_MESSAGE_TYPE_VCALL )
m_netMask = 0x01U ;
else
m_netMask = 0x00U ;
break ;
case NXDN_SR_2_4 :
m_netMask | = 0x02U ;
m_netLayer3 . decode ( netData + 2U , 18U , 18U ) ;
break ;
case NXDN_SR_3_4 :
m_netMask | = 0x04U ;
m_netLayer3 . decode ( netData + 2U , 18U , 36U ) ;
break ;
case NXDN_SR_4_4 :
m_netMask | = 0x08U ;
m_netLayer3 . decode ( netData + 2U , 18U , 54U ) ;
break ;
default :
break ;
}
if ( m_netMask ! = 0x0FU )
return ;
unsigned char type = m_netLayer3 . getMessageType ( ) ;
if ( type ! = NXDN_MESSAGE_TYPE_VCALL )
return ;
unsigned short srcId = m_netLayer3 . getSourceUnitId ( ) ;
unsigned short dstId = m_netLayer3 . getDestinationGroupId ( ) ;
bool grp = m_netLayer3 . getIsGroup ( ) ;
2018-02-06 20:40:29 +00:00
std : : string source = m_lookup - > find ( srcId ) ;
LogMessage ( " NXDN, received network transmission from %s to %s%u " , source . c_str ( ) , grp ? " TG " : " " , dstId ) ;
m_display - > writeNXDN ( source . c_str ( ) , grp , dstId , " N " ) ;
m_netTimeoutTimer . start ( ) ;
m_packetTimer . start ( ) ;
m_elapsed . start ( ) ;
m_netState = RS_NET_AUDIO ;
2018-02-15 20:46:22 +00:00
m_netFrames = 1U ;
// Create a dummy start message
unsigned char start [ NXDN_FRAME_LENGTH_BYTES + 2U ] ;
start [ 0U ] = TAG_DATA ;
start [ 1U ] = 0x00U ;
// Generate the sync
CSync : : addNXDNSync ( start + 2U ) ;
// Generate the LICH
CNXDNLICH lich ;
lich . setRFCT ( NXDN_LICH_RFCT_RDCH ) ;
lich . setFCT ( NXDN_LICH_USC_SACCH_NS ) ;
lich . setOption ( NXDN_LICH_STEAL_FACCH ) ;
2018-03-08 07:16:41 +00:00
lich . setDirection ( m_remoteGateway | | ! m_duplex ? NXDN_LICH_DIRECTION_INBOUND : NXDN_LICH_DIRECTION_OUTBOUND ) ;
2018-02-15 20:46:22 +00:00
lich . encode ( start + 2U ) ;
CNXDNSACCH sacch ;
sacch . setRAN ( m_ran ) ;
sacch . setStructure ( NXDN_SR_SINGLE ) ;
sacch . setData ( SACCH_IDLE ) ;
sacch . encode ( start + 2U ) ;
unsigned char message [ 22U ] ;
m_netLayer3 . getData ( message ) ;
CNXDNFACCH1 facch ;
facch . setData ( message ) ;
facch . encode ( start + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS ) ;
facch . encode ( start + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS ) ;
scrambler ( start + 2U ) ;
writeQueueNet ( start ) ;
2018-02-06 20:40:29 +00:00
}
2018-02-15 20:46:22 +00:00
2018-02-06 20:40:29 +00:00
m_netFrames + + ;
2018-02-15 20:20:07 +00:00
data [ 0U ] = TAG_DATA ;
2018-02-06 20:40:29 +00:00
data [ 1U ] = 0x00U ;
CNXDNSACCH sacch ;
2018-02-27 20:05:49 +00:00
sacch . setRaw ( netData + 1U ) ;
2018-02-15 20:20:07 +00:00
sacch . setRAN ( m_ran ) ;
sacch . encode ( data + 2U ) ;
2018-02-06 20:40:29 +00:00
if ( option = = NXDN_LICH_STEAL_NONE ) {
2018-02-19 20:51:43 +00:00
CNXDNAudio audio ;
audio . encode ( netData + 5U + 0U , data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 0U ) ;
audio . encode ( netData + 5U + 14U , data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 18U ) ;
2018-02-06 20:40:29 +00:00
} else if ( option = = NXDN_LICH_STEAL_FACCH1_1 ) {
CNXDNFACCH1 facch1 ;
2018-02-27 20:05:49 +00:00
facch1 . setRaw ( netData + 5U + 0U ) ;
2018-02-15 21:32:28 +00:00
facch1 . encode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS ) ;
2018-02-06 20:40:29 +00:00
2018-02-19 20:51:43 +00:00
CNXDNAudio audio ;
audio . encode ( netData + 5U + 14U , data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 18U ) ;
2018-02-06 20:40:29 +00:00
} else if ( option = = NXDN_LICH_STEAL_FACCH1_2 ) {
2018-02-19 20:51:43 +00:00
CNXDNAudio audio ;
audio . encode ( netData + 5U + 0U , data + 2U + NXDN_FSW_LICH_SACCH_LENGTH_BYTES + 0U ) ;
2018-02-06 20:40:29 +00:00
CNXDNFACCH1 facch1 ;
2018-02-27 20:05:49 +00:00
facch1 . setRaw ( netData + 5U + 14U ) ;
2018-02-15 21:32:28 +00:00
facch1 . encode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS ) ;
2018-02-06 20:40:29 +00:00
} else {
CNXDNFACCH1 facch11 ;
2018-02-27 20:05:49 +00:00
facch11 . setRaw ( netData + 5U + 0U ) ;
2018-02-15 21:32:28 +00:00
facch11 . encode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS ) ;
2018-02-06 20:40:29 +00:00
CNXDNFACCH1 facch12 ;
2018-02-27 20:05:49 +00:00
facch12 . setRaw ( netData + 5U + 14U ) ;
2018-02-15 21:32:28 +00:00
facch12 . encode ( data + 2U , NXDN_FSW_LENGTH_BITS + NXDN_LICH_LENGTH_BITS + NXDN_SACCH_LENGTH_BITS + NXDN_FACCH1_LENGTH_BITS ) ;
2018-02-06 20:40:29 +00:00
}
scrambler ( data + 2U ) ;
writeQueueNet ( data ) ;
}
2018-02-05 20:42:43 +00:00
}
2018-01-16 20:10:35 +00:00
2018-01-11 19:35:33 +00:00
void CNXDNControl : : clock ( unsigned int ms )
{
if ( m_network ! = NULL )
writeNetwork ( ) ;
m_rfTimeoutTimer . clock ( ms ) ;
m_netTimeoutTimer . clock ( ms ) ;
if ( m_netState = = RS_NET_AUDIO ) {
m_networkWatchdog . clock ( ms ) ;
if ( m_networkWatchdog . hasExpired ( ) ) {
2018-02-21 08:10:59 +00:00
LogMessage ( " NXDN, network watchdog has expired, %.1f seconds " , float ( m_netFrames ) / 12.5F ) ;
2018-01-11 19:35:33 +00:00
writeEndNet ( ) ;
}
}
}
void CNXDNControl : : writeQueueRF ( 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 = NXDN_FRAME_LENGTH_BYTES + 2U ;
unsigned int space = m_queue . freeSpace ( ) ;
if ( space < ( len + 1U ) ) {
LogError ( " NXDN, overflow in the NXDN RF queue " ) ;
return ;
}
m_queue . addData ( & len , 1U ) ;
m_queue . addData ( data , len ) ;
}
void CNXDNControl : : writeQueueNet ( const unsigned char * data )
{
assert ( data ! = NULL ) ;
if ( m_netTimeoutTimer . isRunning ( ) & & m_netTimeoutTimer . hasExpired ( ) )
return ;
unsigned char len = NXDN_FRAME_LENGTH_BYTES + 2U ;
unsigned int space = m_queue . freeSpace ( ) ;
if ( space < ( len + 1U ) ) {
LogError ( " NXDN, overflow in the NXDN RF queue " ) ;
return ;
}
m_queue . addData ( & len , 1U ) ;
m_queue . addData ( data , len ) ;
}
2018-05-17 18:23:01 +00:00
void CNXDNControl : : writeNetwork ( const unsigned char * data , NXDN_NETWORK_MESSAGE_TYPE type )
2018-01-11 19:35:33 +00:00
{
assert ( data ! = NULL ) ;
if ( m_network = = NULL )
return ;
if ( m_rfTimeoutTimer . isRunning ( ) & & m_rfTimeoutTimer . hasExpired ( ) )
return ;
2018-05-17 18:23:01 +00:00
m_network - > write ( data , type ) ;
2018-01-11 19:35:33 +00:00
}
2018-01-15 23:10:54 +00:00
void CNXDNControl : : scrambler ( unsigned char * data ) const
{
assert ( data ! = NULL ) ;
2018-01-25 20:19:31 +00:00
for ( unsigned int i = 0U ; i < NXDN_FRAME_LENGTH_BYTES ; i + + )
data [ i ] ^ = SCRAMBLER [ i ] ;
2018-01-15 23:10:54 +00:00
}
2018-01-11 19:35:33 +00:00
bool CNXDNControl : : openFile ( )
{
if ( m_fp ! = NULL )
return true ;
time_t t ;
: : time ( & t ) ;
struct tm * tm = : : localtime ( & t ) ;
char name [ 100U ] ;
: : sprintf ( name , " NXDN_%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 ;
2018-06-11 20:22:56 +00:00
: : fwrite ( " NXDN " , 1U , 4U , m_fp ) ;
2018-01-11 19:35:33 +00:00
return true ;
}
bool CNXDNControl : : writeFile ( const unsigned char * data )
{
if ( m_fp = = NULL )
return false ;
: : fwrite ( data , 1U , NXDN_FRAME_LENGTH_BYTES , m_fp ) ;
return true ;
}
void CNXDNControl : : closeFile ( )
{
if ( m_fp ! = NULL ) {
: : fclose ( m_fp ) ;
m_fp = NULL ;
}
}
2019-01-10 09:05:15 +00:00
bool CNXDNControl : : isBusy ( ) const
{
return m_rfState ! = RS_RF_LISTENING | | m_netState ! = RS_NET_IDLE ;
}
2019-01-19 17:15:24 +00:00
void CNXDNControl : : enable ( bool enabled )
{
if ( ! enabled & & m_enabled ) {
m_queue . clear ( ) ;
// Reset the RF section
m_rfState = RS_RF_LISTENING ;
m_rfMask = 0x00U ;
m_rfLayer3 . reset ( ) ;
m_rfTimeoutTimer . stop ( ) ;
// Reset the networking section
m_netState = RS_NET_IDLE ;
m_netMask = 0x00U ;
m_netLayer3 . reset ( ) ;
m_netTimeoutTimer . stop ( ) ;
m_networkWatchdog . stop ( ) ;
m_packetTimer . stop ( ) ;
}
m_enabled = enabled ;
}