|
|
@ -8,7 +8,9 @@ |
|
|
|
|
|
|
|
|
namespace mesh { |
|
|
namespace mesh { |
|
|
|
|
|
|
|
|
#define MAX_RX_DELAY_MILLIS 32000 // 32 seconds
|
|
|
#define MAX_RX_DELAY_MILLIS 32000 // 32 seconds
|
|
|
|
|
|
#define MIN_TX_BUDGET_RESERVE_MS 100 // min budget (ms) required before allowing next TX
|
|
|
|
|
|
#define MIN_TX_BUDGET_AIRTIME_DIV 2 // require at least 1/N of estimated airtime as budget before TX
|
|
|
|
|
|
|
|
|
#ifndef NOISE_FLOOR_CALIB_INTERVAL |
|
|
#ifndef NOISE_FLOOR_CALIB_INTERVAL |
|
|
#define NOISE_FLOOR_CALIB_INTERVAL 2000 // 2 seconds
|
|
|
#define NOISE_FLOOR_CALIB_INTERVAL 2000 // 2 seconds
|
|
|
@ -20,12 +22,34 @@ void Dispatcher::begin() { |
|
|
_err_flags = 0; |
|
|
_err_flags = 0; |
|
|
radio_nonrx_start = _ms->getMillis(); |
|
|
radio_nonrx_start = _ms->getMillis(); |
|
|
|
|
|
|
|
|
|
|
|
duty_cycle_window_ms = getDutyCycleWindowMs(); |
|
|
|
|
|
float duty_cycle = 1.0f / (1.0f + getAirtimeBudgetFactor()); |
|
|
|
|
|
tx_budget_ms = (unsigned long)(duty_cycle_window_ms * duty_cycle); |
|
|
|
|
|
last_budget_update = _ms->getMillis(); |
|
|
|
|
|
|
|
|
_radio->begin(); |
|
|
_radio->begin(); |
|
|
prev_isrecv_mode = _radio->isInRecvMode(); |
|
|
prev_isrecv_mode = _radio->isInRecvMode(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
float Dispatcher::getAirtimeBudgetFactor() const { |
|
|
float Dispatcher::getAirtimeBudgetFactor() const { |
|
|
return 2.0; // default, 33.3% (1/3rd)
|
|
|
return 1.0; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void Dispatcher::updateTxBudget() { |
|
|
|
|
|
unsigned long now = _ms->getMillis(); |
|
|
|
|
|
unsigned long elapsed = now - last_budget_update; |
|
|
|
|
|
|
|
|
|
|
|
float duty_cycle = 1.0f / (1.0f + getAirtimeBudgetFactor()); |
|
|
|
|
|
unsigned long max_budget = (unsigned long)(getDutyCycleWindowMs() * duty_cycle); |
|
|
|
|
|
unsigned long refill = (unsigned long)(elapsed * duty_cycle); |
|
|
|
|
|
|
|
|
|
|
|
if (refill > 0) { |
|
|
|
|
|
tx_budget_ms += refill; |
|
|
|
|
|
if (tx_budget_ms > max_budget) { |
|
|
|
|
|
tx_budget_ms = max_budget; |
|
|
|
|
|
} |
|
|
|
|
|
last_budget_update = now; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
int Dispatcher::calcRxDelay(float score, uint32_t air_time) const { |
|
|
int Dispatcher::calcRxDelay(float score, uint32_t air_time) const { |
|
|
@ -61,11 +85,24 @@ void Dispatcher::loop() { |
|
|
if (outbound) { // waiting for outbound send to be completed
|
|
|
if (outbound) { // waiting for outbound send to be completed
|
|
|
if (_radio->isSendComplete()) { |
|
|
if (_radio->isSendComplete()) { |
|
|
long t = _ms->getMillis() - outbound_start; |
|
|
long t = _ms->getMillis() - outbound_start; |
|
|
total_air_time += t; // keep track of how much air time we are using
|
|
|
total_air_time += t; |
|
|
//Serial.print(" airtime="); Serial.println(t);
|
|
|
//Serial.print(" airtime="); Serial.println(t);
|
|
|
|
|
|
|
|
|
// will need radio silence up to next_tx_time
|
|
|
updateTxBudget(); |
|
|
next_tx_time = futureMillis(t * getAirtimeBudgetFactor()); |
|
|
|
|
|
|
|
|
if (t > tx_budget_ms) { |
|
|
|
|
|
tx_budget_ms = 0; |
|
|
|
|
|
} else { |
|
|
|
|
|
tx_budget_ms -= t; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (tx_budget_ms < MIN_TX_BUDGET_RESERVE_MS) { |
|
|
|
|
|
float duty_cycle = 1.0f / (1.0f + getAirtimeBudgetFactor()); |
|
|
|
|
|
unsigned long needed = MIN_TX_BUDGET_RESERVE_MS - tx_budget_ms; |
|
|
|
|
|
next_tx_time = futureMillis((unsigned long)(needed / duty_cycle)); |
|
|
|
|
|
} else { |
|
|
|
|
|
next_tx_time = _ms->getMillis(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
_radio->onSendFinished(); |
|
|
_radio->onSendFinished(); |
|
|
logTx(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len); |
|
|
logTx(outbound, 2 + outbound->getPathByteLen() + outbound->payload_len); |
|
|
@ -235,9 +272,20 @@ void Dispatcher::processRecvPacket(Packet* pkt) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void Dispatcher::checkSend() { |
|
|
void Dispatcher::checkSend() { |
|
|
if (_mgr->getOutboundCount(_ms->getMillis()) == 0) return; // nothing waiting to send
|
|
|
if (_mgr->getOutboundCount(_ms->getMillis()) == 0) return; |
|
|
if (!millisHasNowPassed(next_tx_time)) return; // still in 'radio silence' phase (from airtime budget setting)
|
|
|
|
|
|
if (_radio->isReceiving()) { // LBT - check if radio is currently mid-receive, or if channel activity
|
|
|
updateTxBudget(); |
|
|
|
|
|
|
|
|
|
|
|
uint32_t est_airtime = _radio->getEstAirtimeFor(MAX_TRANS_UNIT); |
|
|
|
|
|
if (tx_budget_ms < est_airtime / MIN_TX_BUDGET_AIRTIME_DIV) { |
|
|
|
|
|
float duty_cycle = 1.0f / (1.0f + getAirtimeBudgetFactor()); |
|
|
|
|
|
unsigned long needed = est_airtime / MIN_TX_BUDGET_AIRTIME_DIV - tx_budget_ms; |
|
|
|
|
|
next_tx_time = futureMillis((unsigned long)(needed / duty_cycle)); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!millisHasNowPassed(next_tx_time)) return; |
|
|
|
|
|
if (_radio->isReceiving()) { |
|
|
if (cad_busy_start == 0) { |
|
|
if (cad_busy_start == 0) { |
|
|
cad_busy_start = _ms->getMillis(); // record when CAD busy state started
|
|
|
cad_busy_start = _ms->getMillis(); // record when CAD busy state started
|
|
|
} |
|
|
} |
|
|
|