use crate::core::scalar::ControlScalar;
#[derive(Debug, Clone, Copy)]
pub struct BoostConverter<S: ControlScalar> {
pub l: S,
pub c: S,
pub r_load: S,
pub v_in: S,
i_l: S,
v_c: S,
}
impl<S: ControlScalar> BoostConverter<S> {
pub fn new(l: S, c: S, r_load: S, v_in: S) -> Self {
Self {
l,
c,
r_load,
v_in,
i_l: S::ZERO,
v_c: S::ZERO,
}
}
pub fn step(&mut self, d: S, dt: S) {
let d_clamped = d.clamp_val(S::ZERO, S::ONE);
let one_minus_d = S::ONE - d_clamped;
let di_l = (self.v_in - one_minus_d * self.v_c) / self.l;
let dv_c = (one_minus_d * self.i_l - self.v_c / self.r_load) / self.c;
self.i_l += di_l * dt;
self.v_c += dv_c * dt;
if self.i_l < S::ZERO {
self.i_l = S::ZERO;
}
}
pub fn v_out(&self) -> S {
self.v_c
}
pub fn i_l(&self) -> S {
self.i_l
}
pub fn duty_for_voltage(&self, v_out_ref: S) -> S {
if v_out_ref > self.v_in {
let d = S::ONE - self.v_in / v_out_ref;
d.clamp_val(S::ZERO, S::from_f64(0.95))
} else {
S::ZERO
}
}
pub fn reset(&mut self) {
self.i_l = S::ZERO;
self.v_c = S::ZERO;
}
}
#[derive(Debug, Clone, Copy)]
pub struct BoostVoltageController<S: ControlScalar> {
pub kp: S,
pub ki: S,
pub d_min: S,
pub d_max: S,
integral: S,
}
impl<S: ControlScalar> BoostVoltageController<S> {
pub fn new(kp: S, ki: S, d_min: S, d_max: S) -> Self {
Self {
kp,
ki,
d_min,
d_max,
integral: S::ZERO,
}
}
pub fn update(&mut self, v_ref: S, v_out: S, dt: S) -> S {
let error = v_ref - v_out;
self.integral += error * dt;
let d = self.kp * error + self.ki * self.integral;
let d_clamped = d.clamp_val(self.d_min, self.d_max);
if d_clamped != d {
self.integral -= error * dt;
}
d_clamped
}
pub fn reset(&mut self) {
self.integral = S::ZERO;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn boost_open_loop_steady_state() {
let mut boost = BoostConverter::new(1e-3_f64, 100e-6, 10.0, 12.0);
let dt = 1e-6_f64;
let d = 0.5_f64;
for _ in 0..200_000 {
boost.step(d, dt);
}
let v_out = boost.v_out();
assert!(
v_out > 18.0 && v_out < 30.0,
"v_out={:.2}V (expected ~24V)",
v_out
);
}
#[test]
fn boost_closed_loop_regulation() {
let mut boost = BoostConverter::new(1e-3_f64, 100e-6, 20.0, 12.0);
let mut ctrl = BoostVoltageController::new(0.01_f64, 5.0, 0.0, 0.9);
let dt = 1e-5_f64;
let v_ref = 24.0_f64;
for _ in 0..500_000 {
let d = ctrl.update(v_ref, boost.v_out(), dt);
boost.step(d, dt);
}
let v_out = boost.v_out();
assert!(
v_out > 20.0,
"v_out={:.2}V should exceed 20V (ref={:.2}V)",
v_out,
v_ref
);
}
#[test]
fn duty_for_voltage_correct() {
let boost = BoostConverter::new(1e-3_f64, 100e-6, 10.0, 12.0);
let d = boost.duty_for_voltage(24.0_f64);
assert!((d - 0.5).abs() < 0.01, "d={:.3}", d);
}
#[test]
fn zero_duty_no_boost() {
let mut boost = BoostConverter::new(1e-4_f64, 100e-6, 10.0, 5.0);
for _ in 0..10000 {
boost.step(0.0, 1e-6);
}
assert!(boost.v_out() < 5.0, "v_out={:.2}", boost.v_out());
}
}