3-phase PV router
Loading...
Searching...
No Matches
test_main.cpp
Go to the documentation of this file.
1#include <unity.h>
2#include <cstdio>
3#include <chrono>
4#include <vector>
5#include <functional>
6
7// Mock Arduino types for native compilation
8typedef unsigned char byte;
9
10// Include EWMA implementation (copied from main project)
11constexpr uint8_t round_up_to_power_of_2(uint16_t v)
12{
13 if (__builtin_popcount(v) == 1) { return __builtin_ctz(v) - 1; }
14
15 uint8_t next_pow_of_2{ 0 };
16
17 while (v)
18 {
19 v >>= 1;
20 ++next_pow_of_2;
21 }
22
23 return --next_pow_of_2;
24}
25
26template< uint8_t A = 10 >
27class EWMA_average
28{
29private:
30 int32_t ema_raw{ 0 };
31 int32_t ema{ 0 };
32 int32_t ema_ema_raw{ 0 };
33 int32_t ema_ema{ 0 };
34 int32_t ema_ema_ema_raw{ 0 };
35 int32_t ema_ema_ema{ 0 };
36
37public:
49
50 auto getAverageS() const
51 {
52 return ema;
53 }
54 auto getAverageD() const
55 {
56 return (ema << 1) - ema_ema;
57 }
58 auto getAverageT() const
59 {
60 return ema + (ema - ema_ema) + (ema - ema_ema_ema);
61 }
62
63 void reset()
64 {
66 }
67};
68
69// Simple moving average for comparison
71{
72private:
73 static const int WINDOW_SIZE = 32;
75 int index;
76 int count;
77 int32_t sum;
78
79public:
81 : index(0), count(0), sum(0)
82 {
83 for (int i = 0; i < WINDOW_SIZE; i++) values[i] = 0;
84 }
85
86 void addValue(int32_t value)
87 {
88 sum -= values[index];
89 values[index] = value;
90 sum += value;
91 index = (index + 1) % WINDOW_SIZE;
92 if (count < WINDOW_SIZE) count++;
93 }
94
95 int32_t getAverage() const
96 {
97 return count > 0 ? sum / count : 0;
98 }
99
100 void reset()
101 {
102 index = count = sum = 0;
103 for (int i = 0; i < WINDOW_SIZE; i++) values[i] = 0;
104 }
105};
106
107void setUp(void)
108{
109 // Set up before each test
110}
111
112void tearDown(void)
113{
114 // Clean up after each test
115}
116
117// Benchmark utility function
118double benchmark_operation(std::function< void() > operation, int iterations = 100000)
119{
120 auto start = std::chrono::high_resolution_clock::now();
121
122 for (int i = 0; i < iterations; i++)
123 {
124 operation();
125 }
126
127 auto end = std::chrono::high_resolution_clock::now();
128 auto duration = std::chrono::duration_cast< std::chrono::nanoseconds >(end - start);
129
130 return double(duration.count()) / iterations; // nanoseconds per operation
131}
132
134{
135 printf("\n=== EWMA Performance Benchmark ===\n");
136 printf("Operations per test: 100,000\n");
137 printf("Results in nanoseconds per operation\n\n");
138
139 // Test instances
142 volatile int32_t dummy_result = 0;
143 volatile int32_t test_value = 1000;
144
145 // Benchmark EMA operations
146 double ema_time = benchmark_operation([&]()
147 {
148 ema.addValue(test_value);
149 dummy_result = ema.getAverageS();
150 });
151
152 double dema_time = benchmark_operation([&]()
153 {
154 ema.addValue(test_value);
155 dummy_result = ema.getAverageD();
156 });
157
158 double tema_time = benchmark_operation([&]()
159 {
160 ema.addValue(test_value);
161 dummy_result = ema.getAverageT();
162 });
163
164 double sma_time = benchmark_operation([&]()
165 {
166 sma.addValue(test_value);
167 dummy_result = sma.getAverage();
168 });
169
170 printf("EMA (Single): %8.2f ns/op\n", ema_time);
171 printf("DEMA (Double): %8.2f ns/op\n", dema_time);
172 printf("TEMA (Triple): %8.2f ns/op\n", tema_time);
173 printf("Simple Moving Avg: %8.2f ns/op\n", sma_time);
174
175 printf("\nPerformance ratios (vs EMA):\n");
176 printf("DEMA overhead: %6.2fx\n", dema_time / ema_time);
177 printf("TEMA overhead: %6.2fx\n", tema_time / ema_time);
178 printf("SMA overhead: %6.2fx\n", sma_time / ema_time);
179
180 // Basic performance assertions
181 TEST_ASSERT_LESS_THAN(1000, ema_time); // Should be under 1 microsecond
182 TEST_ASSERT_LESS_THAN(2000, dema_time); // DEMA should be under 2 microseconds
183 TEST_ASSERT_LESS_THAN(3000, tema_time); // TEMA should be under 3 microseconds
184}
185
187{
188 printf("\n=== Cloud Immunity Simulation ===\n");
189
190 // Cloud simulation data (realistic power values with cloud effects)
191 std::vector< int32_t > cloud_data = {
192 1000, 1020, 980, 1050, 1030, 990, 1100, 1080, 950, 1200, // Clear morning
193 400, 600, 300, 800, 200, 900, 150, 750, 100, 850, // Cloudy period
194 1300, 1350, 1320, 1380, 1340, 1300, 1400, 1420, 1380, 1450, // Clear afternoon
195 50, 30, 80, 20, 100, 10, 120, 5, 150, 0 // Evening fade
196 };
197
198 // Test different alpha values
199 EWMA_average< 8 > ema_fast; // Fast response (cloud sensitive)
200 EWMA_average< 32 > ema_med; // Medium response
201 EWMA_average< 128 > ema_slow; // Slow response (cloud immune)
202
203 printf("Processing %zu power measurements...\n", cloud_data.size());
204 printf("Time,Input,EMA_Fast,DEMA_Med,TEMA_Med,EMA_Slow\n");
205
206 int relay_changes_ema_fast = 0;
207 int relay_changes_dema_med = 0;
208 int relay_changes_tema_med = 0;
209 int relay_changes_ema_slow = 0;
210
211 bool prev_relay_ema_fast = false;
212 bool prev_relay_dema_med = false;
213 bool prev_relay_tema_med = false;
214 bool prev_relay_ema_slow = false;
215
216 const int32_t RELAY_THRESHOLD = 500; // 500W threshold for relay
217
218 for (size_t i = 0; i < cloud_data.size(); i++)
219 {
220 int32_t input = cloud_data[i];
221
222 // Feed to all filters
223 ema_fast.addValue(input);
224 ema_med.addValue(input);
225 ema_slow.addValue(input);
226
227 int32_t ema_fast_val = ema_fast.getAverageS();
228 int32_t dema_med_val = ema_med.getAverageD();
229 int32_t tema_med_val = ema_med.getAverageT();
230 int32_t ema_slow_val = ema_slow.getAverageS();
231
232 // Count relay state changes
233 bool relay_ema_fast = ema_fast_val > RELAY_THRESHOLD;
234 bool relay_dema_med = dema_med_val > RELAY_THRESHOLD;
235 bool relay_tema_med = tema_med_val > RELAY_THRESHOLD;
236 bool relay_ema_slow = ema_slow_val > RELAY_THRESHOLD;
237
238 if (relay_ema_fast != prev_relay_ema_fast) relay_changes_ema_fast++;
239 if (relay_dema_med != prev_relay_dema_med) relay_changes_dema_med++;
240 if (relay_tema_med != prev_relay_tema_med) relay_changes_tema_med++;
241 if (relay_ema_slow != prev_relay_ema_slow) relay_changes_ema_slow++;
242
243 prev_relay_ema_fast = relay_ema_fast;
244 prev_relay_dema_med = relay_dema_med;
245 prev_relay_tema_med = relay_tema_med;
246 prev_relay_ema_slow = relay_ema_slow;
247
248 printf("%zu,%d,%d,%d,%d,%d\n",
249 i * 5, (int)input, (int)ema_fast_val, (int)dema_med_val,
250 (int)tema_med_val, (int)ema_slow_val);
251 }
252
253 printf("\nRelay State Changes (lower = better cloud immunity):\n");
254 printf("EMA Fast (α=8): %d changes\n", relay_changes_ema_fast);
255 printf("DEMA Med (α=32): %d changes\n", relay_changes_dema_med);
256 printf("TEMA Med (α=32): %d changes\n", relay_changes_tema_med);
257 printf("EMA Slow (α=128): %d changes\n", relay_changes_ema_slow);
258
259 // TEMA should have fewer or equal relay changes than EMA (better cloud immunity)
260 TEST_ASSERT_LESS_OR_EQUAL(relay_changes_tema_med, relay_changes_ema_fast);
261 TEST_ASSERT_LESS_OR_EQUAL(relay_changes_dema_med, relay_changes_ema_fast);
262}
263
265{
266 printf("\n=== Responsiveness Test ===\n");
267
268 // Test how quickly each filter responds to a step change
269 std::vector< int32_t > step_data = {
270 0, 0, 0, 0, 0, // Start at zero
271 1000, 1000, 1000, 1000, 1000, // Step to 1000W
272 0, 0, 0, 0, 0 // Step back to zero
273 };
274
278
279 printf("Step response test (0W -> 1000W -> 0W):\n");
280 printf("Sample,Input,EMA,DEMA,TEMA\n");
281
282 for (size_t i = 0; i < step_data.size(); i++)
283 {
284 int32_t input = step_data[i];
285
286 ema.addValue(input);
287 dema.addValue(input);
288 tema.addValue(input);
289
290 printf("%zu,%d,%d,%d,%d\n",
291 i, (int)input, (int)ema.getAverageS(),
292 (int)dema.getAverageD(), (int)tema.getAverageT());
293 }
294
295 // DEMA and TEMA should respond faster than EMA to step changes
296 // This is qualitative - the actual test would require settling time analysis
297 TEST_ASSERT_TRUE(true); // Placeholder - results visible in output
298}
299
301{
302 printf("\n=== Alpha Parameter Effects ===\n");
303
304 // Test how different alpha values affect convergence
305 // Use alpha values that work well with TEMA (need at least 4 for TEMA to work)
306 EWMA_average< 8 > ema_fast; // α=8, fast
307 EWMA_average< 32 > ema_med; // α=32, medium
308 EWMA_average< 128 > ema_slow; // α=128, slow
309
310 const int32_t target_value = 800;
311 const int samples = 20;
312
313 printf("Convergence to %dW with different α values:\n", (int)target_value);
314 printf("Sample,α=8,α=32,α=128\n");
315
316 for (int i = 0; i < samples; i++)
317 {
318 ema_fast.addValue(target_value);
319 ema_med.addValue(target_value);
320 ema_slow.addValue(target_value);
321
322 printf("%d,%d,%d,%d\n",
323 i, (int)ema_fast.getAverageS(), (int)ema_med.getAverageS(),
324 (int)ema_slow.getAverageS());
325 }
326
327 // Verify that smaller alpha converges faster
328 TEST_ASSERT_GREATER_THAN(ema_med.getAverageS(), ema_fast.getAverageS());
329 TEST_ASSERT_GREATER_THAN(ema_slow.getAverageS(), ema_med.getAverageS());
330}
331
332int main()
333{
334 UNITY_BEGIN();
335
340
341 return UNITY_END();
342}
Implements an Exponentially Weighted Moving Average (EWMA).
Definition ewma_avg.hpp:108
void addValue(int32_t input)
Definition test_main.cpp:38
int32_t ema_ema_ema
Definition ewma_avg.hpp:162
int32_t ema
Definition ewma_avg.hpp:166
int32_t ema_ema_ema_raw
Definition ewma_avg.hpp:161
auto getAverageD() const
Definition test_main.cpp:54
auto getAverageT() const
Definition test_main.cpp:58
int32_t ema_ema_raw
Definition ewma_avg.hpp:163
int32_t ema_raw
Definition ewma_avg.hpp:165
auto getAverageS() const
Definition test_main.cpp:50
int32_t ema_ema
Definition ewma_avg.hpp:164
static const int WINDOW_SIZE
Definition test_main.cpp:73
int32_t getAverage() const
Definition test_main.cpp:95
void addValue(int32_t value)
Definition test_main.cpp:86
int32_t values[WINDOW_SIZE]
Definition test_main.cpp:74
void setUp(void)
Definition test_main.cpp:6
void tearDown(void)
Definition test_main.cpp:11
uint8_t i
unsigned char byte
Definition test_main.cpp:7
constexpr uint8_t round_up_to_power_of_2(uint16_t v)
Definition test_main.cpp:10
int main()
double benchmark_operation(std::function< void() > operation, int iterations=100000)
void test_performance_comparison()
void test_responsiveness_comparison()
constexpr uint8_t round_up_to_power_of_2(uint16_t v)
Definition test_main.cpp:11
void test_alpha_parameter_effects()
void test_cloud_immunity_simulation()