ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
telemetry.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file telemetry.cpp
6 * @brief Telemetry math helpers implementation (PC_ENABLE_TELEMETRY).
7 */
8
9#include "telemetry.h"
10
11#if PC_ENABLE_TELEMETRY
12
13#include <math.h>
14
15void pc_window_init(pc_window *w, float *buf, uint16_t cap)
16{
17 w->buf = buf;
18 w->cap = cap;
19 w->count = 0;
20 w->head = 0;
21 w->sum = 0.0;
22 w->sum_sq = 0.0;
23}
24
25void pc_window_push(pc_window *w, float sample)
26{
27 if (!w->buf || w->cap == 0)
28 {
29 return;
30 }
31 if (w->count == w->cap)
32 {
33 // Full: evict the oldest sample (at head) from the running sums.
34 float old = w->buf[w->head];
35 w->sum -= (double)old;
36 w->sum_sq -= (double)old * (double)old;
37 }
38 else
39 {
40 w->count++;
41 }
42 w->buf[w->head] = sample;
43 w->sum += (double)sample;
44 w->sum_sq += (double)sample * (double)sample;
45 w->head = (uint16_t)((w->head + 1) % w->cap);
46}
47
48uint16_t pc_window_count(const pc_window *w)
49{
50 return w->count;
51}
52
53float pc_window_mean(const pc_window *w)
54{
55 if (w->count == 0)
56 {
57 return 0.0f;
58 }
59 return (float)(w->sum / (double)w->count);
60}
61
62float pc_window_variance(const pc_window *w)
63{
64 if (w->count == 0)
65 {
66 return 0.0f;
67 }
68 double mean = w->sum / (double)w->count;
69 double var = w->sum_sq / (double)w->count - mean * mean;
70 return var < 0.0 ? 0.0f : (float)var; // clamp tiny negatives from rounding
71}
72
73float pc_window_stddev(const pc_window *w)
74{
75 return sqrtf(pc_window_variance(w));
76}
77
78float pc_window_min(const pc_window *w)
79{
80 if (w->count == 0)
81 {
82 return 0.0f;
83 }
84 float m = w->buf[0];
85 for (uint16_t i = 1; i < w->count; i++)
86 {
87 if (w->buf[i] < m)
88 {
89 m = w->buf[i];
90 }
91 }
92 return m;
93}
94
95float pc_window_max(const pc_window *w)
96{
97 if (w->count == 0)
98 {
99 return 0.0f;
100 }
101 float m = w->buf[0];
102 for (uint16_t i = 1; i < w->count; i++)
103 {
104 if (w->buf[i] > m)
105 {
106 m = w->buf[i];
107 }
108 }
109 return m;
110}
111
112void pc_rate_init(pc_rate *r)
113{
114 r->last_value = 0.0f;
115 r->last_ms = 0;
116 r->primed = false;
117}
118
119float pc_rate_update(pc_rate *r, float value, uint32_t now_ms)
120{
121 if (!r->primed)
122 {
123 r->last_value = value;
124 r->last_ms = now_ms;
125 r->primed = true;
126 return 0.0f;
127 }
128 uint32_t dt_ms = (uint32_t)(now_ms - r->last_ms); // wraps correctly
129 float rate = 0.0f;
130 if (dt_ms != 0)
131 {
132 rate = (value - r->last_value) * 1000.0f / (float)dt_ms;
133 }
134 r->last_value = value;
135 r->last_ms = now_ms;
136 return rate;
137}
138
139void pc_totalizer_init(pc_totalizer *t)
140{
141 t->total = 0.0;
142 t->last_rate = 0.0f;
143 t->last_ms = 0;
144 t->primed = false;
145}
146
147double pc_totalizer_add(pc_totalizer *t, float rate, uint32_t now_ms)
148{
149 if (!t->primed)
150 {
151 t->last_rate = rate;
152 t->last_ms = now_ms;
153 t->primed = true;
154 return t->total;
155 }
156 uint32_t dt_ms = (uint32_t)(now_ms - t->last_ms); // wraps correctly
157 double dt_s = (double)dt_ms / 1000.0;
158 // Trapezoidal: average of the two rate endpoints over the interval.
159 t->total += ((double)t->last_rate + (double)rate) * 0.5 * dt_s;
160 t->last_rate = rate;
161 t->last_ms = now_ms;
162 return t->total;
163}
164
165double pc_totalizer_total(const pc_totalizer *t)
166{
167 return t->total;
168}
169
170void pc_totalizer_reset(pc_totalizer *t)
171{
172 t->total = 0.0;
173 t->last_rate = 0.0f;
174 t->last_ms = 0;
175 t->primed = false;
176}
177
178#endif // PC_ENABLE_TELEMETRY
Zero-heap telemetry math helpers (PC_ENABLE_TELEMETRY).