Merge pull request #1037 from mc-hamster/ChannelUtilization

Report on channel utilization on the screen and myNodeInfo
This commit is contained in:
Jm Casler 2021-12-29 01:32:22 -08:00 committed by GitHub
commit 94aff87706
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 123 additions and 84 deletions

View File

@ -1,117 +1,135 @@
#include "configuration.h"
#include "airtime.h" #include "airtime.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "configuration.h"
#define periodsToLog 24
AirTime *airTime; AirTime *airTime;
uint32_t secondsPerPeriod = 3600;
uint32_t lastMillis = 0;
uint32_t secSinceBoot = 0;
// AirTime at;
// Don't read out of this directly. Use the helper functions. // Don't read out of this directly. Use the helper functions.
struct airtimeStruct {
uint32_t periodTX[periodsToLog]; // AirTime transmitted
uint32_t periodRX[periodsToLog]; // AirTime received and repeated (Only valid mesh packets)
uint32_t periodRX_ALL[periodsToLog]; // AirTime received regardless of valid mesh packet. Could include noise.
uint8_t lastPeriodIndex;
} airtimes;
void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms) void AirTime::logAirtime(reportTypes reportType, uint32_t airtime_ms)
{ {
// TODO: Is the airtimes array still necessary? It's now in myNodeInfo anyway
if (reportType == TX_LOG) { if (reportType == TX_LOG) {
DEBUG_MSG("AirTime - Packet transmitted : %ums\n", airtime_ms); DEBUG_MSG("AirTime - Packet transmitted : %ums\n", airtime_ms);
airtimes.periodTX[0] = airtimes.periodTX[0] + airtime_ms; this->airtimes.periodTX[0] = this->airtimes.periodTX[0] + airtime_ms;
myNodeInfo.air_period_tx[0] = myNodeInfo.air_period_tx[0] + airtime_ms; myNodeInfo.air_period_tx[0] = myNodeInfo.air_period_tx[0] + airtime_ms;
} else if (reportType == RX_LOG) { } else if (reportType == RX_LOG) {
DEBUG_MSG("AirTime - Packet received : %ums\n", airtime_ms); DEBUG_MSG("AirTime - Packet received : %ums\n", airtime_ms);
airtimes.periodRX[0] = airtimes.periodRX[0] + airtime_ms; this->airtimes.periodRX[0] = this->airtimes.periodRX[0] + airtime_ms;
myNodeInfo.air_period_rx[0] = myNodeInfo.air_period_rx[0] + airtime_ms; myNodeInfo.air_period_rx[0] = myNodeInfo.air_period_rx[0] + airtime_ms;
} else if (reportType == RX_ALL_LOG) { } else if (reportType == RX_ALL_LOG) {
DEBUG_MSG("AirTime - Packet received (noise?) : %ums\n", airtime_ms); DEBUG_MSG("AirTime - Packet received (noise?) : %ums\n", airtime_ms);
airtimes.periodRX_ALL[0] = airtimes.periodRX_ALL[0] + airtime_ms; this->airtimes.periodRX_ALL[0] = this->airtimes.periodRX_ALL[0] + airtime_ms;
} else {
DEBUG_MSG("AirTime - Unknown report time. This should never happen!!\n");
}
} }
uint8_t currentPeriodIndex() uint8_t channelUtilPeriod = (getSecondsSinceBoot() / 10) % CHANNEL_UTILIZATION_PERIODS;
this->channelUtilization[channelUtilPeriod] = channelUtilization[channelUtilPeriod] + airtime_ms;
}
uint8_t AirTime::currentPeriodIndex()
{ {
return ((getSecondsSinceBoot() / secondsPerPeriod) % periodsToLog); return ((getSecondsSinceBoot() / SECONDS_PER_PERIOD) % PERIODS_TO_LOG);
} }
void airtimeRotatePeriod() void AirTime::airtimeRotatePeriod()
{ {
if (airtimes.lastPeriodIndex != currentPeriodIndex()) { if (this->airtimes.lastPeriodIndex != currentPeriodIndex()) {
DEBUG_MSG("Rotating airtimes to a new period = %u\n", currentPeriodIndex()); DEBUG_MSG("Rotating airtimes to a new period = %u\n", currentPeriodIndex());
for (int i = periodsToLog - 2; i >= 0; --i) { for (int i = PERIODS_TO_LOG - 2; i >= 0; --i) {
airtimes.periodTX[i + 1] = airtimes.periodTX[i]; this->airtimes.periodTX[i + 1] = this->airtimes.periodTX[i];
airtimes.periodRX[i + 1] = airtimes.periodRX[i]; this->airtimes.periodRX[i + 1] = this->airtimes.periodRX[i];
airtimes.periodRX_ALL[i + 1] = airtimes.periodRX_ALL[i]; this->airtimes.periodRX_ALL[i + 1] = this->airtimes.periodRX_ALL[i];
myNodeInfo.air_period_tx[i + 1] = myNodeInfo.air_period_tx[i]; myNodeInfo.air_period_tx[i + 1] = myNodeInfo.air_period_tx[i];
myNodeInfo.air_period_rx[i + 1] = myNodeInfo.air_period_rx[i]; myNodeInfo.air_period_rx[i + 1] = myNodeInfo.air_period_rx[i];
} }
airtimes.periodTX[0] = 0;
airtimes.periodRX[0] = 0; this->airtimes.periodTX[0] = 0;
airtimes.periodRX_ALL[0] = 0; this->airtimes.periodRX[0] = 0;
this->airtimes.periodRX_ALL[0] = 0;
myNodeInfo.air_period_tx[0] = 0; myNodeInfo.air_period_tx[0] = 0;
myNodeInfo.air_period_rx[0] = 0; myNodeInfo.air_period_rx[0] = 0;
this->airtimes.lastPeriodIndex = currentPeriodIndex();
airtimes.lastPeriodIndex = currentPeriodIndex();
} }
} }
uint32_t *airtimeReport(reportTypes reportType) uint32_t *AirTime::airtimeReport(reportTypes reportType)
{ {
if (reportType == TX_LOG) { if (reportType == TX_LOG) {
return airtimes.periodTX; return this->airtimes.periodTX;
} else if (reportType == RX_LOG) { } else if (reportType == RX_LOG) {
return airtimes.periodRX; return this->airtimes.periodRX;
} else if (reportType == RX_ALL_LOG) { } else if (reportType == RX_ALL_LOG) {
return airtimes.periodRX_ALL; return this->airtimes.periodRX_ALL;
} }
return 0; return 0;
} }
uint8_t getPeriodsToLog() uint8_t AirTime::getPeriodsToLog()
{ {
return periodsToLog; return PERIODS_TO_LOG;
} }
uint32_t getSecondsPerPeriod() uint32_t AirTime::getSecondsPerPeriod()
{ {
return secondsPerPeriod; return SECONDS_PER_PERIOD;
} }
uint32_t getSecondsSinceBoot() uint32_t AirTime::getSecondsSinceBoot()
{ {
return secSinceBoot; return this->secSinceBoot;
}
float AirTime::channelUtilizationPercent()
{
uint32_t sum = 0;
for (uint32_t i = 0; i < CHANNEL_UTILIZATION_PERIODS; i++) {
sum += this->channelUtilization[i];
// DEBUG_MSG("ChanUtilArray %u %u\n", i, this->channelUtilization[i]);
}
return (float(sum) / float(CHANNEL_UTILIZATION_PERIODS * 10 * 1000)) * 100;
} }
AirTime::AirTime() : concurrency::OSThread("AirTime") {} AirTime::AirTime() : concurrency::OSThread("AirTime") {}
int32_t AirTime::runOnce() int32_t AirTime::runOnce()
{ {
//DEBUG_MSG("AirTime::runOnce()\n");
airtimeRotatePeriod();
secSinceBoot++; secSinceBoot++;
/* uint8_t utilPeriod = (getSecondsSinceBoot() / 10) % CHANNEL_UTILIZATION_PERIODS;
This actually doesn't need to be run once per second but we currently use it for the
secSinceBoot counter. if (firstTime) {
airtimeRotatePeriod();
for (uint32_t i = 0; i < CHANNEL_UTILIZATION_PERIODS; i++) {
this->channelUtilization[i] = 0;
}
firstTime = false;
lastUtilPeriod = utilPeriod;
} else {
// Reset the channelUtilization window when we roll over
if (lastUtilPeriod != utilPeriod) {
lastUtilPeriod = utilPeriod;
this->channelUtilization[utilPeriod] = 0;
}
// Update channel_utilization every second.
myNodeInfo.channel_utilization = airTime->channelUtilizationPercent();
}
If we have a better counter of how long the device has been online (and not millis())
then we can change this to something less frequent. Maybe once ever 5 seconds?
*/
return (1000 * 1); return (1000 * 1);
} }

