forked from Discriminator/PDW
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathHealthSource.cpp
More file actions
89 lines (78 loc) · 3.4 KB
/
Copy pathHealthSource.cpp
File metadata and controls
89 lines (78 loc) · 3.4 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
// HealthSource.cpp -- selectable RX-health source (see HealthSource.h)
// FIX [HealthSource]
#include <windows.h>
#include "Headers/pdw.h"
#include "HealthSource.h"
#include "utils/rxq.h"
extern PROFILE Profile;
extern double dRX_Quality;
/* FIX [HealthRxqStale]: the penalty EMA only recomputes on decoded frames
** (FLEX valid-BIW / POCSAG message paths, see Rxq_UpdateTimeBased call sites) -
** on dead air NOTHING recomputes it, so it freezes at its last (typically
** healthy) value. Without this check a receiver that dies after a healthy
** period keeps showing a green ~100% and the [RxQualZero] dead-receiver mail
** alert can never fire for this source. After this window without a recompute
** the score reads 0 ("nothing decodable is coming in").
**
** Deliberately implemented HERE and not as a timer-driven Rxq_UpdateTimeBased():
** the EMA also feeds the telnet wire format, whose call-site parity with
** p2kflexDecoder is a recorded design decision ([RxqFrameGate]) - the wire
** score keeps freezing exactly like p2kflex does; only the Health-panel/alert
** interpretation of it decays. Caveat: on networks with legitimate traffic
** gaps longer than this window (sporadic POCSAG) the score dips to 0 between
** transmissions; the mail alert additionally needs nRxQualMinutes consecutive
** low minutes, and the classic needle source is the better pick there. */
#define HEALTH_RXQ_STALE_MS 120000UL /* 2 min without a decoded frame = dead air */
static int HealthRxqIsStale(void)
{
unsigned long long last = Rxq_LastUpdateTick();
if (!Rxq_HasData() || !last) return 0; /* cold start: HSTAT_IDLE handles it */
return (GetTickCount64() - last) > HEALTH_RXQ_STALE_MS;
}
double Health_GetScore(void)
{
if (Profile.nHealthSource == HEALTH_SRC_TELNET)
{
if (HealthRxqIsStale()) return 0.0; // FIX [HealthRxqStale]: dead air, not the frozen EMA
double q = Rxq_GetEMA();
if (q < 0.0) q = 0.0;
if (q > 100.0) q = 100.0;
return q;
}
return dRX_Quality;
}
int Health_HasData(void)
{
if (Profile.nHealthSource == HEALTH_SRC_TELNET)
return Rxq_HasData();
// Classic source: dRX_Quality sits at exactly 0.0 until the first decode
// touches CountBiterrors (same convention the RX-Q corner box uses to
// show "RX-Q" instead of a percentage). But 0.0 is ambiguous: a LIVE
// receiver decoding pure noise also reads exactly 0.0. Make the flag
// sticky once any real (non-zero) reading has been seen, so a true 0%
// period plots as a 0-line in the trend (and shows "0%") instead of
// being treated as "no data yet".
static int s_needleSeenData = 0;
if (dRX_Quality != 0.0) s_needleSeenData = 1;
return s_needleSeenData;
}
int Health_GetStatus(void)
{
if (!Health_HasData()) return HSTAT_IDLE;
double q = Health_GetScore();
// Red boundary follows the mail-alert threshold so "red" always means
// "this is mail-alert territory". Clamp to keep a sane band even if the
// user configured an extreme threshold.
int red = Profile.nRxQualThreshold;
if (red < 1) red = 1;
if (red > 95) red = 95;
if (q < (double)red) return HSTAT_RED;
if (q >= 96.0) return HSTAT_GREEN; // same green bar as the RX-Q corner box
return HSTAT_ORANGE;
}
const char *Health_SourceName(int src)
{
// FIX [HealthSourceName]: user-facing name is "Penalty system" - the algorithm is
// shared with the telnet wire score but works regardless of the telnet server.
return (src == HEALTH_SRC_TELNET) ? "Penalty system" : "RX needle";
}