pedalkernel/compiler/warnings.rs
1//! Voltage compatibility checking for pedal circuits.
2
3use crate::dsl::*;
4
5// ═══════════════════════════════════════════════════════════════════════════
6// Voltage compatibility check
7// ═══════════════════════════════════════════════════════════════════════════
8
9/// Severity of a voltage compatibility warning.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum WarningSeverity {
12 /// Informational — the component should be fine, but behavior may differ
13 /// from the canonical 9V version (e.g. slightly different bias point).
14 Info,
15 /// The component is likely operating outside its typical ratings and may
16 /// clip, distort differently, or wear out faster in a real build.
17 Caution,
18 /// The component would almost certainly fail or be damaged at this voltage
19 /// in a physical build.
20 Danger,
21}
22
23/// A single voltage compatibility warning for a component.
24#[derive(Debug, Clone)]
25pub struct VoltageWarning {
26 pub component_id: String,
27 pub severity: WarningSeverity,
28 pub message: String,
29}
30
31/// Check a pedal definition for voltage compatibility at a given supply voltage.
32///
33/// Returns a list of warnings for components that may not tolerate the target
34/// voltage. These are heuristic — the DSL doesn't carry voltage ratings, so
35/// we infer from component types and typical part specs:
36///
37/// - **Germanium transistors**: Vce(max) typically 15–32V. Caution above 15V.
38/// - **Electrolytic capacitors** (≥ 1 µF): often rated 10–16V. Caution above 12V.
39/// - **Op-amps**: common audio opamps (TL072, NE5532) rated to ±18V (36V total).
40/// Caution above 18V.
41/// - **Silicon transistors**: usually Vce(max) ≥ 40V. Caution above 24V.
42/// - **Germanium diodes**: forward behaviour is voltage-independent, but reverse
43/// breakdown is lower (~50V Ge vs ~100V+ Si). Only flagged above 24V.
44///
45/// Note: passive elements (R, L) and silicon diodes are unaffected in the WDF
46/// model and are not flagged.
47pub fn check_voltage_compatibility(pedal: &PedalDef, voltage: f64) -> Vec<VoltageWarning> {
48 let mut warnings = Vec::new();
49
50 for comp in &pedal.components {
51 let tag = comp.kind.type_tag();
52
53 if comp.kind.is_bjt() {
54 if let Some(name) = comp.kind.model_name() {
55 let is_ge = crate::models::bjt_is_germanium(name);
56 if is_ge {
57 // Germanium PNPs: typical Vce(max) = 15-32V
58 if voltage > 18.0 {
59 warnings.push(VoltageWarning {
60 component_id: comp.id.clone(),
61 severity: WarningSeverity::Danger,
62 message: format!(
63 "Germanium transistor {} likely exceeds Vce(max) at {:.0}V \
64 (typical Ge PNP rated 15-32V)",
65 comp.id, voltage
66 ),
67 });
68 } else if voltage > 12.0 {
69 warnings.push(VoltageWarning {
70 component_id: comp.id.clone(),
71 severity: WarningSeverity::Caution,
72 message: format!(
73 "Germanium transistor {} may run hot at {:.0}V \
74 — bias point shifts, tone will differ from 9V",
75 comp.id, voltage
76 ),
77 });
78 }
79 } else if voltage > 24.0 {
80 // Silicon transistors: typically Vce(max) >= 40V
81 warnings.push(VoltageWarning {
82 component_id: comp.id.clone(),
83 severity: WarningSeverity::Caution,
84 message: format!(
85 "Transistor {} at {:.0}V — verify Vce(max) rating of actual part",
86 comp.id, voltage
87 ),
88 });
89 }
90 }
91 } else if let Some(ot) = comp.kind.op_amp_type() {
92 // Use the op-amp type's known supply_max for accurate warnings
93 let max_supply = ot.supply_max();
94 if voltage > max_supply * 0.5 {
95 // Operating above half the max total supply (typical single-supply limit)
96 let severity = if voltage > max_supply {
97 WarningSeverity::Danger
98 } else {
99 WarningSeverity::Caution
100 };
101 warnings.push(VoltageWarning {
102 component_id: comp.id.clone(),
103 severity,
104 message: format!(
105 "Op-amp {} ({:?}) at {:.0}V — max total supply {:.0}V (±{:.0}V split)",
106 comp.id,
107 ot,
108 voltage,
109 max_supply,
110 max_supply / 2.0
111 ),
112 });
113 }
114 } else if let Some(farads) = comp.kind.capacitance() {
115 // Electrolytics (>= 1uF) often have low voltage ratings.
116 // 10uF caps commonly rated 10V or 16V.
117 if farads >= 1e-6 {
118 if voltage > 16.0 {
119 warnings.push(VoltageWarning {
120 component_id: comp.id.clone(),
121 severity: WarningSeverity::Danger,
122 message: format!(
123 "Electrolytic cap {} ({:.0}uF) may exceed voltage rating at {:.0}V \
124 — common ratings are 10V, 16V, 25V",
125 comp.id,
126 farads * 1e6,
127 voltage
128 ),
129 });
130 } else if voltage > 12.0 {
131 warnings.push(VoltageWarning {
132 component_id: comp.id.clone(),
133 severity: WarningSeverity::Caution,
134 message: format!(
135 "Electrolytic cap {} ({:.0}uF) — ensure voltage rating >= {:.0}V",
136 comp.id,
137 farads * 1e6,
138 voltage
139 ),
140 });
141 }
142 }
143 } else if comp.kind.is_diode_family() {
144 // Check for germanium diodes
145 if comp.kind.diode_type() == Some(DiodeType::Germanium) {
146 // Ge diode forward behaviour is voltage-independent in the WDF,
147 // but reverse breakdown is lower (~50V vs 100V+ Si).
148 // Also: more temperature-sensitive at higher power dissipation.
149 if voltage > 18.0 {
150 warnings.push(VoltageWarning {
151 component_id: comp.id.clone(),
152 severity: WarningSeverity::Info,
153 message: format!(
154 "Germanium diode {} — higher power dissipation at {:.0}V \
155 may shift forward voltage (temperature dependent)",
156 comp.id, voltage
157 ),
158 });
159 }
160 }
161 } else if tag == "BBD delay" {
162 // BBDs are typically rated 10-15V. They're sensitive to
163 // over-voltage which causes excess clock noise and distortion.
164 if voltage > 15.0 {
165 warnings.push(VoltageWarning {
166 component_id: comp.id.clone(),
167 severity: WarningSeverity::Danger,
168 message: format!(
169 "BBD {} may exceed max supply at {:.0}V — typical rating 10-15V",
170 comp.id, voltage
171 ),
172 });
173 }
174 } else if tag == "VCO" || tag == "VCF" {
175 // Synth ICs: CEM/AS/V series typically run +/-5V to +/-9V (10-18V total)
176 if voltage > 18.0 {
177 warnings.push(VoltageWarning {
178 component_id: comp.id.clone(),
179 severity: WarningSeverity::Danger,
180 message: format!(
181 "Synth IC {} exceeds max supply at {:.0}V — CEM/AS/V series rated ±9V (18V max)",
182 comp.id, voltage
183 ),
184 });
185 }
186 } else if tag == "analog switch" && voltage > 20.0 {
187 warnings.push(VoltageWarning {
188 component_id: comp.id.clone(),
189 severity: WarningSeverity::Danger,
190 message: format!(
191 "Analog switch {} exceeds max supply at {:.0}V — CD4066/DG411 rated 20V max",
192 comp.id, voltage
193 ),
194 });
195 }
196 // Resistors, inductors, Si/LED diodes, pots, VCA, comparator, matched
197 // transistors, tempco: no voltage concerns within the 5-24V range
198 // (or handled by supply_max spec if provided).
199 }
200
201 warnings
202}