Files
MeshCore/test/fixtures/morse_hil_timing.cpp

109 lines
4.7 KiB
C++

#include <helpers/MorseTx.h>
#include <cassert>
#include <cstring>
#include <string>
#include <vector>
using mesh::MorseTx;
using Result = mesh::MorseTxResult;
static std::vector<uint32_t> phases(const char* text, uint8_t wpm, uint32_t start,
bool hardware_delay = false) {
uint32_t planned = 0;
const auto valid = MorseTx::validate(text, wpm, &planned);
MorseTx timer;
if (valid != Result::Started) {
assert(valid == Result::TooLong && timer.start(text, wpm, start) == valid);
assert(!timer.active() && !timer.keyed());
return {};
}
assert(timer.start(text, wpm, start) == Result::Started);
std::vector<uint32_t> result;
uint32_t now = start, total = 0;
while (timer.active()) {
const uint32_t phase = timer.nextDeadline() - now;
assert(phase > 0 && phase < 65536);
assert(timer.remainingMillis(now) == planned - total);
assert(timer.advance(now + phase - 1) == MorseTx::Edge::None);
now += phase; total += phase; result.push_back(phase);
const auto edge = timer.advance(now);
assert(edge != MorseTx::Edge::None && edge != MorseTx::Edge::Expired);
if (timer.active() && hardware_delay) {
now += 3; timer.edgeApplied(now);
assert(timer.nextDeadline() > now); // The native transition owns its delay.
}
assert(result.size() < 2000);
}
assert(total == planned && timer.remainingMillis(now) == 0);
return result;
}
int main() {
#if defined(MESH_MORSE_HIL) && MESH_MORSE_HIL
using mesh::MorseHilTimingConfig;
assert(mesh::morseHilTiming().jitter_percent == 0);
const auto baseline = phases("SOS E T", 25, 100);
for (const auto config : {MorseHilTimingConfig(), MorseHilTimingConfig(20,0,0,1),
MorseHilTimingConfig(0,20,0,1), MorseHilTimingConfig(0,-20,0,1),
MorseHilTimingConfig(0,0,50,1), MorseHilTimingConfig(15,10,25,12345),
MorseHilTimingConfig(20,-20,50,0xffffffff)}) {
assert(mesh::setMorseHilTiming(config));
for (const uint8_t wpm : {uint8_t(1),uint8_t(25),uint8_t(60),uint8_t(200),uint8_t(255)}) {
for (const char* text : {"E", "SOS E T", " a B ", ". , ? ' ! / ( ) & : ; = + - _ \" $ @"}) {
const auto first = phases(text, wpm, 100);
assert(first == phases(text, wpm, 0xffffffe0));
}
}
}
assert(mesh::setMorseHilTiming(MorseHilTimingConfig(15,10,25,12345)));
const auto expected = phases("SOS E T",25,100);
assert(expected != baseline);
assert(expected == phases("SOS E T",25,100,true));
MorseTx running;
assert(running.start("SOS E T",25,100) == Result::Started);
assert(mesh::setMorseHilTiming(MorseHilTimingConfig(20,-20,50,54321)));
uint32_t now=100;
std::vector<uint32_t> actual;
while(running.active()) {
const uint32_t phase=running.nextDeadline()-now;
actual.push_back(phase); now+=phase; running.advance(now);
}
assert(actual == expected); // Active settings are a snapshot, not live globals.
assert(mesh::setMorseHilTiming(MorseHilTimingConfig(10,0,0,12345)));
const auto fast = phases(std::string(80,'E').c_str(),255,100);
bool changed_mark=false;
for(size_t index=0;index<fast.size();index+=2) if(fast[index]!=5) changed_mark=true;
assert(changed_mark); // Fractional jitter must survive at five-ms nominal dots.
assert(mesh::setMorseHilTiming(MorseHilTimingConfig()));
uint32_t duration=0;
assert(MorseTx::validate(std::string(38,'E').c_str(),1,&duration)==Result::Started && duration==178800);
assert(mesh::setMorseHilTiming(MorseHilTimingConfig(0,0,50,1)));
duration=0xaabbccdd;
assert(MorseTx::validate(std::string(38,'E').c_str(),1,&duration)==Result::TooLong);
assert(duration==0xaabbccdd);
assert(!mesh::setMorseHilTiming(MorseHilTimingConfig(21,0,0,1)));
assert(!mesh::setMorseHilTiming(MorseHilTimingConfig(0,21,0,1)));
assert(!mesh::setMorseHilTiming(MorseHilTimingConfig(0,-21,0,1)));
assert(!mesh::setMorseHilTiming(MorseHilTimingConfig(0,0,51,1)));
assert(!mesh::setMorseHilTiming(MorseHilTimingConfig(0,0,0,0)));
assert(mesh::morseHilTiming().extra_gap_percent==50);
MorseTx deadline;
assert(deadline.start("SOS",25,0xffffffe0)==Result::Started);
assert(deadline.advance(deadline.hardDeadline())==MorseTx::Edge::Expired);
assert(!deadline.active() && !deadline.keyed());
assert(mesh::setMorseHilTiming(MorseHilTimingConfig()));
#else
struct ShippingLayout {
char text[MorseTx::MaxTextLength+1];
uint32_t next,hard_end,remaining;
uint16_t unit,phase;
uint8_t character,symbol,code,count;
bool active,keyed;
};
static_assert(sizeof(MorseTx)==sizeof(ShippingLayout),"production TX layout must not grow");
assert(phases("SOS",25,100)==std::vector<uint32_t>({48,48,48,48,48,144,144,48,144,48,144,144,48,48,48,48,48}));
#endif
}