2020-12-26 00:10:38 +00:00
|
|
|
#include "airtime.h"
|
2021-12-07 21:04:50 +00:00
|
|
|
#include "NodeDB.h"
|
2021-12-29 07:34:49 +00:00
|
|
|
#include "configuration.h"
|
2020-12-25 06:12:59 +00:00
|
|
|
|
2022-01-24 07:00:14 +00:00
|
|
|
AirTime *airTime = NULL;
|
2021-01-01 20:31:46 +00:00
|
|
|
|
2020-12-26 00:10:38 +00:00
|
|
|
// Don't read out of this directly. Use the helper functions.
|
2021-12-29 08:45:36 +00:00
|
|
|
|
2023-06-08 13:07:32 +00:00
|
|
|
uint32_t air_period_tx[PERIODS_TO_LOG];
|
|
|
|
uint32_t air_period_rx[PERIODS_TO_LOG];
|
|
|
|
|
2021-01-01 20:31:46 +00:00
|
|
|
void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms)
|
2020-12-25 06:12:59 +00:00
|
|
|
{
|
2021-12-29 08:45:36 +00:00
|
|
|
|
2020-12-26 00:10:38 +00:00
|
|
|
if (reportType == TX_LOG) {
|
2022-12-30 02:41:37 +00:00
|
|
|
LOG_DEBUG("AirTime - Packet transmitted : %ums\n", airtime_ms);
|
2021-12-29 08:45:36 +00:00
|
|
|
this->airtimes.periodTX[0] = this->airtimes.periodTX[0] + airtime_ms;
|
2023-06-08 13:07:32 +00:00
|
|
|
air_period_tx[0] = air_period_tx[0] + airtime_ms;
|
2022-01-15 17:44:29 +00:00
|
|
|
|
2022-01-15 19:19:50 +00:00
|
|
|
this->utilizationTX[this->getPeriodUtilHour()] = this->utilizationTX[this->getPeriodUtilHour()] + airtime_ms;
|
2020-12-26 00:10:38 +00:00
|
|
|
} else if (reportType == RX_LOG) {
|
2022-12-30 02:41:37 +00:00
|
|
|
LOG_DEBUG("AirTime - Packet received : %ums\n", airtime_ms);
|
2021-12-29 08:45:36 +00:00
|
|
|
this->airtimes.periodRX[0] = this->airtimes.periodRX[0] + airtime_ms;
|
2023-06-08 13:07:32 +00:00
|
|
|
air_period_rx[0] = air_period_rx[0] + airtime_ms;
|
2020-12-26 00:10:38 +00:00
|
|
|
} else if (reportType == RX_ALL_LOG) {
|
2022-12-30 02:41:37 +00:00
|
|
|
LOG_DEBUG("AirTime - Packet received (noise?) : %ums\n", airtime_ms);
|
2021-12-29 08:45:36 +00:00
|
|
|
this->airtimes.periodRX_ALL[0] = this->airtimes.periodRX_ALL[0] + airtime_ms;
|
2020-12-26 00:10:38 +00:00
|
|
|
}
|
2021-12-29 07:34:49 +00:00
|
|
|
|
2022-01-15 17:44:29 +00:00
|
|
|
// Log all airtime type for channel utilization
|
2022-01-15 19:19:50 +00:00
|
|
|
this->channelUtilization[this->getPeriodUtilMinute()] = channelUtilization[this->getPeriodUtilMinute()] + airtime_ms;
|
2020-12-26 00:10:38 +00:00
|
|
|
}
|
2020-12-25 06:12:59 +00:00
|
|
|
|
2021-12-29 07:34:49 +00:00
|
|
|
uint8_t AirTime::currentPeriodIndex()
|
2020-12-26 00:10:38 +00:00
|
|
|
{
|
2022-03-03 04:38:14 +00:00
|
|
|
return ((getSecondsSinceBoot() / SECONDS_PER_PERIOD) % PERIODS_TO_LOG);
|
2020-12-26 00:10:38 +00:00
|
|
|
}
|
2020-12-25 06:12:59 +00:00
|
|
|
|
2023-01-21 13:34:29 +00:00
|
|
|
uint8_t AirTime::getPeriodUtilMinute()
|
|
|
|
{
|
2022-01-15 19:19:50 +00:00
|
|
|
return (getSecondsSinceBoot() / 10) % CHANNEL_UTILIZATION_PERIODS;
|
|
|
|
}
|
|
|
|
|
2023-01-21 13:34:29 +00:00
|
|
|
uint8_t AirTime::getPeriodUtilHour()
|
|
|
|
{
|
2022-01-15 19:19:50 +00:00
|
|
|
return (getSecondsSinceBoot() / 60) % MINUTES_IN_HOUR;
|
|
|
|
}
|
|
|
|
|
2021-12-29 07:34:49 +00:00
|
|
|
void AirTime::airtimeRotatePeriod()
|
2020-12-26 00:10:38 +00:00
|
|
|
{
|
2021-01-01 20:31:46 +00:00
|
|
|
|
2022-01-15 23:27:25 +00:00
|
|
|
if (this->airtimes.lastPeriodIndex != this->currentPeriodIndex()) {
|
2022-12-30 02:41:37 +00:00
|
|
|
LOG_DEBUG("Rotating airtimes to a new period = %u\n", this->currentPeriodIndex());
|
2021-01-01 20:31:46 +00:00
|
|
|
|
2022-03-03 04:38:14 +00:00
|
|
|
for (int i = PERIODS_TO_LOG - 2; i >= 0; --i) {
|
2021-12-29 08:45:36 +00:00
|
|
|
this->airtimes.periodTX[i + 1] = this->airtimes.periodTX[i];
|
|
|
|
this->airtimes.periodRX[i + 1] = this->airtimes.periodRX[i];
|
|
|
|
this->airtimes.periodRX_ALL[i + 1] = this->airtimes.periodRX_ALL[i];
|
2021-12-07 21:04:50 +00:00
|
|
|
|
2023-06-08 13:07:32 +00:00
|
|
|
air_period_tx[i + 1] = this->airtimes.periodTX[i];
|
|
|
|
air_period_rx[i + 1] = this->airtimes.periodRX[i];
|
2020-12-27 06:39:43 +00:00
|
|
|
}
|
2022-01-15 19:19:50 +00:00
|
|
|
|
2021-12-29 08:45:36 +00:00
|
|
|
this->airtimes.periodTX[0] = 0;
|
|
|
|
this->airtimes.periodRX[0] = 0;
|
|
|
|
this->airtimes.periodRX_ALL[0] = 0;
|
2021-01-01 20:31:46 +00:00
|
|
|
|
2023-06-08 13:07:32 +00:00
|
|
|
air_period_tx[0] = 0;
|
|
|
|
air_period_rx[0] = 0;
|
2021-12-07 21:04:50 +00:00
|
|
|
|
2022-01-15 23:27:25 +00:00
|
|
|
this->airtimes.lastPeriodIndex = this->currentPeriodIndex();
|
2020-12-26 00:10:38 +00:00
|
|
|
}
|
2020-12-25 06:12:59 +00:00
|
|
|
}
|
|
|
|
|
2021-12-29 08:45:36 +00:00
|
|
|
uint32_t *AirTime::airtimeReport(reportTypes reportType)
|
2020-12-25 06:12:59 +00:00
|
|
|
{
|
2020-12-26 00:10:38 +00:00
|
|
|
|
2020-12-27 06:39:43 +00:00
|
|
|
if (reportType == TX_LOG) {
|
2021-12-29 08:45:36 +00:00
|
|
|
return this->airtimes.periodTX;
|
2020-12-27 06:39:43 +00:00
|
|
|
} else if (reportType == RX_LOG) {
|
2021-12-29 08:45:36 +00:00
|
|
|
return this->airtimes.periodRX;
|
2020-12-27 06:39:43 +00:00
|
|
|
} else if (reportType == RX_ALL_LOG) {
|
2021-12-29 08:45:36 +00:00
|
|
|
return this->airtimes.periodRX_ALL;
|
2020-12-26 00:10:38 +00:00
|
|
|
}
|
2020-12-27 06:39:43 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2020-12-26 00:10:38 +00:00
|
|
|
|
2021-12-29 07:34:49 +00:00
|
|
|
uint8_t AirTime::getPeriodsToLog()
|
2020-12-27 18:50:52 +00:00
|
|
|
{
|
2022-03-03 04:38:14 +00:00
|
|
|
return PERIODS_TO_LOG;
|
2020-12-27 18:50:52 +00:00
|
|
|
}
|
|
|
|
|
2021-12-29 07:34:49 +00:00
|
|
|
uint32_t AirTime::getSecondsPerPeriod()
|
2020-12-27 06:39:43 +00:00
|
|
|
{
|
2021-12-29 07:34:49 +00:00
|
|
|
return SECONDS_PER_PERIOD;
|
2020-12-27 18:50:52 +00:00
|
|
|
}
|
|
|
|
|
2021-12-29 07:34:49 +00:00
|
|
|
uint32_t AirTime::getSecondsSinceBoot()
|
2020-12-27 18:50:52 +00:00
|
|
|
{
|
2021-12-29 07:34:49 +00:00
|
|
|
return this->secSinceBoot;
|
|
|
|
}
|
|
|
|
|
|
|
|
float AirTime::channelUtilizationPercent()
|
|
|
|
{
|
|
|
|
uint32_t sum = 0;
|
|
|
|
for (uint32_t i = 0; i < CHANNEL_UTILIZATION_PERIODS; i++) {
|
|
|
|
sum += this->channelUtilization[i];
|
2022-12-30 02:41:37 +00:00
|
|
|
// LOG_DEBUG("ChanUtilArray %u %u\n", i, this->channelUtilization[i]);
|
2021-12-29 07:34:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return (float(sum) / float(CHANNEL_UTILIZATION_PERIODS * 10 * 1000)) * 100;
|
2020-12-27 18:50:52 +00:00
|
|
|
}
|
2021-01-01 20:31:46 +00:00
|
|
|
|
2022-01-15 17:44:29 +00:00
|
|
|
float AirTime::utilizationTXPercent()
|
|
|
|
{
|
|
|
|
uint32_t sum = 0;
|
|
|
|
for (uint32_t i = 0; i < MINUTES_IN_HOUR; i++) {
|
|
|
|
sum += this->utilizationTX[i];
|
|
|
|
}
|
|
|
|
|
2022-01-15 20:24:16 +00:00
|
|
|
return (float(sum) / float(MS_IN_HOUR)) * 100;
|
2022-01-15 17:44:29 +00:00
|
|
|
}
|
|
|
|
|
2023-01-11 20:52:19 +00:00
|
|
|
bool AirTime::isTxAllowedChannelUtil(bool polite)
|
|
|
|
{
|
2023-01-21 13:34:29 +00:00
|
|
|
uint8_t percentage = (polite ? polite_channel_util_percent : max_channel_util_percent);
|
2023-01-11 20:52:19 +00:00
|
|
|
if (channelUtilizationPercent() < percentage) {
|
2023-01-21 13:34:29 +00:00
|
|
|
return true;
|
2023-01-11 20:52:19 +00:00
|
|
|
} else {
|
|
|
|
LOG_WARN("Channel utilization is >%d percent. Skipping this opportunity to send.\n", percentage);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-21 13:34:29 +00:00
|
|
|
bool AirTime::isTxAllowedAirUtil()
|
2023-01-11 20:52:19 +00:00
|
|
|
{
|
|
|
|
if (!config.lora.override_duty_cycle && myRegion->dutyCycle < 100) {
|
2023-01-12 13:03:06 +00:00
|
|
|
if (utilizationTXPercent() < myRegion->dutyCycle * polite_duty_cycle_percent / 100) {
|
2023-01-21 13:34:29 +00:00
|
|
|
return true;
|
2023-01-11 20:52:19 +00:00
|
|
|
} else {
|
2023-01-21 13:34:29 +00:00
|
|
|
LOG_WARN("Tx air utilization is >%f percent. Skipping this opportunity to send.\n",
|
|
|
|
myRegion->dutyCycle * polite_duty_cycle_percent / 100);
|
2023-01-11 20:52:19 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-11-26 19:50:00 +00:00
|
|
|
// Get the amount of minutes we have to be silent before we can send again
|
2023-01-21 13:34:29 +00:00
|
|
|
uint8_t AirTime::getSilentMinutes(float txPercent, float dutyCycle)
|
|
|
|
{
|
|
|
|
float newTxPercent = txPercent;
|
|
|
|
for (int8_t i = MINUTES_IN_HOUR - 1; i >= 0; --i) {
|
|
|
|
newTxPercent -= ((float)this->utilizationTX[i] / (MS_IN_MINUTE * MINUTES_IN_HOUR / 100));
|
|
|
|
if (newTxPercent < dutyCycle)
|
|
|
|
return MINUTES_IN_HOUR - 1 - i;
|
|
|
|
}
|
2022-11-26 19:50:00 +00:00
|
|
|
|
2023-01-21 13:34:29 +00:00
|
|
|
return MINUTES_IN_HOUR;
|
2022-01-24 07:00:14 +00:00
|
|
|
}
|
2021-01-01 20:31:46 +00:00
|
|
|
|
2023-01-21 13:34:29 +00:00
|
|
|
AirTime::AirTime() : concurrency::OSThread("AirTime"), airtimes({}) {}
|
|
|
|
|
2021-01-01 20:31:46 +00:00
|
|
|
int32_t AirTime::runOnce()
|
|
|
|
{
|
|
|
|
secSinceBoot++;
|
|
|
|
|
2022-01-15 19:19:50 +00:00
|
|
|
uint8_t utilPeriod = this->getPeriodUtilMinute();
|
|
|
|
uint8_t utilPeriodTX = this->getPeriodUtilHour();
|
2021-12-29 07:34:49 +00:00
|
|
|
|
|
|
|
if (firstTime) {
|
|
|
|
|
2022-01-15 17:44:29 +00:00
|
|
|
// Init utilizationTX window to all 0
|
|
|
|
for (uint32_t i = 0; i < MINUTES_IN_HOUR; i++) {
|
|
|
|
this->utilizationTX[i] = 0;
|
|
|
|
}
|
|
|
|
|
2022-01-15 16:24:39 +00:00
|
|
|
// Init channelUtilization window to all 0
|
2021-12-29 07:34:49 +00:00
|
|
|
for (uint32_t i = 0; i < CHANNEL_UTILIZATION_PERIODS; i++) {
|
|
|
|
this->channelUtilization[i] = 0;
|
|
|
|
}
|
|
|
|
|
2022-01-15 16:24:39 +00:00
|
|
|
// Init airtime windows to all 0
|
2023-06-08 13:07:32 +00:00
|
|
|
for (int i = 0; i < PERIODS_TO_LOG; i++) {
|
2022-01-15 16:24:39 +00:00
|
|
|
this->airtimes.periodTX[i] = 0;
|
|
|
|
this->airtimes.periodRX[i] = 0;
|
|
|
|
this->airtimes.periodRX_ALL[i] = 0;
|
|
|
|
|
2023-06-08 13:07:32 +00:00
|
|
|
// air_period_tx[i] = 0;
|
|
|
|
// air_period_rx[i] = 0;
|
2022-01-15 16:24:39 +00:00
|
|
|
}
|
|
|
|
|
2021-12-29 07:34:49 +00:00
|
|
|
firstTime = false;
|
|
|
|
lastUtilPeriod = utilPeriod;
|
|
|
|
} else {
|
2022-01-15 23:27:25 +00:00
|
|
|
this->airtimeRotatePeriod();
|
2021-12-29 07:34:49 +00:00
|
|
|
|
|
|
|
// Reset the channelUtilization window when we roll over
|
|
|
|
if (lastUtilPeriod != utilPeriod) {
|
|
|
|
lastUtilPeriod = utilPeriod;
|
|
|
|
|
|
|
|
this->channelUtilization[utilPeriod] = 0;
|
|
|
|
}
|
|
|
|
|
2022-01-15 17:44:29 +00:00
|
|
|
if (lastUtilPeriodTX != utilPeriodTX) {
|
|
|
|
lastUtilPeriodTX = utilPeriodTX;
|
|
|
|
|
|
|
|
this->utilizationTX[utilPeriodTX] = 0;
|
|
|
|
}
|
2021-12-29 07:34:49 +00:00
|
|
|
}
|
2023-01-21 13:34:29 +00:00
|
|
|
/*
|
|
|
|
LOG_DEBUG("utilPeriodTX %d TX Airtime %3.2f%\n", utilPeriodTX, airTime->utilizationTXPercent());
|
|
|
|
for (uint32_t i = 0; i < MINUTES_IN_HOUR; i++) {
|
|
|
|
LOG_DEBUG(
|
|
|
|
"%d,", this->utilizationTX[i]
|
|
|
|
);
|
|
|
|
}
|
|
|
|
LOG_DEBUG("\n");
|
|
|
|
*/
|
2021-01-02 05:20:34 +00:00
|
|
|
return (1000 * 1);
|
2023-06-08 13:07:32 +00:00
|
|
|
}
|