mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-01 21:08:19 +00:00
sync with dev
This commit is contained in:
@@ -203,7 +203,7 @@ Path_len byte:
|
||||
|
||||
**Direct routing**: Packet carries a source-routed `path[]`. Each relay node checks if the first path hash matches its own identity, removes itself, and forwards. Path is built from previous flood packets' accumulated hashes.
|
||||
|
||||
**Deduplication**: `SimpleMeshTables` maintains a circular buffer of 128 packet hashes (8 bytes each, SHA-256 truncated) and 64 ACK CRCs. `hasSeen()` prevents duplicate processing and retransmission.
|
||||
**Deduplication**: `SimpleMeshTables` maintains a circular buffer of 160 packet hashes (8 bytes each, SHA-256 truncated); ACKs are deduped through the same packet-hash path. `wasSeen()` is a pure query; call sites insert explicitly via `markSeen()` to prevent duplicate processing and retransmission.
|
||||
|
||||
### 4.5 Dispatcher Scheduling
|
||||
|
||||
|
||||
@@ -247,7 +247,7 @@ Changes are persisted immediately unless noted. Some require a reboot.
|
||||
| `set multi.acks <0\|1>` | | Enable extra ACK transmits |
|
||||
| `set path.hash.mode <mode>` | 0, 1, or 2 | Path hashing algorithm |
|
||||
| `set loop.detect <mode>` | `off`, `minimal`, `moderate`, `strict` | Loop detection sensitivity |
|
||||
| `set radio.rxgain <0\|1\|on\|off>` | | RX gain boost *(reboot required)* |
|
||||
| `set radio.rxgain <0\|1\|on\|off>` | | RX gain boost, applied live. Replies `Error: unsupported` on radios without RX boost (SX127x); the pref is still saved. |
|
||||
| `set rxduty <0\|1\|on\|off>` | | RX duty cycle mode *(reboot required)*. Window timing auto-sized per SF/BW/preamble from the SX126x datasheet constraints (boot log line `rxduty:` shows the result). Zero-loss guarantee assumes senders on preamble-32 firmware (current MeshCore at SF≤8); legacy preamble-16 senders are only caught ~50% worst-phase — keep off until the local mesh has converted. Presets with 16-symbol preambles (SF≥9) fall back to continuous RX automatically. |
|
||||
| `set adc.multiplier <mult>` | (0 = use board default) | Battery voltage ADC calibration multiplier |
|
||||
| `set prv.key <hex>` | 64-char hex (32-byte key) | Replace private key; derive new identity *(reboot to apply)* |
|
||||
|
||||
@@ -881,7 +881,7 @@ void LoRaRadioBase::enableRxDutyCycle(bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
void LoRaRadioBase::setRxBoost(bool enable)
|
||||
bool LoRaRadioBase::setRxBoost(bool enable)
|
||||
{
|
||||
_rx_boost_enabled = enable;
|
||||
LOG_INF("RX boost %s (+3dB sensitivity, +2mA)",
|
||||
@@ -889,6 +889,7 @@ void LoRaRadioBase::setRxBoost(bool enable)
|
||||
if (atomic_get(&_in_recv_mode)) {
|
||||
hwSetRxBoost(enable);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} /* namespace mesh */
|
||||
|
||||
@@ -78,7 +78,8 @@ public:
|
||||
/* Power saving */
|
||||
void enableRxDutyCycle(bool enable);
|
||||
bool isRxDutyCycleEnabled() const { return _rx_duty_cycle_enabled; }
|
||||
void setRxBoost(bool enable);
|
||||
/* Returns false when the chip has no RX boost feature (SX127x). */
|
||||
virtual bool setRxBoost(bool enable);
|
||||
bool isRxBoostEnabled() const { return _rx_boost_enabled; }
|
||||
|
||||
/* Duty-cycle preamble false-positive counter.
|
||||
|
||||
@@ -21,6 +21,13 @@ public:
|
||||
|
||||
void begin() override;
|
||||
|
||||
/* SX127x has no RX boost feature — report unsupported so the
|
||||
* radio.rxgain CLI can reply "Error: unsupported". */
|
||||
bool setRxBoost(bool enable) override {
|
||||
(void)enable;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
/* Hardware primitives */
|
||||
bool hwConfigure(const struct lora_modem_config &cfg) override;
|
||||
|
||||
@@ -1146,6 +1146,10 @@ void RepeaterMesh::setTxPower(int8_t power_dbm) {
|
||||
radio_set_tx_power(power_dbm);
|
||||
}
|
||||
|
||||
bool RepeaterMesh::setRxBoostedGain(bool enable) {
|
||||
return getRadioDriver(_radio).setRxBoost(enable);
|
||||
}
|
||||
|
||||
void RepeaterMesh::formatNeighborsReply(char* reply) {
|
||||
char* dp = reply;
|
||||
|
||||
|
||||
@@ -233,6 +233,7 @@ public:
|
||||
void eraseLogFile() override;
|
||||
void dumpLogFile() override;
|
||||
void setTxPower(int8_t power_dbm) override;
|
||||
bool setRxBoostedGain(bool enable) override;
|
||||
void formatNeighborsReply(char* reply) override;
|
||||
void removeNeighbor(const uint8_t* pubkey, int key_len) override;
|
||||
void formatStatsReply(char* reply) override;
|
||||
|
||||
@@ -918,6 +918,10 @@ void RoomServerMesh::setTxPower(int8_t power_dbm) {
|
||||
radio_set_tx_power(power_dbm);
|
||||
}
|
||||
|
||||
bool RoomServerMesh::setRxBoostedGain(bool enable) {
|
||||
return getRadioDriver(_radio).setRxBoost(enable);
|
||||
}
|
||||
|
||||
/* A room server keeps no neighbour table (it is not a repeater). */
|
||||
void RoomServerMesh::formatNeighborsReply(char* reply) {
|
||||
strcpy(reply, "not supported");
|
||||
|
||||
@@ -182,6 +182,7 @@ public:
|
||||
void eraseLogFile() override;
|
||||
void dumpLogFile() override;
|
||||
void setTxPower(int8_t power_dbm) override;
|
||||
bool setRxBoostedGain(bool enable) override;
|
||||
void formatNeighborsReply(char* reply) override;
|
||||
void formatStatsReply(char* reply) override;
|
||||
void formatRadioStatsReply(char* reply) override;
|
||||
|
||||
@@ -895,9 +895,15 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
|
||||
else if (memcmp(arg, "off", 3) == 0) val = 0;
|
||||
else if (arg[0] == '0' || arg[0] == '1') val = atoi(arg);
|
||||
if (val == 0 || val == 1) {
|
||||
/* Always save (upstream f3d4d8cd), then apply live and
|
||||
* report when the radio has no RX boost feature. */
|
||||
_prefs->rx_boost = (uint8_t)val;
|
||||
savePrefs();
|
||||
snprintf(reply, CLI_REPLY_SIZE, "OK - radio.rxgain=%d (reboot to apply)", _prefs->rx_boost);
|
||||
if (_callbacks->setRxBoostedGain(val == 1)) {
|
||||
snprintf(reply, CLI_REPLY_SIZE, "OK - radio.rxgain=%d", _prefs->rx_boost);
|
||||
} else {
|
||||
strcpy(reply, "Error: unsupported");
|
||||
}
|
||||
} else {
|
||||
strcpy(reply, "Error: must be 0, 1, on, or off");
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ public:
|
||||
virtual void eraseLogFile() = 0;
|
||||
virtual void dumpLogFile() = 0;
|
||||
virtual void setTxPower(int8_t power_dbm) = 0;
|
||||
/* Apply RX boosted gain live; returns false when the radio has no
|
||||
* RX boost feature (upstream PR #2844 semantics). */
|
||||
virtual bool setRxBoostedGain(bool enable) { (void)enable; return false; }
|
||||
/* Repeater-specific — default replies keep companion builds clean.
|
||||
* Repeater overrides all four; companions get "not available". */
|
||||
virtual void formatNeighborsReply(char* reply) { strcpy(reply, "not available"); }
|
||||
|
||||
@@ -21,7 +21,8 @@ struct GroupChannel {
|
||||
|
||||
class MeshTables {
|
||||
public:
|
||||
virtual bool hasSeen(const Packet *packet) = 0;
|
||||
virtual bool wasSeen(const Packet *packet) = 0; /* pure query, no insert */
|
||||
virtual void markSeen(const Packet *packet) = 0; /* explicit insert */
|
||||
virtual void clear(const Packet *packet) = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
_direct_dups = _flood_dups = 0;
|
||||
}
|
||||
|
||||
bool hasSeen(const Packet *packet) override {
|
||||
bool wasSeen(const Packet *packet) override {
|
||||
uint8_t hash[MAX_HASH_SIZE];
|
||||
packet->calculatePacketHash(hash);
|
||||
const uint8_t *sp = _hashes;
|
||||
@@ -38,9 +38,14 @@ public:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void markSeen(const Packet *packet) override {
|
||||
uint8_t hash[MAX_HASH_SIZE];
|
||||
packet->calculatePacketHash(hash);
|
||||
memcpy(&_hashes[_next_idx * MAX_HASH_SIZE], hash, MAX_HASH_SIZE);
|
||||
_next_idx = (_next_idx + 1) % MAX_PACKET_HASHES;
|
||||
return false;
|
||||
}
|
||||
|
||||
void clear(const Packet *packet) override {
|
||||
|
||||
@@ -431,6 +431,12 @@ void Dispatcher::checkSend()
|
||||
len += outbound->payload_len;
|
||||
|
||||
uint32_t max_airtime = _radio->getEstAirtimeFor(len) * 3 / 2;
|
||||
/* Short packets (ACKs) have est airtimes small enough that
|
||||
* IRQ/work-queue latency alone can blow the watchdog and clip
|
||||
* the TX mid-air (upstream 4f8cb8db: 200ms est floor, x1.5). */
|
||||
if (max_airtime < 300) {
|
||||
max_airtime = 300;
|
||||
}
|
||||
outbound_start = now;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZEPHCORE_PACKET_LOGGING)
|
||||
|
||||
+27
-16
@@ -146,7 +146,8 @@ DispatcherAction Mesh::forwardMultipartDirect(Packet *pkt)
|
||||
tmp.path_len = Packet::copyPath(tmp.path, pkt->path, MAX_PATH_SIZE, pkt->path_len);
|
||||
tmp.payload_len = pkt->payload_len - 1;
|
||||
memcpy(tmp.payload, &pkt->payload[1], tmp.payload_len);
|
||||
if (!_tables->hasSeen(&tmp)) {
|
||||
if (!_tables->wasSeen(&tmp)) {
|
||||
_tables->markSeen(&tmp);
|
||||
removeSelfFromPath(&tmp);
|
||||
routeDirectRecvAcks(&tmp, ((uint32_t)remaining + 1) * 300);
|
||||
}
|
||||
@@ -205,7 +206,8 @@ DispatcherAction Mesh::onRecvPacket(Packet *pkt)
|
||||
uint16_t offset = (uint16_t)pkt->path_len << path_sz;
|
||||
if (offset >= len) {
|
||||
onTraceRecv(pkt, trace_tag, auth_code, flags, pkt->path, &pkt->payload[i], len);
|
||||
} else if (self_id.isHashMatch(&pkt->payload[i + offset], 1 << path_sz) && allowPacketForward(pkt) && !_tables->hasSeen(pkt)) {
|
||||
} else if (self_id.isHashMatch(&pkt->payload[i + offset], 1 << path_sz) && allowPacketForward(pkt) && !_tables->wasSeen(pkt)) {
|
||||
_tables->markSeen(pkt);
|
||||
pkt->path[pkt->path_len++] = (int8_t)(pkt->getSNR() * 4);
|
||||
uint32_t d = getDirectRetransmitDelay(pkt);
|
||||
return ACTION_RETRANSMIT_DELAYED(5, d);
|
||||
@@ -241,13 +243,15 @@ DispatcherAction Mesh::onRecvPacket(Packet *pkt)
|
||||
return forwardMultipartDirect(pkt);
|
||||
}
|
||||
if (pkt->getPayloadType() == PAYLOAD_TYPE_ACK) {
|
||||
if (!_tables->hasSeen(pkt)) {
|
||||
if (!_tables->wasSeen(pkt)) {
|
||||
_tables->markSeen(pkt);
|
||||
removeSelfFromPath(pkt);
|
||||
routeDirectRecvAcks(pkt, 0);
|
||||
}
|
||||
return ACTION_RELEASE;
|
||||
}
|
||||
if (!_tables->hasSeen(pkt)) {
|
||||
if (!_tables->wasSeen(pkt)) {
|
||||
_tables->markSeen(pkt);
|
||||
removeSelfFromPath(pkt);
|
||||
return ACTION_RETRANSMIT_DELAYED(0, getDirectRetransmitDelay(pkt));
|
||||
}
|
||||
@@ -282,7 +286,8 @@ DispatcherAction Mesh::onRecvPacket(Packet *pkt)
|
||||
} else {
|
||||
uint32_t ack_crc;
|
||||
memcpy(&ack_crc, pkt->payload, 4);
|
||||
if (!_tables->hasSeen(pkt)) {
|
||||
if (!_tables->wasSeen(pkt)) {
|
||||
_tables->markSeen(pkt);
|
||||
onAckRecv(pkt, ack_crc);
|
||||
action = routeRecvPacket(pkt);
|
||||
}
|
||||
@@ -300,7 +305,8 @@ DispatcherAction Mesh::onRecvPacket(Packet *pkt)
|
||||
uint8_t *macAndData = &pkt->payload[i];
|
||||
if (i + CIPHER_MAC_SIZE >= (int)pkt->payload_len) {
|
||||
LOG_WRN("onRecvPacket: incomplete packet (i=%d, payload_len=%d)", i, pkt->payload_len);
|
||||
} else if (!_tables->hasSeen(pkt)) {
|
||||
} else if (!_tables->wasSeen(pkt)) {
|
||||
_tables->markSeen(pkt);
|
||||
if (self_id.isHashMatch(&dest_hash)) {
|
||||
int num = searchPeersByHash(&src_hash);
|
||||
bool found = false;
|
||||
@@ -360,7 +366,8 @@ DispatcherAction Mesh::onRecvPacket(Packet *pkt)
|
||||
uint8_t *macAndData = &pkt->payload[i];
|
||||
if (i + 2 >= (int)pkt->payload_len) {
|
||||
// incomplete packet
|
||||
} else if (!_tables->hasSeen(pkt)) {
|
||||
} else if (!_tables->wasSeen(pkt)) {
|
||||
_tables->markSeen(pkt);
|
||||
if (self_id.isHashMatch(&dest_hash)) {
|
||||
Identity sender(sender_pub_key);
|
||||
uint8_t secret[PUB_KEY_SIZE];
|
||||
@@ -385,7 +392,8 @@ DispatcherAction Mesh::onRecvPacket(Packet *pkt)
|
||||
uint8_t *macAndData = &pkt->payload[i];
|
||||
if (i + 2 >= (int)pkt->payload_len) {
|
||||
// incomplete packet
|
||||
} else if (!_tables->hasSeen(pkt)) {
|
||||
} else if (!_tables->wasSeen(pkt)) {
|
||||
_tables->markSeen(pkt);
|
||||
GroupChannel channels[4];
|
||||
int num = searchChannelsByHash(&channel_hash, channels, 4);
|
||||
for (int j = 0; j < num; j++) {
|
||||
@@ -410,7 +418,8 @@ DispatcherAction Mesh::onRecvPacket(Packet *pkt)
|
||||
i += 4;
|
||||
const uint8_t *signature = &pkt->payload[i];
|
||||
i += SIGNATURE_SIZE;
|
||||
if (i <= (int)pkt->payload_len && !self_id.matches(id.pub_key) && !_tables->hasSeen(pkt)) {
|
||||
if (i <= (int)pkt->payload_len && !self_id.matches(id.pub_key) && !_tables->wasSeen(pkt)) {
|
||||
_tables->markSeen(pkt);
|
||||
uint8_t *app_data = (uint8_t *)&pkt->payload[i];
|
||||
size_t app_data_len = pkt->payload_len - (size_t)i;
|
||||
if (app_data_len > MAX_ADVERT_DATA_SIZE) app_data_len = MAX_ADVERT_DATA_SIZE;
|
||||
@@ -427,7 +436,8 @@ DispatcherAction Mesh::onRecvPacket(Packet *pkt)
|
||||
break;
|
||||
}
|
||||
case PAYLOAD_TYPE_RAW_CUSTOM:
|
||||
if (pkt->isRouteDirect() && !_tables->hasSeen(pkt)) {
|
||||
if (pkt->isRouteDirect() && !_tables->wasSeen(pkt)) {
|
||||
_tables->markSeen(pkt);
|
||||
onRawDataRecv(pkt);
|
||||
}
|
||||
break;
|
||||
@@ -444,7 +454,8 @@ DispatcherAction Mesh::onRecvPacket(Packet *pkt)
|
||||
tmp.payload_len = pkt->payload_len - 1;
|
||||
memcpy(tmp.payload, &pkt->payload[1], tmp.payload_len);
|
||||
|
||||
if (!_tables->hasSeen(&tmp)) {
|
||||
if (!_tables->wasSeen(&tmp)) {
|
||||
_tables->markSeen(&tmp);
|
||||
uint32_t ack_crc;
|
||||
memcpy(&ack_crc, tmp.payload, 4);
|
||||
onAckRecv(&tmp, ack_crc);
|
||||
@@ -538,7 +549,7 @@ void Mesh::sendFlood(Packet *packet, uint32_t delay_millis, uint8_t path_hash_si
|
||||
packet->header &= ~PH_ROUTE_MASK;
|
||||
packet->header |= ROUTE_TYPE_FLOOD;
|
||||
packet->setPathHashSizeAndCount(path_hash_size, 0);
|
||||
_tables->hasSeen(packet);
|
||||
_tables->markSeen(packet); /* mark as already sent in case it is rebroadcast back to us */
|
||||
#ifdef CONFIG_ZEPHCORE_APC
|
||||
{
|
||||
uint32_t h = ContentionTracker::computePacketHash32(packet);
|
||||
@@ -573,7 +584,7 @@ void Mesh::sendFlood(Packet *packet, uint16_t *transport_codes, uint32_t delay_m
|
||||
packet->transport_codes[0] = transport_codes[0];
|
||||
packet->transport_codes[1] = transport_codes[1];
|
||||
packet->setPathHashSizeAndCount(path_hash_size, 0);
|
||||
_tables->hasSeen(packet);
|
||||
_tables->markSeen(packet); /* mark as already sent in case it is rebroadcast back to us */
|
||||
#ifdef CONFIG_ZEPHCORE_APC
|
||||
{
|
||||
uint32_t h = ContentionTracker::computePacketHash32(packet);
|
||||
@@ -625,7 +636,7 @@ void Mesh::sendDirect(Packet *packet, const uint8_t *path, uint8_t path_len, uin
|
||||
}
|
||||
}
|
||||
|
||||
_tables->hasSeen(packet);
|
||||
_tables->markSeen(packet); /* mark as already sent in case it is rebroadcast back to us */
|
||||
sendPacket(packet, pri, delay_millis);
|
||||
}
|
||||
|
||||
@@ -634,7 +645,7 @@ void Mesh::sendZeroHop(Packet *packet, uint32_t delay_millis)
|
||||
packet->header &= ~PH_ROUTE_MASK;
|
||||
packet->header |= ROUTE_TYPE_DIRECT;
|
||||
packet->path_len = 0;
|
||||
_tables->hasSeen(packet);
|
||||
_tables->markSeen(packet); /* mark as already sent in case it is rebroadcast back to us */
|
||||
sendPacket(packet, 0, delay_millis);
|
||||
}
|
||||
|
||||
@@ -645,7 +656,7 @@ void Mesh::sendZeroHop(Packet *packet, uint16_t *transport_codes, uint32_t delay
|
||||
packet->transport_codes[0] = transport_codes[0];
|
||||
packet->transport_codes[1] = transport_codes[1];
|
||||
packet->path_len = 0;
|
||||
_tables->hasSeen(packet);
|
||||
_tables->markSeen(packet); /* mark as already sent in case it is rebroadcast back to us */
|
||||
sendPacket(packet, 0, delay_millis);
|
||||
}
|
||||
|
||||
|
||||
@@ -682,6 +682,10 @@ public:
|
||||
LOG_INF("TX power %d dBm requested (reboot to apply)", power_dbm);
|
||||
}
|
||||
|
||||
bool setRxBoostedGain(bool enable) override {
|
||||
return lora_radio.setRxBoost(enable);
|
||||
}
|
||||
|
||||
mesh::LocalIdentity& getSelfId() override { return companion_mesh.self_id; }
|
||||
|
||||
void saveIdentity(const mesh::LocalIdentity& new_id) override {
|
||||
|
||||
Reference in New Issue
Block a user