From 207c6559ab980150b0259d1514934d60d9d2f086 Mon Sep 17 00:00:00 2001 From: mikecarper Date: Wed, 16 Sep 2026 14:58:38 -0700 Subject: [PATCH] Use stock-compatible radio test exit --- docs/_javascript/preset_test.js | 56 ++++++++++++++++++++++++++++++- docs/_stylesheets/preset_test.css | 11 ++++++ docs/preset_test.md | 44 ++++++++++++++++++++---- test/test_preset_test.js | 28 ++++++++++++++++ 4 files changed, 132 insertions(+), 7 deletions(-) diff --git a/docs/_javascript/preset_test.js b/docs/_javascript/preset_test.js index 5de9023c..ed6038d7 100644 --- a/docs/_javascript/preset_test.js +++ b/docs/_javascript/preset_test.js @@ -9,6 +9,10 @@ bw: "500", sf: "8", cr: "7", + normalfreq: "910.525", + normalbw: "62.5", + normalsf: "7", + normalcr: "5", tx: "22", }); const VALID_BANDWIDTHS = Object.freeze([ @@ -108,6 +112,10 @@ const bw = strictNumber(raw.bw, "bw"); const sf = strictInteger(raw.sf, "sf"); const cr = strictInteger(raw.cr, "cr"); + const normalFreq = strictNumber(raw.normalfreq, "normalfreq"); + const normalBw = strictNumber(raw.normalbw, "normalbw"); + const normalSf = strictInteger(raw.normalsf, "normalsf"); + const normalCr = strictInteger(raw.normalcr, "normalcr"); const tx = strictSignedNumber(raw.tx, "tx"); if (endMs <= startMs) { @@ -129,6 +137,22 @@ if (cr < 5 || cr > 8) { throw new PresetTestError("cr must be between 5 and 8"); } + if (normalFreq < 150 || normalFreq > 2500) { + throw new PresetTestError("normalfreq must be between 150 and 2500 MHz"); + } + if (!VALID_BANDWIDTHS.some(function (allowed) { + return Math.abs(allowed - normalBw) < 0.01; + })) { + throw new PresetTestError( + "normalbw must be one of " + VALID_BANDWIDTHS.join(", ") + " kHz" + ); + } + if (normalSf < 5 || normalSf > 12) { + throw new PresetTestError("normalsf must be between 5 and 12"); + } + if (normalCr < 5 || normalCr > 8) { + throw new PresetTestError("normalcr must be between 5 and 8"); + } if (tx < -30 || tx > 60) { throw new PresetTestError("tx must be between -30 and 60 dBm"); } @@ -143,9 +167,15 @@ bw: bw, sf: sf, cr: cr, + normalFreq: normalFreq, + normalBw: normalBw, + normalSf: normalSf, + normalCr: normalCr, tx: tx, freqText: numberText(freq), bwText: numberText(bw), + normalFreqText: numberText(normalFreq), + normalBwText: numberText(normalBw), txText: numberText(tx), }); } @@ -179,6 +209,9 @@ function commandsFor(config, nowMs) { const tuple = [config.freqText, config.bwText, config.sf, config.cr].join(","); + const normalTuple = [ + config.normalFreqText, config.normalBwText, config.normalSf, config.normalCr, + ].join(","); const minutes = remainingMinutes(config, nowMs); return Object.freeze({ stockNow: "tempradio " + tuple + "," + minutes, @@ -191,7 +224,7 @@ "set radio2.cross on\nset tempradioat2 " + tuple + ",rxtx," + config.startEpoch + "," + config.endEpoch + "\nget tempradioat2", stockCancelBefore: "get tempradioat\ndel tempradioat all", - stockCancelDuring: "normalradio", + stockCancelDuring: "tempradio " + normalTuple + ",1", stockLeaveIn30: "tempradio " + tuple + ",30", companionCancelBefore: "get tempradioat2\ndel tempradioat2 all\nset radio2.cross auto", @@ -369,6 +402,10 @@ params.set("bw", values.bw); params.set("sf", values.sf); params.set("cr", values.cr); + params.set("normalfreq", values.normalfreq); + params.set("normalbw", values.normalbw); + params.set("normalsf", values.normalsf); + params.set("normalcr", values.normalcr); params.set("tx", values.tx); return configFromSearch("?" + params.toString()); } @@ -397,6 +434,10 @@ url.searchParams.set("bw", config.bwText); url.searchParams.set("sf", String(config.sf)); url.searchParams.set("cr", String(config.cr)); + url.searchParams.set("normalfreq", config.normalFreqText); + url.searchParams.set("normalbw", config.normalBwText); + url.searchParams.set("normalsf", String(config.normalSf)); + url.searchParams.set("normalcr", String(config.normalCr)); url.searchParams.set("tx", config.txText); return url.toString(); } @@ -472,6 +513,11 @@ setText(root, '[data-field="bw-display"]', config.bwText); setText(root, '[data-field="sf"]', String(config.sf)); setText(root, '[data-field="cr"]', String(config.cr)); + setText( + root, + '[data-field="normal-profile"]', + [config.normalFreqText, config.normalBwText, config.normalSf, config.normalCr].join(",") + ); setText(root, '[data-role="start-zoned"]', formatZoned(config.startMs, config.tz)); setText(root, '[data-role="end-zoned"]', formatZoned(config.endMs, config.tz)); setText(root, '[data-role="display-zone"]', config.tz); @@ -507,6 +553,10 @@ generator.elements.bw.value = config.bwText; generator.elements.sf.value = String(config.sf); generator.elements.cr.value = String(config.cr); + generator.elements.normalfreq.value = config.normalFreqText; + generator.elements.normalbw.value = config.normalBwText; + generator.elements.normalsf.value = String(config.normalSf); + generator.elements.normalcr.value = String(config.normalCr); generator.elements.tx.value = config.txText; const zoneList = root.querySelector("#preset-test-time-zones"); @@ -531,6 +581,10 @@ bw: generator.elements.bw.value, sf: generator.elements.sf.value, cr: generator.elements.cr.value, + normalfreq: generator.elements.normalfreq.value, + normalbw: generator.elements.normalbw.value, + normalsf: generator.elements.normalsf.value, + normalcr: generator.elements.normalcr.value, tx: generator.elements.tx.value, }); const url = configuredUrl( diff --git a/docs/_stylesheets/preset_test.css b/docs/_stylesheets/preset_test.css index c383a6c9..ffd5f0f6 100644 --- a/docs/_stylesheets/preset_test.css +++ b/docs/_stylesheets/preset_test.css @@ -278,6 +278,17 @@ font-weight: 700; } +.preset-test-generator-subheading { + grid-column: 1 / -1; + border-bottom: 1px solid var(--preset-border); + padding: 0.3rem 0 0.35rem; + color: var(--preset-cyan); + font-size: 0.74rem; + font-weight: 800; + letter-spacing: 0.05em; + text-transform: uppercase; +} + .preset-test-generator input, .preset-test-generator select { box-sizing: border-box; diff --git a/docs/preset_test.md b/docs/preset_test.md index dd886275..ea9d773e 100644 --- a/docs/preset_test.md +++ b/docs/preset_test.md @@ -47,7 +47,8 @@ tuple; no source edit is required.

The test will include bridges. If the Puget Sound area experiences a widespread power outage or loss of cellular service, we will end the - test early and revert to the normal 910.525 MHz channel. + test early and revert to the normal + 910.525,62.5,7,5 profile.

@@ -212,12 +213,14 @@ clock

Stock · while it is running

-

Leave now

+

Leave now · stock compatible

- +

- normalradio cancels pending and active primary TempRadio - windows and restores the saved primary tuple after its reply drains. + Stock MeshCore has no normalradio command. This starts a + one-minute temporary session on the normal return profile without + saving over the node's configuration. When that minute ends, the + node restores its saved radio settings.

Leave in 30 minutes

@@ -283,6 +286,7 @@ clock autocomplete="off" spellcheck="false"> Use an IANA name such as America/Los_Angeles or UTC. +
Test radio profile
+
Normal return profile
+ + + + @@ -361,7 +391,9 @@ clock Advanced use: start and end also accept ISO-8601 timestamps with explicit UTC offsets or Unix epoch seconds. The other URL parameters are tz, freq, bw, - sf, cr, and estimate-only tx. + sf, cr, normalfreq, + normalbw, normalsf, normalcr, and + estimate-only tx.

diff --git a/test/test_preset_test.js b/test/test_preset_test.js index dfcba65d..3d34dcab 100644 --- a/test/test_preset_test.js +++ b/test/test_preset_test.js @@ -18,6 +18,10 @@ assert.strictEqual(config.sf, 8); assert.strictEqual(config.cr, 7); assert.strictEqual(config.tz, "America/Los_Angeles"); assert.strictEqual(config.tx, 22); +assert.strictEqual(config.normalFreq, 910.525); +assert.strictEqual(config.normalBw, 62.5); +assert.strictEqual(config.normalSf, 7); +assert.strictEqual(config.normalCr, 5); assert.strictEqual(defaults.startEpoch, config.startEpoch); assert.strictEqual(defaults.endEpoch, config.endEpoch); assert.strictEqual(defaults.freq, config.freq); @@ -58,6 +62,10 @@ assert.strictEqual( "get tempradioat2" ); assert.strictEqual(commands.stockLeaveIn30, "tempradio 910.1,500,8,7,30"); +assert.strictEqual( + commands.stockCancelDuring, + "tempradio 910.525,62.5,7,5,1" +); assert.strictEqual( commands.companionLeaveIn30, "set radio2.cross on\n" + @@ -101,6 +109,10 @@ const generated = tool.configFromGenerator({ bw: "500", sf: "8", cr: "7", + normalfreq: "910.525", + normalbw: "62.5", + normalsf: "7", + normalcr: "5", tx: "22", }); assert.strictEqual(generated.startEpoch, config.startEpoch); @@ -136,6 +148,18 @@ assert.throws( () => tool.configFromSearch("?tx=61"), /tx must be between/ ); +assert.throws( + () => tool.configFromSearch("?normalbw=100"), + /normalbw must be one of/ +); + +const alternateReturn = tool.configFromSearch( + "?normalfreq=915.5&normalbw=125&normalsf=9&normalcr=6" +); +assert.strictEqual( + tool.commandsFor(alternateReturn, alternateReturn.startMs).stockCancelDuring, + "tempradio 915.5,125,9,6,1" +); const changed = tool.configFromSearch( "?start=1790035200&end=1790208000&freq=915.25&bw=125&sf=10&cr=5" @@ -154,6 +178,10 @@ assert.strictEqual(shared.searchParams.get("end"), "2026-09-24T00:00:00.000Z"); assert.strictEqual(shared.searchParams.get("freq"), "915.25"); assert.strictEqual(shared.searchParams.get("tz"), "America/Los_Angeles"); assert.strictEqual(shared.searchParams.get("tx"), "22"); +assert.strictEqual(shared.searchParams.get("normalfreq"), "910.525"); +assert.strictEqual(shared.searchParams.get("normalbw"), "62.5"); +assert.strictEqual(shared.searchParams.get("normalsf"), "7"); +assert.strictEqual(shared.searchParams.get("normalcr"), "5"); assert.strictEqual(shared.searchParams.has("stale"), false); assert.strictEqual(shared.hash, "#commands");