mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-28 04:07:47 +00:00
[Timing Stats] Add duty cycle column + LoRa TTN on air timings
This commit is contained in:
@@ -242,6 +242,11 @@ String rn2xx3::peekLastError() const
|
||||
return _rn2xx3_handler.peekLastError();
|
||||
}
|
||||
|
||||
float rn2xx3::getLoRaAirTime(uint8_t pl) const
|
||||
{
|
||||
return _rn2xx3_handler.getLoRaAirTime(pl);
|
||||
}
|
||||
|
||||
String rn2xx3::getLastError()
|
||||
{
|
||||
return _rn2xx3_handler.getLastError();
|
||||
|
||||
@@ -221,6 +221,42 @@ public:
|
||||
* This can be overwritten by the network when using OTAA.
|
||||
* So to force a datarate, call this function after initOTAA().
|
||||
*/
|
||||
/*
|
||||
EU / CN
|
||||
Frequencies:
|
||||
|
||||
EU 863-870 MHz (LoRaWAN Specification (2015), Page 35, Table 14)
|
||||
CN 779-787 MHz (LoRaWAN Specification (2015), Page 44, Table 25)
|
||||
EU 433 MHz (LoRaWAN Specification (2015), Page 48, Table 31)
|
||||
DataRate Modulation SF BW bit/s
|
||||
0 LoRa 12 125 250
|
||||
1 LoRa 11 125 440
|
||||
2 LoRa 10 125 980
|
||||
3 LoRa 9 125 1'760
|
||||
4 LoRa 8 125 3'125
|
||||
5 LoRa 7 125 5'470
|
||||
6 LoRa 7 250 11'000
|
||||
7 FSK 50 kbps 50'000
|
||||
Data rates 8-15 are reserved.
|
||||
|
||||
US
|
||||
Frequencies:
|
||||
|
||||
US 902-928 MHz (LoRaWAN Specification (2015), Page 40, Table 18)
|
||||
DataRate Modulation SF BW bit/s
|
||||
0 LoRa 10 125 980
|
||||
1 LoRa 9 125 1'760
|
||||
2 LoRa 8 125 3'125
|
||||
3 LoRa 7 125 5'470
|
||||
4 LoRa 8 500 12'500
|
||||
8 LoRa 12 500 980
|
||||
9 LoRa 11 500 1'760
|
||||
10 LoRa 10 500 3'900
|
||||
11 LoRa 9 500 7'000
|
||||
12 LoRa 8 500 12'500
|
||||
13 LoRa 7 500 21'900
|
||||
Data rates 5-7 and 14-15 are reserved.
|
||||
*/
|
||||
bool setDR(int dr);
|
||||
|
||||
/*
|
||||
@@ -287,6 +323,11 @@ public:
|
||||
|
||||
String peekLastError() const;
|
||||
|
||||
// Compute the air time for a packet in msec.
|
||||
// Formula used from https://www.loratools.nl/#/airtime
|
||||
// @param pl Payload length in bytes
|
||||
float getLoRaAirTime(uint8_t pl) const;
|
||||
|
||||
bool hasJoined() const {
|
||||
return _rn2xx3_handler.Status.Joined;
|
||||
}
|
||||
|
||||
@@ -441,6 +441,7 @@ bool rn2xx3_handler::setSF(uint8_t sf)
|
||||
if (dr >= 0)
|
||||
{
|
||||
_sf = sf;
|
||||
_dr = dr;
|
||||
return setDR(dr);
|
||||
}
|
||||
}
|
||||
@@ -590,10 +591,10 @@ bool rn2xx3_handler::setFrequencyPlan(RN2xx3_datatypes::Freq_plan fp)
|
||||
default:
|
||||
{
|
||||
// set default channels 868.1, 868.3 and 868.5?
|
||||
returnValue = false; // well we didn't do anything, so yes, false
|
||||
break;
|
||||
return false; // well we didn't do anything, so yes, false
|
||||
}
|
||||
}
|
||||
_fp = fp;
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
@@ -697,6 +698,56 @@ bool rn2xx3_handler::getRxDelayValues(uint32_t& rxdelay1,
|
||||
return _rxdelay1 != 0 && _rxdelay2 != 0;
|
||||
}
|
||||
|
||||
float rn2xx3_handler::getLoRaAirTime(uint8_t pl) const
|
||||
{
|
||||
uint8_t sf = _sf; // Spreading factor 7 - 12
|
||||
uint16_t bw = 125; // Bandwidth 125 kHz default for LoRaWAN. 250 kHz also supported.
|
||||
uint8_t cr = 1; // Code Rate 4 / (CR + 4) = 4/5. 4/5 default for LoRaWAN
|
||||
uint8_t n_preamble = 8; // Preamble length Default for frame = 8, beacon = 10
|
||||
bool header = true; // Explicit header Default on for LoRaWAN
|
||||
bool crc = true; // CRC Default on for LoRaWAN
|
||||
|
||||
if (sf > 12) {
|
||||
sf = 12;
|
||||
} else if (sf < 7) {
|
||||
sf = 7;
|
||||
}
|
||||
|
||||
if (cr > 4) {
|
||||
cr = 4;
|
||||
} else if (cr < 1) {
|
||||
cr = 1;
|
||||
}
|
||||
|
||||
// Symbols in frame
|
||||
int payload_length = 8;
|
||||
{
|
||||
int beta_offset = 28;
|
||||
|
||||
if (crc) { beta_offset += 16; }
|
||||
|
||||
if (!header) { beta_offset -= 20; }
|
||||
float beta_f = 8.0f * pl - 4.0f * sf + beta_offset;
|
||||
bool lowDataRateOptimization = (bw == 125 && sf >= 11);
|
||||
|
||||
if (lowDataRateOptimization) {
|
||||
beta_f = beta_f / (4.0f * (sf - 2));
|
||||
} else {
|
||||
beta_f = beta_f / (4.0f * sf);
|
||||
}
|
||||
int beta = static_cast<int>(beta_f + 1.0f); // ceil
|
||||
|
||||
if (beta > 0) {
|
||||
payload_length += (beta * (cr + 4));
|
||||
}
|
||||
}
|
||||
|
||||
// t_symbol and t_air in msec
|
||||
float t_symbol = static_cast<float>(1 << sf) / bw;
|
||||
float t_air = ((n_preamble + 4.25f) + payload_length) * t_symbol;
|
||||
return t_air;
|
||||
}
|
||||
|
||||
void rn2xx3_handler::set_state(rn2xx3_handler::RN_state state) {
|
||||
const bool was_processing_cmd = _processing_cmd != Active_cmd::none;
|
||||
|
||||
|
||||
@@ -189,6 +189,12 @@ public:
|
||||
bool getRxDelayValues(uint32_t& rxdelay1,
|
||||
uint32_t& rxdelay2);
|
||||
|
||||
// Compute the air time for a packet in msec.
|
||||
// Formula used from https://www.loratools.nl/#/airtime
|
||||
// @param pl Payload length in bytes
|
||||
float getLoRaAirTime(uint8_t pl) const;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
RN2xx3_datatypes::Model configureModuleType();
|
||||
@@ -259,6 +265,7 @@ private:
|
||||
RN2xx3_datatypes::Model _moduleType = RN2xx3_datatypes::Model::RN_NA;
|
||||
RN2xx3_datatypes::Freq_plan _fp = RN2xx3_datatypes::Freq_plan::TTN_EU;
|
||||
uint8_t _sf = 7;
|
||||
uint8_t _dr = 5;
|
||||
|
||||
bool _otaa = true; // Switch between OTAA or ABP activation (default OTAA)
|
||||
bool _asyncMode = false; // When set, the user must call async_loop() frequently
|
||||
|
||||
@@ -18,6 +18,7 @@ void handle_timingstats() {
|
||||
html_table_header(F("Function"));
|
||||
html_table_header(F("#calls"));
|
||||
html_table_header(F("call/sec"));
|
||||
html_table_header(F("duty (%)"));
|
||||
html_table_header(F("min (ms)"));
|
||||
html_table_header(F("Avg (ms)"));
|
||||
html_table_header(F("max (ms)"));
|
||||
@@ -35,6 +36,8 @@ void handle_timingstats() {
|
||||
addRowLabel(F("Time span"));
|
||||
addHtml(String(timespan));
|
||||
addHtml(F(" sec"));
|
||||
addRowLabel(F("*"));
|
||||
addHtml(F("Duty cycle based on average < 1 msec is highly unreliable"));
|
||||
html_end_table();
|
||||
|
||||
sendHeadandTail_stdtemplate(_TAIL);
|
||||
@@ -56,17 +59,34 @@ void format_using_threshhold(unsigned long value) {
|
||||
|
||||
void stream_html_timing_stats(const TimingStats& stats, long timeSinceLastReset) {
|
||||
unsigned long minVal, maxVal;
|
||||
unsigned int c = stats.getMinMax(minVal, maxVal);
|
||||
const unsigned int c = stats.getMinMax(minVal, maxVal);
|
||||
|
||||
html_TD();
|
||||
addHtml(String(c));
|
||||
html_TD();
|
||||
float call_per_sec = static_cast<float>(c) / static_cast<float>(timeSinceLastReset) * 1000.0f;
|
||||
const float call_per_sec = static_cast<float>(c) / static_cast<float>(timeSinceLastReset) * 1000.0f;
|
||||
const float avg = stats.getAvg();
|
||||
addHtml(String(call_per_sec, 2));
|
||||
html_TD();
|
||||
{
|
||||
const float duty = (call_per_sec * avg / 10000.0f);
|
||||
String duty_str = String(duty, 2);
|
||||
if (avg < 1000) {
|
||||
// Unreliable as average is below 1 msec
|
||||
duty_str += '*';
|
||||
html_I(duty_str);
|
||||
} else if (duty > 10.0f) {
|
||||
// Over 10% of the time
|
||||
html_B(duty_str);
|
||||
} else {
|
||||
addHtml(duty_str);
|
||||
}
|
||||
}
|
||||
|
||||
html_TD();
|
||||
format_using_threshhold(minVal);
|
||||
html_TD();
|
||||
format_using_threshhold(stats.getAvg());
|
||||
format_using_threshhold(avg);
|
||||
html_TD();
|
||||
format_using_threshhold(maxVal);
|
||||
}
|
||||
|
||||
@@ -268,6 +268,13 @@ struct C018_data_struct {
|
||||
return sampleSetCounter;
|
||||
}
|
||||
|
||||
float getLoRaAirTime(uint8_t pl) const {
|
||||
if (isInitialized()) {
|
||||
return myLora->getLoRaAirTime(pl);
|
||||
}
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
void async_loop() {
|
||||
if (isInitialized()) {
|
||||
rn2xx3_handler::RN_state state = myLora->async_loop();
|
||||
@@ -809,6 +816,21 @@ bool do_process_c018_delay_queue(int controller_number, const C018_queue_element
|
||||
|
||||
bool do_process_c018_delay_queue(int controller_number, const C018_queue_element& element, ControllerSettingsStruct& ControllerSettings) {
|
||||
bool success = C018_data.txHexBytes(element.packed, ControllerSettings.Port);
|
||||
if (success) {
|
||||
uint8_t pl = (element.packed.length() / 2) + 13; // We have a LoRaWAN header of 13 bytes.
|
||||
float airtime_ms = C018_data.getLoRaAirTime(pl);
|
||||
if (airtime_ms > 0.0) {
|
||||
ADD_TIMER_STAT(C018_AIR_TIME, static_cast<unsigned long>(airtime_ms * 1000));
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
String log = F("LoRaWAN : Payload Length: ");
|
||||
log += pl;
|
||||
log += F(" Air Time: ");
|
||||
log += String(airtime_ms, 3);
|
||||
log += F(" ms");
|
||||
addLog(LOG_LEVEL_INFO, log);
|
||||
}
|
||||
}
|
||||
}
|
||||
String error = C018_data.getLastError(); // Clear the error string.
|
||||
|
||||
if (loglevelActiveFor(LOG_LEVEL_INFO)) {
|
||||
|
||||
@@ -228,6 +228,7 @@ String getMiscStatsName(int stat) {
|
||||
case PARSE_SYSVAR: return F("parseSystemVariables()");
|
||||
case PARSE_SYSVAR_NOCHANGE: return F("parseSystemVariables() No change");
|
||||
case HANDLE_SERVING_WEBPAGE: return F("handle webpage");
|
||||
case C018_AIR_TIME: return F("C018 LoRa TTN - Air Time");
|
||||
case C001_DELAY_QUEUE:
|
||||
case C002_DELAY_QUEUE:
|
||||
case C003_DELAY_QUEUE:
|
||||
|
||||
@@ -54,26 +54,33 @@
|
||||
# define C018_DELAY_QUEUE 33
|
||||
# define C019_DELAY_QUEUE 34
|
||||
# define C020_DELAY_QUEUE 35
|
||||
# define TRY_CONNECT_HOST_TCP 36
|
||||
# define TRY_CONNECT_HOST_UDP 37
|
||||
# define HOST_BY_NAME_STATS 38
|
||||
# define CONNECT_CLIENT_STATS 39
|
||||
# define LOAD_CUSTOM_TASK_STATS 40
|
||||
# define WIFI_ISCONNECTED_STATS 41
|
||||
# define WIFI_NOTCONNECTED_STATS 42
|
||||
# define LOAD_TASK_SETTINGS 43
|
||||
# define TRY_OPEN_FILE 44
|
||||
# define FS_GC_SUCCESS 45
|
||||
# define FS_GC_FAIL 46
|
||||
# define PARSE_SYSVAR 47
|
||||
# define PARSE_SYSVAR_NOCHANGE 48
|
||||
# define PARSE_TEMPLATE_PADDED 49
|
||||
# define RULES_PROCESSING 50
|
||||
# define GRAT_ARP_STATS 51
|
||||
# define BACKGROUND_TASKS 52
|
||||
# define HANDLE_SCHEDULER_IDLE 53
|
||||
# define HANDLE_SCHEDULER_TASK 54
|
||||
# define HANDLE_SERVING_WEBPAGE 55
|
||||
# define C021_DELAY_QUEUE 36
|
||||
# define C022_DELAY_QUEUE 37
|
||||
# define C023_DELAY_QUEUE 38
|
||||
# define C024_DELAY_QUEUE 39
|
||||
# define C025_DELAY_QUEUE 40
|
||||
# define C018_AIR_TIME 41
|
||||
# define TRY_CONNECT_HOST_TCP 42
|
||||
# define TRY_CONNECT_HOST_UDP 43
|
||||
# define HOST_BY_NAME_STATS 44
|
||||
# define CONNECT_CLIENT_STATS 45
|
||||
# define LOAD_CUSTOM_TASK_STATS 46
|
||||
# define WIFI_ISCONNECTED_STATS 47
|
||||
# define WIFI_NOTCONNECTED_STATS 48
|
||||
# define LOAD_TASK_SETTINGS 49
|
||||
# define TRY_OPEN_FILE 50
|
||||
# define FS_GC_SUCCESS 51
|
||||
# define FS_GC_FAIL 52
|
||||
# define PARSE_SYSVAR 53
|
||||
# define PARSE_SYSVAR_NOCHANGE 54
|
||||
# define PARSE_TEMPLATE_PADDED 55
|
||||
# define RULES_PROCESSING 56
|
||||
# define GRAT_ARP_STATS 57
|
||||
# define BACKGROUND_TASKS 58
|
||||
# define HANDLE_SCHEDULER_IDLE 59
|
||||
# define HANDLE_SCHEDULER_TASK 60
|
||||
# define HANDLE_SERVING_WEBPAGE 61
|
||||
|
||||
|
||||
class TimingStats {
|
||||
public:
|
||||
@@ -109,7 +116,7 @@ extern std::map<int, TimingStats> controllerStats;
|
||||
extern std::map<int, TimingStats> miscStats;
|
||||
extern unsigned long timingstats_last_reset;
|
||||
|
||||
# define START_TIMER const unsigned statisticsTimerStart(micros());
|
||||
# define START_TIMER const unsigned long statisticsTimerStart(micros());
|
||||
# define STOP_TIMER_TASK(T, F) \
|
||||
if (mustLogFunction(F)) pluginStats[(T) * 256 + (F)].add(usecPassedSince(statisticsTimerStart));
|
||||
# define STOP_TIMER_CONTROLLER(T, F) \
|
||||
@@ -118,12 +125,16 @@ extern unsigned long timingstats_last_reset;
|
||||
// #define STOP_TIMER_LOADFILE miscStats[LOADFILE_STATS].add(usecPassedSince(statisticsTimerStart));
|
||||
# define STOP_TIMER(L) miscStats[L].add(usecPassedSince(statisticsTimerStart));
|
||||
|
||||
// Add a timer statistic value in usec.
|
||||
# define ADD_TIMER_STAT(L, T) miscStats[L].add(T);
|
||||
|
||||
#else // ifdef USES_TIMING_STATS
|
||||
|
||||
# define START_TIMER
|
||||
# define STOP_TIMER_TASK(T, F) ;
|
||||
# define STOP_TIMER_CONTROLLER(T, F) ;
|
||||
# define STOP_TIMER(L) ;
|
||||
# define ADD_TIMER_STAT(L, T) ;
|
||||
|
||||
|
||||
// FIXME TD-er: This class is used as a parameter in functions defined in .ino files.
|
||||
|
||||
@@ -10,6 +10,10 @@ public:
|
||||
|
||||
LongTermTimer() {}
|
||||
|
||||
explicit LongTermTimer(bool usenow) : _timer_usec(0ull) {
|
||||
if (usenow) setNow();
|
||||
}
|
||||
|
||||
//explicit LongTermTimer(uint64_t start_time) : _timer_usec(start_time) {}
|
||||
|
||||
inline bool operator<(const LongTermTimer& rhs) const
|
||||
|
||||
Reference in New Issue
Block a user