Skip to content

Commit e2aa7b9

Browse files
committed
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.
1 parent c58c9b2 commit e2aa7b9

5 files changed

Lines changed: 144 additions & 15 deletions

File tree

examples/companion_radio/MyMesh.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,8 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
885885
_prefs.tx_power_dbm = LORA_TX_POWER;
886886
_prefs.gps_enabled = 0; // GPS disabled by default
887887
_prefs.gps_interval = 0; // No automatic GPS updates by default
888+
_prefs.radio_fem_rxgain = 1;
889+
_prefs.radio_fem_txgain = 0;
888890
//_prefs.rx_delay_base = 10.0f; enable once new algo fixed
889891
_prefs.setRepeatEn(false);
890892
#if defined(USE_SX1262) || defined(USE_SX1268)
@@ -974,6 +976,8 @@ void MyMesh::begin(bool has_display) {
974976
radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
975977
radio_driver.setTxPower(_prefs.tx_power_dbm);
976978
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
979+
board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain);
980+
board.setLoRaFemPaGainEnabled(_prefs.radio_fem_txgain);
977981
MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s",
978982
radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled");
979983
}

examples/companion_radio/NodePrefs.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class NodePrefs : public ConfigSerializer { // persisted to file
3232
uint32_t gps_interval = 0; // GPS read interval in seconds
3333
uint8_t autoadd_config = 0; // bitmask for auto-add contacts config
3434
uint8_t rx_boosted_gain = 0; // SX126x RX boosted gain mode (0=power saving, 1=boosted)
35+
uint8_t radio_fem_rxgain = 0; // external LoRa FEM RX gain (LNA)
36+
uint8_t radio_fem_txgain = 0; // external LoRa FEM TX gain (low by default)
3537
uint8_t _client_repeat = 0; // DEPRECATED -> use repeat.disable_fwd
3638
uint8_t path_hash_mode = 0; // which path mode to use when sending
3739
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 @@ class NodePrefs : public ConfigSerializer { // persisted to file
5052
//def("cad", _parent->cad_enabled);
5153
//def("int_thr", _parent->interference_threshold);
5254
def("rxgain", _parent->rx_boosted_gain);
53-
def("fem_rxgain", _parent->rx_boosted_gain);
55+
def("fem_rxgain", _parent->radio_fem_rxgain);
56+
def("fem_txgain", _parent->radio_fem_txgain);
5457
def("tx", _parent->tx_power_dbm);
5558
def("af", _parent->airtime_factor);
5659
def("rxdelay", _parent->rx_delay_base);
@@ -133,4 +136,4 @@ class NodePrefs : public ConfigSerializer { // persisted to file
133136
// new accessor methods
134137
bool isRepeatEn() const { return repeat.disable_fwd == 0; }
135138
void setRepeatEn(bool en) { repeat.disable_fwd = en ? 0 : 1; }
136-
};
139+
};

src/helpers/CommonCLI.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // Legacy
102102
file.read((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292
103103
file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293
104104
file.read((uint8_t *)&_prefs->cad_enabled, sizeof(_prefs->cad_enabled)); // 294
105-
file.read((uint8_t *)&_prefs->radio_fem_txgain, sizeof(_prefs->radio_fem_txgain)); // 295
106-
// next: 296
105+
// next: 295
107106

108107
// sanitise bad pref values
109108
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include <gtest/gtest.h>
2+
3+
#include <cstdio>
4+
#include <cstring>
5+
#include <string>
6+
7+
#include "../../examples/companion_radio/NodePrefs.h"
8+
9+
class ReplayStream : public Stream {
10+
const char* _text;
11+
int _pos = 0;
12+
int _len;
13+
14+
public:
15+
explicit ReplayStream(const char* text) : _text(text), _len(strlen(text)) { }
16+
17+
int available() override { return _len - _pos; }
18+
int read() override { return _pos < _len ? _text[_pos++] : -1; }
19+
int peek() override { return _pos < _len ? _text[_pos] : -1; }
20+
};
21+
22+
class CaptureStream : public Stream {
23+
std::string _text;
24+
25+
size_t emit(long long value) {
26+
char text[24];
27+
int length = snprintf(text, sizeof(text), "%lld", value);
28+
return write(reinterpret_cast<const uint8_t*>(text), length);
29+
}
30+
31+
public:
32+
size_t write(uint8_t value) override {
33+
_text.push_back(static_cast<char>(value));
34+
return 1;
35+
}
36+
37+
size_t write(const uint8_t* buffer, size_t size) override {
38+
_text.append(reinterpret_cast<const char*>(buffer), size);
39+
return size;
40+
}
41+
42+
size_t print(unsigned char value, int = DEC) override { return emit(value); }
43+
size_t print(int value, int = DEC) override { return emit(value); }
44+
size_t print(unsigned int value, int = DEC) override { return emit(value); }
45+
size_t print(long value, int = DEC) override { return emit(value); }
46+
size_t print(unsigned long value, int = DEC) override { return emit(value); }
47+
size_t print(long long value, int = DEC) override { return emit(value); }
48+
size_t print(unsigned long long value, int = DEC) override { return emit(value); }
49+
50+
const std::string& text() const { return _text; }
51+
};
52+
53+
TEST(CompanionNodePrefs, RxGainSettingsRoundTripIndependently) {
54+
NodePrefs saved;
55+
saved.rx_boosted_gain = 0;
56+
saved.radio_fem_rxgain = 1;
57+
saved.radio_fem_txgain = 0;
58+
59+
CaptureStream output;
60+
ASSERT_TRUE(saved.saveSerial(output));
61+
EXPECT_NE(std::string::npos, output.text().find("rxgain:0"));
62+
EXPECT_NE(std::string::npos, output.text().find("fem_rxgain:1"));
63+
EXPECT_NE(std::string::npos, output.text().find("fem_txgain:0"));
64+
65+
ReplayStream input("{radio:{rxgain:1,fem_rxgain:0,fem_txgain:1}}");
66+
NodePrefs loaded;
67+
loaded.rx_boosted_gain = 0;
68+
loaded.radio_fem_rxgain = 1;
69+
loaded.radio_fem_txgain = 0;
70+
71+
ASSERT_TRUE(loaded.loadSerial(input));
72+
EXPECT_EQ(1, loaded.rx_boosted_gain);
73+
EXPECT_EQ(0, loaded.radio_fem_rxgain);
74+
EXPECT_EQ(1, loaded.radio_fem_txgain);
75+
}
76+
77+
int main(int argc, char** argv) {
78+
::testing::InitGoogleTest(&argc, argv);
79+
return RUN_ALL_TESTS();
80+
}

test/test_config_serializer/test_config_serializer.cpp

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
#include <gtest/gtest.h>
22
#include "helpers/ConfigSerializer.h"
33

4+
class NativeFileSystem {
5+
public:
6+
void mkdir(const char*) { }
7+
};
8+
#define FILESYSTEM NativeFileSystem
9+
#include "helpers/CommonCLI.h"
10+
#undef FILESYSTEM
11+
412
#define TEST_INT_S "56"
513
#define TEST_INT 56
614
#define TEST_FLOAT_S "-6.123"
@@ -21,6 +29,19 @@ class MockInputStream : public Stream {
2129
class MockPrintStream : public Stream {
2230
int len = 0;
2331
uint8_t _buf[1024];
32+
33+
size_t printSigned(long long value) {
34+
char text[24];
35+
snprintf(text, sizeof(text), "%lld", value);
36+
return Print::print(text);
37+
}
38+
39+
size_t printUnsigned(unsigned long long value) {
40+
char text[24];
41+
snprintf(text, sizeof(text), "%llu", value);
42+
return Print::print(text);
43+
}
44+
2445
public:
2546
size_t write(uint8_t b) override {
2647
if (len < sizeof(_buf)) {
@@ -30,17 +51,17 @@ class MockPrintStream : public Stream {
3051
return 0;
3152
}
3253

33-
size_t print(unsigned char b, int r) override { if (b == TEST_INT) return Print::print(TEST_INT_S); return 0; }
34-
size_t print(int v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
35-
size_t print(unsigned int v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
36-
size_t print(long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
37-
size_t print(unsigned long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
38-
size_t print(long long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
39-
size_t print(unsigned long long v, int r) override { if (v == TEST_INT) return Print::print(TEST_INT_S); return 0; }
40-
size_t print(double v, int p = 2) override {
41-
if (p == 6) return Print::print(TEST_DOUBLE_S);
42-
if (p == 4) return Print::print(TEST_FLOAT_S);
43-
return 0;
54+
size_t print(unsigned char v, int r) override { return printUnsigned(v); }
55+
size_t print(int v, int r) override { return printSigned(v); }
56+
size_t print(unsigned int v, int r) override { return printUnsigned(v); }
57+
size_t print(long v, int r) override { return printSigned(v); }
58+
size_t print(unsigned long v, int r) override { return printUnsigned(v); }
59+
size_t print(long long v, int r) override { return printSigned(v); }
60+
size_t print(unsigned long long v, int r) override { return printUnsigned(v); }
61+
size_t print(double v, int p = 2) override {
62+
char text[32];
63+
snprintf(text, sizeof(text), "%.*f", p, v);
64+
return Print::print(text);
4465
}
4566

4667
int getLength() const { return len; }
@@ -171,6 +192,28 @@ TEST(ConfigSerializer, LoadSerial_IgnoreUnknowns) {
171192
EXPECT_TRUE(match);
172193
}
173194

195+
TEST(NodePrefs, FemGainSettingsRoundTrip) {
196+
NodePrefs saved;
197+
saved.radio_fem_rxgain = 0;
198+
saved.radio_fem_txgain = 1;
199+
200+
MockPrintStream output;
201+
ASSERT_TRUE(saved.saveSerial(output));
202+
203+
std::string serialised(reinterpret_cast<const char*>(output.getBytes()), output.getLength());
204+
EXPECT_NE(std::string::npos, serialised.find("fem_rxgain:0"));
205+
EXPECT_NE(std::string::npos, serialised.find("fem_txgain:1"));
206+
207+
MockInputStream input(serialised.c_str());
208+
NodePrefs loaded;
209+
loaded.radio_fem_rxgain = 1;
210+
loaded.radio_fem_txgain = 0;
211+
212+
ASSERT_TRUE(loaded.loadSerial(input));
213+
EXPECT_EQ(0, loaded.radio_fem_rxgain);
214+
EXPECT_EQ(1, loaded.radio_fem_txgain);
215+
}
216+
174217

175218
// ── main ───────────────────────────────────────────────────────
176219

0 commit comments

Comments
 (0)