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 55a99206..992be9fe 100644 --- a/p25/P25Defines.h +++ b/p25/P25Defines.h @@ -299,34 +299,34 @@ 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 // TSBK Outbound Signalling Packet (OSP) Opcode(s) const uint8_t TSBK_OSP_GRP_VCH_GRANT_UPD = 0x02U; // GRP VCH GRANT UPD - Group Voice Channel Grant Update 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 // 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) 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 bb7bfbaf..f4496251 100644 --- a/p25/lc/TSBK.cpp +++ b/p25/lc/TSBK.cpp @@ -554,6 +554,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); @@ -765,6 +767,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) && @@ -892,59 +942,6 @@ void TSBK::encode(uint8_t* data, bool rawTSBK, bool noTrellis) } } break; - case TSBK_OSP_TIME_DATE_ANN: - { - //Setup - std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); - time_t tt = std::chrono::system_clock::to_time_t(now); - tm local_tm = *gmtime(&tt); - - unsigned long tmM = 0U; - tmM |= (local_tm.tm_mon + 1); // Month; +1 to account for tm_mon being 0-11 and p25 being 1-12 - unsigned long tmMDAY = 0U; - tmMDAY |= local_tm.tm_mday; // Day of month - uint32_t tmY = 0U; - tmY |= local_tm.tm_year; // Year - uint32_t tmH = 0U; - tmH |= local_tm.tm_hour; // Hour - uint32_t tmMin = 0U; - tmMin |= local_tm.tm_min; // Min - uint32_t i = local_tm.tm_sec; - - uint16_t lto = 0U; - - // catch Leap Seconds - uint32_t tmS = 0U; - if (i > 59U) { - tmS |= 59U; - } else { - tmS |= i; - } - - // fix year from from 1900 to, from 2000 - tmY = tmY - 100U; - - tsbkValue = 0xE0U + // VL, VT and VD flags set - ((lto >> 8) & 0x0F); // LTO MSB (Upper 4-bits) - tsbkValue = (tsbkValue << 8) + (lto & 0xFFU); // LTO LSB - - // Date - tsbkValue = (tsbkValue << 4) + (tmM & 0x0FU); // Month - tsbkValue = (tsbkValue << 5) + (tmMDAY & 0x1FU); // Day of Month - tsbkValue = (tsbkValue << 13) + (tmY & 0x1FFFU); // Year - tsbkValue = (tsbkValue << 2); // Reserved - - // Time - tsbkValue = (tsbkValue << 5) + (tmH & 0x1FU); // Hour - tsbkValue = (tsbkValue << 6) + (tmMin & 0x3FU); // Minute - tsbkValue = (tsbkValue << 6) + (tmS & 0x3FU); // Seconds - tsbkValue = (tsbkValue << 7); // Reserved - -#if DEBUG_P25_TSBK - LogDebug(LOG_P25, "TSBK_OSP_TIME_DATE_ANN, tmM = %u, tmMDAY = %u, tmY = %u, tmH = %u, tmMin = %u, tmS = %u", tmM, tmMDAY, tmY, tmH, tmMin, tmS); -#endif - } - 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); @@ -1195,6 +1192,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), @@ -1263,6 +1261,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 6e1afbb4..4e2380df 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 e62d4d9f..000ed647 100644 --- a/p25/packet/Trunk.cpp +++ b/p25/packet/Trunk.cpp @@ -1160,6 +1160,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), @@ -1246,6 +1247,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) @@ -1273,17 +1279,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); @@ -1292,17 +1301,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? @@ -1834,12 +1839,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 904177c7..a173576d 100644 --- a/p25/packet/Trunk.h +++ b/p25/packet/Trunk.h @@ -144,6 +144,8 @@ namespace p25 Timer m_adjSiteUpdateTimer; uint32_t m_adjSiteUpdateInterval; + uint16_t m_microslotCount; + bool m_ctrlTSDUMBF; bool m_localEmergAlarm;