@@ -51,15 +51,6 @@ size_t decimatedPointCount(size_t count, bool full, size_t max_points) {
5151 return (count > maxPts) ? maxPts : count;
5252}
5353
54- size_t decimatedIndex (size_t out_idx, size_t out_count, size_t src_count) {
55- if (src_count <= 1u || out_count <= 1u ) {
56- return 0u ;
57- }
58- const uint64_t num = static_cast <uint64_t >(out_idx) * static_cast <uint64_t >(src_count - 1u );
59- const uint64_t den = static_cast <uint64_t >(out_count - 1u );
60- return static_cast <size_t >((num + (den / 2u )) / den);
61- }
62-
6354void appendBoolJson (String& out, bool value) {
6455 out += (value ? " true" : " false" );
6556}
@@ -78,6 +69,111 @@ void appendFloatJson(String& out, float value) {
7869 }
7970}
8071
72+ void appendSeriesJson (String& out, const float * series, size_t count, size_t points, bool downsampled) {
73+ out += " ,\" v\" :[" ;
74+ if (count == 0 || points == 0 ) {
75+ out += " ]" ;
76+ return ;
77+ }
78+
79+ if (!downsampled || points >= count) {
80+ for (size_t i = 0 ; i < count; i++) {
81+ if (i > 0 ) {
82+ out += " ," ;
83+ }
84+ appendFloatJson (out, series[i]);
85+ }
86+ out += " ]" ;
87+ return ;
88+ }
89+
90+ if (points == 1u ) {
91+ appendFloatJson (out, series[0 ]);
92+ out += " ]" ;
93+ return ;
94+ }
95+
96+ if (points == 2u ) {
97+ appendFloatJson (out, series[0 ]);
98+ out += " ," ;
99+ appendFloatJson (out, series[count - 1u ]);
100+ out += " ]" ;
101+ return ;
102+ }
103+
104+ // Largest-Triangle-Three-Buckets (LTTB):
105+ // preserve first/last, pick representative points that maximize local area.
106+ appendFloatJson (out, series[0 ]);
107+ const double every = static_cast <double >(count - 2u ) / static_cast <double >(points - 2u );
108+ size_t a = 0u ;
109+
110+ for (size_t i = 0 ; i < points - 2u ; i++) {
111+ size_t avgRangeStart = static_cast <size_t >(floor ((static_cast <double >(i) + 1.0 ) * every)) + 1u ;
112+ size_t avgRangeEnd = static_cast <size_t >(floor ((static_cast <double >(i) + 2.0 ) * every)) + 1u ;
113+ if (avgRangeStart >= count) {
114+ avgRangeStart = count - 1u ;
115+ }
116+ if (avgRangeEnd > count) {
117+ avgRangeEnd = count;
118+ }
119+ if (avgRangeEnd <= avgRangeStart) {
120+ avgRangeEnd = (avgRangeStart + 1u <= count) ? (avgRangeStart + 1u ) : count;
121+ }
122+
123+ double avgX = 0.0 ;
124+ double avgY = 0.0 ;
125+ size_t avgRangeLen = 0u ;
126+ for (size_t j = avgRangeStart; j < avgRangeEnd; j++) {
127+ avgX += static_cast <double >(j);
128+ avgY += static_cast <double >(series[j]);
129+ avgRangeLen++;
130+ }
131+ if (avgRangeLen > 0u ) {
132+ avgX /= static_cast <double >(avgRangeLen);
133+ avgY /= static_cast <double >(avgRangeLen);
134+ } else {
135+ avgX = static_cast <double >(avgRangeStart);
136+ avgY = static_cast <double >(series[avgRangeStart]);
137+ }
138+
139+ size_t rangeOffs = static_cast <size_t >(floor (static_cast <double >(i) * every)) + 1u ;
140+ size_t rangeTo = static_cast <size_t >(floor ((static_cast <double >(i) + 1.0 ) * every)) + 1u ;
141+
142+ const size_t lastIndex = count - 1u ;
143+ if (rangeOffs >= lastIndex) {
144+ rangeOffs = lastIndex - 1u ;
145+ }
146+ if (rangeTo > lastIndex) {
147+ rangeTo = lastIndex;
148+ }
149+ if (rangeTo <= rangeOffs) {
150+ rangeTo = rangeOffs + 1u ;
151+ }
152+
153+ const double ax = static_cast <double >(a);
154+ const double ay = static_cast <double >(series[a]);
155+
156+ size_t nextA = rangeOffs;
157+ double maxArea = -1.0 ;
158+ for (size_t j = rangeOffs; j < rangeTo; j++) {
159+ const double area = fabs ((ax - avgX) * (static_cast <double >(series[j]) - ay)
160+ - (ax - static_cast <double >(j)) * (avgY - ay));
161+ if (area > maxArea) {
162+ maxArea = area;
163+ nextA = j;
164+ }
165+ }
166+
167+ out += " ," ;
168+ appendFloatJson (out, series[nextA]);
169+ a = nextA;
170+ }
171+
172+ out += " ," ;
173+ appendFloatJson (out, series[count - 1u ]);
174+ out += " ]" ;
175+ }
176+
81177} // namespace
82178
83179void telemetryLogInit () {
@@ -198,19 +294,15 @@ String telemetryLogGetJson(bool full, size_t max_points) {
198294 appendBoolJson (out, connected_now);
199295 out += " ,\" downsampled\" :" ;
200296 appendBoolJson (out, downsampled);
297+ if (downsampled) {
298+ out += " ,\" sampleMethod\" :\" lttb\" " ;
299+ }
201300 if (!full) {
202301 out += " ,\" maxPoints\" :" ;
203302 out += String (maxPointsUsed);
204303 }
205- out += " ,\" v\" :[" ;
206- for (size_t i = 0 ; i < points; i++) {
207- if (i > 0 ) {
208- out += " ," ;
209- }
210- const size_t srcIndex = downsampled ? decimatedIndex (i, points, count) : i;
211- appendFloatJson (out, s_copy[srcIndex]);
212- }
213- out += " ]}" ;
304+ appendSeriesJson (out, s_copy, count, points, downsampled);
305+ out += " }" ;
214306
215307 return out;
216308}
0 commit comments