mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-09-14 15:16:40 +00:00
Merge upstream companion-v1.17.1 into power-saving
Pulls in real fixes: scoped-reply routing, RX boosted-gain restored correctly after AGC reset, T-Echo Lite/Card SPI pin corrections (unused pins now map to NRFX_SPIM_PIN_NOT_USED via 0 instead of -1) and TCXO voltage fix, Heltec T096/T1 and MeshPocket pin fixes, T-beam Supreme S3 display fix, LR2021 preamble/IRQ timeout handling. None of the touched variants overlap with our active Heltec V3/V4, Cardputer ADV, GAT562, or WioTracker builds. # Conflicts: # examples/companion_radio/MyMesh.cpp # examples/companion_radio/MyMesh.h # examples/companion_radio/NodePrefs.h # src/helpers/radiolib/CustomSX1262Wrapper.h # src/helpers/radiolib/RadioLibWrappers.cpp # src/helpers/ui/SH1106Display.cpp # variants/lilygo_techo_lite/platformio.ini # variants/lilygo_techo_lite/variant.h
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "../../examples/companion_radio/NodePrefs.h"
|
||||
|
||||
class ReplayStream : public Stream {
|
||||
const char* _text;
|
||||
int _pos = 0;
|
||||
int _len;
|
||||
|
||||
public:
|
||||
explicit ReplayStream(const char* text) : _text(text), _len(strlen(text)) { }
|
||||
|
||||
int available() override { return _len - _pos; }
|
||||
int read() override { return _pos < _len ? _text[_pos++] : -1; }
|
||||
int peek() override { return _pos < _len ? _text[_pos] : -1; }
|
||||
};
|
||||
|
||||
class CaptureStream : public Stream {
|
||||
std::string _text;
|
||||
|
||||
size_t emit(long long value) {
|
||||
char text[24];
|
||||
int length = snprintf(text, sizeof(text), "%lld", value);
|
||||
return write(reinterpret_cast<const uint8_t*>(text), length);
|
||||
}
|
||||
|
||||
public:
|
||||
size_t write(uint8_t value) override {
|
||||
_text.push_back(static_cast<char>(value));
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t write(const uint8_t* buffer, size_t size) override {
|
||||
_text.append(reinterpret_cast<const char*>(buffer), size);
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t print(unsigned char value, int = DEC) override { return emit(value); }
|
||||
size_t print(int value, int = DEC) override { return emit(value); }
|
||||
size_t print(unsigned int value, int = DEC) override { return emit(value); }
|
||||
size_t print(long value, int = DEC) override { return emit(value); }
|
||||
size_t print(unsigned long value, int = DEC) override { return emit(value); }
|
||||
size_t print(long long value, int = DEC) override { return emit(value); }
|
||||
size_t print(unsigned long long value, int = DEC) override { return emit(value); }
|
||||
|
||||
const std::string& text() const { return _text; }
|
||||
};
|
||||
|
||||
#if 0
|
||||
// Re-enable test once we can SET fem_ values in companion
|
||||
TEST(CompanionNodePrefs, RxGainSettingsRoundTripIndependently) {
|
||||
NodePrefs saved;
|
||||
saved.rx_boosted_gain = 0;
|
||||
saved.radio_fem_rxgain = 1;
|
||||
saved.radio_fem_txgain = 0;
|
||||
|
||||
CaptureStream output;
|
||||
ASSERT_TRUE(saved.saveSerial(output));
|
||||
EXPECT_NE(std::string::npos, output.text().find("rxgain:0"));
|
||||
EXPECT_NE(std::string::npos, output.text().find("fem_rxgain:1"));
|
||||
EXPECT_NE(std::string::npos, output.text().find("fem_txgain:0"));
|
||||
|
||||
ReplayStream input("{radio:{rxgain:1,fem_rxgain:0,fem_txgain:1}}");
|
||||
NodePrefs loaded;
|
||||
loaded.rx_boosted_gain = 0;
|
||||
loaded.radio_fem_rxgain = 1;
|
||||
loaded.radio_fem_txgain = 0;
|
||||
|
||||
ASSERT_TRUE(loaded.loadSerial(input));
|
||||
EXPECT_EQ(1, loaded.rx_boosted_gain);
|
||||
EXPECT_EQ(0, loaded.radio_fem_rxgain);
|
||||
EXPECT_EQ(1, loaded.radio_fem_txgain);
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -1,6 +1,14 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "helpers/ConfigSerializer.h"
|
||||
|
||||
class NativeFileSystem {
|
||||
public:
|
||||
void mkdir(const char*) { }
|
||||
};
|
||||
#define FILESYSTEM NativeFileSystem
|
||||
#include "helpers/CommonCLI.h"
|
||||
#undef FILESYSTEM
|
||||
|
||||
#define TEST_INT_S "56"
|
||||
#define TEST_INT 56
|
||||
#define TEST_FLOAT_S "-6.123"
|
||||
@@ -21,6 +29,19 @@ public:
|
||||
class MockPrintStream : public Stream {
|
||||
int len = 0;
|
||||
uint8_t _buf[1024];
|
||||
|
||||
size_t printSigned(long long value) {
|
||||
char text[24];
|
||||
snprintf(text, sizeof(text), "%lld", value);
|
||||
return Print::print(text);
|
||||
}
|
||||
|
||||
size_t printUnsigned(unsigned long long value) {
|
||||
char text[24];
|
||||
snprintf(text, sizeof(text), "%llu", value);
|
||||
return Print::print(text);
|
||||
}
|
||||
|
||||
public:
|
||||
size_t write(uint8_t b) override {
|
||||
if (len < sizeof(_buf)) {
|
||||
@@ -30,17 +51,17 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t print(unsigned char b, int r) override { if (b == TEST_INT) return Print::print(TEST_INT_S); return 0; }
|
||||
size_t print(int v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
|
||||
size_t print(unsigned int v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
|
||||
size_t print(long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
|
||||
size_t print(unsigned long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
|
||||
size_t print(long long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
|
||||
size_t print(unsigned long long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
|
||||
size_t print(double v, int p = 2) override {
|
||||
if (p == 6) return Print::print(TEST_DOUBLE_S);
|
||||
if (p == 4) return Print::print(TEST_FLOAT_S);
|
||||
return 0;
|
||||
size_t print(unsigned char v, int r) override { return printUnsigned(v); }
|
||||
size_t print(int v, int r) override { return printSigned(v); }
|
||||
size_t print(unsigned int v, int r) override { return printUnsigned(v); }
|
||||
size_t print(long v, int r) override { return printSigned(v); }
|
||||
size_t print(unsigned long v, int r) override { return printUnsigned(v); }
|
||||
size_t print(long long v, int r) override { return printSigned(v); }
|
||||
size_t print(unsigned long long v, int r) override { return printUnsigned(v); }
|
||||
size_t print(double v, int p = 2) override {
|
||||
char text[32];
|
||||
snprintf(text, sizeof(text), "%.*f", p, v);
|
||||
return Print::print(text);
|
||||
}
|
||||
|
||||
int getLength() const { return len; }
|
||||
@@ -171,6 +192,28 @@ TEST(ConfigSerializer, LoadSerial_IgnoreUnknowns) {
|
||||
EXPECT_TRUE(match);
|
||||
}
|
||||
|
||||
TEST(NodePrefs, FemGainSettingsRoundTrip) {
|
||||
NodePrefs saved;
|
||||
saved.radio_fem_rxgain = 0;
|
||||
saved.radio_fem_txgain = 1;
|
||||
|
||||
MockPrintStream output;
|
||||
ASSERT_TRUE(saved.saveSerial(output));
|
||||
|
||||
std::string serialised(reinterpret_cast<const char*>(output.getBytes()), output.getLength());
|
||||
EXPECT_NE(std::string::npos, serialised.find("fem_rxgain:0"));
|
||||
EXPECT_NE(std::string::npos, serialised.find("fem_txgain:1"));
|
||||
|
||||
MockInputStream input(serialised.c_str());
|
||||
NodePrefs loaded;
|
||||
loaded.radio_fem_rxgain = 1;
|
||||
loaded.radio_fem_txgain = 0;
|
||||
|
||||
ASSERT_TRUE(loaded.loadSerial(input));
|
||||
EXPECT_EQ(0, loaded.radio_fem_rxgain);
|
||||
EXPECT_EQ(1, loaded.radio_fem_txgain);
|
||||
}
|
||||
|
||||
|
||||
// ── main ───────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "helpers/RoutingPolicy.h"
|
||||
|
||||
using namespace mesh;
|
||||
|
||||
static Packet makeFlood(uint8_t route_type, uint8_t payload_type, uint8_t hops) {
|
||||
Packet p;
|
||||
p.header = route_type | (payload_type << PH_TYPE_SHIFT);
|
||||
p.setPathHashSizeAndCount(1, hops);
|
||||
p.payload_len = 1;
|
||||
return p;
|
||||
}
|
||||
|
||||
TEST(FloodHopLimit, UnscopedFloodIsDroppedAtFirstHopWhenMaxUnscopedIsZero) {
|
||||
auto pkt = makeFlood(ROUTE_TYPE_FLOOD, PAYLOAD_TYPE_RESPONSE, 0);
|
||||
EXPECT_TRUE(isFloodHopLimitExceeded(&pkt, 64, 0, 8));
|
||||
}
|
||||
|
||||
TEST(FloodHopLimit, ScopedFloodIsForwardedWhenMaxUnscopedIsZero) {
|
||||
for (uint8_t hops = 0; hops < 4; hops++) {
|
||||
auto pkt = makeFlood(ROUTE_TYPE_TRANSPORT_FLOOD, PAYLOAD_TYPE_RESPONSE, hops);
|
||||
EXPECT_FALSE(isFloodHopLimitExceeded(&pkt, 64, 0, 8)) << "hops=" << (int)hops;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(FloodHopLimit, UnscopedFloodSurvivesUpToMaxUnscopedHops) {
|
||||
// matches the reported workaround: raising flood.max.unscoped to the expected hop count
|
||||
auto ok = makeFlood(ROUTE_TYPE_FLOOD, PAYLOAD_TYPE_RESPONSE, 2);
|
||||
EXPECT_FALSE(isFloodHopLimitExceeded(&ok, 64, 3, 8));
|
||||
|
||||
auto too_far = makeFlood(ROUTE_TYPE_FLOOD, PAYLOAD_TYPE_RESPONSE, 3);
|
||||
EXPECT_TRUE(isFloodHopLimitExceeded(&too_far, 64, 3, 8));
|
||||
}
|
||||
|
||||
TEST(FloodHopLimit, ScopedFloodStillHonoursFloodMaxAndAdvertMax) {
|
||||
auto beyond_max = makeFlood(ROUTE_TYPE_TRANSPORT_FLOOD, PAYLOAD_TYPE_RESPONSE, 5);
|
||||
EXPECT_TRUE(isFloodHopLimitExceeded(&beyond_max, 5, 64, 8));
|
||||
|
||||
auto advert = makeFlood(ROUTE_TYPE_TRANSPORT_FLOOD, PAYLOAD_TYPE_ADVERT, 8);
|
||||
EXPECT_TRUE(isFloodHopLimitExceeded(&advert, 64, 64, 8));
|
||||
}
|
||||
|
||||
// flood.max.unscoped=0 hits adverts too, well before flood_max_advert applies: a node
|
||||
// still advertising un-scoped is invisible past its immediate neighbours
|
||||
TEST(FloodHopLimit, UnscopedAdvertIsAlsoDroppedAtHopZero) {
|
||||
auto advert = makeFlood(ROUTE_TYPE_FLOOD, PAYLOAD_TYPE_ADVERT, 0);
|
||||
EXPECT_TRUE(isFloodHopLimitExceeded(&advert, 64, 0, 8));
|
||||
|
||||
auto scoped = makeFlood(ROUTE_TYPE_TRANSPORT_FLOOD, PAYLOAD_TYPE_ADVERT, 0);
|
||||
EXPECT_FALSE(isFloodHopLimitExceeded(&scoped, 64, 0, 8));
|
||||
}
|
||||
|
||||
TEST(ReplyRoute, FloodRequestGetsAPathReturn) {
|
||||
EXPECT_EQ(REPLY_ROUTE_PATH_RETURN,
|
||||
chooseReplyRoute(true, false, false));
|
||||
EXPECT_EQ(REPLY_ROUTE_PATH_RETURN,
|
||||
chooseReplyRoute(true, false, true));
|
||||
}
|
||||
|
||||
TEST(ReplyRoute, DirectRequestWithSuppliedPathRepliesDirect) {
|
||||
EXPECT_EQ(REPLY_ROUTE_DIRECT_SUPPLIED, chooseReplyRoute(false, true, false));
|
||||
}
|
||||
|
||||
// the reported bug: a DIRECT login (app already has a path) was answered by flooding, even
|
||||
// with an out_path stored. Under flood.max.unscoped=0 that reply never arrives.
|
||||
TEST(ReplyRoute, DirectRequestWithKnownOutPathRepliesDirect) {
|
||||
EXPECT_EQ(REPLY_ROUTE_DIRECT_OUT_PATH,
|
||||
chooseReplyRoute(false, false, true));
|
||||
}
|
||||
|
||||
TEST(ReplyRoute, SuppliedPathWinsOverStoredOutPath) {
|
||||
EXPECT_EQ(REPLY_ROUTE_DIRECT_SUPPLIED, chooseReplyRoute(false, true, true));
|
||||
}
|
||||
|
||||
TEST(ReplyRoute, DirectRequestWithNoReturnPathFallsBackToFlood) {
|
||||
EXPECT_EQ(REPLY_ROUTE_FLOOD, chooseReplyRoute(false, false, false));
|
||||
}
|
||||
|
||||
TEST(ReplyScope, MirrorsTheRequestScopeWhenKnown) {
|
||||
EXPECT_EQ(REPLY_SCOPE_REQUEST, chooseReplyScope(true,
|
||||
false,
|
||||
false));
|
||||
EXPECT_EQ(REPLY_SCOPE_REQUEST, chooseReplyScope(true, false, true));
|
||||
}
|
||||
|
||||
// un-scoped is itself a known scope, so mirror it. Replying scoped would change a path that
|
||||
// works today, and repeaters not holding our default Region would drop it anyway.
|
||||
TEST(ReplyScope, RepliesUnscopedToAnUnscopedFloodEvenWhenADefaultScopeExists) {
|
||||
EXPECT_EQ(REPLY_SCOPE_NONE, chooseReplyScope(false,
|
||||
true,
|
||||
true));
|
||||
}
|
||||
|
||||
// second half of the bug: a DIRECT request carries no transport codes, so recv_pkt_region is
|
||||
// always NULL. Un-scoped is dropped under flood.max.unscoped=0, and floods the mesh otherwise.
|
||||
TEST(ReplyScope, FallsBackToDefaultScopeWhenRequestScopeUnknown) {
|
||||
EXPECT_EQ(REPLY_SCOPE_DEFAULT, chooseReplyScope(false,
|
||||
false,
|
||||
true));
|
||||
}
|
||||
|
||||
TEST(ReplyScope, SendsUnscopedOnlyWhenNoScopeIsAvailableAtAll) {
|
||||
EXPECT_EQ(REPLY_SCOPE_NONE, chooseReplyScope(false, false, false));
|
||||
EXPECT_EQ(REPLY_SCOPE_NONE, chooseReplyScope(false, true, false));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
Reference in New Issue
Block a user