Skip to main content

PidInstanceF32

Struct PidInstanceF32 

Source
pub struct PidInstanceF32 {
    pub a0: f32,
    pub a1: f32,
    pub a2: f32,
    pub state: [f32; 3],
    pub kp: f32,
    pub ki: f32,
    pub kd: f32,
}
Expand description

Instance structure for the floating-point PID Control.

Fields§

§a0: f32§a1: f32§a2: f32§state: [f32; 3]§kp: f32§ki: f32§kd: f32

Implementations§

Source§

impl PidInstanceF32

Source

pub fn new(kp: f32, ki: f32, kd: f32) -> Self

Examples found in repository?
examples/basic_usage.rs (line 36)
5fn main() {
6    println!("=== embedded-dsp Basic Usage Example ===");
7
8    // 1. Vector Operations
9    let a = [1.0f32, 2.0, 3.0, 4.0];
10    let b = [10.0f32, 20.0, 30.0, 40.0];
11    let mut vec_out = [0.0f32; 4];
12    add_f32(&a, &b, &mut vec_out);
13    println!("Vector Add: {:?}", vec_out);
14
15    let dot = dot_prod_f32(&a, &b);
16    println!("Vector Dot Product: {}", dot);
17
18    // 2. Q15 Fixed-Point Saturating Math
19    let q15_a = [q15::from_bits(20000), q15::from_bits(25000)];
20    let q15_b = [q15::from_bits(15000), q15::from_bits(10000)];
21    let mut q15_out = [q15::ZERO; 2];
22    add_q15(&q15_a, &q15_b, &mut q15_out);
23    println!("Q15 Saturating Add (clamped at 32767): {:?}", q15_out);
24
25    // 3. FIR Filtering
26    let coeffs = [0.25f32, 0.5, 0.25]; // 3-tap moving average filter
27    let mut state = [0.0f32; 3 + 4 - 1];
28    let mut fir = FirInstanceF32::init(3, &coeffs, &mut state);
29
30    let input_signal = [1.0f32, 2.0, 3.0, 4.0];
31    let mut filtered_signal = [0.0f32; 4];
32    fir_f32(&mut fir, &input_signal, &mut filtered_signal);
33    println!("FIR Filter Output: {:?}", filtered_signal);
34
35    // 4. PID Motor Controller
36    let mut pid = PidInstanceF32::new(2.0, 0.1, 0.05);
37    let control_output = pid.process(10.0);
38    println!("PID Control Signal: {}", control_output);
39
40    // 5. 64-Point Complex FFT
41    let mut fft_data = [0.0f32; 128]; // 64 complex pairs [re, im, ...]
42    for i in 0..64 {
43        fft_data[2 * i] = (i as f32 * 0.1).sin();
44    }
45    cfft_f32(&mut fft_data, 64, 0, 1);
46    println!("64-Point Complex FFT processed successfully!");
47
48    // 6. 1D Conditional Median Filtering (Impulse / Spike Rejection)
49    let spiky_signal = [1.0f32, 1.1, 1.0, 100.0, 1.2, 1.1, 1.0];
50    let mut clean_signal = [0.0f32; 7];
51    median_filter_1d_f32(&spiky_signal, &mut clean_signal, 3, 5.0);
52    println!("Conditional Median Filter Out: {:?}", clean_signal);
53
54    // 7. Welch's Method Power Spectral Density (PSD)
55    let mut psd_out = [0.0f32; 32];
56    let mut sine_wave = [0.0f32; 128];
57    for (i, val) in sine_wave.iter_mut().enumerate() {
58        *val = (2.0 * core::f32::consts::PI * 100.0 * (i as f32) / 1000.0).sin();
59    }
60    welch_psd_f32(
61        &sine_wave,
62        &mut psd_out,
63        64,
64        32,
65        1000.0,
66        WelchWindow::Hamming,
67        true,
68    );
69    println!("Welch PSD (dB) at bin 6: {:.2} dB", psd_out[6]);
70
71    // 8. 2D Spatial Processing (2D DCT & Sobel Edge Detection)
72    let img_4x4 = [
73        0.0f32, 0.0, 10.0, 10.0, 0.0, 0.0, 10.0, 10.0, 0.0, 0.0, 10.0, 10.0, 0.0, 0.0, 10.0, 10.0,
74    ];
75    let mut edges = [0.0f32; 16];
76    sobel_edge_detection_f32(&img_4x4, &mut edges, 4, 4, 15.0);
77    println!("2D Sobel Edge Output (4x4): {:?}", edges);
78
79    // 9. Weighted Polynomial Least-Squares Sensor Calibration
80    let x_cal = [0.0f32, 1.0, 2.0, 3.0, 4.0];
81    let y_cal = [2.0f32, 5.0, 8.0, 11.0, 14.0]; // y = 2 + 3x
82    let mut cal_coeffs = [0.0f32; 2];
83    polynomial_least_squares_fit(&x_cal, &y_cal, None, 1, &mut cal_coeffs);
84    println!(
85        "Fitted Sensor Calibration: y = {:.2} + {:.2}*x",
86        cal_coeffs[0], cal_coeffs[1]
87    );
88}
More examples
Hide additional examples
examples/motor_control_foc.rs (line 186)
15fn main() {
16    println!("===============================================================================");
17    println!("       embedded-dsp Field-Oriented Control (FOC) & Motor Control Loop          ");
18    println!("===============================================================================");
19    println!();
20
21    const CONTROL_RATE_HZ: f32 = 20000.0; // 20 kHz FOC current loop (50 μs per step)
22    const NUM_CONTROL_CYCLES: usize = 20;
23
24    // -----------------------------------------------------------------------------------------
25    // 1. 3-Phase Current Feedback Simulation with Inverter Switching Ripple
26    // -----------------------------------------------------------------------------------------
27    println!("--- 1. 3-Phase Current Sensing with Inverter PWM Switching Noise ---");
28    let target_torque_current = 5.0f32; // 5 Amps commanded torque current (Iq)
29    let rotor_speed_rpm = 3000.0f32;
30    let pole_pairs = 4.0f32;
31    let electrical_freq_hz = (rotor_speed_rpm * pole_pairs) / 60.0; // 200 Hz electrical freq
32    let omega_e = 2.0 * core::f32::consts::PI * electrical_freq_hz;
33
34    let mut ia_raw = [0.0f32; NUM_CONTROL_CYCLES];
35    let mut ib_raw = [0.0f32; NUM_CONTROL_CYCLES];
36    let mut ic_raw = [0.0f32; NUM_CONTROL_CYCLES];
37    let mut theta_e = [0.0f32; NUM_CONTROL_CYCLES];
38
39    for i in 0..NUM_CONTROL_CYCLES {
40        let t = i as f32 / CONTROL_RATE_HZ;
41        let theta = (omega_e * t) % (2.0 * core::f32::consts::PI);
42        theta_e[i] = theta;
43
44        // Ideal stator currents (balanced 3-phase, 120° apart, Iq = 5A, Id = 0A)
45        // Ia = -Iq * sin(θ), Ib = -Iq * sin(θ - 2π/3), Ic = -Iq * sin(θ + 2π/3)
46        let ia_ideal = -target_torque_current * theta.sin();
47        let ib_ideal = -target_torque_current * (theta - 2.0 * core::f32::consts::PI / 3.0).sin();
48        let ic_ideal = -target_torque_current * (theta + 2.0 * core::f32::consts::PI / 3.0).sin();
49
50        // 20 kHz PWM inverter switching noise (±0.4A ripple)
51        let ripple = if i % 2 == 0 { 0.35f32 } else { -0.35f32 };
52        ia_raw[i] = ia_ideal + ripple;
53        ib_raw[i] = ib_ideal - ripple * 0.5;
54        ic_raw[i] = ic_ideal - ripple * 0.5;
55    }
56
57    println!(
58        "  Initial Raw Stator Currents at t=0: Ia={:.2}A, Ib={:.2}A, Ic={:.2}A",
59        ia_raw[0], ib_raw[0], ic_raw[0]
60    );
61
62    // -----------------------------------------------------------------------------------------
63    // 2. Real-Time Current Filtering (O(1) Recursive Moving Average)
64    // -----------------------------------------------------------------------------------------
65    println!("\n--- 2. Stator Current Feedback Filtering (O(1) Recursive Moving Average) ---");
66    let mut ma_filter_ia = RecursiveMovingAverage::<8>::new();
67    let mut ma_filter_ib = RecursiveMovingAverage::<8>::new();
68    let mut ia_filtered = [0.0f32; NUM_CONTROL_CYCLES];
69    let mut ib_filtered = [0.0f32; NUM_CONTROL_CYCLES];
70
71    for i in 0..NUM_CONTROL_CYCLES {
72        ia_filtered[i] = ma_filter_ia.process(ia_raw[i]);
73        ib_filtered[i] = ma_filter_ib.process(ib_raw[i]);
74    }
75    println!(
76        "  Raw Ia[4] = {:.3}A -> Filtered Ia[4] = {:.3}A",
77        ia_raw[4], ia_filtered[4]
78    );
79    println!(
80        "  Raw Ib[4] = {:.3}A -> Filtered Ib[4] = {:.3}A",
81        ib_raw[4], ib_filtered[4]
82    );
83
84    // -----------------------------------------------------------------------------------------
85    // 3. Fast Trigonometric Evaluation & Angle Transformations
86    // -----------------------------------------------------------------------------------------
87    println!("\n--- 3. Fast Trigonometry vs Table Lookup for Rotor Angle θ ---");
88    let test_angle = core::f32::consts::FRAC_PI_3; // 60 degrees (π/3 rad)
89    let fast_s_i16 = fast_sin_i16(test_angle);
90    let fast_c_i16 = fast_cos_i16(test_angle);
91    let std_s = test_angle.sin();
92    let std_c = test_angle.cos();
93    println!(
94        "  θ = 60°: LUT fast_sin_i16 = {} ({:.4}, Exact: {:.4})",
95        fast_s_i16,
96        fast_s_i16 as f32 / 32768.0,
97        std_s
98    );
99    println!(
100        "  θ = 60°: LUT fast_cos_i16 = {} ({:.4}, Exact: {:.4})",
101        fast_c_i16,
102        fast_c_i16 as f32 / 32768.0,
103        std_c
104    );
105
106    // Q31 Fixed-Point CORDIC Sine/Cosine (angle in [-π, π) mapped to Q31)
107    let angle_q31 = q31::from_bits(((test_angle / core::f32::consts::PI) * 2147483648.0) as i32);
108    let s_q31 = sin_q31(angle_q31);
109    let c_q31 = cos_q31(angle_q31);
110    println!(
111        "  CORDIC Q31 Sine/Cosine: sin = {} ({:.4}, Exact: {:.4}), cos = {} ({:.4}, Exact: {:.4})",
112        s_q31,
113        s_q31.to_bits() as f64 / 2147483648.0,
114        std_s,
115        c_q31,
116        c_q31.to_bits() as f64 / 2147483648.0,
117        std_c
118    );
119
120    // -----------------------------------------------------------------------------------------
121    // 4. Forward Clarke & Park Transforms (f32 & Q15)
122    // -----------------------------------------------------------------------------------------
123    println!("\n--- 4. Forward Clarke & Park Transforms ---");
124    let mut i_alpha = 0.0f32;
125    let mut i_beta = 0.0f32;
126    let mut i_d = 0.0f32;
127    let mut i_q = 0.0f32;
128
129    // Evaluate step 10
130    let step_idx = 10;
131    clarke_f32(
132        ia_filtered[step_idx],
133        ib_filtered[step_idx],
134        &mut i_alpha,
135        &mut i_beta,
136    );
137    park_f32(i_alpha, i_beta, theta_e[step_idx], &mut i_d, &mut i_q);
138
139    println!("  f32 Forward Transforms at Step {}:", step_idx);
140    println!(
141        "    • Stator Stationary Frame: I_alpha = {:>6.3} A, I_beta = {:>6.3} A",
142        i_alpha, i_beta
143    );
144    println!(
145        "    • Rotor Synchronous Frame: I_d (Flux) = {:>6.3} A, I_q (Torque) = {:>6.3} A",
146        i_d, i_q
147    );
148
149    // Fixed-point Q15 Clarke and Park verification
150    let q15_scale = 1000.0f32; // 1 Amp = 1000 Q15 counts
151    let ia_q15 = q15::from_bits((ia_filtered[step_idx] * q15_scale) as i16);
152    let ib_q15 = q15::from_bits((ib_filtered[step_idx] * q15_scale) as i16);
153    let mut q15_alpha = q15::ZERO;
154    let mut q15_beta = q15::ZERO;
155    let mut q15_d = q15::ZERO;
156    let mut q15_q = q15::ZERO;
157
158    clarke_q15(ia_q15, ib_q15, &mut q15_alpha, &mut q15_beta);
159    let sin_q15 = q15::from_bits(fast_sin_i16(theta_e[step_idx]));
160    let cos_q15 = q15::from_bits(fast_cos_i16(theta_e[step_idx]));
161    park_q15(
162        q15_alpha, q15_beta, sin_q15, cos_q15, &mut q15_d, &mut q15_q,
163    );
164
165    println!("  Q15 Forward Transforms:");
166    println!(
167        "    • Q15 Clarke: alpha = {} ({:.3} A), beta = {} ({:.3} A)",
168        q15_alpha,
169        q15_alpha.to_bits() as f32 / q15_scale,
170        q15_beta,
171        q15_beta.to_bits() as f32 / q15_scale
172    );
173    println!(
174        "    • Q15 Park  : d = {} ({:.3} A), q = {} ({:.3} A)",
175        q15_d,
176        q15_d.to_bits() as f32 / q15_scale,
177        q15_q,
178        q15_q.to_bits() as f32 / q15_scale
179    );
180
181    // -----------------------------------------------------------------------------------------
182    // 5. Dual Current Regulators (Id & Iq PID Loops) + Outer Speed Controller
183    // -----------------------------------------------------------------------------------------
184    println!("\n--- 5. Vector Current Regulators (Dual PID Loops for Id & Iq) ---");
185    // Outer Velocity Loop: Target 3000 RPM, Actual 2950 RPM -> Error = 50 RPM
186    let mut speed_pid = PidInstanceF32::new(0.08, 0.005, 0.001);
187    let speed_error_rpm = 50.0f32;
188    let demanded_iq = speed_pid.process(speed_error_rpm).clamp(-15.0, 15.0);
189    println!(
190        "  Outer Speed PID: Error = {:.1} RPM -> Commanded I_q* = {:.3} A",
191        speed_error_rpm, demanded_iq
192    );
193
194    // Inner Current Regulators:
195    // Id controller: Setpoint = 0.0 A (Zero d-axis current for Maximum Torque Per Ampere)
196    // Iq controller: Setpoint = demanded_iq
197    let mut id_pid = PidInstanceF32::new(2.5, 0.15, 0.0);
198    let mut iq_pid = PidInstanceF32::new(2.5, 0.15, 0.0);
199
200    let id_setpoint = 0.0f32;
201    let iq_setpoint = demanded_iq;
202
203    let id_error = id_setpoint - i_d;
204    let iq_error = iq_setpoint - i_q;
205
206    let v_d_command = id_pid.process(id_error).clamp(-24.0, 24.0); // 24V DC bus voltage limit
207    let v_q_command = iq_pid.process(iq_error).clamp(-24.0, 24.0);
208
209    println!("  Inner Current PIDs:");
210    println!(
211        "    • d-Axis (Flux)  : Error = {:>6.3} A -> Commanded V_d = {:>6.3} V",
212        id_error, v_d_command
213    );
214    println!(
215        "    • q-Axis (Torque): Error = {:>6.3} A -> Commanded V_q = {:>6.3} V",
216        iq_error, v_q_command
217    );
218
219    // -----------------------------------------------------------------------------------------
220    // 6. Inverse Park & Inverse Clarke Transforms (Modulation Voltages)
221    // -----------------------------------------------------------------------------------------
222    println!("\n--- 6. Inverse Park & Inverse Clarke Transforms (SVPWM Modulation) ---");
223    let mut v_alpha = 0.0f32;
224    let mut v_beta = 0.0f32;
225    let mut v_a = 0.0f32;
226    let mut v_b = 0.0f32;
227
228    inv_park_f32(
229        v_d_command,
230        v_q_command,
231        theta_e[step_idx],
232        &mut v_alpha,
233        &mut v_beta,
234    );
235    inv_clarke_f32(v_alpha, v_beta, &mut v_a, &mut v_b);
236    let v_c = -v_a - v_b; // 3-phase balanced neutral
237
238    println!(
239        "  Inverse Park   : V_alpha = {:>6.3} V, V_beta = {:>6.3} V",
240        v_alpha, v_beta
241    );
242    println!(
243        "  Inverse Clarke : V_a = {:>6.3} V, V_b = {:>6.3} V, V_c = {:>6.3} V",
244        v_a, v_b, v_c
245    );
246
247    // Compute PWM Duty Cycles for Inverter Gate Drivers (normalized to 0.0 .. 1.0)
248    let v_dc = 24.0f32;
249    let duty_a = (v_a / v_dc + 0.5).clamp(0.0, 1.0);
250    let duty_b = (v_b / v_dc + 0.5).clamp(0.0, 1.0);
251    let duty_c = (v_c / v_dc + 0.5).clamp(0.0, 1.0);
252
253    println!("  Generated Inverter PWM Duty Cycles:");
254    println!("    • Phase A Duty : {:>5.1} %", duty_a * 100.0);
255    println!("    • Phase B Duty : {:>5.1} %", duty_b * 100.0);
256    println!("    • Phase C Duty : {:>5.1} %", duty_c * 100.0);
257
258    println!();
259    println!("===============================================================================");
260    println!("             Field-Oriented Control (FOC) Execution Complete!                  ");
261    println!("===============================================================================");
262}
Source

