Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/companion_radio/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe

// defaults
memset(&_prefs, 0, sizeof(_prefs));
_prefs.airtime_factor = 1.0; // one half
_prefs.airtime_factor = 1.0;
strcpy(_prefs.node_name, "NONAME");
_prefs.freq = LORA_FREQ;
_prefs.sf = LORA_SF;
Expand Down
2 changes: 1 addition & 1 deletion examples/simple_repeater/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc

// defaults
memset(&_prefs, 0, sizeof(_prefs));
_prefs.airtime_factor = 1.0; // one half
_prefs.airtime_factor = 1.0;
_prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0;
_prefs.tx_delay_factor = 0.5f; // was 0.25f
_prefs.direct_tx_delay_factor = 0.2f; // was zero
Expand Down
2 changes: 1 addition & 1 deletion examples/simple_room_server/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc

// defaults
memset(&_prefs, 0, sizeof(_prefs));
_prefs.airtime_factor = 1.0; // one half
_prefs.airtime_factor = 1.0;
_prefs.rx_delay_base = 0.0f; // off by default, was 10.0
_prefs.tx_delay_factor = 0.5f; // was 0.25f;
_prefs.direct_tx_delay_factor = 0.2f; // was zero
Expand Down
2 changes: 1 addition & 1 deletion examples/simple_secure_chat/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ class MyMesh : public BaseChatMesh, ContactVisitor {
{
// defaults
memset(&_prefs, 0, sizeof(_prefs));
_prefs.airtime_factor = 2.0; // one third
_prefs.airtime_factor = 1.0;
strcpy(_prefs.node_name, "NONAME");
_prefs.freq = LORA_FREQ;
_prefs.tx_power_dbm = LORA_TX_POWER;
Expand Down
2 changes: 1 addition & 1 deletion examples/simple_sensor/SensorMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise

// defaults
memset(&_prefs, 0, sizeof(_prefs));
_prefs.airtime_factor = 1.0; // one half
_prefs.airtime_factor = 1.0;
_prefs.rx_delay_base = 0.0f; // turn off by default, was 10.0;
_prefs.tx_delay_factor = 0.5f; // was 0.25f
_prefs.direct_tx_delay_factor = 0.2f; // was zero
Expand Down
60 changes: 53 additions & 7 deletions src/Dispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,34 @@ void Dispatcher::begin() {
_err_flags = 0;
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();
prev_isrecv_mode = _radio->isInRecvMode();
}

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);
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 {
Expand Down Expand Up @@ -61,11 +83,24 @@ void Dispatcher::loop() {
if (outbound) { // waiting for outbound send to be completed
if (_radio->isSendComplete()) {
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);

// will need radio silence up to next_tx_time
next_tx_time = futureMillis(t * getAirtimeBudgetFactor());
updateTxBudget();

if (t > tx_budget_ms) {
tx_budget_ms = 0;
} else {
tx_budget_ms -= t;
}

if (tx_budget_ms < 100) {
float duty_cycle = 1.0f / (1.0f + getAirtimeBudgetFactor());
unsigned long needed = 100 - tx_budget_ms;
next_tx_time = futureMillis((unsigned long)(needed / duty_cycle));
} else {
next_tx_time = _ms->getMillis();
}

_radio->onSendFinished();
logTx(outbound, 2 + outbound->path_len + outbound->payload_len);
Expand Down Expand Up @@ -224,9 +259,20 @@ void Dispatcher::processRecvPacket(Packet* pkt) {
}

void Dispatcher::checkSend() {
if (_mgr->getOutboundCount(_ms->getMillis()) == 0) return; // nothing waiting to send
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
if (_mgr->getOutboundCount(_ms->getMillis()) == 0) return;

updateTxBudget();

uint32_t est_airtime = _radio->getEstAirtimeFor(MAX_TRANS_UNIT);
if (tx_budget_ms < est_airtime / 2) {
float duty_cycle = 1.0f / (1.0f + getAirtimeBudgetFactor());
unsigned long needed = est_airtime / 2 - 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) {
cad_busy_start = _ms->getMillis(); // record when CAD busy state started
}
Expand Down
11 changes: 10 additions & 1 deletion src/Dispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,12 @@ class Dispatcher {
bool prev_isrecv_mode;
uint32_t n_sent_flood, n_sent_direct;
uint32_t n_recv_flood, n_recv_direct;
unsigned long tx_budget_ms;
unsigned long last_budget_update;
unsigned long duty_cycle_window_ms;

void processRecvPacket(Packet* pkt);
void updateTxBudget();

protected:
PacketManager* _mgr;
Expand All @@ -142,6 +146,9 @@ class Dispatcher {
_err_flags = 0;
radio_nonrx_start = 0;
prev_isrecv_mode = true;
tx_budget_ms = 0;
last_budget_update = 0;
duty_cycle_window_ms = 3600000;
}

virtual DispatcherAction onRecvPacket(Packet* pkt) = 0;
Expand All @@ -159,6 +166,7 @@ class Dispatcher {
virtual uint32_t getCADFailMaxDuration() const;
virtual int getInterferenceThreshold() const { return 0; } // disabled by default
virtual int getAGCResetInterval() const { return 0; } // disabled by default
virtual unsigned long getDutyCycleWindowMs() const { return 3600000; }

public:
void begin();
Expand All @@ -168,8 +176,9 @@ class Dispatcher {
void releasePacket(Packet* packet);
void sendPacket(Packet* packet, uint8_t priority, uint32_t delay_millis=0);

unsigned long getTotalAirTime() const { return total_air_time; } // in milliseconds
unsigned long getTotalAirTime() const { return total_air_time; }
unsigned long getReceiveAirTime() const {return rx_air_time; }
unsigned long getRemainingTxBudget() const { return tx_budget_ms; }
uint32_t getNumSentFlood() const { return n_sent_flood; }
uint32_t getNumSentDirect() const { return n_sent_direct; }
uint32_t getNumRecvFlood() const { return n_recv_flood; }
Expand Down