use crate::{MetrologyInsightSignal, MetrologyInsightSocket, PowerMetrics};
#[allow(dead_code)]
fn real_power_from_rms_and_power_factor(
voltage_rms: f32,
current_rms: f32,
power_factor: f32,
) -> f32 {
voltage_rms * current_rms * power_factor
}
fn real_power_from_signals(signal_v: &[f32], signal_i: &[f32]) -> f32 {
if signal_v.is_empty() || signal_v.len() != signal_i.len() {
return 0.0;
}
signal_v
.iter()
.zip(signal_i.iter())
.map(|(&v, &i)| v * i)
.sum::<f32>()
/ signal_v.len() as f32
}
fn reactive_power_from_angle(real_power: f32, apparent_power: f32, c2v_angle_deg: f32) -> f32 {
let q_from_p = real_power * crate::math::tan(c2v_angle_deg.to_radians());
if q_from_p.abs() > apparent_power {
apparent_power * c2v_angle_deg.signum()
} else {
q_from_p
}
}
fn apparent_power_from_rms(voltage_rms: f32, current_rms: f32) -> f32 {
voltage_rms * current_rms
}
fn power_factor_from_apparent_and_real(apparent_power: f32, real_power: f32) -> f32 {
if apparent_power.abs() > 0.0 {
(real_power / apparent_power).clamp(-1.0, 1.0)
} else {
0.0
}
}
fn calculate_all_power_metrics(
voltage_signal: &mut MetrologyInsightSignal,
current_signal: &mut MetrologyInsightSignal,
c2v_angle: f32,
) -> PowerMetrics {
let real_power = real_power_from_signals(
voltage_signal.real_wave_slice(),
current_signal.real_wave_slice(),
);
let apparent_power = apparent_power_from_rms(voltage_signal.rms, current_signal.rms);
let reactive_power = reactive_power_from_angle(real_power, apparent_power, c2v_angle);
let power_factor_calc = power_factor_from_apparent_and_real(apparent_power, real_power);
let displacement_pf = crate::math::cos(c2v_angle.to_radians());
PowerMetrics {
real_power,
reactive_power,
apparent_power,
power_factor: power_factor_calc,
displacement_pf,
}
}
pub fn update_power_metrics(socket: &mut MetrologyInsightSocket, active_phases: usize) {
for i in 0..active_phases {
let c2v_angle = socket.phases[i].phase_angles.c2v_angle;
socket.phases[i].power_metrics = calculate_all_power_metrics(
&mut socket.phases[i].voltage,
&mut socket.phases[i].current,
c2v_angle,
);
}
let mut total_real: f32 = 0.0;
let mut total_react: f32 = 0.0;
for i in 0..active_phases {
total_real += socket.phases[i].power_metrics.real_power;
total_react += socket.phases[i].power_metrics.reactive_power;
}
let total_apparent = libm::sqrtf(total_real * total_real + total_react * total_react);
let total_pf = if total_apparent > 0.0 {
total_real / total_apparent
} else {
0.0
};
socket.power_metrics_total = PowerMetrics {
real_power: total_real,
reactive_power: total_react,
apparent_power: total_apparent,
power_factor: total_pf.clamp(-1.0, 1.0),
displacement_pf: 0.0, };
if socket.power_metrics_total.real_power.abs() > 0.0 {
socket.power_metrics_total.displacement_pf = crate::math::cos(crate::math::atan2(
socket.power_metrics_total.reactive_power,
socket.power_metrics_total.real_power,
));
}
}