Repository navigation
Expand file tree
/
Copy pathLinkHealth.cpp
More file actions
105 lines (95 loc) · 4.46 KB
/
Copy pathLinkHealth.cpp
File metadata and controls
105 lines (95 loc) · 4.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include "LinkHealth.h"
namespace devourer {
LinkHealthVerdict classify_link_health(const LinkHealthInput &in,
const LinkHealthThresholds &th) {
LinkHealthVerdict v;
v.rssi_dbm = in.rssi_raw - 110;
v.snr_db = in.snr_raw / 2.0;
v.evm_db = in.evm_valid ? in.evm_raw / 2.0 : 0.0;
if (in.igi_valid) {
v.igi_at_floor = in.igi <= in.igi_min;
v.igi_at_ceiling = in.igi >= in.igi_max;
}
if (in.frames == 0) {
v.verdict = LinkVerdict::NoSignal;
v.label = "NO_SIGNAL";
v.cause = "no frames decoded this window";
v.fix = "check the TX is up, the channel/bandwidth matches, and the "
"canonical SA is being sent";
return v;
}
/* Signal-strength band and constellation cleanliness. EVM is the primary
* discriminator (SNR misses saturation); RSSI splits strong-dirty from
* weak-dirty. When EVM is absent (CCK-only stream), fall back to SNR. */
const bool strong = in.rssi_raw >= th.rssi_strong;
const bool weak = in.rssi_raw <= th.rssi_weak;
const bool snr_poor = in.snr_raw < th.snr_lo;
const bool snr_good = in.snr_raw >= th.snr_good;
const bool evm_poor = in.evm_valid && in.evm_raw > th.evm_poor;
const bool evm_good = !in.evm_valid || in.evm_raw < th.evm_good;
const bool dirty = evm_poor || (!in.evm_valid && snr_poor);
const bool noisy = in.energy_valid && in.fa_ofdm > th.fa_high;
/* Strong signal but a dirty constellation = the near-field failure the
* classifier exists for: front-end saturation and/or the strong signal
* self-jamming via wall reflections. IGI pinned at its floor (the AGC has
* already backed gain all the way off and still can't cope) corroborates. */
if (strong && dirty) {
v.verdict = LinkVerdict::Saturated;
v.label = "SATURATED";
v.cause = "strong RSSI but poor EVM — receiver front-end overload and/or "
"the strong signal self-jamming via reflections (near-field). "
"SNR alone can look fine here";
v.fix = "REDUCE TX power (SetTxPowerOffsetQdb, e.g. -40..-80 qdB), add an "
"attenuator, or increase distance — do NOT add power/antenna";
return v;
}
/* Not strong, but dirty AND the false-alarm rate is up = something other
* than your own signal is raising the floor: external / co-channel. */
if (!strong && dirty && noisy) {
v.verdict = LinkVerdict::Interference;
v.label = "INTERFERENCE";
v.cause = "elevated false-alarm rate with a degraded constellation while "
"the wanted signal is not strong — external / co-channel energy "
"raising the noise floor";
v.fix = "change channel (FastRetune / hop away from the interferer); a "
"narrowband notch (DEVOURER_RX_NBI) only helps an in-band spur";
return v;
}
/* Weak signal, poor SNR, AGC wide open = a genuine range/sensitivity limit. */
if (weak && (snr_poor || !snr_good)) {
v.verdict = LinkVerdict::Weak;
v.label = "WEAK";
v.cause = "low RSSI and low SNR — genuine range / sensitivity limit "
"(this is where more power or a better antenna actually helps)";
v.fix = "raise TX power, improve antenna gain/alignment, or reduce "
"distance; consider a narrowband re-clock for link budget";
return v;
}
/* Decodes cleanly with comfortable margin. */
if (snr_good && evm_good && !strong) {
v.verdict = LinkVerdict::Healthy;
v.label = "HEALTHY";
v.cause = "good SNR and EVM with RSSI in the linear range";
v.fix = "none — link is in its comfortable operating region";
return v;
}
/* A strong-but-clean link is healthy too, but flag that it is near the top
* of the range (one step from the saturation regime on a near-field bench). */
if (snr_good && evm_good && strong) {
v.verdict = LinkVerdict::Healthy;
v.label = "HEALTHY";
v.cause = "good SNR and EVM, but RSSI is high — near the top of the linear "
"range; a little more power or less distance risks saturation";
v.fix = "fine as-is; leave headroom before adding TX power on a short link";
return v;
}
/* Everything else: decoding but without a comfortable margin. */
v.verdict = LinkVerdict::Marginal;
v.label = "MARGINAL";
v.cause = "decoding, but SNR/EVM is below a comfortable margin and no single "
"cause dominates";
v.fix = "watch the trend; if RSSI is high, try backing off power first "
"(cheap to test), otherwise a small power/antenna improvement";
return v;
}
} // namespace devourer