1use serde::{Deserialize, Serialize};
7
8use crate::CalcError;
9
10pub struct ThermalInput {
12 pub r_theta_ja: f64,
14 pub power_w: f64,
16 pub t_ambient_c: f64,
18}
19
20#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
22pub struct ThermalResult {
23 pub t_junction_c: f64,
25 pub t_junction_f: f64,
27}
28
29pub fn calculate(input: &ThermalInput) -> Result<ThermalResult, CalcError> {
31 if input.r_theta_ja <= 0.0 {
32 return Err(CalcError::OutOfRange {
33 name: "r_theta_ja",
34 value: input.r_theta_ja,
35 expected: "> 0",
36 });
37 }
38 if input.power_w < 0.0 {
39 return Err(CalcError::OutOfRange {
40 name: "power_w",
41 value: input.power_w,
42 expected: ">= 0",
43 });
44 }
45
46 let t_junction_c = input.r_theta_ja * input.power_w + input.t_ambient_c;
47 let t_junction_f = 1.8 * t_junction_c + 32.0;
48
49 Ok(ThermalResult { t_junction_c, t_junction_f })
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55 use approx::assert_relative_eq;
56
57 #[test]
58 fn basic_junction_temp() {
59 let result = calculate(&ThermalInput {
60 r_theta_ja: 50.0,
61 power_w: 1.0,
62 t_ambient_c: 25.0,
63 })
64 .unwrap();
65
66 assert_relative_eq!(result.t_junction_c, 75.0, epsilon = 1e-10);
67 assert_relative_eq!(result.t_junction_f, 167.0, epsilon = 1e-10);
68 }
69
70 #[test]
71 fn high_thermal_resistance() {
72 let result = calculate(&ThermalInput {
73 r_theta_ja: 100.0,
74 power_w: 0.5,
75 t_ambient_c: 25.0,
76 })
77 .unwrap();
78
79 assert_relative_eq!(result.t_junction_c, 75.0, epsilon = 1e-10);
80 }
81
82 #[test]
83 fn zero_power() {
84 let result = calculate(&ThermalInput {
85 r_theta_ja: 50.0,
86 power_w: 0.0,
87 t_ambient_c: 25.0,
88 })
89 .unwrap();
90
91 assert_relative_eq!(result.t_junction_c, 25.0, epsilon = 1e-10);
92 }
93
94 #[test]
95 fn invalid_r_theta() {
96 assert!(calculate(&ThermalInput {
97 r_theta_ja: -1.0,
98 power_w: 1.0,
99 t_ambient_c: 25.0,
100 })
101 .is_err());
102 }
103}