pub fn init(&mut self, reset_state_flag: i32)

Source

pub fn reset(&mut self)

Source

pub fn process(&mut self, in_val: f32) -> f32

Examples found in repository?
examples/basic_usage.rs (line 37)
5fn main() {
6    println!("=== embedded-dsp Basic Usage Example ===");
7
8    // 1. Vector Operations
9    let a = [1.0f32, 2.0, 3.0, 4.0];
10    let b = [10.0f32, 20.0, 30.0, 40.0];
11    let mut vec_out = [0.0f32; 4];
12    add_f32(&a, &b, &mut vec_out);
13    println!("Vector Add: {:?}", vec_out);
14
15    let dot = dot_prod_f32(&a, &b);
16    println!("Vector Dot Product: {}", dot);
17
18    // 2. Q15 Fixed-Point Saturating Math
19    let q15_a = [q15::from_bits(20000), q15::from_bits(25000)];
20    let q15_b = [q15::from_bits(15000), q15::from_bits(10000)];
21    let mut q15_out = [q15::ZERO; 2];
22    add_q15(&q15_a, &q15_b, &mut q15_out);
23    println!("Q15 Saturating Add (clamped at 32767): {:?}", q15_out);
24
25    // 3. FIR Filtering
26    let coeffs = [0.25f32, 0.5, 0.25]; // 3-tap moving average filter
27    let mut state = [0.0f32; 3 + 4 - 1];
28    let mut fir = FirInstanceF32::init(3, &coeffs, &mut state);
29
30    let input_signal = [1.0f32, 2.0, 3.0, 4.0];
31    let mut filtered_signal = [0.0f32; 4];
32    fir_f32(&mut fir, &input_signal, &mut filtered_signal);
33    println!("FIR Filter Output: {:?}", filtered_signal);
34
35    // 4. PID Motor Controller
36    let mut pid = PidInstanceF32::new(2.0, 0.1, 0.05);
37    let control_output = pid.process(10.0);
38    println!("PID Control Signal: {}", control_output);
39
40    // 5. 64-Point Complex FFT
41    let mut fft_data = [0.0f32; 128]; // 64 complex pairs [re, im, ...]
42    for i in 0..64 {
43        fft_data[2 * i] = (i as f32 * 0.1).sin();
44    }
45    cfft_f32(&mut fft_data, 64, 0, 1);
46    println!("64-Point Complex FFT processed successfully!");
47
48    // 6. 1D Conditional Median Filtering (Impulse / Spike Rejection)
49    let spiky_signal = [1.0f32, 1.1, 1.0, 100.0, 1.2, 1.1, 1.0];
50    let mut clean_signal = [0.0f32; 7];
51    median_filter_1d_f32(&spiky_signal, &mut clean_signal, 3, 5.0);
52    println!("Conditional Median Filter Out: {:?}", clean_signal);
53
54    // 7. Welch's Method Power Spectral Density (PSD)
55    let mut psd_out = [0.0f32; 32];
56    let mut sine_wave = [0.0f32; 128];
57    for (i, val) in sine_wave.iter_mut().enumerate() {
58        *val = (2.0 * core::f32::consts::PI * 100.0 * (i as f32) / 1000.0).sin();
59    }
60    welch_psd_f32(
61        &sine_wave,
62        &mut psd_out,
63        64,
64        32,
65        1000.0,
66        WelchWindow::Hamming,
67        true,
68    );
69    println!("Welch PSD (dB) at bin 6: {:.2} dB", psd_out[6]);
70
71    // 8. 2D Spatial Processing (2D DCT & Sobel Edge Detection)
72    let img_4x4 = [
73        0.0f32, 0.0, 10.0, 10.0, 0.0, 0.0, 10.0, 10.0, 0.0, 0.0, 10.0, 10.0, 0.0, 0.0, 10.0, 10.0,
74    ];
75    let mut edges = [0.0f32; 16];
76    sobel_edge_detection_f32(&img_4x4, &mut edges, 4, 4, 15.0);
77    println!("2D Sobel Edge Output (4x4): {:?}", edges);
78
79    // 9. Weighted Polynomial Least-Squares Sensor Calibration
80    let x_cal = [0.0f32, 1.0, 2.0, 3.0, 4.0];
81    let y_cal = [2.0f32, 5.0, 8.0, 11.0, 14.0]; // y = 2 + 3x
82    let mut cal_coeffs = [0.0f32; 2];
83    polynomial_least_squares_fit(&x_cal, &y_cal, None, 1, &mut cal_coeffs);
84    println!(
85        "Fitted Sensor Calibration: y = {:.2} + {:.2}*x",
86        cal_coeffs[0], cal_coeffs[1]
87    );
88}
More examples
Hide additional examples
examples/motor_control_foc.rs (line 188)
15fn main() {
16    println!("===============================================================================");
17    println!("       embedded-dsp Field-Oriented Control (FOC) & Motor Control Loop          ");
18    println!("===============================================================================");
19    println!();
20
21    const CONTROL_RATE_HZ: f32 = 20000.0; // 20 kHz FOC current loop (50 μs per step)
22    const NUM_CONTROL_CYCLES: usize = 20;
23
24    // -----------------------------------------------------------------------------------------
25    // 1. 3-Phase Current Feedback Simulation with Inverter Switching Ripple
26    // -----------------------------------------------------------------------------------------
27    println!("--- 1. 3-Phase Current Sensing with Inverter PWM Switching Noise ---");
28    let target_torque_current = 5.0f32; // 5 Amps commanded torque current (Iq)
29    let rotor_speed_rpm = 3000.0f32;
30    let pole_pairs = 4.0f32;
31    let electrical_freq_hz = (rotor_speed_rpm * pole_pairs) / 60.0; // 200 Hz electrical freq
32    let omega_e = 2.0 * core::f32::consts::PI * electrical_freq_hz;
33
34    let mut ia_raw = [0.0f32; NUM_CONTROL_CYCLES];
35    let mut ib_raw = [0.0f32; NUM_CONTROL_CYCLES];
36    let mut ic_raw = [0.0f32; NUM_CONTROL_CYCLES];
37    let mut theta_e = [0.0f32; NUM_CONTROL_CYCLES];
38
39    for i in 0..NUM_CONTROL_CYCLES {
40        let t = i as f32 / CONTROL_RATE_HZ;
41        let theta = (omega_e * t) % (2.0 * core::f32::consts::PI);
42        theta_e[i] = theta;
43
44        // Ideal stator currents (balanced 3-phase, 120° apart, Iq = 5A, Id = 0A)
45        // Ia = -Iq * sin(θ), Ib = -Iq * sin(θ - 2π/3), Ic = -Iq * sin(θ + 2π/3)
46        let ia_ideal = -target_torque_current * theta.sin();
47        let ib_ideal = -target_torque_current * (theta - 2.0 * core::f32::consts::PI / 3.0).sin();
48        let ic_ideal = -target_torque_current * (theta + 2.0 * core::f32::consts::PI / 3.0).sin();
49
50        // 20 kHz PWM inverter switching noise (±0.4A ripple)
51        let ripple = if i % 2 == 0 { 0.35f32 } else { -0.35f32 };
52        ia_raw[i] = ia_ideal + ripple;
53        ib_raw[i] = ib_ideal - ripple * 0.5;
54        ic_raw[i] = ic_ideal - ripple * 0.5;
55    }
56
57    println!(
58        "  Initial Raw Stator Currents at t=0: Ia={:.2}A, Ib={:.2}A, Ic={:.2}A",
59        ia_raw[0], ib_raw[0], ic_raw[0]
60    );
61
62    // -----------------------------------------------------------------------------------------
63    // 2. Real-Time Current Filtering (O(1) Recursive Moving Average)
64    // -----------------------------------------------------------------------------------------
65    println!("\n--- 2. Stator Current Feedback Filtering (O(1) Recursive Moving Average) ---");
66    let mut ma_filter_ia = RecursiveMovingAverage::<8>::new();
67    let mut ma_filter_ib = RecursiveMovingAverage::<8>::new();
68    let mut ia_filtered = [0.0f32; NUM_CONTROL_CYCLES];
69    let mut ib_filtered = [0.0f32; NUM_CONTROL_CYCLES];
70
71    for i in 0..NUM_CONTROL_CYCLES {
72        ia_filtered[i] = ma_filter_ia.process(ia_raw[i]);
73        ib_filtered[i] = ma_filter_ib.process(ib_raw[i]);
74    }
75    println!(
76        "  Raw Ia[4] = {:.3}A -> Filtered Ia[4] = {:.3}A",
77        ia_raw[4], ia_filtered[4]
78    );
79    println!(
80        "  Raw Ib[4] = {:.3}A -> Filtered Ib[4] = {:.3}A",
81        ib_raw[4], ib_filtered[4]
82    );
83
84    // -----------------------------------------------------------------------------------------
85    // 3. Fast Trigonometric Evaluation & Angle Transformations
86    // -----------------------------------------------------------------------------------------
87    println!("\n--- 3. Fast Trigonometry vs Table Lookup for Rotor Angle θ ---");
88    let test_angle = core::f32::consts::FRAC_PI_3; // 60 degrees (π/3 rad)
89    let fast_s_i16 = fast_sin_i16(test_angle);
90    let fast_c_i16 = fast_cos_i16(test_angle);
91    let std_s = test_angle.sin();
92    let std_c = test_angle.cos();
93    println!(
94        "  θ = 60°: LUT fast_sin_i16 = {} ({:.4}, Exact: {:.4})",
95        fast_s_i16,
96        fast_s_i16 as f32 / 32768.0,
97        std_s
98    );
99    println!(
100        "  θ = 60°: LUT fast_cos_i16 = {} ({:.4}, Exact: {:.4})",
101        fast_c_i16,
102        fast_c_i16 as f32 / 32768.0,
103        std_c
104    );
105
106    // Q31 Fixed-Point CORDIC Sine/Cosine (angle in [-π, π) mapped to Q31)
107    let angle_q31 = q31::from_bits(((test_angle / core::f32::consts::PI) * 2147483648.0) as i32);
108    let s_q31 = sin_q31(angle_q31);
109    let c_q31 = cos_q31(angle_q31);
110    println!(
111        "  CORDIC Q31 Sine/Cosine: sin = {} ({:.4}, Exact: {:.4}), cos = {} ({:.4}, Exact: {:.4})",
112        s_q31,
113        s_q31.to_bits() as f64 / 2147483648.0,
114        std_s,
115        c_q31,
116        c_q31.to_bits() as f64 / 2147483648.0,
117        std_c
118    );
119
120    // -----------------------------------------------------------------------------------------
121    // 4. Forward Clarke & Park Transforms (f32 & Q15)
122    // -----------------------------------------------------------------------------------------
123    println!("\n--- 4. Forward Clarke & Park Transforms ---");
124    let mut i_alpha = 0.0f32;
125    let mut i_beta = 0.0f32;
126    let mut i_d = 0.0f32;
127    let mut i_q = 0.0f32;
128
129    // Evaluate step 10
130    let step_idx = 10;
131    clarke_f32(
132        ia_filtered[step_idx],
133        ib_filtered[step_idx],
134        &mut i_alpha,
135        &mut i_beta,
136    );
137    park_f32(i_alpha, i_beta, theta_e[step_idx], &mut i_d, &mut i_q);
138
139    println!("  f32 Forward Transforms at Step {}:", step_idx);
140    println!(
141        "    • Stator Stationary Frame: I_alpha = {:>6.3} A, I_beta = {:>6.3} A",
142        i_alpha, i_beta
143    );
144    println!(
145        "    • Rotor Synchronous Frame: I_d (Flux) = {:>6.3} A, I_q (Torque) = {:>6.3} A",
146        i_d, i_q
147    );
148
149    // Fixed-point Q15 Clarke and Park verification
150    let q15_scale = 1000.0f32; // 1 Amp = 1000 Q15 counts
151    let ia_q15 = q15::from_bits((ia_filtered[step_idx] * q15_scale) as i16);
152    let ib_q15 = q15::from_bits((ib_filtered[step_idx] * q15_scale) as i16);
153    let mut q15_alpha = q15::ZERO;
154    let mut q15_beta = q15::ZERO;
155    let mut q15_d = q15::ZERO;
156    let mut q15_q = q15::ZERO;
157
158    clarke_q15(ia_q15, ib_q15, &mut q15_alpha, &mut q15_beta);
159    let sin_q15 = q15::from_bits(fast_sin_i16(theta_e[step_idx]));
160    let cos_q15 = q15::from_bits(fast_cos_i16(theta_e[step_idx]));
161    park_q15(
162        q15_alpha, q15_beta, sin_q15, cos_q15, &mut q15_d, &mut q15_q,
163    );
164
165    println!("  Q15 Forward Transforms:");
166    println!(
167        "    • Q15 Clarke: alpha = {} ({:.3} A), beta = {} ({:.3} A)",
168        q15_alpha,
169        q15_alpha.to_bits() as f32 / q15_scale,
170        q15_beta,
171        q15_beta.to_bits() as f32 / q15_scale
172    );
173    println!(
174        "    • Q15 Park  : d = {} ({:.3} A), q = {} ({:.3} A)",
175        q15_d,
176        q15_d.to_bits() as f32 / q15_scale,
177        q15_q,
178        q15_q.to_bits() as f32 / q15_scale
179    );
180
181    // -----------------------------------------------------------------------------------------
182    // 5. Dual Current Regulators (Id & Iq PID Loops) + Outer Speed Controller
183    // -----------------------------------------------------------------------------------------
184    println!("\n--- 5. Vector Current Regulators (Dual PID Loops for Id & Iq) ---");
185    // Outer Velocity Loop: Target 3000 RPM, Actual 2950 RPM -> Error = 50 RPM
186    let mut speed_pid = PidInstanceF32::new(0.08, 0.005, 0.001);
187    let speed_error_rpm = 50.0f32;
188    let demanded_iq = speed_pid.process(speed_error_rpm).clamp(-15.0, 15.0);
189    println!(
190        "  Outer Speed PID: Error = {:.1} RPM -> Commanded I_q* = {:.3} A",
191        speed_error_rpm, demanded_iq
192    );
193
194    // Inner Current Regulators:
195    // Id controller: Setpoint = 0.0 A (Zero d-axis current for Maximum Torque Per Ampere)
196    // Iq controller: Setpoint = demanded_iq
197    let mut id_pid = PidInstanceF32::new(2.5, 0.15, 0.0);
198    let mut iq_pid = PidInstanceF32::new(2.5, 0.15, 0.0);
199
200    let id_setpoint = 0.0f32;
201    let iq_setpoint = demanded_iq;
202
203    let id_error = id_setpoint - i_d;
204    let iq_error = iq_setpoint - i_q;
205
206    let v_d_command = id_pid.process(id_error).clamp(-24.0, 24.0); // 24V DC bus voltage limit
207    let v_q_command = iq_pid.process(iq_error).clamp(-24.0, 24.0);
208
209    println!("  Inner Current PIDs:");
210    println!(
211        "    • d-Axis (Flux)  : Error = {:>6.3} A -> Commanded V_d = {:>6.3} V",
212        id_error, v_d_command
213    );
214    println!(
215        "    • q-Axis (Torque): Error = {:>6.3} A -> Commanded V_q = {:>6.3} V",
216        iq_error, v_q_command
217    );
218
219    // -----------------------------------------------------------------------------------------
220    // 6. Inverse Park & Inverse Clarke Transforms (Modulation Voltages)
221    // -----------------------------------------------------------------------------------------
222    println!("\n--- 6. Inverse Park & Inverse Clarke Transforms (SVPWM Modulation) ---");
223    let mut v_alpha = 0.0f32;
224    let mut v_beta = 0.0f32;
225    let mut v_a = 0.0f32;
226    let mut v_b = 0.0f32;
227
228    inv_park_f32(
229        v_d_command,
230        v_q_command,
231        theta_e[step_idx],
232        &mut v_alpha,
233        &mut v_beta,
234    );
235    inv_clarke_f32(v_alpha, v_beta, &mut v_a, &mut v_b);
236    let v_c = -v_a - v_b; // 3-phase balanced neutral
237
238    println!(
239        "  Inverse Park   : V_alpha = {:>6.3} V, V_beta = {:>6.3} V",
240        v_alpha, v_beta
241    );
242    println!(
243        "  Inverse Clarke : V_a = {:>6.3} V, V_b = {:>6.3} V, V_c = {:>6.3} V",
244        v_a, v_b, v_c
245    );
246
247    // Compute PWM Duty Cycles for Inverter Gate Drivers (normalized to 0.0 .. 1.0)
248    let v_dc = 24.0f32;
249    let duty_a = (v_a / v_dc + 0.5).clamp(0.0, 1.0);
250    let duty_b = (v_b / v_dc + 0.5).clamp(0.0, 1.0);
251    let duty_c = (v_c / v_dc + 0.5).clamp(0.0, 1.0);
252
253    println!("  Generated Inverter PWM Duty Cycles:");
254    println!("    • Phase A Duty : {:>5.1} %", duty_a * 100.0);
255    println!("    • Phase B Duty : {:>5.1} %", duty_b * 100.0);
256    println!("    • Phase C Duty : {:>5.1} %", duty_c * 100.0);
257
258    println!();
259    println!("===============================================================================");
260    println!("             Field-Oriented Control (FOC) Execution Complete!                  ");
261    println!("===============================================================================");
262}

