Use 4.6-chirp minimum dwell for both radio profiles
This commit is contained in:
1 parent
b5440c75c7
commit
239f6a84f5
14 files changed
+545
-70
No files matched your search
Vendored
+18
@@ -17,6 +17,15 @@ struct RadioProfileCLI {
|
||||
bool savePrimaryPreamble(uint16_t) { return true; }
|
||||
void appendPreamble(char*, size_t) {}
|
||||
void appendSavedPreamble(char*, size_t, uint8_t, float) {}
|
||||
mutable unsigned warning_calls = 0;
|
||||
mutable uint8_t warning_sf = 0;
|
||||
mutable float warning_bw = 0;
|
||||
mutable uint16_t warning_preamble = 0;
|
||||
void appendPrimaryChirpWarning(char* reply, size_t capacity, uint8_t sf, float bw, uint16_t preamble) const {
|
||||
++warning_calls; warning_sf = sf; warning_bw = bw; warning_preamble = preamble;
|
||||
const size_t used = strlen(reply);
|
||||
if (used < capacity) snprintf(reply + used, capacity - used, "; WARN recommended preamble: radio=32");
|
||||
}
|
||||
bool acceptsPrimary(float, float, uint8_t, uint8_t, uint16_t) const { return true; }
|
||||
};
|
||||
}
|
||||
@@ -119,10 +128,19 @@ int main() {
|
||||
unsigned saves=cli.callbacks.saves;
|
||||
cli.call(sender,"set radio 916,125,8,6",reply);
|
||||
assert(!strncmp(reply,"OK",2) && cli.callbacks.saves==saves+1);
|
||||
assert(strstr(reply,"WARN recommended preamble: radio=32"));
|
||||
assert(cli._radio_profiles.warning_calls==1 && cli._radio_profiles.warning_sf==8);
|
||||
assert(cli._radio_profiles.warning_bw==125 && cli._radio_profiles.warning_preamble==0);
|
||||
cli.call(sender,"get radio",reply);
|
||||
float freq=0,bw=0; int sf=0,cr=0;
|
||||
assert(sscanf(reply,"> %f,%f,%d,%d",&freq,&bw,&sf,&cr)==4);
|
||||
assert(freq==916 && bw==125 && sf==8 && cr==6);
|
||||
cli.call(sender,"set radio 916,125,8,6,16",reply);
|
||||
assert(strstr(reply,"WARN recommended preamble: radio=32"));
|
||||
assert(cli._radio_profiles.warning_calls==2 && cli._radio_profiles.warning_preamble==16);
|
||||
cli.call(sender,"set radio 916,125,13,6",reply);
|
||||
assert(!strncmp(reply,"Error",5) && !strstr(reply,"WARN"));
|
||||
assert(cli._radio_profiles.warning_calls==2);
|
||||
saves=cli.callbacks.saves;
|
||||
cli.call(sender,"set extra.sf 9,10",reply);
|
||||
#if defined(USE_LR2021)
|
||||
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
#include <RadioProfiles.h>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
|
||||
using Profiles=mesh::RadioProfiles;
|
||||
mesh::RadioProfileParams params(unsigned sf,float bw) {
|
||||
mesh::RadioProfileParams p; p.freq=909.5;p.sf=sf;p.bw=bw;p.cr=5;return p;
|
||||
}
|
||||
int main() {
|
||||
assert(Profiles::MinListenSymbols==4.6);
|
||||
assert(Profiles::SlowListenSymbols==4.6);
|
||||
assert(Profiles::MinFastListenSymbols==4.6);
|
||||
assert(Profiles::SwitchBudgetUs==600);
|
||||
assert(Profiles::LoopBudgetUs==300);
|
||||
auto a=params(10,125),b=params(10,125);
|
||||
auto t=Profiles::calculateChirpTiming(a,b,1,0,1000,4000);
|
||||
assert(t.valid && t.slow==0 && t.symbol_us[0]==8192);
|
||||
assert(t.listen_us[0]==37684 && t.listen_us[1]==37684);
|
||||
assert(t.preamble[0]==32 && t.preamble[1]==32);
|
||||
// The function accepts board-specific budgets; a V4 measurement must not
|
||||
// become a universal allowance for the GPIO-expander Indicator.
|
||||
a=params(6,125);b=a;
|
||||
auto fast=Profiles::calculateChirpTiming(a,b,0,0,1000,4000);
|
||||
auto indicator=Profiles::calculateChirpTiming(a,b,0,0,9000,4000);
|
||||
assert(indicator.preamble[0]>fast.preamble[0]);
|
||||
assert(indicator.preamble[1]>fast.preamble[1]);
|
||||
assert(fast.listen_us[0]>=4.6*512 && fast.listen_us[1]>=4.6*512);
|
||||
b.bw=0;assert(!Profiles::calculateChirpTiming(a,b).valid);
|
||||
b.bw=NAN;assert(!Profiles::calculateChirpTiming(a,b).valid);
|
||||
b=params(13,125);assert(!Profiles::calculateChirpTiming(a,b).valid);
|
||||
b=params(10,1e-30f);assert(Profiles::minimumListenUs(b)==0);
|
||||
a=params(7,62.5);b=params(8,500);
|
||||
t=Profiles::calculateChirpTiming(a,b);
|
||||
assert(t.preamble[1]==88); // Existing measured acquisition floor retained.
|
||||
auto swapped=Profiles::calculateChirpTiming(b,a);
|
||||
assert(swapped.slow==1 && swapped.preamble[0]==88);
|
||||
assert(swapped.preamble[1]==t.preamble[0]);
|
||||
for (float bw0: {7.8f,62.5f,125.0f,250.0f,500.0f})
|
||||
for (float bw1: {7.8f,62.5f,125.0f,250.0f,500.0f})
|
||||
for (unsigned sf0=5;sf0<=12;++sf0) for(unsigned sf1=5;sf1<=12;++sf1) {
|
||||
Profiles p;p.primary=params(sf0,bw0);p.secondary.params=params(sf1,bw1);
|
||||
p.secondary.mode=mesh::RadioProfileMode::Rx;
|
||||
auto minimum=Profiles::calculateChirpTiming(p.primary,p.secondary.params);
|
||||
assert(minimum.valid);
|
||||
for(unsigned i=0;i<2;++i) {
|
||||
const double symbols=4.6;
|
||||
assert(minimum.listen_us[i]==std::ceil(symbols*minimum.symbol_us[i]));
|
||||
assert(minimum.preamble[i]>=32 && std::fmod(minimum.preamble[i],8)==0);
|
||||
assert(p.automaticPreamble(i)==minimum.preamble[i]);
|
||||
}
|
||||
if (!p.automaticPreambleFits()) continue;
|
||||
auto actual=p.chirpTiming();
|
||||
assert(actual.valid);
|
||||
assert(p.listenUs(actual.slow)==Profiles::minimumListenUs(p.params(actual.slow),true));
|
||||
assert(p.listenUs(actual.slow^1)>=Profiles::minimumListenUs(p.params(actual.slow^1)));
|
||||
const double available=std::floor(p.preamble(actual.slow,32)*actual.symbol_us[actual.slow]/2
|
||||
-p.listenUs(actual.slow)-2*Profiles::SwitchBudgetUs-Profiles::LoopBudgetUs);
|
||||
const uint32_t fast_floor=Profiles::minimumListenUs(p.params(actual.slow^1));
|
||||
assert(p.listenUs(actual.slow^1)==(available>fast_floor ? (uint32_t)available : fast_floor));
|
||||
assert(2*actual.cycle_us<=p.preamble(actual.slow,32)*actual.symbol_us[actual.slow]+1e-6);
|
||||
assert(actual.preamble[0]<=p.preamble(0,32));
|
||||
assert(actual.preamble[1]<=p.preamble(1,32));
|
||||
}
|
||||
Profiles p;p.primary=params(7,62.5);p.secondary.params=params(7,500);
|
||||
p.secondary.mode=mesh::RadioProfileMode::Rx;
|
||||
assert(p.listenUs(0)==9421 && p.listenUs(1)==21847);
|
||||
assert(p.chirpTiming().switch_us==600 && p.chirpTiming().loop_us==300 && p.chirpTiming().preamble[1]==64);
|
||||
auto before=p.chirpTiming();const auto wire0=p.preamble(0,32),wire1=p.preamble(1,32);
|
||||
p.longest_switch_us=8428;auto overrun=p.chirpTiming();
|
||||
assert(overrun.switch_us==8428 && overrun.preamble[0]>before.preamble[0]);
|
||||
assert(p.preamble(0,32)==wire0 && p.preamble(1,32)==wire1);
|
||||
p.longest_switch_us=0;p.primary.preamble=64;
|
||||
assert(p.chirpTiming().preamble[0]==64 && p.listenUs(1)>21847);
|
||||
p.secondary.params.preamble=32;
|
||||
assert(p.preamble(1,32)==32 && p.chirpTiming().preamble[1]>32);
|
||||
p.secondary.mode=mesh::RadioProfileMode::Off;
|
||||
assert(!p.chirpTiming().valid);
|
||||
assert(p.listenUs(0)==9421); // Same minimum helper; single-profile RX does not scan.
|
||||
// Exact tested SF8/500 pair, explicit 32 preserved; order is not hard-coded.
|
||||
p.primary=params(7,62.5);p.primary.preamble=32;
|
||||
p.secondary.params=params(8,500);p.secondary.params.preamble=32;
|
||||
p.secondary.mode=mesh::RadioProfileMode::Rx;
|
||||
assert(p.automaticPreambleFits() && p.listenUs(0)==9421 && p.listenUs(1)==21847);
|
||||
assert(p.preamble(0,32)==32 && p.preamble(1,32)==32);
|
||||
assert(p.chirpTiming().preamble[1]==88);
|
||||
std::swap(p.primary,p.secondary.params);
|
||||
assert(p.slowerProfile()==1 && p.listenUs(1)==9421 && p.listenUs(0)==21847);
|
||||
p.secondary.params.preamble=8;
|
||||
assert(!p.automaticPreambleFits());
|
||||
// This formerly fit the 4.1 fast floor (1050 us), but not 4.6 (1178 us).
|
||||
p.primary=params(7,500);p.secondary.params=params(7,500);
|
||||
p.primary.preamble=30;
|
||||
assert(p.listenUs(0)==1178 && p.listenUs(1)>=1178);
|
||||
assert(!p.automaticPreambleFits());
|
||||
p.primary.preamble=32;
|
||||
assert(p.automaticPreambleFits() && p.listenUs(1)==1418);
|
||||
// Long requested visits remain visible, not replaced by policy minima.
|
||||
t=Profiles::calculateChirpTiming(params(7,62.5),params(8,500),20000,30000);
|
||||
assert(t.listen_us[0]==20000 && t.listen_us[1]==30000 && t.cycle_us==51500);
|
||||
}
|
||||
+74
-3
@@ -47,10 +47,17 @@ int main() {
|
||||
Fixture f;
|
||||
assert(!strcmp(f.cmd("get radio2"),"> off"));
|
||||
assert(!strcmp(f.cmd("get radio2.cross"),"> auto"));
|
||||
f.cmd("set radio2 910.5,500,7,5,rxtx");
|
||||
assert(strstr(f.cmd("set radio2 910.5,500,7,5,rxtx"),"WARN recommended preamble: radio2=64"));
|
||||
assert(!f.radio.p.enabled()); f.advance(2000); assert(f.radio.p.enabled());
|
||||
assert(strstr(f.cmd("get radio2"),"rxtx,120 (auto)"));
|
||||
assert(strstr(f.cmd("get radio2.scan"),"slow=radio; listen_us=9831,6937"));
|
||||
assert(strstr(f.cmd("get radio2"),"rxtx,64 (auto)"));
|
||||
assert(strstr(f.cmd("get radio2.scan"),"slow=radio; listen_us=9421,21847"));
|
||||
assert(strstr(f.cmd("get radio2.timing"),"chirps=4.60,85.34; need=32,64"));
|
||||
assert(strstr(f.reply,"switch=600us"));
|
||||
assert(strstr(f.reply,"loop=300us"));
|
||||
assert(strstr(f.reply,"estimate"));
|
||||
assert(strstr(f.reply,"WARN recommended preamble: radio2=64"));
|
||||
char timing_reply[160]; strcpy(timing_reply, f.reply);
|
||||
assert(!strcmp(f.cmd("get radio.timing"),timing_reply));
|
||||
f.cmd("set tempradio2 911.5,500,8,5,rx,2,64");
|
||||
assert(!f.radio.p.secondary_temporary); f.advance(2000);
|
||||
assert(f.radio.p.secondary_temporary && f.radio.p.secondary.params.freq==911.5f);
|
||||
@@ -74,8 +81,72 @@ int main() {
|
||||
f.cmd("set tempradio2 910.5,500,7,5,rxtx,0",false);
|
||||
f.cmd("set tempradio2 910.5,500,7,5,rxtx,10081",false);
|
||||
f.cmd("set radio2.status 1",false);
|
||||
f.cmd("set radio2.timing 1",false);
|
||||
f.cmd("set radio.timing 1",false);
|
||||
assert(strstr(f.cmd("get radio2.timing extra"),"read-only"));
|
||||
assert(!f.cli.handle("set radio2junk 910.5",f.reply));
|
||||
}
|
||||
{
|
||||
Fixture f;
|
||||
assert(!strcmp(f.cmd("get radio2.timing"),"> off"));
|
||||
// A short explicit preamble is not rewritten, and cannot hide a warning.
|
||||
assert(strstr(f.cmd("set radio2 910.5,500,7,5,rx,32"),"WARN recommended preamble: radio2=64"));
|
||||
assert(strstr(f.reply,"short override: radio2"));
|
||||
f.advance(2000);
|
||||
assert(f.radio.p.secondary.params.preamble==32);
|
||||
assert(strstr(f.cmd("get radio2"),"short override: radio2"));
|
||||
f.radio.p.longest_switch_us=8428;
|
||||
assert(strstr(f.cmd("get radio2.timing"),"switch=8428us"));
|
||||
assert(strstr(f.reply,"WARN recommended preamble: radio="));
|
||||
assert(f.radio.p.secondary.params.preamble==32);
|
||||
// Primary getters use the saved primary's SF/BW instead of a temporary one.
|
||||
f.radio.p.primary.sf=10; f.radio.p.primary.bw=125;
|
||||
strcpy(f.reply,"> radio");
|
||||
f.cli.appendSavedPreamble(f.reply,sizeof(f.reply),7,62.5);
|
||||
assert(strstr(f.reply,"WARN recommended preamble: radio="));
|
||||
// Bounded appenders cannot corrupt neighboring bytes, even with tiny replies.
|
||||
for (size_t n=1;n<=160;++n) {
|
||||
char bytes[162]; memset(bytes,0x55,sizeof(bytes)); bytes[0]=0;
|
||||
mesh::RadioProfileCLI::appendChirpWarning(bytes,n,f.radio.p);
|
||||
assert(memchr(bytes,0,n)); assert(bytes[n]==0x55);
|
||||
}
|
||||
f.radio.p.primary.sf=10;f.radio.p.primary.bw=125;
|
||||
f.radio.p.secondary.params.sf=10;f.radio.p.secondary.params.bw=125;
|
||||
f.radio.p.longest_switch_us=0;
|
||||
assert(!strstr(f.cmd("get radio2.timing"),"WARN"));
|
||||
// A selected value below 32 still gets the actual recommendation, not
|
||||
// merely a warning label with no preamble to use.
|
||||
assert(strstr(f.cmd("set radio2 910.5,125,10,5,rx,16"),"WARN recommended preamble: radio2=32"));
|
||||
assert(strstr(f.reply,"short override: radio2"));
|
||||
}
|
||||
{
|
||||
Fixture f;
|
||||
f.radio.p.primary.sf=7;f.radio.p.primary.bw=500;
|
||||
assert(strstr(f.cmd("set radio2 910.5,62.5,7,5,rx"),"WARN recommended preamble: radio=64"));
|
||||
f.advance(2000);
|
||||
assert(strstr(f.cmd("get radio2.timing"),"need=64,32"));
|
||||
strcpy(f.reply,"OK - reboot to apply");
|
||||
f.cli.appendPrimaryChirpWarning(f.reply,sizeof(f.reply),7,500,32);
|
||||
assert(strstr(f.reply,"WARN recommended preamble: radio=64"));
|
||||
assert(strstr(f.reply,"short override: radio"));
|
||||
strcpy(f.reply,"OK - reboot to apply");
|
||||
f.cli.appendPrimaryChirpWarning(f.reply,sizeof(f.reply),7,62.5,0);
|
||||
assert(!strstr(f.reply,"WARN")); // Preview requested tuple, not the live one.
|
||||
f.radio.p.primary.sf=5;f.radio.p.primary.bw=500;
|
||||
f.radio.p.secondary.params.sf=5;f.radio.p.secondary.params.bw=500;
|
||||
assert(strstr(f.cmd("get radio2.timing"),"WARN recommended preamble: radio="));
|
||||
assert(strstr(f.reply,",radio2="));
|
||||
}
|
||||
{
|
||||
Fixture f;
|
||||
// The tested pair is allocated automatically without changing explicit 32.
|
||||
assert(strstr(f.cmd("set radio2 910.5,500,8,5,rx,32"),"WARN recommended preamble: radio2=88"));
|
||||
f.advance(2000);
|
||||
assert(f.radio.p.secondary.params.preamble==32);
|
||||
assert(strstr(f.cmd("get radio2.scan"),"slow=radio; listen_us=9421,21847"));
|
||||
assert(strstr(f.cmd("get radio2.timing"),"chirps=4.60,42.67; need=32,88"));
|
||||
assert(strstr(f.reply,"loop=300us"));
|
||||
}
|
||||
{
|
||||
Fixture f;
|
||||
f.cmd("set radio2 910.5,500,9,5,rxtx,40"); f.advance(2000);
|
||||
|
||||
+9
-7
@@ -94,17 +94,19 @@ TEST(RadioProfiles, CrossPolicyAndReceiveOnlyMatrix) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RadioProfiles, AutomaticPreamblesIncludeMeasuredSwitchingMargin) {
|
||||
TEST(RadioProfiles, AutomaticPreamblesIncludeNominalSwitchingMargin) {
|
||||
DualProfileTestRadio radio;
|
||||
auto& p = radio.config;
|
||||
const uint16_t expected[] = {120, 88, 48};
|
||||
EXPECT_EQ(600, p.SwitchBudgetUs);
|
||||
EXPECT_EQ(300, p.LoopBudgetUs);
|
||||
const uint16_t expected[] = {64, 88, 32};
|
||||
for (int sf = 7; sf <= 9; ++sf) {
|
||||
p.secondary.params.sf = sf;
|
||||
EXPECT_EQ(expected[sf-7], p.preamble(1, 32));
|
||||
EXPECT_EQ(32, p.preamble(0, 32));
|
||||
EXPECT_EQ(0, p.preamble(1, 32) % 8);
|
||||
EXPECT_EQ(9831, p.listenUs(0));
|
||||
EXPECT_EQ(6937, p.listenUs(1));
|
||||
EXPECT_EQ(9421, p.listenUs(0));
|
||||
EXPECT_EQ(21847, p.listenUs(1));
|
||||
EXPECT_LE(2 * (p.listenUs(0) + p.listenUs(1) + 2 * p.SwitchBudgetUs + p.LoopBudgetUs),
|
||||
p.preamble(0, 32) * p.symbolUs(p.primary));
|
||||
EXPECT_LE(p.listenUs(1) + 2 * p.SwitchBudgetUs + p.LoopBudgetUs
|
||||
@@ -122,10 +124,10 @@ TEST(RadioProfiles, SlowerProfileDeterminesOrderAndFasterReceiveWindow) {
|
||||
EXPECT_EQ(0, p.slowerProfile());
|
||||
std::swap(p.primary, p.secondary.params);
|
||||
EXPECT_EQ(1, p.slowerProfile());
|
||||
EXPECT_EQ(9831, p.listenUs(1));
|
||||
EXPECT_EQ(6937, p.listenUs(0));
|
||||
EXPECT_EQ(9421, p.listenUs(1));
|
||||
EXPECT_EQ(21847, p.listenUs(0));
|
||||
p.secondary.params.preamble = 40;
|
||||
EXPECT_EQ(15129, p.listenUs(0));
|
||||
EXPECT_EQ(30039, p.listenUs(0));
|
||||
p.secondary.params.preamble = 8;
|
||||
EXPECT_FALSE(p.automaticPreambleFits()); // no safe time for the faster channel
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
"""Compile real Companion command dispatch with local and LoRa callers."""
|
||||
from pathlib import Path
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
from test_replay_reset_integration import extract_braced
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
# MinGW does not ship these runtimes; keep sanitizer coverage on Linux.
|
||||
SANITIZER_FLAGS = [] if os.name == "nt" else ["-fsanitize=address,undefined", "-fno-pie", "-no-pie"]
|
||||
|
||||
PREAMBLE = r'''
|
||||
#include <cassert>
|
||||
@@ -32,6 +35,7 @@ struct RadioProfileCLI {
|
||||
bool savePrimaryPreamble(uint16_t) { return true; }
|
||||
uint16_t primaryPreamble() const { return 0; }
|
||||
void appendSavedPreamble(char*, size_t, uint8_t, float) {}
|
||||
void appendPrimaryChirpWarning(char*, size_t, uint8_t, float, uint16_t) const {}
|
||||
};
|
||||
struct Packet {};
|
||||
void resetLazyPersistenceAfterSuccess(unsigned& when, uint8_t& failures) { when=0; failures=0; }
|
||||
@@ -246,7 +250,7 @@ int main() {
|
||||
binary = Path(temp) / "test"
|
||||
result = subprocess.run([
|
||||
"c++", "-std=c++17", "-x", "c++", "-", "-I"+str(ROOT / "src"),
|
||||
"-fsanitize=address,undefined", "-fno-pie", "-no-pie", "-o", str(binary),
|
||||
*SANITIZER_FLAGS, "-o", str(binary),
|
||||
], input=code, text=True, capture_output=True)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
result = subprocess.run([str(binary)], capture_output=True, text=True)
|
||||
@@ -289,7 +293,7 @@ int main() {
|
||||
binary = Path(temp) / "test"
|
||||
result = subprocess.run([
|
||||
"c++", "-std=c++17", "-include", "initializer_list", "-x", "c++", "-",
|
||||
"-fsanitize=address,undefined", "-fno-pie", "-no-pie", "-o", str(binary),
|
||||
*SANITIZER_FLAGS, "-o", str(binary),
|
||||
], input=code, text=True, capture_output=True)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
result = subprocess.run([str(binary)], capture_output=True, text=True)
|
||||
@@ -308,7 +312,7 @@ int main() {
|
||||
"c++", "-std=c++17", "-x", "c++", "-", "-I"+str(ROOT / "src"),
|
||||
"-DENABLE_PRIVATE_KEY_EXPORT="+str(export),
|
||||
*(["-DWITH_WEBCONFIG=1"] if webconfig else []),
|
||||
"-fsanitize=address,undefined", "-fno-pie", "-no-pie", "-o", str(binary),
|
||||
*SANITIZER_FLAGS, "-o", str(binary),
|
||||
], input=PREAMBLE+implementation+SCENARIOS, text=True, capture_output=True)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
result = subprocess.run([str(binary)], capture_output=True, text=True)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
"""Compile the portable production chirp math, independent of radio hardware."""
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
ROOT=Path(__file__).resolve().parents[1]
|
||||
|
||||
class RadioChirpMathTest(unittest.TestCase):
|
||||
def test_floor_budgets_rounding_and_profile_order(self):
|
||||
compiler=shutil.which('g++') or shutil.which('clang++')
|
||||
self.assertIsNotNone(compiler)
|
||||
with tempfile.TemporaryDirectory() as folder:
|
||||
exe=Path(folder)/'chirps.exe'
|
||||
done=subprocess.run([compiler,'-std=c++17','-Wall','-Wextra','-I',str(ROOT/'src'),
|
||||
str(ROOT/'test/fixtures/radio_profiles/chirp_math.cpp'),'-o',str(exe)],capture_output=True,text=True)
|
||||
self.assertEqual(done.returncode,0,done.stderr)
|
||||
done=subprocess.run([str(exe)],capture_output=True,text=True)
|
||||
self.assertEqual(done.returncode,0,done.stderr)
|
||||
|
||||
if __name__=='__main__':unittest.main()
|
||||
@@ -302,6 +302,7 @@ struct RadioProfileCLI {
|
||||
return cli::parseRadioPreambleSuffix(input, fields, legacy, size, preamble);
|
||||
}
|
||||
bool acceptsPrimary(float, float, uint8_t, uint8_t, uint16_t) { return true; }
|
||||
void appendPrimaryChirpWarning(char*, size_t, uint8_t, float, uint16_t) const {}
|
||||
};
|
||||
class Utils { public: static int parseTextParts(char*, const char*[], int, char separator=','); };
|
||||
@PARTS@
|
||||
|
||||
Reference in new issue
Block a user