29 float apparentPower = 0.0f;
30 float powerFactor = NAN;
37 virtual void begin() { _enabled =
true; };
38 virtual void end() { _enabled =
false; };
39 virtual const char* type()
const {
return "virtual"; }
51 _dutyCycleLimit = _contrain(limit, 0, 1);
52 if (_dutyCycle > _dutyCycleLimit)
63 _dutyCycleMin = _contrain(min, 0, _dutyCycleMax);
74 _dutyCycleMax = _contrain(max, _dutyCycleMin, 1);
111 _powerLUTEnabled =
false;
115 if (semiPeriod > 0) {
117 _semiPeriod = semiPeriod;
120 if (_semiPeriod == 0) {
121 ESP_LOGE(
"MycilaDimmer",
"enablePowerLUT: semiPeriod must be provided or must be already set when enabling power LUT");
123 assert(_semiPeriod > 0);
125 _powerLUTEnabled =
true;
150 bool isOnline()
const {
return _enabled && _online; }
159 _dutyCycleFire = 0.0f;
204 _dutyCycle = _contrain(dutyCycle, 0, _dutyCycleLimit);
208 if (_powerLUTEnabled) {
210 _dutyCycleFire = 0.0f;
211 }
else if (mapped == 1) {
212 _dutyCycleFire = 1.0f;
214 _dutyCycleFire = 1.0f -
static_cast<float>(_lookupFiringDelay(mapped, _semiPeriod)) /
static_cast<float>(_semiPeriod);
217 _dutyCycleFire = mapped;
235 float getDutyCycleMapped()
const {
return _dutyCycleMin + _dutyCycle * (_dutyCycleMax - _dutyCycleMin); }
255 bool calculateHarmonics(
float* array,
size_t n)
const {
256 if (array ==
nullptr || n == 0)
260 if (!
isOnline() || _dutyCycleFire <= 0.0f) {
261 for (
size_t i = 0; i < n; i++) {
267 if (_dutyCycleFire >= 1.0f) {
269 for (
size_t i = 1; i < n; i++) {
276 for (
size_t i = 0; i < n; i++) {
280 return _calculateHarmonics(array, n);
283 virtual bool calculateMetrics(
Metrics& metrics,
float gridVoltage,
float loadResistance)
const {
return false; }
285#ifdef MYCILA_JSON_SUPPORT
291 virtual void toJson(
const JsonObject& root)
const {
292 root[
"type"] = type();
293 root[
"enabled"] = _enabled;
294 root[
"online"] = _online;
295 root[
"state"] =
isOn() ?
"on" :
"off";
296 root[
"duty_cycle"] = _dutyCycle;
298 root[
"duty_cycle_fire"] = _dutyCycleFire;
299 root[
"duty_cycle_limit"] = _dutyCycleLimit;
300 root[
"duty_cycle_min"] = _dutyCycleMin;
301 root[
"duty_cycle_max"] = _dutyCycleMax;
302 root[
"power_lut"] = _powerLUTEnabled;
303 root[
"power_lut_semi_period"] = _semiPeriod;
304 JsonObject harmonics = root[
"harmonics"].to<JsonObject>();
306 if (calculateHarmonics(output, 11)) {
307 for (
size_t i = 0; i < 11; i++) {
308 if (!std::isnan(output[i])) {
310 snprintf(key,
sizeof(key),
"H%d",
static_cast<int>(2 * i + 1));
311 harmonics[key] = output[i];
319 bool _enabled =
false;
320 bool _online =
false;
322 float _dutyCycle = 0.0f;
323 float _dutyCycleFire = 0.0f;
324 float _dutyCycleLimit = 1.0f;
325 float _dutyCycleMin = 0.0f;
326 float _dutyCycleMax = 1.0f;
328 bool _powerLUTEnabled =
false;
329 uint16_t _semiPeriod = 0;
331 virtual bool _apply() {
return _enabled; }
332 virtual bool _calculateHarmonics(
float* array,
size_t n)
const {
333 for (
size_t i = 0; i < n; i++) {
343 static uint16_t _lookupFiringDelay(
float dutyCycle, uint16_t semiPeriod);
345 static inline float _contrain(
float amt,
float low,
float high) {
346 return (amt < low) ? low : ((amt > high) ? high : amt);
349 static bool _calculatePhaseControlHarmonics(
float dutyCycleFire,
float* array,
size_t n) {
353 const float firingAngle = M_PI * (1.0f - dutyCycleFire);
357 const float sin_2a = sinf(2.0f * firingAngle);
358 const float i1_rms = sqrtf((2.0f / M_PI) * (M_PI - firingAngle + 0.5f * sin_2a));
360 if (i1_rms <= 0.001f)
366 const float scale_factor = (2.0f / M_PI) * 0.70710678f * 100.0f / i1_rms;
372 for (
size_t i = 1; i < n; i++) {
373 const float n_f =
static_cast<float>(2 * i + 1);
374 const float n_minus_1 = n_f - 1.0f;
375 const float n_plus_1 = n_f + 1.0f;
378 const float coeff = cosf(n_minus_1 * firingAngle) / n_minus_1 -
379 cosf(n_plus_1 * firingAngle) / n_plus_1;
382 array[i] = fabsf(coeff) * scale_factor;
388 static bool _calculatePhaseControlMetrics(
Metrics& metrics,
float dutyCycleFire,
float gridVoltage,
float loadResistance) {
389 if (loadResistance > 0 && gridVoltage > 0) {
390 if (dutyCycleFire > 0) {
391 const float nominalPower = gridVoltage * gridVoltage / loadResistance;
392 if (dutyCycleFire >= 1.0f) {
394 metrics.powerFactor = 1.0f;
396 metrics.power = nominalPower;
397 metrics.voltage = gridVoltage;
398 metrics.current = gridVoltage / loadResistance;
399 metrics.apparentPower = nominalPower;
403 metrics.powerFactor = std::sqrt(dutyCycleFire);
404 metrics.thdi = 100.0f * std::sqrt(1 / dutyCycleFire - 1);
405 metrics.power = dutyCycleFire * nominalPower;
406 metrics.voltage = metrics.powerFactor * gridVoltage;
407 metrics.current = metrics.voltage / loadResistance;
408 metrics.apparentPower = gridVoltage * metrics.current;
413 metrics.voltage = 0.0f;
414 metrics.current = 0.0f;
415 metrics.power = 0.0f;
416 metrics.apparentPower = 0.0f;
417 metrics.powerFactor = NAN;