Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion include/virtual_sx1262.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,8 @@ typedef struct {
/* Events the chip had and the firmware never saw, because IrqMask had them
* masked off. A firmware waiting on an interrupt it did not enable looks
* exactly like a quiet mesh, and this is the only thing that tells the two
* apart. */
* apart. One per event, however many of its bits were masked: a header is one
* event whether one interrupt or three would have announced it. */
uint32_t irq_suppressed;
/* Scans run, and scans that found a carrier. A host comparing a firmware that
* scans against one that does not needs to know the scans happened and what
Expand Down
9 changes: 7 additions & 2 deletions src/VirtualSX1262.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,14 @@ void VirtualSX1262::tick(uint64_t nowMs) {
// The receiver's own preamble length stands in for the transmitter's: a
// mesh agrees on one, and the chip is told nothing about who is sending.
// An implicit-header receiver has no header to find.
//
// HeaderValid alone: SyncWordValid is the GFSK modem's interrupt, and a LoRa
// receiver reports its header through HeaderValid (DS_SX1261-2 table 13-29).
// Raising both made every header count as suppressed on a firmware that
// correctly leaves the FSK bit out of its mask.
const double headerEnd = preambleSyms_ + kSyncAndSfdSymbols + kHeaderBlockSymbols;
if (!headerImplicit_ && !headerRaised_ && sinceMs >= headerEnd * symMs) {
raiseIrq(kIrqHeaderValid | kIrqSyncWordValid);
raiseIrq(kIrqHeaderValid);
headerRaised_ = true;
}
}
Expand Down Expand Up @@ -203,7 +208,7 @@ void VirtualSX1262::setChannelBusy(bool busy) {
preambleRaised_ = false;
headerRaised_ = false;
if (stuckIrqMs_ == 0) {
irq_ &= (uint16_t) ~(kIrqPreambleDetected | kIrqHeaderValid | kIrqSyncWordValid);
irq_ &= (uint16_t) ~(kIrqPreambleDetected | kIrqHeaderValid);
}
}
channelBusy_ = busy;
Expand Down
7 changes: 4 additions & 3 deletions src/registers.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,10 @@ namespace {
// The header cannot be read until the whole preamble, the two sync symbols and
// the 2.25-symbol SFD have gone by, and it is the first interleaver block after
// them: eight symbols, always at CR 4/8. A fixed twelve symbols raised
// HeaderValid 32 symbols early for MeshCore's 32-symbol preamble at SF8 and
// below (16 at SF9 and up), which shrank the stretch where a firmware that can see
// preambles answers differently from one that cannot to a third of its real width.
// HeaderValid 32.25 symbols early for MeshCore's 32-symbol preamble at SF8 and
// below (16 at SF9 and up). That shrank the stretch where a firmware that can
// see preambles answers differently from one that cannot, from PreambleDetected
// to HeaderValid, to eight symbols of its real 40.25: a fifth.
[[maybe_unused]] constexpr double kPreambleSymbols = 4.0;
[[maybe_unused]] constexpr double kSyncAndSfdSymbols = 4.25;
[[maybe_unused]] constexpr double kHeaderBlockSymbols = 8.0;
Expand Down
42 changes: 42 additions & 0 deletions test/test_interrupts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,34 @@
*/
#include "harness.h"

/* Two frames heard under one IrqMask, the way a receive loop takes them: a
* carrier long enough to carry its header, the frame, the carrier gone, and the
* status cleared before the next. Answers what the chip counted as suppressed. */
static uint32_t suppressed_over_two_frames(uint16_t mask) {
vsx_chip* c = vsx_create();
uint64_t now = 0;
bring_up(c, &now);
/* SetDioIrqParams: the mask under test, and RxDone alone routed to DIO1. */
const uint8_t dio[] = {0x08, (uint8_t)(mask >> 8), (uint8_t)mask, 0, 0x02, 0, 0, 0, 0};
vsx_spi_transaction(c, dio, nullptr, sizeof(dio));
const uint8_t frame[] = {1, 2, 3, 4};
for (int n = 0; n < 2; ++n) {
vsx_set_channel_busy(c, 1);
for (int i = 0; i < 150; ++i) {
vsx_tick(c, ++now);
}
vsx_deliver_frame(c, frame, sizeof(frame));
vsx_set_channel_busy(c, 0);
vsx_tick(c, ++now);
const uint8_t clear[] = {0x02, 0xFF, 0xFF};
vsx_spi_transaction(c, clear, nullptr, sizeof(clear));
}
vsx_counters k;
vsx_get_counters(c, &k);
vsx_destroy(c);
return k.irq_suppressed;
}

int main() {
std::printf("virtual-sx1262");

Expand Down Expand Up @@ -293,5 +321,19 @@ int main() {
vsx_destroy(c);
}

/* ---------------------------------------------------------------- */
CASE("a LoRa header is not counted against a mask that leaves FSK out");
/* The header was raised as HeaderValid and SyncWordValid together, and
* SyncWordValid is the GFSK modem's. No LoRa firmware enables it, so every
* header counted as suppressed and a correctly configured firmware looked as
* though it were still missing interrupts. The two masks are MeshCore's own:
* 1.17 enables PreambleDetected for receive and 1.16 does not. */
{
check(suppressed_over_two_frames(0x0276) == 0,
"1.17's mask misses nothing on a frame it receives");
check(suppressed_over_two_frames(0x0272) == 2,
"1.16's misses PreambleDetected, once per frame");
}

return report("");
}
5 changes: 3 additions & 2 deletions test/test_reception.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ int main() {
}
check((irq_flags(c) & IRQ_HEADER) != 0,
"while the carrier is on the air, HeaderValid is set");
check((irq_flags(c) & IRQ_SYNC_WORD) == 0,
"and SyncWordValid is not: that is the FSK modem's, and this is LoRa");

const uint8_t frame[] = {0x11, 0x00, 0x33, 0x0a, 0xc2, 0x1f};
vsx_deliver_frame(c, frame, sizeof(frame));
Expand All @@ -41,7 +43,6 @@ int main() {
check((f & IRQ_RX_DONE) != 0, "the delivered packet raises RxDone");
check((f & IRQ_HEADER) == 0, "HeaderValid is clear once the carrier has gone");
check((f & IRQ_PREAMBLE) == 0, "PreambleDetected is clear too");
check((f & IRQ_SYNC_WORD) == 0, "SyncWordValid is clear too");
vsx_destroy(c);
}

Expand Down Expand Up @@ -220,7 +221,7 @@ int main() {
/* ---------------------------------------------------------------- */
CASE("HeaderValid waits for the preamble, the SFD and the header block");
/* It was raised twelve symbols into the carrier whatever the preamble, so for
* MeshCore's sixteen-symbol preamble it came sixteen symbols early. A
* the chip's default sixteen-symbol preamble it came 16.25 symbols early. A
* firmware that sees preambles and one that does not answer differently only
* between PreambleDetected and HeaderValid, and that stretch was a third of
* its real width. At SF8 and 62.5 kHz a symbol is 4.096 ms, so the header ends
Expand Down
Loading