Arrival time fix perhaps (#11274)
* Add explicit presence for MeshPacket.rx_time (arrival time) rx_time is now proto3 optional with a has_rx_time presence bit, matching the rx_rssi treatment. A node with no GPS and no phone connected yet has no time source at all, so a bare 0 was indistinguishable from a genuine 1970-01-01 reading; downstream consumers (replay packets, JSON serialization) now check has_rx_time instead of the value. * Dedupe rx_time stamping into a shared helper; trim a debug log string Extract the repeated haveTime/rx_time/has_rx_time stamp logic (5 call sites across Router.cpp, MeshBeaconModule.cpp, MeshService.cpp) into Router::computeRxTimeStamp()/stampRxTime(). Also shorten the new RTC.cpp LOG_DEBUG string. Saves 48 bytes of flash on rak4631 (measured), no behavior change. * Fix has_rx_rssi presence carried unconditionally through StoreForward replay preparePayload() set has_rx_rssi = true unconditionally on replay, regardless of whether the packet's rx_rssi at store time was a genuine measurement (e.g. MQTT-relayed packets carry no real RSSI). Store the presence bit alongside rx_rssi in PacketHistoryStruct and restore it on replay instead. Flagged by Copilot on #11271 (same root cause the has_rx_time explicit presence work fixes) but never addressed before that PR merged. * Trim comment blocks to the repo's 1-2 line guideline .github/copilot-instructions.md:338 caps code comments at 1-2 lines; several blocks added across the rx_time explicit-presence work ran well past that. Also consolidates Time.cpp's file-level doc comment into Time.h, where the rest of the Time:: API contract already lives. No behavior change. * Add rx_time explicit-presence test coverage - test_meshpacket_serializer: has_rx_time=false fixture plus tests asserting JsonSerialize/JsonSerializeEncrypted emit 0 rather than leaking the millis() placeholder, alongside the has_rx_time=true baseline. - test_stream_api: two tests driving a real PhoneAPI handshake (want_config_id through STATE_SEND_PACKETS) that simulate a phone time-giving transaction arriving before vs. after a queued packet is drained - covering both the reconciled and the ships-with-placeholder-absent paths of MeshService::reconcilePendingRxTimes(). * Fix three correctness issues flagged in review - Time.h: drop the reserved-identifier include guard (_MT_TIME_H); pragma once already covers it, matching convention elsewhere (e.g. RTC.h). - Time.cpp: rebase getMillis64()'s wrap accumulator when the test seam swaps clock sources, so a real<->injected clock jump isn't miscounted as a genuine 32-bit wrap. - NodeInfoModule: the 12h reply-suppression window is a local dedup duration, not a wall-clock reading - switch it to Time::getMillis64() so RTC-quality jumps and replayed packets' stale rx_time can't perturb it. - StoreForwardModule: has_rx_time was derived from *current* RTC quality at replay time rather than stored at capture time, so a history entry saved while time-blind could be misreported as a valid epoch once the clock later improved. Persist the presence bit in PacketHistoryStruct instead. * tryfix CI * post review fixes * more test fixes
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
#include "../test_helpers.h"
|
||||
|
||||
void test_timestamp_present_when_has_rx_time()
|
||||
{
|
||||
const char *test_text = "hi";
|
||||
meshtastic_MeshPacket packet =
|
||||
create_test_packet(meshtastic_PortNum_TEXT_MESSAGE_APP, reinterpret_cast<const uint8_t *>(test_text), strlen(test_text));
|
||||
|
||||
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
|
||||
Json::Value root = parse_json(json);
|
||||
TEST_ASSERT_TRUE(root.isMember("timestamp"));
|
||||
TEST_ASSERT_EQUAL_UINT32(1609459200u, root["timestamp"].asUInt());
|
||||
}
|
||||
|
||||
void test_timestamp_zeroed_when_rx_time_absent()
|
||||
{
|
||||
const char *test_text = "hi";
|
||||
meshtastic_MeshPacket packet = create_test_packet_no_rx_time(meshtastic_PortNum_TEXT_MESSAGE_APP,
|
||||
reinterpret_cast<const uint8_t *>(test_text), strlen(test_text));
|
||||
|
||||
std::string json = MeshPacketSerializer::JsonSerialize(&packet, false);
|
||||
Json::Value root = parse_json(json);
|
||||
TEST_ASSERT_TRUE(root.isMember("timestamp"));
|
||||
TEST_ASSERT_EQUAL_UINT32(0u, root["timestamp"].asUInt()); // must not leak the millis() placeholder
|
||||
}
|
||||
|
||||
void test_encrypted_timestamp_zeroed_when_rx_time_absent()
|
||||
{
|
||||
const char *test_text = "hi";
|
||||
meshtastic_MeshPacket packet = create_test_packet_no_rx_time(meshtastic_PortNum_TEXT_MESSAGE_APP,
|
||||
reinterpret_cast<const uint8_t *>(test_text), strlen(test_text));
|
||||
|
||||
std::string json = MeshPacketSerializer::JsonSerializeEncrypted(&packet);
|
||||
Json::Value root = parse_json(json);
|
||||
TEST_ASSERT_TRUE(root.isMember("timestamp"));
|
||||
TEST_ASSERT_EQUAL_UINT32(0u, root["timestamp"].asUInt());
|
||||
}
|
||||
@@ -37,6 +37,7 @@ static meshtastic_MeshPacket create_test_packet(meshtastic_PortNum port, const u
|
||||
packet.want_ack = false;
|
||||
packet.priority = meshtastic_MeshPacket_Priority_UNSET;
|
||||
packet.rx_time = 1609459200;
|
||||
packet.has_rx_time = true; // rx_time has explicit presence; mark this synthetic reading as measured
|
||||
packet.rx_snr = 10.5f;
|
||||
packet.hop_start = 3;
|
||||
packet.rx_rssi = -85;
|
||||
@@ -62,3 +63,14 @@ static meshtastic_MeshPacket create_test_packet(meshtastic_PortNum port, const u
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
// Same as create_test_packet(), but with rx_time absent (has_rx_time = false) and a nonzero
|
||||
// millis()-style placeholder in rx_time, to verify serializers treat it as unknown, not a reading.
|
||||
static meshtastic_MeshPacket create_test_packet_no_rx_time(meshtastic_PortNum port, const uint8_t *payload, size_t payload_size,
|
||||
int payload_variant = meshtastic_MeshPacket_decoded_tag)
|
||||
{
|
||||
meshtastic_MeshPacket packet = create_test_packet(port, payload, payload_size, payload_variant);
|
||||
packet.rx_time = 123456; // a plausible millis() placeholder, not a real epoch
|
||||
packet.has_rx_time = false;
|
||||
return packet;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,9 @@ void test_telemetry_environment_metrics_complete_coverage();
|
||||
void test_telemetry_environment_metrics_unset_fields();
|
||||
void test_encrypted_packet_serialization();
|
||||
void test_empty_encrypted_packet();
|
||||
void test_timestamp_present_when_has_rx_time();
|
||||
void test_timestamp_zeroed_when_rx_time_absent();
|
||||
void test_encrypted_timestamp_zeroed_when_rx_time_absent();
|
||||
|
||||
void setup()
|
||||
{
|
||||
@@ -52,6 +55,11 @@ void setup()
|
||||
RUN_TEST(test_encrypted_packet_serialization);
|
||||
RUN_TEST(test_empty_encrypted_packet);
|
||||
|
||||
// rx_time explicit-presence tests
|
||||
RUN_TEST(test_timestamp_present_when_has_rx_time);
|
||||
RUN_TEST(test_timestamp_zeroed_when_rx_time_absent);
|
||||
RUN_TEST(test_encrypted_timestamp_zeroed_when_rx_time_absent);
|
||||
|
||||
UNITY_END();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user