mirror of
https://github.com/meshcore-dev/MeshCore.git
synced 2026-08-14 07:19:46 +00:00
feat(companion_radio): add external FEM gain preferences for RX and TX for companions
Introduced consistent preferences for external LoRa FEM RX and TX gain settings in NodePrefs. Updated companion MyMesh to apply these settings during initialization and transmission. Added unit tests to verify the round-trip serialization of these new preferences.
This commit is contained in:
@@ -885,6 +885,8 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
|
||||
_prefs.tx_power_dbm = LORA_TX_POWER;
|
||||
_prefs.gps_enabled = 0; // GPS disabled by default
|
||||
_prefs.gps_interval = 0; // No automatic GPS updates by default
|
||||
_prefs.radio_fem_rxgain = 1;
|
||||
_prefs.radio_fem_txgain = 0;
|
||||
//_prefs.rx_delay_base = 10.0f; enable once new algo fixed
|
||||
_prefs.setRepeatEn(false);
|
||||
#if defined(USE_SX1262) || defined(USE_SX1268)
|
||||
@@ -974,6 +976,8 @@ void MyMesh::begin(bool has_display) {
|
||||
radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
|
||||
radio_driver.setTxPower(_prefs.tx_power_dbm);
|
||||
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
|
||||
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
|
||||
board.setLoRaFemPaGainEnabled(_prefs.radio_fem_txgain);
|
||||
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
|
||||
radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ public:
|
||||
uint32_t gps_interval = 0; // GPS read interval in seconds
|
||||
uint8_t autoadd_config = 0; // bitmask for auto-add contacts config
|
||||
uint8_t rx_boosted_gain = 0; // SX126x RX boosted gain mode (0=power saving, 1=boosted)
|
||||
uint8_t radio_fem_rxgain = 0; // external LoRa FEM RX gain (LNA)
|
||||
uint8_t radio_fem_txgain = 0; // external LoRa FEM TX gain (low by default)
|
||||
uint8_t _client_repeat = 0; // DEPRECATED -> use repeat.disable_fwd
|
||||
uint8_t path_hash_mode = 0; // which path mode to use when sending
|
||||
uint8_t autoadd_max_hops = 0; // 0 = no limit, 1 = direct (0 hops), N = up to N-1 hops (max 64)
|
||||
@@ -50,7 +52,8 @@ private:
|
||||
//def("cad", _parent->cad_enabled);
|
||||
//def("int_thr", _parent->interference_threshold);
|
||||
def("rxgain", _parent->rx_boosted_gain);
|
||||
def("fem_rxgain", _parent->rx_boosted_gain);
|
||||
def("fem_rxgain", _parent->radio_fem_rxgain);
|
||||
def("fem_txgain", _parent->radio_fem_txgain);
|
||||
def("tx", _parent->tx_power_dbm);
|
||||
def("af", _parent->airtime_factor);
|
||||
def("rxdelay", _parent->rx_delay_base);
|
||||
@@ -133,4 +136,4 @@ public:
|
||||
// new accessor methods
|
||||
bool isRepeatEn() const { return repeat.disable_fwd == 0; }
|
||||
void setRepeatEn(bool en) { repeat.disable_fwd = en ? 0 : 1; }
|
||||
};
|
||||
};
|
||||
|
||||
@@ -102,8 +102,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // Legacy
|
||||
file.read((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292
|
||||
file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293
|
||||
file.read((uint8_t *)&_prefs->cad_enabled, sizeof(_prefs->cad_enabled)); // 294
|
||||
file.read((uint8_t *)&_prefs->radio_fem_txgain, sizeof(_prefs->radio_fem_txgain)); // 295
|
||||
// next: 296
|
||||
// next: 295
|
||||
|
||||
// sanitise bad pref values
|
||||
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
#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; }
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 ───────────────────────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user