mirror of https://github.com/lumapu/ahoy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
7.8 KiB
187 lines
7.8 KiB
//-----------------------------------------------------------------------------
|
|
// 2023 Ahoy, https://github.com/lumpapu/ahoy
|
|
// Creative Commons - http://creativecommons.org/licenses/by-nc-sa/4.0/deed
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifndef __HEURISTIC_H__
|
|
#define __HEURISTIC_H__
|
|
|
|
#include "../utils/dbg.h"
|
|
#include "hmInverter.h"
|
|
#include "HeuristicInv.h"
|
|
|
|
#define RF_TEST_PERIOD_MAX_SEND_CNT 50
|
|
#define RF_TEST_PERIOD_MAX_FAIL_CNT 5
|
|
|
|
#define RF_TX_TEST_CHAN_1ST_USE 0xff
|
|
|
|
#define RF_TX_CHAN_QUALITY_GOOD 2
|
|
#define RF_TX_CHAN_QUALITY_OK 1
|
|
#define RF_TX_CHAN_QUALITY_LOW -1
|
|
#define RF_TX_CHAN_QUALITY_BAD -2
|
|
|
|
class Heuristic {
|
|
public:
|
|
uint8_t getTxCh(Inverter<> *iv) {
|
|
if((IV_HMS == iv->ivGen) || (IV_HMT == iv->ivGen))
|
|
return 0; // not used for these inverter types
|
|
|
|
HeuristicInv *ih = &iv->heuristics;
|
|
|
|
// start with the next index: round robbin in case of same 'best' quality
|
|
uint8_t curId = (ih->txRfChId + 1) % RF_MAX_CHANNEL_ID;
|
|
ih->lastBestTxChId = ih->txRfChId;
|
|
ih->txRfChId = curId;
|
|
curId = (curId + 1) % RF_MAX_CHANNEL_ID;
|
|
for(uint8_t i = 1; i < RF_MAX_CHANNEL_ID; i++) {
|
|
if(ih->txRfQuality[curId] > ih->txRfQuality[ih->txRfChId])
|
|
ih->txRfChId = curId;
|
|
curId = (curId + 1) % RF_MAX_CHANNEL_ID;
|
|
}
|
|
|
|
if(ih->testPeriodSendCnt < 0xff)
|
|
ih->testPeriodSendCnt++;
|
|
|
|
if((ih->txRfChId == ih->lastBestTxChId) && (ih->testPeriodSendCnt >= RF_TEST_PERIOD_MAX_SEND_CNT)) {
|
|
if(ih->testPeriodFailCnt > RF_TEST_PERIOD_MAX_FAIL_CNT) {
|
|
// try round robbin another chan and see if it works even better
|
|
ih->testChId = (ih->testChId + 1) % RF_MAX_CHANNEL_ID;
|
|
if(ih->testChId == ih->txRfChId)
|
|
ih->testChId = (ih->testChId + 1) % RF_MAX_CHANNEL_ID;
|
|
|
|
// give it a fair chance but remember old status in case of immediate fail
|
|
ih->saveOldTestQuality = ih->txRfQuality[ih->testChId];
|
|
ih->txRfQuality[ih->testChId] = ih->txRfQuality[ih->txRfChId];
|
|
ih->txRfChId = ih->testChId;
|
|
ih->testChId = RF_TX_TEST_CHAN_1ST_USE; // mark the chan as a test and as 1st use during new test period
|
|
DPRINTLN(DBG_INFO, F("Test CH ") + String(id2Ch(ih->txRfChId)));
|
|
}
|
|
|
|
// start new test period
|
|
ih->testPeriodSendCnt = 0;
|
|
ih->testPeriodFailCnt = 0;
|
|
} else if(ih->txRfChId != ih->lastBestTxChId) {
|
|
// start new test period
|
|
ih->testPeriodSendCnt = 0;
|
|
ih->testPeriodFailCnt = 0;
|
|
}
|
|
|
|
return id2Ch(ih->txRfChId);
|
|
}
|
|
|
|
void evalTxChQuality(Inverter<> *iv, bool crcPass, uint8_t retransmits, uint8_t rxFragments) {
|
|
HeuristicInv *ih = &iv->heuristics;
|
|
|
|
#if (DBG_DEBUG == DEBUG_LEVEL)
|
|
DPRINT(DBG_DEBUG, "eval ");
|
|
DBGPRINT(String(crcPass));
|
|
DBGPRINT(", ");
|
|
DBGPRINT(String(retransmits));
|
|
DBGPRINT(", ");
|
|
DBGPRINT(String(rxFragments));
|
|
DBGPRINT(", ");
|
|
DBGPRINTLN(String(ih->lastRxFragments));
|
|
#endif
|
|
|
|
if(ih->lastRxFragments == rxFragments) {
|
|
if(crcPass)
|
|
updateQuality(ih, RF_TX_CHAN_QUALITY_GOOD);
|
|
else if(!retransmits || isNewTxCh(ih)) { // nothing received: send probably lost
|
|
if(RF_TX_TEST_CHAN_1ST_USE == ih->testChId) {
|
|
// switch back to original quality
|
|
DPRINTLN(DBG_INFO, F("Test failed (-2)"));
|
|
ih->txRfQuality[ih->txRfChId] = ih->saveOldTestQuality;
|
|
}
|
|
updateQuality(ih, RF_TX_CHAN_QUALITY_BAD);
|
|
if(ih->testPeriodFailCnt < 0xff)
|
|
ih->testPeriodFailCnt++;
|
|
}
|
|
} else if(!ih->lastRxFragments && crcPass) {
|
|
if(!retransmits || isNewTxCh(ih)) {
|
|
// every fragment received successfull immediately
|
|
updateQuality(ih, RF_TX_CHAN_QUALITY_GOOD);
|
|
} else {
|
|
// every fragment received successfully
|
|
updateQuality(ih, RF_TX_CHAN_QUALITY_OK);
|
|
}
|
|
} else if(crcPass) {
|
|
if(isNewTxCh(ih)) {
|
|
// last Fragment successfully received on new send channel
|
|
updateQuality(ih, RF_TX_CHAN_QUALITY_OK);
|
|
}
|
|
} else if(!retransmits || isNewTxCh(ih)) {
|
|
// no complete receive for this send channel
|
|
if((rxFragments - ih->lastRxFragments) > 2) {
|
|
// graceful evaluation for big inverters that have to send 4 answer packets
|
|
updateQuality(ih, RF_TX_CHAN_QUALITY_OK);
|
|
} else if((rxFragments - ih->lastRxFragments) < 2) {
|
|
if(RF_TX_TEST_CHAN_1ST_USE == ih->testChId) {
|
|
// switch back to original quality
|
|
DPRINTLN(DBG_INFO, F("Test failed (-1)"));
|
|
ih->txRfQuality[ih->txRfChId] = ih->saveOldTestQuality;
|
|
}
|
|
updateQuality(ih, RF_TX_CHAN_QUALITY_LOW);
|
|
if(ih->testPeriodFailCnt < 0xff)
|
|
ih->testPeriodFailCnt++;
|
|
} // else: _QUALITY_NEUTRAL, keep any test channel
|
|
} // else: dont overestimate burst distortion
|
|
|
|
ih->testChId = ih->txRfChId; // reset to best
|
|
ih->lastRxFragments = rxFragments;
|
|
}
|
|
|
|
void printStatus(Inverter<> *iv) {
|
|
DPRINT_IVID(DBG_INFO, iv->id);
|
|
DBGPRINT(F("Radio infos:"));
|
|
if((IV_HMS != iv->ivGen) && (IV_HMT != iv->ivGen)) {
|
|
for(uint8_t i = 0; i < RF_MAX_CHANNEL_ID; i++) {
|
|
DBGPRINT(F(" "));
|
|
DBGPRINT(String(iv->heuristics.txRfQuality[i]));
|
|
}
|
|
DBGPRINT(F(" |"));
|
|
}
|
|
DBGPRINT(F(" t: "));
|
|
DBGPRINT(String(iv->radioStatistics.txCnt));
|
|
DBGPRINT(F(", s: "));
|
|
DBGPRINT(String(iv->radioStatistics.rxSuccess));
|
|
DBGPRINT(F(", f: "));
|
|
DBGPRINT(String(iv->radioStatistics.rxFail));
|
|
DBGPRINT(F(", n: "));
|
|
DBGPRINT(String(iv->radioStatistics.rxFailNoAnser));
|
|
DBGPRINT(F(" | p: ")); // better debugging for helpers...
|
|
if((IV_HMS == iv->ivGen) || (IV_HMT == iv->ivGen))
|
|
DBGPRINTLN(String(iv->config->powerLevel-10));
|
|
else
|
|
DBGPRINTLN(String(iv->config->powerLevel));
|
|
}
|
|
|
|
private:
|
|
bool isNewTxCh(HeuristicInv *ih) {
|
|
return ih->txRfChId != ih->lastBestTxChId;
|
|
}
|
|
|
|
void updateQuality(HeuristicInv *ih, uint8_t quality) {
|
|
ih->txRfQuality[ih->txRfChId] += quality;
|
|
if(ih->txRfQuality[ih->txRfChId] > RF_MAX_QUALITY)
|
|
ih->txRfQuality[ih->txRfChId] = RF_MAX_QUALITY;
|
|
else if(ih->txRfQuality[ih->txRfChId] < RF_MIN_QUALTIY)
|
|
ih->txRfQuality[ih->txRfChId] = RF_MIN_QUALTIY;
|
|
}
|
|
|
|
inline uint8_t id2Ch(uint8_t id) {
|
|
switch(id) {
|
|
case 0: return 3;
|
|
case 1: return 23;
|
|
case 2: return 40;
|
|
case 3: return 61;
|
|
case 4: return 75;
|
|
}
|
|
return 3; // standard
|
|
}
|
|
|
|
private:
|
|
uint8_t mChList[5] = {03, 23, 40, 61, 75};
|
|
};
|
|
|
|
|
|
#endif /*__HEURISTIC_H__*/
|
|
|