29 float apparentPower = 0.0f;
30 float powerFactor = NAN;
37 virtual bool begin() {
41 virtual void end() { _enabled =
false; }
42 virtual const char* type()
const {
return "virtual"; }
54 _dutyCycleLimit = _contrain(limit, 0, 1);
55 if (_dutyCycle > _dutyCycleLimit)
66 _dutyCycleMin = _contrain(min, 0, _dutyCycleMax);
77 _dutyCycleMax = _contrain(max, _dutyCycleMin, 1);
127 static void setSemiPeriod(uint16_t semiPeriod) { _semiPeriod = semiPeriod; }
142 bool isOnline()
const {
return _enabled && _online && (!_powerLUTEnabled || _semiPeriod > 0); }
151 _dutyCycleFire = 0.0f;
196 _dutyCycle = _contrain(dutyCycle, 0, _dutyCycleLimit);
200 if (_powerLUTEnabled) {
202 _dutyCycleFire = 0.0f;
203 }
else if (mapped == 1) {
204 _dutyCycleFire = 1.0f;
206 _dutyCycleFire = _semiPeriod > 0 ? (1.0f -
static_cast<float>(_lookupFiringDelay(mapped, _semiPeriod)) /
static_cast<float>(_semiPeriod)) : mapped;
209 _dutyCycleFire = mapped;
227 float getDutyCycleMapped()
const {
return _dutyCycleMin + _dutyCycle * (_dutyCycleMax - _dutyCycleMin); }
247 bool calculateHarmonics(
float* array,
size_t n)
const {
248 if (array ==
nullptr || n == 0)
252 if (!
isOnline() || _dutyCycleFire <= 0.0f) {
253 for (
size_t i = 0; i < n; i++) {
259 if (_dutyCycleFire >= 1.0f) {
261 for (
size_t i = 1; i < n; i++) {
268 for (
size_t i = 0; i < n; i++) {
272 return _calculateHarmonics(array, n);
275 virtual bool calculateMetrics(
Metrics& metrics,
float gridVoltage,
float loadResistance)
const {
return false; }
277#ifdef MYCILA_JSON_SUPPORT
283 virtual void toJson(
const JsonObject& root)
const;
287 bool _enabled =
false;
288 bool _online =
false;
290 float _dutyCycle = 0.0f;
291 float _dutyCycleFire = 0.0f;
292 float _dutyCycleLimit = 1.0f;
293 float _dutyCycleMin = 0.0f;
294 float _dutyCycleMax = 1.0f;
296 bool _powerLUTEnabled =
false;
298 static uint16_t _semiPeriod;
300 virtual bool _apply() {
return _enabled; }
301 virtual bool _calculateHarmonics(
float* array,
size_t n)
const {
302 for (
size_t i = 0; i < n; i++) {
312 static uint16_t _lookupFiringDelay(
float dutyCycle, uint16_t semiPeriod);
314 static inline float _contrain(
float amt,
float low,
float high) {
315 return (amt < low) ? low : ((amt > high) ? high : amt);
318 static bool _calculatePhaseControlHarmonics(
float dutyCycleFire,
float* array,
size_t n) {
322 const float firingAngle = M_PI * (1.0f - dutyCycleFire);
326 const float sin_2a = sinf(2.0f * firingAngle);
327 const float i1_rms = sqrtf((2.0f / M_PI) * (M_PI - firingAngle + 0.5f * sin_2a));
329 if (i1_rms <= 0.001f)
335 const float scale_factor = (2.0f / M_PI) * 0.70710678f * 100.0f / i1_rms;
341 for (
size_t i = 1; i < n; i++) {
342 const float n_f =
static_cast<float>(2 * i + 1);
343 const float n_minus_1 = n_f - 1.0f;
344 const float n_plus_1 = n_f + 1.0f;
347 const float coeff = cosf(n_minus_1 * firingAngle) / n_minus_1 -
348 cosf(n_plus_1 * firingAngle) / n_plus_1;
351 array[i] = fabsf(coeff) * scale_factor;
357 static bool _calculatePhaseControlMetrics(
Metrics& metrics,
float dutyCycleFire,
float gridVoltage,
float loadResistance) {
358 if (loadResistance > 0 && gridVoltage > 0) {
359 if (dutyCycleFire > 0) {
360 const float nominalPower = gridVoltage * gridVoltage / loadResistance;
361 if (dutyCycleFire >= 1.0f) {
363 metrics.powerFactor = 1.0f;
365 metrics.power = nominalPower;
366 metrics.voltage = gridVoltage;
367 metrics.current = gridVoltage / loadResistance;
368 metrics.apparentPower = nominalPower;
372 metrics.powerFactor = std::sqrt(dutyCycleFire);
373 metrics.thdi = 100.0f * std::sqrt(1 / dutyCycleFire - 1);
374 metrics.power = dutyCycleFire * nominalPower;
375 metrics.voltage = metrics.powerFactor * gridVoltage;
376 metrics.current = metrics.voltage / loadResistance;
377 metrics.apparentPower = gridVoltage * metrics.current;
382 metrics.voltage = 0.0f;
383 metrics.current = 0.0f;
384 metrics.power = 0.0f;
385 metrics.apparentPower = 0.0f;
386 metrics.powerFactor = NAN;