Trait Implementations§

Source§

impl Clone for PidInstanceF32

Source§

fn clone(&self) -> PidInstanceF32

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for PidInstanceF32

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for PidInstanceF32

Source§

fn default() -> PidInstanceF32

Returns the “default value” for a type. Read more
Source§

impl DspNode<f32> for PidInstanceF32

Available on crate feature controller only.
Source§

fn process_sample(&mut self, input: f32) -> f32

Process a single input sample and produce one output sample.
Source§

fn process_block(&mut self, in_buf: &[T], out_buf: &mut [T])

Process a block of samples from in_buf into out_buf.
Source§

fn process_in_place(&mut self, buf: &mut [T])

Process a block of samples in place.
Source§

fn then<Next>(self, next: Next) -> Chain<Self, Next>
where Self: Sized, Next: DspNode<T>,

Chains this node with another processing node into a sequential pipeline.
Source§

impl PartialEq for PidInstanceF32

Source§

fn eq(&self, other: &PidInstanceF32) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for PidInstanceF32

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Az for T

Source§

fn az<Dst>(self) -> Dst
where T: Cast<Dst>,

Casts the value.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<Src, Dst> CastFrom<Src> for Dst
where Src: Cast<Dst>,

Source§

fn cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CheckedAs for T

Source§

