mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-10-14 20:07:23 +00:00
companionPrefsEncode()/serverPrefsEncode() never wrote the led_brightness byte that companionPrefsDecode()/serverPrefsDecode() already read, desyncing every field that follows it by one byte on the companion side (wifi_enabled/wifi_ssid/wifi_pwd) and silently truncating the server format by one byte. Caught by CI's ASan/UBSan run on the host regression suite: an out-of-bounds read/write in the pinned layout tests. Since led_brightness never existed upstream, nothing already accounted for it: fixing only the encoder still leaves the pinned byte tables and the documented 272/311-byte totals (COMPANION_PREFS_SIZE/ SERVER_PREFS_SIZE, used by the real prefs stores too, not just tests) one byte short of what the encoder now actually produces. - PrefsCodec.h: bump both size constants (272->273, 311->312). - PrefsCodec.cpp: add the missing w.put() in both encoders, mirroring the position already used by both decoders. - prefs_codec_tests.cpp: add led_brightness to both layout tables at its real file offset (shifts wifi_enabled/wifi_ssid/wifi_pwd by one byte on the companion side; server's is appended last, nothing else shifts), extend both historical-boundary length lists with the new full-format length, and give it a non-default value in sample() so the layout/roundtrip tests actually exercise it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
389 lines
15 KiB
C++
389 lines
15 KiB
C++
/*
|
|
* SPDX-License-Identifier: MIT
|
|
* PrefsCodec - byte codecs for the two binary prefs files. See PrefsCodec.h.
|
|
*/
|
|
|
|
#include "PrefsCodec.h"
|
|
#include <string.h>
|
|
|
|
namespace {
|
|
|
|
/* Sequential writer / reader over a byte buffer. Rd::get() mirrors fs_read()
|
|
* on a short file, as the server store used to read it: a field that straddles
|
|
* the end gets the bytes that exist. Rd::take() reads whole fields only. Past
|
|
* the end, both leave the field as it was. */
|
|
struct Wr {
|
|
uint8_t *b;
|
|
size_t off;
|
|
void put(const void *src, size_t n) { memcpy(b + off, src, n); off += n; }
|
|
void zeros(size_t n) { memset(b + off, 0, n); off += n; }
|
|
};
|
|
|
|
struct Rd {
|
|
const uint8_t *b;
|
|
size_t len;
|
|
size_t off;
|
|
void get(void *dst, size_t n) {
|
|
size_t k = off < len ? (len - off < n ? len - off : n) : 0;
|
|
memcpy(dst, b + off, k);
|
|
off += n;
|
|
}
|
|
void skip(size_t n) { off += n; }
|
|
/* Whole fields only: one that does not fit keeps its value. */
|
|
void take(void *dst, size_t n) {
|
|
if (off + n <= len) {
|
|
memcpy(dst, b + off, n);
|
|
}
|
|
off += n;
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
/* ── Companion: /lfs/new_prefs ─────────────────────────────────────── */
|
|
|
|
size_t companionPrefsEncode(const NodePrefs &p, uint8_t *buf, size_t cap)
|
|
{
|
|
if (cap < COMPANION_PREFS_SIZE) {
|
|
return 0;
|
|
}
|
|
Wr w = { buf, 0 };
|
|
|
|
/* 0-91: the layout upstream's new_prefs had */
|
|
w.put(&p.airtime_factor, 4);
|
|
w.put(p.node_name, 32);
|
|
w.zeros(4);
|
|
w.put(&p.node_lat, 8);
|
|
w.put(&p.node_lon, 8);
|
|
w.put(&p.freq, 4);
|
|
w.put(&p.sf, 1);
|
|
w.put(&p.cr, 1);
|
|
w.put(&p.client_repeat, 1);
|
|
w.put(&p.manual_add_contacts, 1);
|
|
w.put(&p.bw, 4);
|
|
w.put(&p.tx_power_dbm, 1);
|
|
w.put(&p.telemetry_mode_base, 1);
|
|
w.put(&p.telemetry_mode_loc, 1);
|
|
w.put(&p.telemetry_mode_env, 1);
|
|
w.put(&p.rx_delay_base, 4);
|
|
w.put(&p.advert_loc_policy, 1);
|
|
w.put(&p.multi_acks, 1);
|
|
w.put(&p.path_hash_mode, 1);
|
|
w.zeros(1);
|
|
w.put(&p.ble_pin, 4);
|
|
w.put(&p.buzzer_quiet, 1);
|
|
w.put(&p.gps_enabled, 1);
|
|
w.put(&p.gps_interval, 4);
|
|
w.put(&p.autoadd_config, 1);
|
|
w.put(&p.autoadd_max_hops, 1);
|
|
/* 92-: ZephCore. Upstream put default_scope_name at 93. */
|
|
w.put(&p.rx_boost, 1);
|
|
w.put(&p.leds_disabled, 1);
|
|
w.put(&p._reserved_apc_enabled, 1); /* 94-95: APC, removed in 1.16.6 */
|
|
w.put(&p._reserved_apc_margin, 1);
|
|
w.put(p.default_scope_name, 31);
|
|
w.put(p.default_scope_key, 16);
|
|
w.put(&p.ble_disabled, 1);
|
|
w.put(&p.display_brightness, 1);
|
|
w.put(&p.wake_on_msg, 1);
|
|
w.put(&p.screen_off_secs, 2);
|
|
w.put(&p.auto_shutdown_mv, 2);
|
|
w.put(&p.rx_duty_cycle, 1);
|
|
w.put(&p.meshtimesync, 1);
|
|
w.put(&p.v_contact_enabled, 1);
|
|
w.put(&p.v_battery_alert_mv, 2);
|
|
w.put(&p.cad_auto, 1);
|
|
w.put(&p.cad_offset, 1);
|
|
w.put(&p.probe_interval, 1);
|
|
w.put(&p.cad_busycap, 1);
|
|
w.put(&p.adc_multiplier, 4);
|
|
w.put(p.extra_sf, EXTRA_SF_MAX);
|
|
w.put(&p.v_contact_flags, 1);
|
|
w.put(&p.fem_rxgain, 1);
|
|
w.put(&p.display_rotate, 1);
|
|
w.put(&p.input_rotate, 1);
|
|
w.put(&p.cad_base, 1);
|
|
w.put(&p.tz_offset, 1);
|
|
w.put(&p.leds_radio_mode, 1);
|
|
w.put(&p.leds_hb_mode, 1);
|
|
w.put(&p.led_brightness, 1);
|
|
w.put(&p.wifi_enabled, 1);
|
|
w.put(p.wifi_ssid, sizeof(p.wifi_ssid));
|
|
w.put(p.wifi_pwd, sizeof(p.wifi_pwd));
|
|
return w.off;
|
|
}
|
|
|
|
bool companionPrefsDecode(NodePrefs &prefs, const uint8_t *buf, size_t len)
|
|
{
|
|
/* gps_interval ends at 90: anything shorter is not a prefs file */
|
|
if (len < 90) {
|
|
return false;
|
|
}
|
|
/* Committed only if the radio preset is plausible: a file in some other
|
|
* layout must not become our radio settings. */
|
|
NodePrefs p = prefs;
|
|
Rd r = { buf, len, 0 };
|
|
|
|
r.take(&p.airtime_factor, 4);
|
|
r.take(p.node_name, 32);
|
|
r.skip(4);
|
|
r.take(&p.node_lat, 8);
|
|
r.take(&p.node_lon, 8);
|
|
r.take(&p.freq, 4);
|
|
r.take(&p.sf, 1);
|
|
r.take(&p.cr, 1);
|
|
r.take(&p.client_repeat, 1);
|
|
r.take(&p.manual_add_contacts, 1);
|
|
r.take(&p.bw, 4);
|
|
if (p.freq < ZC_RADIO_FREQ_MIN_MHZ || p.freq > ZC_RADIO_FREQ_MAX_MHZ ||
|
|
p.sf < 5 || p.sf > 12 ||
|
|
p.bw < ZC_RADIO_BW_MIN_KHZ || p.bw > ZC_RADIO_BW_MAX_KHZ) {
|
|
return false;
|
|
}
|
|
r.take(&p.tx_power_dbm, 1);
|
|
r.take(&p.telemetry_mode_base, 1);
|
|
r.take(&p.telemetry_mode_loc, 1);
|
|
r.take(&p.telemetry_mode_env, 1);
|
|
r.take(&p.rx_delay_base, 4);
|
|
r.take(&p.advert_loc_policy, 1);
|
|
r.take(&p.multi_acks, 1);
|
|
r.take(&p.path_hash_mode, 1);
|
|
r.skip(1);
|
|
r.take(&p.ble_pin, 4);
|
|
r.take(&p.buzzer_quiet, 1);
|
|
r.take(&p.gps_enabled, 1);
|
|
r.take(&p.gps_interval, 4);
|
|
r.take(&p.autoadd_config, 1);
|
|
r.take(&p.autoadd_max_hops, 1);
|
|
/* Each field below is newer than some deployed files: one past the end
|
|
* keeps the caller's initNodePrefs() default. */
|
|
r.take(&p.rx_boost, 1);
|
|
r.take(&p.leds_disabled, 1);
|
|
r.take(&p._reserved_apc_enabled, 1);
|
|
r.take(&p._reserved_apc_margin, 1);
|
|
r.take(p.default_scope_name, 31);
|
|
r.take(p.default_scope_key, 16);
|
|
r.take(&p.ble_disabled, 1);
|
|
r.take(&p.display_brightness, 1);
|
|
r.take(&p.wake_on_msg, 1);
|
|
r.take(&p.screen_off_secs, 2);
|
|
r.take(&p.auto_shutdown_mv, 2);
|
|
r.take(&p.rx_duty_cycle, 1);
|
|
r.take(&p.meshtimesync, 1);
|
|
r.take(&p.v_contact_enabled, 1);
|
|
r.take(&p.v_battery_alert_mv, 2);
|
|
r.take(&p.cad_auto, 1);
|
|
r.take(&p.cad_offset, 1);
|
|
r.take(&p.probe_interval, 1);
|
|
r.take(&p.cad_busycap, 1);
|
|
r.take(&p.adc_multiplier, 4);
|
|
r.take(p.extra_sf, EXTRA_SF_MAX);
|
|
r.take(&p.v_contact_flags, 1);
|
|
r.take(&p.fem_rxgain, 1);
|
|
r.take(&p.display_rotate, 1);
|
|
r.take(&p.input_rotate, 1);
|
|
/* cad_base 0 = "no base recorded": setCadParams() then leaves the stored
|
|
* offset alone. */
|
|
r.take(&p.cad_base, 1);
|
|
r.take(&p.tz_offset, 1);
|
|
r.take(&p.leds_radio_mode, 1);
|
|
r.take(&p.leds_hb_mode, 1);
|
|
/* led_brightness (ZephCore extension, since 1.17.4). Absent in files
|
|
* written before this field existed; the no-op EOF read leaves the
|
|
* initNodePrefs() default ZEPHCORE_LED_DEFAULT_BRIGHTNESS_PCT (10%),
|
|
* same value a fresh node already showed before this was persisted. */
|
|
r.take(&p.led_brightness, 1);
|
|
r.take(&p.wifi_enabled, 1);
|
|
r.take(p.wifi_ssid, sizeof(p.wifi_ssid));
|
|
r.take(p.wifi_pwd, sizeof(p.wifi_pwd));
|
|
|
|
/* The one validator: bounds, NaNs, and the unterminated char fields */
|
|
sanitizeNodePrefs(&p);
|
|
prefs = p;
|
|
return true;
|
|
}
|
|
|
|
/* ── Server roles: /lfs/repeater/prefs ─────────────────────────────── */
|
|
|
|
size_t serverPrefsEncode(const NodePrefs &prefs, uint8_t *buf, size_t cap)
|
|
{
|
|
if (cap < SERVER_PREFS_SIZE) {
|
|
return 0;
|
|
}
|
|
Wr w = { buf, 0 };
|
|
|
|
/* Same order as Arduino CommonCLI up to offset 290 */
|
|
w.put(&prefs.airtime_factor, sizeof(prefs.airtime_factor));
|
|
w.put(&prefs.node_name, sizeof(prefs.node_name));
|
|
w.zeros(4);
|
|
w.put(&prefs.node_lat, sizeof(prefs.node_lat));
|
|
w.put(&prefs.node_lon, sizeof(prefs.node_lon));
|
|
w.put(&prefs.password, sizeof(prefs.password));
|
|
w.put(&prefs.freq, sizeof(prefs.freq));
|
|
w.put(&prefs.tx_power_dbm, sizeof(prefs.tx_power_dbm));
|
|
w.put(&prefs.disable_fwd, sizeof(prefs.disable_fwd));
|
|
w.put(&prefs.advert_interval, sizeof(prefs.advert_interval));
|
|
w.zeros(1);
|
|
w.put(&prefs.rx_delay_base, sizeof(prefs.rx_delay_base));
|
|
w.put(&prefs.tx_delay_factor, sizeof(prefs.tx_delay_factor));
|
|
w.put(&prefs.guest_password, sizeof(prefs.guest_password));
|
|
w.put(&prefs.direct_tx_delay_factor, sizeof(prefs.direct_tx_delay_factor));
|
|
w.put(&prefs.backoff_multiplier, sizeof(prefs.backoff_multiplier));
|
|
w.put(&prefs.sf, sizeof(prefs.sf));
|
|
w.put(&prefs.cr, sizeof(prefs.cr));
|
|
w.put(&prefs.allow_read_only, sizeof(prefs.allow_read_only));
|
|
w.put(&prefs.multi_acks, sizeof(prefs.multi_acks));
|
|
w.put(&prefs.bw, sizeof(prefs.bw));
|
|
/* 120: leds_disabled, magic-encoded (was agc_reset_interval). */
|
|
{
|
|
uint8_t leds_byte = prefs.leds_disabled ? LEDS_PREF_OFF : LEDS_PREF_ON;
|
|
w.put(&leds_byte, sizeof(leds_byte));
|
|
}
|
|
w.put(&prefs.path_hash_mode, sizeof(prefs.path_hash_mode));
|
|
w.put(&prefs.loop_detect, sizeof(prefs.loop_detect));
|
|
w.zeros(1);
|
|
w.put(&prefs.flood_max, sizeof(prefs.flood_max));
|
|
w.put(&prefs.flood_advert_interval, sizeof(prefs.flood_advert_interval));
|
|
w.put(&prefs.interference_threshold, sizeof(prefs.interference_threshold));
|
|
w.zeros(25); // bridge settings
|
|
w.put(&prefs.powersaving_enabled, sizeof(prefs.powersaving_enabled));
|
|
w.zeros(3);
|
|
w.put(&prefs.gps_enabled, sizeof(prefs.gps_enabled));
|
|
w.put(&prefs.gps_interval, sizeof(prefs.gps_interval));
|
|
w.put(&prefs.advert_loc_policy, sizeof(prefs.advert_loc_policy));
|
|
w.put(&prefs.discovery_mod_timestamp, sizeof(prefs.discovery_mod_timestamp));
|
|
w.put(&prefs.adc_multiplier, sizeof(prefs.adc_multiplier));
|
|
w.put(prefs.owner_info, sizeof(prefs.owner_info));
|
|
/* ZephCore extensions */
|
|
w.put(&prefs.rx_boost, sizeof(prefs.rx_boost));
|
|
w.put(&prefs.rx_duty_cycle, sizeof(prefs.rx_duty_cycle));
|
|
/* RESERVED — formerly apc_enabled / apc_margin (removed in 1.16.6).
|
|
* Written back unchanged to hold the layout. */
|
|
w.put(&prefs._reserved_apc_enabled, sizeof(prefs._reserved_apc_enabled));
|
|
w.put(&prefs._reserved_apc_margin, sizeof(prefs._reserved_apc_margin));
|
|
/* Flood hop-ceiling extensions (offsets 294-295) */
|
|
w.put(&prefs.flood_max_unscoped, sizeof(prefs.flood_max_unscoped));
|
|
w.put(&prefs.flood_max_advert, sizeof(prefs.flood_max_advert));
|
|
/* Mesh time sync on/off (offset 296) */
|
|
w.put(&prefs.meshtimesync, sizeof(prefs.meshtimesync));
|
|
/* Adaptive CAD (offsets 297-300) */
|
|
w.put(&prefs.cad_auto, sizeof(prefs.cad_auto));
|
|
w.put(&prefs.cad_offset, sizeof(prefs.cad_offset));
|
|
w.put(&prefs.probe_interval, sizeof(prefs.probe_interval));
|
|
w.put(&prefs.cad_busycap, sizeof(prefs.cad_busycap));
|
|
/* LR2021 side-detector SFs (offsets 301-303) */
|
|
w.put(prefs.extra_sf, sizeof(prefs.extra_sf));
|
|
/* External FEM RX gain (offset 304) */
|
|
w.put(&prefs.fem_rxgain, sizeof(prefs.fem_rxgain));
|
|
/* Mounting orientation (offsets 305-306) */
|
|
w.put(&prefs.display_rotate, sizeof(prefs.display_rotate));
|
|
w.put(&prefs.input_rotate, sizeof(prefs.input_rotate));
|
|
/* Family base detPeak cad_offset was learned against (offset 307) */
|
|
w.put(&prefs.cad_base, sizeof(prefs.cad_base));
|
|
/* Display timezone offset (offset 308) — signed whole hours from UTC,
|
|
* applied only when formatting the on-device clock */
|
|
w.put(&prefs.tz_offset, sizeof(prefs.tz_offset));
|
|
/* LED activity/heartbeat modes (offsets 309-310) */
|
|
w.put(&prefs.leds_radio_mode, sizeof(prefs.leds_radio_mode));
|
|
w.put(&prefs.leds_hb_mode, sizeof(prefs.leds_hb_mode));
|
|
/* led_brightness (ZephCore extension, since 1.17.4), offset 311. */
|
|
w.put(&prefs.led_brightness, sizeof(prefs.led_brightness));
|
|
return w.off;
|
|
}
|
|
|
|
void serverPrefsDecode(NodePrefs &prefs, const uint8_t *buf, size_t len)
|
|
{
|
|
Rd r = { buf, len, 0 };
|
|
|
|
/* Same order as Arduino CommonCLI up to offset 290 */
|
|
r.get(&prefs.airtime_factor, sizeof(prefs.airtime_factor));
|
|
r.get(&prefs.node_name, sizeof(prefs.node_name));
|
|
r.skip(4);
|
|
r.get(&prefs.node_lat, sizeof(prefs.node_lat));
|
|
r.get(&prefs.node_lon, sizeof(prefs.node_lon));
|
|
r.get(&prefs.password, sizeof(prefs.password));
|
|
r.get(&prefs.freq, sizeof(prefs.freq));
|
|
r.get(&prefs.tx_power_dbm, sizeof(prefs.tx_power_dbm));
|
|
r.get(&prefs.disable_fwd, sizeof(prefs.disable_fwd));
|
|
r.get(&prefs.advert_interval, sizeof(prefs.advert_interval));
|
|
r.skip(1);
|
|
r.get(&prefs.rx_delay_base, sizeof(prefs.rx_delay_base));
|
|
r.get(&prefs.tx_delay_factor, sizeof(prefs.tx_delay_factor));
|
|
r.get(&prefs.guest_password, sizeof(prefs.guest_password));
|
|
r.get(&prefs.direct_tx_delay_factor, sizeof(prefs.direct_tx_delay_factor));
|
|
r.get(&prefs.backoff_multiplier, sizeof(prefs.backoff_multiplier));
|
|
r.get(&prefs.sf, sizeof(prefs.sf));
|
|
r.get(&prefs.cr, sizeof(prefs.cr));
|
|
r.get(&prefs.allow_read_only, sizeof(prefs.allow_read_only));
|
|
r.get(&prefs.multi_acks, sizeof(prefs.multi_acks));
|
|
r.get(&prefs.bw, sizeof(prefs.bw));
|
|
/* 120: leds_disabled, magic-encoded. Formerly agc_reset_interval — see the
|
|
* LEDS_PREF_* comment in NodePrefs.h for why this is not a bare 0/1.
|
|
* leds_byte stays 0 (→ LEDs on) if the file is short. */
|
|
uint8_t leds_byte = 0;
|
|
r.get(&leds_byte, sizeof(leds_byte));
|
|
r.get(&prefs.path_hash_mode, sizeof(prefs.path_hash_mode));
|
|
r.get(&prefs.loop_detect, sizeof(prefs.loop_detect));
|
|
r.skip(1);
|
|
r.get(&prefs.flood_max, sizeof(prefs.flood_max));
|
|
r.get(&prefs.flood_advert_interval, sizeof(prefs.flood_advert_interval));
|
|
r.get(&prefs.interference_threshold, sizeof(prefs.interference_threshold));
|
|
r.skip(25); // bridge settings
|
|
r.get(&prefs.powersaving_enabled, sizeof(prefs.powersaving_enabled));
|
|
r.skip(3);
|
|
r.get(&prefs.gps_enabled, sizeof(prefs.gps_enabled));
|
|
r.get(&prefs.gps_interval, sizeof(prefs.gps_interval));
|
|
r.get(&prefs.advert_loc_policy, sizeof(prefs.advert_loc_policy));
|
|
r.get(&prefs.discovery_mod_timestamp, sizeof(prefs.discovery_mod_timestamp));
|
|
r.get(&prefs.adc_multiplier, sizeof(prefs.adc_multiplier));
|
|
r.get(prefs.owner_info, sizeof(prefs.owner_info));
|
|
/* ZephCore extensions — absent in old files; a field past the end keeps
|
|
* the caller's initNodePrefs() default. */
|
|
r.get(&prefs.rx_boost, sizeof(prefs.rx_boost));
|
|
r.get(&prefs.rx_duty_cycle, sizeof(prefs.rx_duty_cycle));
|
|
/* RESERVED — formerly apc_enabled / apc_margin (APC, removed in 1.16.6).
|
|
* Still consumed so the fields after them stay at their stored offsets. */
|
|
r.get(&prefs._reserved_apc_enabled, sizeof(prefs._reserved_apc_enabled));
|
|
r.get(&prefs._reserved_apc_margin, sizeof(prefs._reserved_apc_margin));
|
|
r.get(&prefs.flood_max_unscoped, sizeof(prefs.flood_max_unscoped));
|
|
r.get(&prefs.flood_max_advert, sizeof(prefs.flood_max_advert));
|
|
r.get(&prefs.meshtimesync, sizeof(prefs.meshtimesync));
|
|
r.get(&prefs.cad_auto, sizeof(prefs.cad_auto));
|
|
r.get(&prefs.cad_offset, sizeof(prefs.cad_offset));
|
|
r.get(&prefs.probe_interval, sizeof(prefs.probe_interval));
|
|
r.get(&prefs.cad_busycap, sizeof(prefs.cad_busycap));
|
|
r.get(prefs.extra_sf, sizeof(prefs.extra_sf));
|
|
r.get(&prefs.fem_rxgain, sizeof(prefs.fem_rxgain));
|
|
r.get(&prefs.display_rotate, sizeof(prefs.display_rotate));
|
|
r.get(&prefs.input_rotate, sizeof(prefs.input_rotate));
|
|
/* cad_base 0 = "no base recorded": setCadParams() then leaves the stored
|
|
* offset alone, since a node upgrading across a table change cannot know
|
|
* which base its offset came from. */
|
|
r.get(&prefs.cad_base, sizeof(prefs.cad_base));
|
|
r.get(&prefs.tz_offset, sizeof(prefs.tz_offset));
|
|
r.get(&prefs.leds_radio_mode, sizeof(prefs.leds_radio_mode));
|
|
r.get(&prefs.leds_hb_mode, sizeof(prefs.leds_hb_mode));
|
|
/* led_brightness (ZephCore extension, since 1.17.4). Absent in files
|
|
* written before this field existed; the no-op EOF read leaves the
|
|
* initNodePrefs() default ZEPHCORE_LED_DEFAULT_BRIGHTNESS_PCT (10%),
|
|
* same value a fresh node already showed before this was persisted. */
|
|
r.get(&prefs.led_brightness, sizeof(prefs.led_brightness));
|
|
|
|
/* Only the explicit "off" magic disables LEDs; a legacy AGC interval or an
|
|
* unwritten byte both mean "on". */
|
|
prefs.leds_disabled = (leds_byte == LEDS_PREF_OFF) ? 1 : 0;
|
|
|
|
if (prefs.freq < ZC_RADIO_FREQ_MIN_MHZ || prefs.freq > ZC_RADIO_FREQ_MAX_MHZ ||
|
|
prefs.sf < 5 || prefs.sf > 12 ||
|
|
prefs.bw < ZC_RADIO_BW_MIN_KHZ || prefs.bw > ZC_RADIO_BW_MAX_KHZ) {
|
|
prefs.freq = mesh::LoRaConfig::FREQ_MHZ;
|
|
prefs.bw = mesh::LoRaConfig::BANDWIDTH_KHZ;
|
|
prefs.sf = mesh::LoRaConfig::SPREADING_FACTOR;
|
|
prefs.cr = mesh::LoRaConfig::CODING_RATE;
|
|
prefs.tx_power_dbm = mesh::LoRaConfig::TX_POWER_DBM;
|
|
}
|
|
/* Bounds, NaNs, and the char fields, which the file stores without
|
|
* terminators. */
|
|
sanitizeNodePrefs(&prefs);
|
|
}
|