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

//-----------------------------------------------------------------------------
// 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__*/