diff --git a/config.example.yml b/config.example.yml index 25720877..a903345a 100644 --- a/config.example.yml +++ b/config.example.yml @@ -107,6 +107,7 @@ system: rfTalkgroupHang: 10 activeTickDelay: 5 idleTickDelay: 5 + localTimeOffset: 0 info: latitude: -83.689428 longitude: -39.194973 diff --git a/host/Host.cpp b/host/Host.cpp index ee087c52..42006eb6 100644 --- a/host/Host.cpp +++ b/host/Host.cpp @@ -1788,6 +1788,8 @@ bool Host::readParams() ::LogWarning(LOG_HOST, "System Identity \"%s\" is too long; truncating to 8 characters, \"%s\".", identity.c_str(), m_identity.c_str()); } + int8_t lto = (int8_t)systemConf["localTimeOffset"].as(0); + removeLockFile(); LogInfo("General Parameters"); @@ -1808,6 +1810,7 @@ bool Host::readParams() LogInfo(" Identity: %s", m_identity.c_str()); LogInfo(" Fixed Mode: %s", m_fixedMode ? "yes" : "no"); LogInfo(" Lock Filename: %s", g_lockFile.c_str()); + LogInfo(" Local Time Offset: %dh", lto); yaml::Node systemInfo = systemConf["info"]; m_latitude = systemInfo["latitude"].as(0.0F); diff --git a/p25/Control.cpp b/p25/Control.cpp index f5815e8f..83f8e8f4 100644 --- a/p25/Control.cpp +++ b/p25/Control.cpp @@ -279,7 +279,9 @@ void Control::setOptions(yaml::Node& conf, const std::string cwCallsign, const s } } - m_siteData = SiteData(netId, sysId, rfssId, siteId, 0U, channelId, channelNo, serviceClass); + int8_t lto = (int8_t)systemConf["localTimeOffset"].as(0); + + m_siteData = SiteData(netId, sysId, rfssId, siteId, 0U, channelId, channelNo, serviceClass, lto); m_siteData.setCallsign(cwCallsign); std::vector<::lookups::IdenTable> entries = m_idenTable->list(); diff --git a/p25/P25Defines.h b/p25/P25Defines.h index a35377fc..33211a7a 100644 --- a/p25/P25Defines.h +++ b/p25/P25Defines.h @@ -299,12 +299,12 @@ namespace p25 const uint8_t TSBK_ISP_TELE_INT_PSTN_REQ = 0x09U; // TELE INT PSTN REQ - Telephone Interconnect Request - Implicit const uint8_t TSBK_ISP_SNDCP_CH_REQ = 0x12U; // SNDCP CH REQ - SNDCP Data Channel Request const uint8_t TSBK_ISP_STS_Q_RSP = 0x19U; // STS Q RSP - Status Query Response + const uint8_t TSBK_ISP_STS_Q_REQ = 0x1CU; // STS_Q_REQ - Status Query Request const uint8_t TSBK_ISP_CAN_SRV_REQ = 0x23U; // CAN SRV REQ - Cancel Service Request const uint8_t TSBK_ISP_EMERG_ALRM_REQ = 0x27U; // EMERG ALRM REQ - Emergency Alarm Request const uint8_t TSBK_ISP_GRP_AFF_Q_RSP = 0x29U; // GRP AFF Q RSP - Group Affiliation Query Response const uint8_t TSBK_ISP_U_DEREG_REQ = 0x2BU; // U DE REG REQ - Unit De-Registration Request const uint8_t TSBK_ISP_LOC_REG_REQ = 0x2DU; // LOC REG REQ - Location Registration Request - const uint8_t TSBK_ISP_STS_Q_REQ = 0x1CU; // STS_Q_REQ - Status Query Request const uint8_t TSBK_ISP_RAD_MON_REQ = 0x1DU; // RAD_MON_REQ - Radio Unit Monitor Request const uint8_t TSBK_ISP_RAD_MON_ENH_REQ = 0x1EU; // RAD_MON_ENH_REQ - Radio Unit Monitor Enhanced Request const uint8_t TSBK_ISP_AUTH_FNE_RST = 0x3AU; // AUTH_FNE_RST - Authentication FNE Result @@ -317,28 +317,27 @@ namespace p25 const uint8_t TSBK_OSP_UU_VCH_GRANT_UPD = 0x06U; // UU VCH GRANT UPD - Unit-to-Unit Voice Channel Grant Update const uint8_t TSBK_OSP_SNDCP_CH_GNT = 0x14U; // SNDCP CH GNT - SNDCP Data Channel Grant const uint8_t TSBK_OSP_SNDCP_CH_ANN = 0x16U; // SNDCP CH ANN - SNDCP Data Channel Announcement + const uint8_t TSBK_OSP_STS_Q = 0x1AU; // STS_Q - Status Query const uint8_t TSBK_OSP_DENY_RSP = 0x27U; // DENY RSP - Deny Response const uint8_t TSBK_OSP_SCCB_EXP = 0x29U; // SCCB - Secondary Control Channel Broadcast - Explicit const uint8_t TSBK_OSP_GRP_AFF_Q = 0x2AU; // GRP AFF Q - Group Affiliation Query const uint8_t TSBK_OSP_LOC_REG_RSP = 0x2BU; // LOC REG RSP - Location Registration Response const uint8_t TSBK_OSP_U_REG_CMD = 0x2DU; // U REG CMD - Unit Registration Command const uint8_t TSBK_OSP_U_DEREG_ACK = 0x2FU; // U DE REG ACK - Unit De-Registration Acknowledge + const uint8_t TSBK_OSP_SYNC_BCAST = 0x30U; // SYNC BCAST - Synchronization Broadcast const uint8_t TSBK_OSP_QUE_RSP = 0x33U; // QUE RSP - Queued Response const uint8_t TSBK_OSP_IDEN_UP_VU = 0x34U; // IDEN UP VU - Channel Identifier Update for VHF/UHF Bands - const uint8_t TSBK_OSP_TIME_DATE_ANN = 0x35U; // TIME_DATE_ANN - Time and Date Announcement const uint8_t TSBK_OSP_SYS_SRV_BCAST = 0x38U; // SYS SRV BCAST - System Service Broadcast const uint8_t TSBK_OSP_SCCB = 0x39U; // SCCB - Secondary Control Channel Broadcast const uint8_t TSBK_OSP_RFSS_STS_BCAST = 0x3AU; // RFSS STS BCAST - RFSS Status Broadcast const uint8_t TSBK_OSP_NET_STS_BCAST = 0x3BU; // NET STS BCAST - Network Status Broadcast const uint8_t TSBK_OSP_ADJ_STS_BCAST = 0x3CU; // ADJ STS BCAST - Adjacent Site Status Broadcast const uint8_t TSBK_OSP_IDEN_UP = 0x3DU; // IDEN UP - Channel Identifier Update - const uint8_t TSBK_OSP_STS_Q = 0x1AU; // STS_Q - Status Query const uint8_t TSBK_OSP_AUTH_DMD = 0x31U; // AUTH_DMD - Authentication Demand const uint8_t TSBK_OSP_AUTH_FNE_RESP = 0x32U; // AUTH_FNE_RESP - Authentication FNE Response const uint8_t TSBK_OSP_RAD_MON_CMD = 0x1DU; // RAD_MON_CMD - Radio Monitor Command const uint8_t TSBK_OSP_RAD_MON_ENH_CMD = 0x1EU; // RAD_MON_ENH_CMD - Radio Unit Monitor Enhanced Command - // TSBK Motorola Outbound Signalling Packet (OSP) Opcode(s) const uint8_t TSBK_OSP_MOT_GRG_ADD = 0x00U; // MOT GRG ADD - Motorola / Group Regroup Add (Patch Supergroup) const uint8_t TSBK_OSP_MOT_GRG_DEL = 0x01U; // MOT GRG DEL - Motorola / Group Regroup Delete (Unpatch Supergroup) diff --git a/p25/SiteData.h b/p25/SiteData.h index 5b80c386..067abdfa 100644 --- a/p25/SiteData.h +++ b/p25/SiteData.h @@ -52,7 +52,8 @@ namespace p25 m_isAdjSite(false), m_callsign("CHANGEME"), m_chCnt(0U), - m_netActive(false) + m_netActive(false), + m_lto(0) { /* stub */ } @@ -65,7 +66,8 @@ namespace p25 /// Channel ID. /// Channel Number. /// Service class. - SiteData(uint32_t netId, uint32_t sysId, uint8_t rfssId, uint8_t siteId, uint8_t lra, uint8_t channelId, uint32_t channelNo, uint8_t serviceClass) : + /// Local time offset. + SiteData(uint32_t netId, uint32_t sysId, uint8_t rfssId, uint8_t siteId, uint8_t lra, uint8_t channelId, uint32_t channelNo, uint8_t serviceClass, int8_t lto) : m_lra(0U), m_netId(P25_WACN_STD_DEFAULT), m_sysId(P25_SID_STD_DEFAULT), @@ -77,7 +79,8 @@ namespace p25 m_isAdjSite(false), m_callsign("CHANGEME"), m_chCnt(0U), - m_netActive(false) + m_netActive(false), + m_lto(0) { // lra clamping if (lra > 0xFFU) // clamp to $FF @@ -119,6 +122,8 @@ namespace p25 m_channelNo = channelNo; m_serviceClass = serviceClass; + + m_lto = lto; } /// Helper to set the site callsign. @@ -190,6 +195,7 @@ namespace p25 m_callsign = "ADJSITE "; m_chCnt = -1; // don't store channel count for adjacent sites m_netActive = true; // adjacent sites are explicitly network active + m_lto = 0; } /// Equals operator. @@ -217,6 +223,8 @@ namespace p25 m_chCnt = data.m_chCnt; m_netActive = data.m_netActive; + + m_lto = data.m_lto; } return *this; @@ -247,6 +255,8 @@ namespace p25 __READONLY_PROPERTY_PLAIN(uint8_t, chCnt, chCnt); /// Flag indicating whether this site is a linked active network member. __READONLY_PROPERTY_PLAIN(bool, netActive, netActive); + /// Local Time Offset. + __READONLY_PROPERTY_PLAIN(int8_t, lto, lto); }; } // namespace p25 diff --git a/p25/lc/TSBK.cpp b/p25/lc/TSBK.cpp index 92a79984..387515b4 100644 --- a/p25/lc/TSBK.cpp +++ b/p25/lc/TSBK.cpp @@ -560,6 +560,8 @@ void TSBK::encode(uint8_t* data, bool rawTSBK, bool noTrellis) assert(data != NULL); const uint32_t services = (m_siteData.netActive()) ? P25_SYS_SRV_NET_ACTIVE : 0U | P25_SYS_SRV_DEFAULT; + std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + time_t tt = std::chrono::system_clock::to_time_t(now); uint8_t tsbk[P25_TSBK_LENGTH_BYTES]; ::memset(tsbk, 0x00U, P25_TSBK_LENGTH_BYTES); @@ -771,6 +773,54 @@ void TSBK::encode(uint8_t* data, bool rawTSBK, bool noTrellis) tsbkValue = (tsbkValue << 12) + m_siteData.sysId(); // System ID tsbkValue = (tsbkValue << 24) + m_srcId; // Source Radio Address break; + case TSBK_OSP_SYNC_BCAST: + { + tm local_tm = *gmtime(&tt); + + uint32_t tmM = (local_tm.tm_mon + 1); + uint32_t tmY = (local_tm.tm_year + 1900) - 2000; + +#if DEBUG_P25_TSBK + LogDebug(LOG_P25, "TSBK_OSP_SYNC_BCAST, tmM = %u / %u, tmY = %u / %u", local_tm.tm_mon, tmM, local_tm.tm_year, tmY); +#endif + + // determine LTO and direction (positive or negative) + bool negativeLTO = false; + uint8_t lto = fabs(m_siteData.lto()) * 2U; // this will cause a bug for half-hour timezone intervals... + if (m_siteData.lto() < 0) + negativeLTO = true; + + // mark the LTO as valid if its non-zero + bool vl = false; + if (lto > 0U) + vl = true; + + uint8_t mc = 0U; + + // wrap microslot count if necessary + if (m_microslotCount > 7999U) + m_microslotCount = 0U; + + tsbkValue = 0x0AU + // US - Unsynced Flag Set / MMU - Microslot/Minute Unlock Flag Set + ((mc & 0x03U) >> 1); // Minute Correction MSB + tsbkValue = (tsbkValue << 8) + + ((mc & 0x01U) << 7) + // Minute Correction LSB + (vl ? 0x40U : 0x00U) + // Valid LTO Flag + (negativeLTO ? 0x20U : 0x00U) + // Add/Subtract LTO Flag + (lto & 0x1F); // LTO + + // Date + tsbkValue = (tsbkValue << 7) + (tmY & 0x7FU); // Number of Years Past 2000 + tsbkValue = (tsbkValue << 4) + (tmM & 0x0FU); // Month + tsbkValue = (tsbkValue << 5) + (local_tm.tm_mday & 0x1FU); // Day of Month + + // Time + tsbkValue = (tsbkValue << 5) + (local_tm.tm_hour & 0x1FU); // Hour + tsbkValue = (tsbkValue << 6) + (local_tm.tm_min & 0x3FU); // Minute + + tsbkValue = (tsbkValue << 13) + (m_microslotCount & 0x1FFFU); // Microslot Count + } + break; case TSBK_OSP_IDEN_UP_VU: { if ((m_siteIdenEntry.chBandwidthKhz() != 0.0F) && (m_siteIdenEntry.chSpaceKhz() != 0.0F) && @@ -898,6 +948,7 @@ void TSBK::encode(uint8_t* data, bool rawTSBK, bool noTrellis) } } break; +<<<<<<< HEAD case TSBK_OSP_TIME_DATE_ANN: { //Setup @@ -956,7 +1007,8 @@ void TSBK::encode(uint8_t* data, bool rawTSBK, bool noTrellis) tsbkValue = (tsbkValue << 48) + (m_txMult & 0x3U); // TX Multiplier tsbkValue = (tsbkValue << 24) + (m_srcId & 0xFFFFFFU); // Source Radio Address tsbkValue = tsbkValue + (m_dstId & 0xFFFFFFU); // Target Radio Address - } break; + } + break; default: if (m_mfId == P25_MFG_STANDARD) { LogError(LOG_P25, "TSBK::encode(), unknown TSBK LCO value, mfId = $%02X, lco = $%02X", m_mfId, m_lco); @@ -1207,6 +1259,7 @@ TSBK::TSBK(SiteData siteData) : m_messageValue(0U), m_statusValue(0U), m_extendedFunction(P25_EXT_FNCT_CHECK), + m_microslotCount(0U), m_dataServiceOptions(0U), m_dataAccessControl(0U), m_dataChannelNo(0U), @@ -1275,6 +1328,8 @@ void TSBK::copy(const TSBK& data) m_extendedFunction = data.m_extendedFunction; + m_microslotCount = data.m_microslotCount; + m_dataChannelNo = data.m_dataChannelNo; m_adjCFVA = data.m_adjCFVA; diff --git a/p25/lc/TSBK.h b/p25/lc/TSBK.h index dfff6904..ed824e90 100644 --- a/p25/lc/TSBK.h +++ b/p25/lc/TSBK.h @@ -144,6 +144,9 @@ namespace p25 /// Extended function opcode. __PROPERTY(uint32_t, extendedFunction, ExtendedFunction); + /// Microslot count. + __PROPERTY(uint16_t, microslotCount, MicroslotCount); + /** SNDCP Channel Request */ /// SNDCP Data Service Options __PROPERTY(uint8_t, dataServiceOptions, DataServiceOptions); diff --git a/p25/packet/Trunk.cpp b/p25/packet/Trunk.cpp index 98d74f68..2f2deb70 100644 --- a/p25/packet/Trunk.cpp +++ b/p25/packet/Trunk.cpp @@ -1214,6 +1214,7 @@ Trunk::Trunk(Control* p25, network::BaseNetwork* network, bool dumpTSBKData, boo m_convFallback(false), m_adjSiteUpdateTimer(1000U), m_adjSiteUpdateInterval(ADJ_SITE_TIMER_TIMEOUT), + m_microslotCount(0U), m_ctrlTSDUMBF(true), m_localEmergAlarm(false), m_sndcpChGrant(false), @@ -1300,6 +1301,11 @@ void Trunk::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS) LogDebug(LOG_P25, "writeRF_ControlData, mbfCnt = %u, frameCnt = %u, seq = %u, adjSS = %u", m_mbfCnt, frameCnt, n, adjSS); } + // bryanb: this is just a simple counter because we treat the SYNC_BCST as unlocked + m_microslotCount++; + if (m_microslotCount > 7999U) + m_microslotCount = 0; + bool forcePad = false; bool alt = (frameCnt % 2U) > 0U; switch (n) @@ -1327,17 +1333,20 @@ void Trunk::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS) else queueRF_TSBK_Ctrl(TSBK_OSP_NET_STS_BCAST); break; - /** update data */ case 4: + queueRF_TSBK_Ctrl(TSBK_OSP_SYNC_BCAST); + break; + /** update data */ + case 5: if (m_p25->m_affiliations.grantSize() > 0) { queueRF_TSBK_Ctrl(TSBK_OSP_GRP_VCH_GRANT_UPD); } break; /** extra data */ - case 5: + case 6: queueRF_TSBK_Ctrl(TSBK_OSP_SNDCP_CH_ANN); break; - case 6: + case 7: // write ADJSS if (adjSS && m_adjSiteTable.size() > 0) { queueRF_TSBK_Ctrl(TSBK_OSP_ADJ_STS_BCAST); @@ -1346,17 +1355,13 @@ void Trunk::writeRF_ControlData(uint8_t frameCnt, uint8_t n, bool adjSS) forcePad = true; } break; - case 7: + case 8: // write SCCB if (adjSS && m_sccbTable.size() > 0) { queueRF_TSBK_Ctrl(TSBK_OSP_SCCB_EXP); break; } break; - case 8: - // write TIME_DATE_ANN - queueRF_TSBK_Ctrl(TSBK_OSP_TIME_DATE_ANN); - break; } // should we insert the BSI bursts? @@ -1888,12 +1893,13 @@ void Trunk::queueRF_TSBK_Ctrl(uint8_t lco) // transmit SNDCP announcement m_rfTSBK.setLCO(TSBK_OSP_SNDCP_CH_ANN); break; - case TSBK_OSP_TIME_DATE_ANN: + case TSBK_OSP_SYNC_BCAST: if (m_debug) { - LogMessage(LOG_RF , P25_TSDU_STR ", TSBK_OSP_TIME_DATE_ANN (Time Date Announce)"); + LogMessage(LOG_RF , P25_TSDU_STR ", TSBK_OSP_SYNC_BCAST (Synchronization Broadcast)"); } - - m_rfTSBK.setLCO(TSBK_OSP_TIME_DATE_ANN); + + m_rfTSBK.setLCO(TSBK_OSP_SYNC_BCAST); + m_rfTSBK.setMicroslotCount(m_microslotCount); m_rfTSBK.setMFId(P25_MFG_STANDARD); break; diff --git a/p25/packet/Trunk.h b/p25/packet/Trunk.h index 41401a44..f1485564 100644 --- a/p25/packet/Trunk.h +++ b/p25/packet/Trunk.h @@ -146,6 +146,8 @@ namespace p25 Timer m_adjSiteUpdateTimer; uint32_t m_adjSiteUpdateInterval; + uint16_t m_microslotCount; + bool m_ctrlTSDUMBF; bool m_localEmergAlarm;