From 63fedee9d4f1318ab26b0ccf06ff7fd4e6cb082e Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Mon, 20 Mar 2023 16:11:17 -1000 Subject: [PATCH 1/2] MP: Discovery is also syncing the clock upon receiving HEARTBEAT frames The MP_HEARTBEAT frame did not include the millis field. Add the millis field to the MP_HEARTBEAT frame to enable the clock to properly synchronize. Prior to adding this field the synchronization would be random and typically larger than 40 mSec. With the millis value in the HEARTBEAT frame the clock synchronization drops to less than 2 mSec. Testing was done by modifying the discovery code to save the millis value when the code transitioned to the RADIO_MP_STANDBY state. In the RADIO_MP_STANDBY if the client had run more than 5 seconds then it would change states to RADIO_DISCOVER_BEGIN. A five minute log was captured and the following clock synchronization offsets were recorded: 759 uSec - from HEARTBEAT frame 431 uSec - from HEARTBEAT frame 405 uSec - from HEARTBEAT frame 240 uSec - from HEARTBEAT frame 258 uSec - from HEARTBEAT frame 79 uSec - from HEARTBEAT frame 145 uSec - from HEARTBEAT frame 663 uSec - from HEARTBEAT frame 131 uSec - from HEARTBEAT frame 370 uSec - from HEARTBEAT frame 410 uSec - from HEARTBEAT frame 438 uSec - from HEARTBEAT frame 344 uSec - from HEARTBEAT frame 564 uSec - from HEARTBEAT frame 668 uSec - from HEARTBEAT frame The scope was setup to record 10 GSamples at 1 MHz. D0 and D2 are connected to the server and D1 and D3 are connected to the client. The trigger parameters were: triggerWidth = 10; triggerWidthIsMultiplier = true; triggerEnable = 127; TRIGGER_TRANSACTION_COMPLETE, //14 TRIGGER_CHANNEL_TIMER_ISR, //24 TRIGGER_RX_SYNC_CLOCKS, //34 TRIGGER_TX_SYNC_CLOCKS, //44 TRIGGER_RX_HEARTBEAT, //54 TRIGGER_TX_HEARTBEAT, //64 TRIGGER_SYNC_CHANNEL_TIMER, //74 --- Firmware/LoRaSerial/LoRaSerial.ino | 2 +- Firmware/LoRaSerial/Radio.ino | 25 +++++++++++++------------ Firmware/LoRaSerial/States.ino | 1 - 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Firmware/LoRaSerial/LoRaSerial.ino b/Firmware/LoRaSerial/LoRaSerial.ino index eb33984f..82561953 100644 --- a/Firmware/LoRaSerial/LoRaSerial.ino +++ b/Firmware/LoRaSerial/LoRaSerial.ino @@ -64,7 +64,7 @@ const int FIRMWARE_VERSION_MINOR = 0; #define UNIQUE_ID_BYTES 16 //Number of bytes in the unique ID //Frame lengths -#define MP_HEARTBEAT_BYTES 1 //Number of data bytes in the MP_HEARTBEAT frame +#define MP_HEARTBEAT_BYTES (sizeof(uint8_t) + sizeof(unsigned long)) //Number of data bytes in the MP_HEARTBEAT frame #define P2P_FIND_PARTNER_BYTES sizeof(unsigned long) //Number of data bytes in the FIND_PARTNER frame #define P2P_SYNC_CLOCKS_BYTES (sizeof(uint8_t) + sizeof(unsigned long)) //Number of data bytes in the SYNC_CLOCKS frame #define P2P_ZERO_ACKS_BYTES sizeof(unsigned long) //Number of data bytes in the ZERO_ACKS frame diff --git a/Firmware/LoRaSerial/Radio.ino b/Firmware/LoRaSerial/Radio.ino index 0b5e29b8..66d8d1df 100644 --- a/Firmware/LoRaSerial/Radio.ino +++ b/Firmware/LoRaSerial/Radio.ino @@ -931,8 +931,7 @@ bool xmitDatagramP2PSyncClocks() radioCallHistory[RADIO_CALL_xmitDatagramP2PSyncClocks] = currentMillis; startOfData = endOfTxData; - memcpy(endOfTxData, &channelNumber, sizeof(channelNumber)); - endOfTxData += sizeof(channelNumber); + *endOfTxData++ = channelNumber; memcpy(endOfTxData, ¤tMillis, sizeof(currentMillis)); endOfTxData += sizeof(unsigned long); @@ -1149,18 +1148,20 @@ bool xmitDatagramMpHeartbeat() radioCallHistory[RADIO_CALL_xmitDatagramMpHeartbeat] = millis(); startOfData = endOfTxData; - memcpy(endOfTxData, &channelNumber, sizeof(channelNumber)); - endOfTxData += sizeof(channelNumber); + *endOfTxData++ = channelNumber; + + memcpy(endOfTxData, ¤tMillis, sizeof(currentMillis)); + endOfTxData += sizeof(unsigned long); /* - endOfTxData ---. - | - V - +----------+---------+----------+------------+---------+----------+ - | Optional | | Optional | Optional | Channel | | - | NET ID | Control | C-Timer | SF6 Length | Number | Trailer | - | 8 bits | 8 bits | 2 bytes | 8 bits | 1 byte | n Bytes | - +----------+---------+----------+------------+---------+----------+ + endOfTxData ---. + | + V + +----------+---------+----------+------------+---------+---------+----------+ + | Optional | | Optional | Optional | Channel | | | + | NET ID | Control | C-Timer | SF6 Length | Number | Millis | Trailer | + | 8 bits | 8 bits | 2 bytes | 8 bits | 1 byte | 4 bytes | n Bytes | + +----------+---------+----------+------------+---------+---------+----------+ */ //Verify the data length diff --git a/Firmware/LoRaSerial/States.ino b/Firmware/LoRaSerial/States.ino index 071b3a5d..682900ad 100644 --- a/Firmware/LoRaSerial/States.ino +++ b/Firmware/LoRaSerial/States.ino @@ -1305,7 +1305,6 @@ void updateRadioState() startChannelTimer(); channelTimerStart -= settings.maxDwellTime; syncChannelTimer(txSyncClocksUsec); - triggerEvent(TRIGGER_RX_SYNC_CLOCKS); if (settings.debugSync) { From 128afbdb2b1dec6b72b6bb23d042c47272c39628 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 21 Mar 2023 08:05:29 -1000 Subject: [PATCH 2/2] MP: Add clock sync support for 150 BPS Rework the clock sync math to determine the number of hops that occurred during the transmitted frame. The resulting math seems to gets the clock sync within 2 mSec. --- Firmware/LoRaSerial/Radio.ino | 47 +++++++++++++++++++++++++++++++--- Firmware/LoRaSerial/States.ino | 12 ++++----- 2 files changed, 50 insertions(+), 9 deletions(-) diff --git a/Firmware/LoRaSerial/Radio.ino b/Firmware/LoRaSerial/Radio.ino index 66d8d1df..3c90a88c 100644 --- a/Firmware/LoRaSerial/Radio.ino +++ b/Firmware/LoRaSerial/Radio.ino @@ -3250,7 +3250,7 @@ void stopChannelTimer() //Given the remote unit's number of ms before its next hop, //adjust our own channelTimer interrupt to be synchronized with the remote unit -void syncChannelTimer(uint32_t frameAirTimeUsec) +void syncChannelTimer(uint32_t frameAirTimeUsec, bool clockStarting) { int16_t adjustment; uint8_t caseNumber; @@ -3318,6 +3318,48 @@ void syncChannelTimer(uint32_t frameAirTimeUsec) //timer update will add only microseconds to when the hop is done. delayedHopCount = timeToHop ? 1 : 0; + // 4800 BPS operation + // + // |<---------------- Millis to HOP ---------------->| + // _____ | |_____________ + // |_|_________________________________________________| + // | | | + // TX Start ^ TX Complete ^ |<-- rmtHopTimeMsec -->| + // RX Complete ^ + // + // + // 150 BPS operation + // + // |<--- Millis to HOP --->| + // _____ | |_________________________ + // |_|_______________________| | |_____________ + // | | | + // TX Start ^ TX Complete ^ | | + // RX Complete ^ | + // |<- ->| + // rmtHopTimeMsec + // + // Millis to HOP + // |<- ->| + // | |_________________________ __ + // ___________|_____| |_________________________| + // | | | + // TX Start ^ TX Complete ^ | | + // RX Complete ^ | + // |<---- ---->| + // rmtHopTimeMsec + // + //For low speed operation move the TX start into the current dwell time period + //to make the rest of the math look like the 4800 BPS operation. + frameAirTimeMsec = settings.maxDwellTime - msToNextHopRemote + + (frameAirTimeUsec + settings.txToRxUsec + micros() - transactionCompleteMicros) / 1000; + while (frameAirTimeMsec >= (settings.maxDwellTime + (settings.maxDwellTime >> 6))) + { + frameAirTimeMsec -= settings.maxDwellTime; + if (clockStarting) + delayedHopCount += 1; //Account for the missing hop when the timer is stopped + } + //The radios are using the same frequencies since the frame was successfully //received. The goal is to adjust the channel timer to fire in close proximity //to the firing of the remote sysstem's channel timer. The following cases @@ -3338,8 +3380,7 @@ void syncChannelTimer(uint32_t frameAirTimeUsec) //Compute the remote system's channel timer firing time offset in milliseconds //using the channel timer value and the adjustments for transmit and receive //time (time of flight) - frameAirTimeMsec = (frameAirTimeUsec + settings.txToRxUsec + micros() - transactionCompleteMicros) / 1000; - rmtHopTimeMsec = msToNextHopRemote - frameAirTimeMsec; + rmtHopTimeMsec = settings.maxDwellTime - frameAirTimeMsec; //Compute when the local system last hopped lclHopTimeMsec = currentMillis - channelTimerStart; diff --git a/Firmware/LoRaSerial/States.ino b/Firmware/LoRaSerial/States.ino index 682900ad..afdd09ef 100644 --- a/Firmware/LoRaSerial/States.ino +++ b/Firmware/LoRaSerial/States.ino @@ -750,7 +750,7 @@ void updateRadioState() COMPUTE_TIMESTAMP_OFFSET(rxData, 1, txDataAckUsec); //The datagram we are expecting - syncChannelTimer(txDataAckUsec); //Adjust freq hop ISR based on remote's remaining clock + syncChannelTimer(txDataAckUsec, 0); //Adjust freq hop ISR based on remote's remaining clock triggerEvent(TRIGGER_RX_ACK); @@ -1067,9 +1067,9 @@ void updateRadioState() //then skip active discovery and go to standby if (((frameAirTimeUsec + txDataAckUsec + settings.txToRxUsec) / 1000) > (settings.maxDwellTime / 2)) { - stopChannelTimer(); channelNumber = 0; setRadioFrequency(false); + stopChannelTimer(); changeState(RADIO_DISCOVER_STANDBY); } else @@ -1160,7 +1160,7 @@ void updateRadioState() //Start and adjust freq hop ISR based on remote's remaining clock startChannelTimer(); channelTimerStart -= settings.maxDwellTime; - syncChannelTimer(txSyncClocksUsec); + syncChannelTimer(txSyncClocksUsec, 1); triggerEvent(TRIGGER_RX_SYNC_CLOCKS); if (settings.debugSync) @@ -1304,7 +1304,7 @@ void updateRadioState() //Start and adjust freq hop ISR based on remote's remaining clock startChannelTimer(); channelTimerStart -= settings.maxDwellTime; - syncChannelTimer(txSyncClocksUsec); + syncChannelTimer(txSyncClocksUsec, 1); if (settings.debugSync) { @@ -1422,7 +1422,7 @@ void updateRadioState() if (settings.server == false) { //Adjust freq hop ISR based on server's remaining clock - syncChannelTimer(txHeartbeatUsec); + syncChannelTimer(txHeartbeatUsec, 0); //Received heartbeat - do not ack. triggerEvent(TRIGGER_RX_HEARTBEAT); @@ -3224,7 +3224,7 @@ void vcReceiveHeartbeat(uint32_t rxMillis) //Adjust freq hop ISR based on server's remaining clock if ((rxSrcVc == VC_SERVER) || (memcmp(rxVcData, myUniqueId, sizeof(myUniqueId)) == 0)) - syncChannelTimer(txHeartbeatUsec); + syncChannelTimer(txHeartbeatUsec, 0); //Update the timestamp offset if (rxSrcVc == VC_SERVER)