View File

@ -23,21 +23,18 @@
RX_ALL_LOG - RX_LOG = Other lora radios on our frequency channel. RX_ALL_LOG - RX_LOG = Other lora radios on our frequency channel.
*/ */
#define CHANNEL_UTILIZATION_PERIODS 6
#define SECONDS_PER_PERIOD 3600
#define PERIODS_TO_LOG 24
enum reportTypes { TX_LOG, RX_LOG, RX_ALL_LOG }; enum reportTypes { TX_LOG, RX_LOG, RX_ALL_LOG };
void logAirtime(reportTypes reportType, uint32_t airtime_ms); void logAirtime(reportTypes reportType, uint32_t airtime_ms);
void airtimeRotatePeriod();
uint8_t currentPeriodIndex();
uint8_t getPeriodsToLog();
uint32_t getSecondsSinceBoot();
uint32_t *airtimeReport(reportTypes reportType); uint32_t *airtimeReport(reportTypes reportType);
uint32_t getSecondsPerPeriod();
class AirTime : private concurrency::OSThread class AirTime : private concurrency::OSThread
{ {
@ -45,6 +42,27 @@ class AirTime : private concurrency::OSThread
AirTime(); AirTime();
void logAirtime(reportTypes reportType, uint32_t airtime_ms); void logAirtime(reportTypes reportType, uint32_t airtime_ms);
float channelUtilizationPercent();
uint32_t channelUtilization[CHANNEL_UTILIZATION_PERIODS];
uint8_t currentPeriodIndex();
void airtimeRotatePeriod();
uint8_t getPeriodsToLog();
uint32_t getSecondsPerPeriod();
uint32_t getSecondsSinceBoot();
uint32_t *airtimeReport(reportTypes reportType);
private:
bool firstTime = true;
uint8_t lastUtilPeriod = 0;
uint32_t secSinceBoot = 0;
struct airtimeStruct {
uint32_t periodTX[PERIODS_TO_LOG]; // AirTime transmitted
uint32_t periodRX[PERIODS_TO_LOG]; // AirTime received and repeated (Only valid mesh packets)
uint32_t periodRX_ALL[PERIODS_TO_LOG]; // AirTime received regardless of valid mesh packet. Could include noise.
uint8_t lastPeriodIndex;
} airtimes;
protected: protected:
virtual int32_t runOnce() override; virtual int32_t runOnce() override;

View File

@ -1398,11 +1398,11 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, uptime); display->drawString(x, y + FONT_HEIGHT_SMALL * 1, uptime);
#ifndef NO_ESP32 // Display Channel Utilization
// Show CPU Frequency. char chUtil[13];
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("CPU " + String(getCpuFrequencyMhz()) + "MHz"), sprintf(chUtil, "ChUtil %2.0f%%", airTime->channelUtilizationPercent());
y + FONT_HEIGHT_SMALL * 1, "CPU " + String(getCpuFrequencyMhz()) + "MHz"); display->drawString(x + SCREEN_WIDTH - display->getStringWidth(chUtil),
#endif y + FONT_HEIGHT_SMALL * 1, chUtil);
// Line 3 // Line 3
if (radioConfig.preferences.gps_format != GpsCoordinateFormat_GpsFormatDMS) // if DMS then don't draw altitude if (radioConfig.preferences.gps_format != GpsCoordinateFormat_GpsFormatDMS) // if DMS then don't draw altitude

View File

@ -86,7 +86,7 @@ extern const pb_msgdesc_t AdminMessage_msg;
#define AdminMessage_fields &AdminMessage_msg #define AdminMessage_fields &AdminMessage_msg
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define AdminMessage_size 461 #define AdminMessage_size 529
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View File

@ -125,7 +125,7 @@ extern const pb_msgdesc_t ChannelFile_msg;
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define LegacyRadioConfig_size 4 #define LegacyRadioConfig_size 4
#define LegacyRadioConfig_LegacyPreferences_size 2 #define LegacyRadioConfig_LegacyPreferences_size 2
#define DeviceState_size 9943 #define DeviceState_size 9949
#define ChannelFile_size 832 #define ChannelFile_size 832
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -164,6 +164,7 @@ typedef struct _MyNodeInfo {
pb_size_t air_period_rx_count; pb_size_t air_period_rx_count;
uint32_t air_period_rx[24]; uint32_t air_period_rx[24];
bool has_wifi; bool has_wifi;
float channel_utilization;
} MyNodeInfo; } MyNodeInfo;
typedef struct _Position { typedef struct _Position {
@ -332,7 +333,7 @@ extern "C" {
#define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0} #define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
#define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN} #define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN}
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0} #define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0} #define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, 0}
#define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN} #define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN}
#define FromRadio_init_default {0, 0, {MyNodeInfo_init_default}} #define FromRadio_init_default {0, 0, {MyNodeInfo_init_default}}
#define ToRadio_init_default {0, {MeshPacket_init_default}} #define ToRadio_init_default {0, {MeshPacket_init_default}}
@ -344,7 +345,7 @@ extern "C" {
#define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0} #define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0}
#define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN} #define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN}
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0} #define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0} #define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, 0}
#define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN} #define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN}
#define FromRadio_init_zero {0, 0, {MyNodeInfo_init_zero}} #define FromRadio_init_zero {0, 0, {MyNodeInfo_init_zero}}
#define ToRadio_init_zero {0, {MeshPacket_init_zero}} #define ToRadio_init_zero {0, {MeshPacket_init_zero}}
@ -378,6 +379,7 @@ extern "C" {
#define MyNodeInfo_air_period_tx_tag 16 #define MyNodeInfo_air_period_tx_tag 16
#define MyNodeInfo_air_period_rx_tag 17 #define MyNodeInfo_air_period_rx_tag 17
#define MyNodeInfo_has_wifi_tag 18 #define MyNodeInfo_has_wifi_tag 18
#define MyNodeInfo_channel_utilization_tag 19
#define Position_latitude_i_tag 1 #define Position_latitude_i_tag 1
#define Position_longitude_i_tag 2 #define Position_longitude_i_tag 2
#define Position_altitude_tag 3 #define Position_altitude_tag 3
@ -559,7 +561,8 @@ X(a, STATIC, SINGULAR, UINT32, min_app_version, 14) \
X(a, STATIC, SINGULAR, UINT32, max_channels, 15) \ X(a, STATIC, SINGULAR, UINT32, max_channels, 15) \
X(a, STATIC, REPEATED, UINT32, air_period_tx, 16) \ X(a, STATIC, REPEATED, UINT32, air_period_tx, 16) \
X(a, STATIC, REPEATED, UINT32, air_period_rx, 17) \ X(a, STATIC, REPEATED, UINT32, air_period_rx, 17) \
X(a, STATIC, SINGULAR, BOOL, has_wifi, 18) X(a, STATIC, SINGULAR, BOOL, has_wifi, 18) \
X(a, STATIC, SINGULAR, FLOAT, channel_utilization, 19)
#define MyNodeInfo_CALLBACK NULL #define MyNodeInfo_CALLBACK NULL
#define MyNodeInfo_DEFAULT NULL #define MyNodeInfo_DEFAULT NULL
@ -637,9 +640,9 @@ extern const pb_msgdesc_t ToRadio_PeerInfo_msg;
#define Data_size 260 #define Data_size 260
#define MeshPacket_size 311 #define MeshPacket_size 311
#define NodeInfo_size 270 #define NodeInfo_size 270
#define MyNodeInfo_size 445 #define MyNodeInfo_size 451
#define LogRecord_size 81 #define LogRecord_size 81
#define FromRadio_size 454 #define FromRadio_size 460
#define ToRadio_size 314 #define ToRadio_size 314
#define ToRadio_PeerInfo_size 8 #define ToRadio_PeerInfo_size 8

View File

@ -502,12 +502,12 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
res->print("\"tx_log\": ["); res->print("\"tx_log\": [");
logArray = airtimeReport(TX_LOG); logArray = airTime->airtimeReport(TX_LOG);
for (int i = 0; i < getPeriodsToLog(); i++) { for (int i = 0; i < airTime->getPeriodsToLog(); i++) {
uint32_t tmp; uint32_t tmp;
tmp = *(logArray + i); tmp = *(logArray + i);
res->printf("%d", tmp); res->printf("%d", tmp);
if (i != getPeriodsToLog() - 1) { if (i != airTime->getPeriodsToLog() - 1) {
res->print(", "); res->print(", ");
} }
} }
@ -515,12 +515,12 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
res->println("],"); res->println("],");
res->print("\"rx_log\": ["); res->print("\"rx_log\": [");
logArray = airtimeReport(RX_LOG); logArray = airTime->airtimeReport(RX_LOG);
for (int i = 0; i < getPeriodsToLog(); i++) { for (int i = 0; i < airTime->getPeriodsToLog(); i++) {
uint32_t tmp; uint32_t tmp;
tmp = *(logArray + i); tmp = *(logArray + i);
res->printf("%d", tmp); res->printf("%d", tmp);
if (i != getPeriodsToLog() - 1) { if (i != airTime->getPeriodsToLog() - 1) {
res->print(", "); res->print(", ");
} }
} }
@ -528,20 +528,20 @@ void handleReport(HTTPRequest *req, HTTPResponse *res)
res->println("],"); res->println("],");
res->print("\"rx_all_log\": ["); res->print("\"rx_all_log\": [");
logArray = airtimeReport(RX_ALL_LOG); logArray = airTime->airtimeReport(RX_ALL_LOG);
for (int i = 0; i < getPeriodsToLog(); i++) { for (int i = 0; i < airTime->getPeriodsToLog(); i++) {
uint32_t tmp; uint32_t tmp;
tmp = *(logArray + i); tmp = *(logArray + i);
res->printf("%d", tmp); res->printf("%d", tmp);
if (i != getPeriodsToLog() - 1) { if (i != airTime->getPeriodsToLog() - 1) {
res->print(", "); res->print(", ");
} }
} }
res->println("],"); res->println("],");
res->printf("\"seconds_since_boot\": %u,\n", getSecondsSinceBoot()); res->printf("\"seconds_since_boot\": %u,\n", airTime->getSecondsSinceBoot());
res->printf("\"seconds_per_period\": %u,\n", getSecondsPerPeriod()); res->printf("\"seconds_per_period\": %u,\n", airTime->getSecondsPerPeriod());
res->printf("\"periods_to_log\": %u\n", getPeriodsToLog()); res->printf("\"periods_to_log\": %u\n", airTime->getPeriodsToLog());
res->println("},"); res->println("},");