From b2dcad2bc292cfbee661e34854338ea512465d42 Mon Sep 17 00:00:00 2001 From: me Date: Mon, 27 Jul 2026 11:34:04 -0700 Subject: [PATCH] =?UTF-8?q?feat:=20ARIB=20STD-T108=E6=BA=96=E6=8B=A0?= =?UTF-8?q?=E3=80=81=E5=8D=98=E4=B8=80=E9=80=81=E4=BF=A14=E7=A7=92?= =?UTF-8?q?=E8=B6=85=E9=81=8E=E6=99=82=E3=81=ABdrop=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=AC=E3=83=BC=E3=83=89=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit isAS923_1_JP()時、checkSend()で送信前にgetEstAirtimeFor()を確認し、 getMaxTxAirtimeMs()(JP=4000ms)を超える場合は送信せずdrop。 n_tx_dropped_airtimeカウンターで追跡。 実機テスト(T1000-E)で確認済み: - 閾値500msでの強制drop連続17回以上でクラッシュ/リーク無し(ACK含む) - 閾値4000msに戻して通常のDM/チャンネルメッセージ送受信・ACK正常動作確認 --- src/Dispatcher.cpp | 17 ++++++++++++++++- src/Dispatcher.h | 5 +++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Dispatcher.cpp b/src/Dispatcher.cpp index 4ac78a1f5c..d2e6f266c3 100644 --- a/src/Dispatcher.cpp +++ b/src/Dispatcher.cpp @@ -64,6 +64,11 @@ uint32_t Dispatcher::getCADFailMaxDuration() const { return 4000; // 4 seconds } +uint32_t Dispatcher::getMaxTxAirtimeMs() const { + if (_radio->isAS923_1_JP()) return 4000; // ARIB STD-T108: single transmission must be <=4s + return UINT32_MAX; // no limit for other regions +} + void Dispatcher::loop() { if (millisHasNowPassed(next_floor_calib_time)) { _radio->triggerNoiseFloorCalibrate(getInterferenceThreshold()); @@ -325,6 +330,16 @@ void Dispatcher::checkSend() { } else { memcpy(&raw[len], outbound->payload, outbound->payload_len); len += outbound->payload_len; + uint32_t send_airtime = _radio->getEstAirtimeFor(len); + if (send_airtime > getMaxTxAirtimeMs()) { + MESH_DEBUG_PRINTLN("%s Dispatcher::checkSend(): DROPPED, airtime %dms exceeds limit", getLogDateTime(), send_airtime); + n_tx_dropped_airtime++; + logTxFail(outbound, len); + releasePacket(outbound); + outbound = NULL; + return; + } + uint32_t max_airtime = _radio->getEstAirtimeFor(len)*3/2; outbound_start = _ms->getMillis(); bool success = _radio->startSendRaw(raw, len); @@ -388,4 +403,4 @@ unsigned long Dispatcher::futureMillis(int millis_from_now) const { return _ms->getMillis() + millis_from_now; } -} \ No newline at end of file +} diff --git a/src/Dispatcher.h b/src/Dispatcher.h index e4f2d4a537..192f1e3d62 100644 --- a/src/Dispatcher.h +++ b/src/Dispatcher.h @@ -130,6 +130,7 @@ class Dispatcher { bool prev_isrecv_mode; uint32_t n_sent_flood, n_sent_direct; uint32_t n_recv_flood, n_recv_direct; + uint32_t n_tx_dropped_airtime; // packets dropped: would exceed getMaxTxAirtimeMs() unsigned long tx_budget_ms; unsigned long last_budget_update; unsigned long duty_cycle_window_ms; @@ -157,6 +158,7 @@ class Dispatcher { tx_budget_ms = 0; last_budget_update = 0; duty_cycle_window_ms = 3600000; + n_tx_dropped_airtime = 0; } virtual DispatcherAction onRecvPacket(Packet* pkt) = 0; @@ -172,6 +174,7 @@ class Dispatcher { virtual int calcRxDelay(float score, uint32_t air_time) const; virtual uint32_t getCADFailRetryDelay() const; virtual uint32_t getCADFailMaxDuration() const; + virtual uint32_t getMaxTxAirtimeMs() const; // single-transmission airtime cap (JP: 4000ms per ARIB STD-T108) virtual int getInterferenceThreshold() const { return 0; } // disabled by default virtual bool getCADEnabled() const { return false; } // hardware CAD disabled by default virtual int getAGCResetInterval() const { return 0; } // disabled by default @@ -192,8 +195,10 @@ class Dispatcher { uint32_t getNumSentDirect() const { return n_sent_direct; } uint32_t getNumRecvFlood() const { return n_recv_flood; } uint32_t getNumRecvDirect() const { return n_recv_direct; } + uint32_t getNumTxDroppedAirtime() const { return n_tx_dropped_airtime; } void resetStats() { n_sent_flood = n_sent_direct = n_recv_flood = n_recv_direct = 0; + n_tx_dropped_airtime = 0; _err_flags = 0; }