fn checked_as<Dst>(self) -> Option<Dst>
where T: CheckedCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> CheckedCastFrom<Src> for Dst
where Src: CheckedCast<Dst>,

Source§

fn checked_cast_from(src: Src) -> Option<Dst>

Casts the value.
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<Src, Dst> LosslessTryInto<Dst> for Src
where Dst: LosslessTryFrom<Src>,

Source§

fn lossless_try_into(self) -> Option<Dst>

Performs the conversion.
Source§

impl<Src, Dst> LossyInto<Dst> for Src
where Dst: LossyFrom<Src>,

Source§

fn lossy_into(self) -> Dst

Performs the conversion.
Source§

impl<T> OverflowingAs for T

Source§

fn overflowing_as<Dst>(self) -> (Dst, bool)
where T: OverflowingCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> OverflowingCastFrom<Src> for Dst
where Src: OverflowingCast<Dst>,

Source§

fn overflowing_cast_from(src: Src) -> (Dst, bool)

Casts the value.
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> SaturatingAs for T

Source§

fn saturating_as<Dst>(self) -> Dst
where T: SaturatingCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> SaturatingCastFrom<Src> for Dst
where Src: SaturatingCast<Dst>,

Source§

fn saturating_cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<T> Scalar for T
where T: 'static + Clone + PartialEq + Debug,

Source§

impl<T> StrictAs for T

Source§

fn strict_as<Dst>(self) -> Dst
where T: StrictCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> StrictCastFrom<Src> for Dst
where Src: StrictCast<Dst>,

Source§

fn strict_cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> UnwrappedAs for T

Source§

fn unwrapped_as<Dst>(self) -> Dst
where T: UnwrappedCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> UnwrappedCastFrom<Src> for Dst
where Src: UnwrappedCast<Dst>,

Source§

fn unwrapped_cast_from(src: Src) -> Dst

Casts the value.
Source§

impl<T> WrappingAs for T

Source§

fn wrapping_as<Dst>(self) -> Dst
where T: WrappingCast<Dst>,

Casts the value.
Source§

impl<Src, Dst> WrappingCastFrom<Src> for Dst
where Src: WrappingCast<Dst>,

Source§

fn wrapping_cast_from(src: Src) -> Dst

Casts the value.