ee-toolkit 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ee_toolkit/smps.py ADDED
@@ -0,0 +1,330 @@
1
+ """
2
+ Switching converter power-stage sizing (boost / buck).
3
+
4
+ All formulas are the standard CCM (continuous-conduction-mode) first-order
5
+ approximations: the input/output voltages are assumed constant within a
6
+ switching cycle, the converter is assumed to stay in CCM, and ESR/ESL are
7
+ ignored (negligible for ceramics, add ΔV_ESR = ΔI·ESR for electrolytics).
8
+ Good to ~10–20 % — the right accuracy for picking parts.
9
+
10
+ Inductor (V = L·dI/dt over the charging interval)
11
+ -------------------------------------------------
12
+ Boost duty: D = 1 - Vin/Vrail (volt-second balance)
13
+ Buck duty: D = Vout/Vrail
14
+ Boost ripple: ΔI = Vin·D / (L·fsw) = Vin(1-Vin/Vrail)/(L·fsw)
15
+ Buck ripple: ΔI = Vout(1-D) / (L·fsw) = Vout(1-Vout/Vrail)/(L·fsw)
16
+
17
+ The ripple term V(1-V/Vrail) is a downward parabola, maximised at V = Vrail/2,
18
+ so that is the worst-case ripple operating point used for sizing L.
19
+
20
+ Capacitor (I = C·dV/dt)
21
+ -----------------------
22
+ Buck output: C = ΔI / (8·fsw·ΔV) (cap absorbs only the triangular ripple)
23
+ Boost output: C = Iout·D / (fsw·ΔV) (cap supplies the FULL load during D·T)
24
+ Buck input RMS: I_rms = Iout·√(D(1-D)) (pulsed input current heating)
25
+ """
26
+ import math
27
+
28
+ import click
29
+
30
+ from .units import parse_si, fmt_si
31
+ from .eseries import nearest_in_series
32
+
33
+
34
+ # ---------------------------------------------------------------------------
35
+ # Duty cycle (steady-state volt-second balance)
36
+ # ---------------------------------------------------------------------------
37
+
38
+ def boost_duty(vin: float, vrail: float) -> float:
39
+ """Boost duty cycle D = 1 - Vin/Vrail."""
40
+ return 1.0 - vin / vrail
41
+
42
+
43
+ def buck_duty(vrail: float, vout: float) -> float:
44
+ """Buck duty cycle D = Vout/Vrail."""
45
+ return vout / vrail
46
+
47
+
48
+ # ---------------------------------------------------------------------------
49
+ # Inductor ripple / sizing
50
+ # ---------------------------------------------------------------------------
51
+
52
+ def boost_inductor_ripple(vin: float, vrail: float, l: float, fsw: float) -> float:
53
+ """Peak-to-peak inductor ripple current of a boost stage (A)."""
54
+ return vin * boost_duty(vin, vrail) / (l * fsw)
55
+
56
+
57
+ def boost_inductance(vin: float, vrail: float, ripple_i: float, fsw: float) -> float:
58
+ """Inductance (H) giving *ripple_i* peak-to-peak ripple at this operating point."""
59
+ return vin * boost_duty(vin, vrail) / (ripple_i * fsw)
60
+
61
+
62
+ def buck_inductor_ripple(vrail: float, vout: float, l: float, fsw: float) -> float:
63
+ """Peak-to-peak inductor ripple current of a buck stage (A)."""
64
+ return vout * (1.0 - buck_duty(vrail, vout)) / (l * fsw)
65
+
66
+
67
+ def buck_inductance(vrail: float, vout: float, ripple_i: float, fsw: float) -> float:
68
+ """Inductance (H) giving *ripple_i* peak-to-peak ripple at this operating point."""
69
+ return vout * (1.0 - buck_duty(vrail, vout)) / (ripple_i * fsw)
70
+
71
+
72
+ # ---------------------------------------------------------------------------
73
+ # Capacitor sizing
74
+ # ---------------------------------------------------------------------------
75
+
76
+ def buck_output_cap(ripple_i: float, fsw: float, ripple_v: float) -> float:
77
+ """Output capacitance (F) bounding buck output ripple to *ripple_v* (capacitive term)."""
78
+ return ripple_i / (8.0 * fsw * ripple_v)
79
+
80
+
81
+ def boost_output_cap(iout: float, duty: float, fsw: float, ripple_v: float) -> float:
82
+ """Output capacitance (F) bounding boost output ripple to *ripple_v*.
83
+
84
+ *iout* is the boost stage output current (= P_rail / Vrail); the cap alone
85
+ supplies the load during the D·T on-time.
86
+ """
87
+ return iout * duty / (fsw * ripple_v)
88
+
89
+
90
+ def buck_input_rms(iout: float, duty: float) -> float:
91
+ """RMS ripple current drawn by a buck stage from its input rail (A)."""
92
+ return iout * math.sqrt(duty * (1.0 - duty))
93
+
94
+
95
+ # ---------------------------------------------------------------------------
96
+ # Worst-case helper
97
+ # ---------------------------------------------------------------------------
98
+
99
+ def worst_ripple_voltage(v_low: float, v_high: float, vrail: float) -> float:
100
+ """Voltage in [v_low, v_high] that maximises ripple V·(1-V/Vrail).
101
+
102
+ Unconstrained maximum is at Vrail/2; clamp to the supplied range.
103
+ """
104
+ v_star = vrail / 2.0
105
+ if v_star < v_low:
106
+ return v_low
107
+ if v_star > v_high:
108
+ return v_high
109
+ return v_star
110
+
111
+
112
+ def inductor_rms(i_avg: float, ripple_i: float) -> float:
113
+ """RMS current of a triangular-ripple inductor waveform (A)."""
114
+ return math.sqrt(i_avg ** 2 + (ripple_i ** 2) / 12.0)
115
+
116
+
117
+ def snap_e12(value: float):
118
+ """Snap *value* to the nearest E12 standard value in any decade.
119
+
120
+ Thin wrapper over :func:`eseries.nearest_in_series` for L/C component picks.
121
+ Returns (snapped_value, error_percent).
122
+ """
123
+ return nearest_in_series(value, 'E12')
124
+
125
+
126
+ # ---------------------------------------------------------------------------
127
+ # Click commands
128
+ # ---------------------------------------------------------------------------
129
+
130
+ def _snap(value: float, unit: str):
131
+ """Snap *value* to the nearest E12 standard value; return (snapped, text)."""
132
+ snapped, err = snap_e12(value)
133
+ return snapped, f"{fmt_si(snapped, unit)} ({err:+.1f}% vs {fmt_si(value, unit)} ideal)"
134
+
135
+
136
+ def _hdr(text: str):
137
+ click.secho(text, fg='cyan', bold=True)
138
+
139
+
140
+ def _row(cells, widths, color=None, marker=''):
141
+ line = ' ' + ' '.join(f"{c:<{w}}" for c, w in zip(cells, widths))
142
+ click.secho(line + marker, fg=color)
143
+
144
+
145
+ @click.group()
146
+ def smps_group():
147
+ """Switching converter power-stage sizing (boost / buck)."""
148
+
149
+
150
+ @smps_group.command('boost')
151
+ @click.option('--vin-min', 'vin_min_str', required=True, metavar='VALUE',
152
+ help='Minimum input voltage (pack empty), e.g. 9')
153
+ @click.option('--vin-max', 'vin_max_str', required=True, metavar='VALUE',
154
+ help='Maximum input voltage (pack full), e.g. 25.2')
155
+ @click.option('--vrail', 'vrail_str', required=True, metavar='VALUE',
156
+ help='Boost output / intermediate-rail voltage, e.g. 30')
157
+ @click.option('--pout', 'pout_str', required=True, metavar='VALUE',
158
+ help='Power delivered to the rail (boost output power), e.g. 105')
159
+ @click.option('--fsw', 'fsw_str', required=True, metavar='VALUE',
160
+ help='Switching frequency, e.g. 250k')
161
+ @click.option('--ripple', default=0.4, show_default=True,
162
+ help='Target peak-to-peak inductor ripple as a fraction of I_avg.')
163
+ @click.option('--eff', default=0.95, show_default=True,
164
+ help='Boost stage efficiency (for input-current estimate).')
165
+ @click.option('--dvrail', 'dvrail_str', default='300m', show_default=True, metavar='VALUE',
166
+ help='Allowed rail voltage ripple, e.g. 300m')
167
+ @click.option('--inductance', 'l_str', default=None, metavar='VALUE',
168
+ help='Analyse a fixed inductor instead of sizing one, e.g. 10u')
169
+ @click.option('--margin', default=1.3, show_default=True,
170
+ help='Safety margin applied to peak current for I_sat selection.')
171
+ def cmd_boost(vin_min_str, vin_max_str, vrail_str, pout_str, fsw_str,
172
+ ripple, eff, dvrail_str, l_str, margin):
173
+ """Size a boost stage (Vin range → fixed rail).
174
+
175
+ \b
176
+ D = 1 - Vin/Vrail
177
+ ΔI = Vin·D / (L·fsw) (worst case at Vin = Vrail/2)
178
+ C_rail = Iout·D / (fsw·ΔV) (cap supplies full load during D·T)
179
+
180
+ Example (3S-6S pack → 30 V rail, 105 W):
181
+ ee-calc smps boost --vin-min 9 --vin-max 25.2 --vrail 30 --pout 105 --fsw 250k
182
+ """
183
+ vin_min = parse_si(vin_min_str)
184
+ vin_max = parse_si(vin_max_str)
185
+ vrail = parse_si(vrail_str)
186
+ pout = parse_si(pout_str)
187
+ fsw = parse_si(fsw_str)
188
+ dvrail = parse_si(dvrail_str)
189
+
190
+ if vrail <= vin_max:
191
+ raise click.UsageError(
192
+ f"Rail ({fmt_si(vrail, 'V')}) must exceed max input "
193
+ f"({fmt_si(vin_max, 'V')}) — a boost can only step up.")
194
+
195
+ p_in = pout / eff
196
+ v_star = worst_ripple_voltage(vin_min, vin_max, vrail)
197
+
198
+ if l_str:
199
+ l = parse_si(l_str)
200
+ l_note = f"{fmt_si(l, 'H')} (as supplied)"
201
+ else:
202
+ i_avg_star = p_in / v_star
203
+ l_ideal = boost_inductance(v_star, vrail, ripple * i_avg_star, fsw)
204
+ l, l_note = _snap(l_ideal, 'H')
205
+
206
+ _hdr("Boost stage")
207
+ click.echo(f" Vin = {fmt_si(vin_min, 'V')} … {fmt_si(vin_max, 'V')}")
208
+ click.echo(f" Vrail = {fmt_si(vrail, 'V')} Pout(rail) = {fmt_si(pout, 'W')} η = {eff:.0%}")
209
+ click.echo(f" fsw = {fmt_si(fsw, 'Hz')} Pin ≈ {fmt_si(p_in, 'W')}")
210
+ click.secho(f" L = {l_note}", fg='green', bold=True)
211
+
212
+ _hdr("\nOperating sweep")
213
+ widths = (10, 7, 10, 10, 9, 10)
214
+ _row(('Vin', 'Duty', 'I_avg', 'ΔI(pp)', 'ripple', 'I_peak'), widths)
215
+ click.echo(" " + "─" * 62)
216
+ peak_max = 0.0
217
+ irms_max = 0.0
218
+ for vin in (vin_min, v_star, vin_max):
219
+ d = boost_duty(vin, vrail)
220
+ i_avg = p_in / vin
221
+ di = boost_inductor_ripple(vin, vrail, l, fsw)
222
+ i_peak = i_avg + di / 2.0
223
+ peak_max = max(peak_max, i_peak)
224
+ irms_max = max(irms_max, inductor_rms(i_avg, di))
225
+ marker = ' ◀ worst ripple' if abs(vin - v_star) < 1e-9 else ''
226
+ _row((f"{vin:.2f} V", f"{d:.3f}", f"{i_avg:.2f} A",
227
+ f"{di:.2f} A", f"{di / i_avg:.0%}", f"{i_peak:.2f} A"),
228
+ widths, marker=marker)
229
+
230
+ i_out = pout / vrail
231
+ c_rail_ideal = boost_output_cap(i_out, boost_duty(vin_min, vrail), fsw, dvrail)
232
+ _, c_note = _snap(c_rail_ideal, 'F')
233
+
234
+ _hdr("\nComponent selection")
235
+ click.secho(f" Inductor : ≥ {fmt_si(peak_max * margin, 'A')} I_sat "
236
+ f"(peak {fmt_si(peak_max, 'A')} × {margin:g}), "
237
+ f"≥ {fmt_si(irms_max, 'A')} I_rms", fg='green')
238
+ click.secho(f" Rail cap : {c_note}", fg='green')
239
+ click.secho(" (size for RMS current too — the buck input adds to this rail)",
240
+ fg='yellow')
241
+
242
+
243
+ @smps_group.command('buck')
244
+ @click.option('--vrail', 'vrail_str', required=True, metavar='VALUE',
245
+ help='Input / intermediate-rail voltage, e.g. 30')
246
+ @click.option('--vout-min', 'vout_min_str', required=True, metavar='VALUE',
247
+ help='Minimum output voltage, e.g. 3.3')
248
+ @click.option('--vout-max', 'vout_max_str', required=True, metavar='VALUE',
249
+ help='Maximum output voltage, e.g. 21')
250
+ @click.option('--iout', 'iout_str', required=True, metavar='VALUE',
251
+ help='Maximum output current, e.g. 5')
252
+ @click.option('--fsw', 'fsw_str', required=True, metavar='VALUE',
253
+ help='Switching frequency, e.g. 250k')
254
+ @click.option('--ripple', default=0.4, show_default=True,
255
+ help='Target peak-to-peak inductor ripple as a fraction of I_out.')
256
+ @click.option('--dvout', 'dvout_str', default='50m', show_default=True, metavar='VALUE',
257
+ help='Allowed output voltage ripple, e.g. 50m')
258
+ @click.option('--inductance', 'l_str', default=None, metavar='VALUE',
259
+ help='Analyse a fixed inductor instead of sizing one, e.g. 15u')
260
+ @click.option('--margin', default=1.3, show_default=True,
261
+ help='Safety margin applied to peak current for I_sat selection.')
262
+ def cmd_buck(vrail_str, vout_min_str, vout_max_str, iout_str, fsw_str,
263
+ ripple, dvout_str, l_str, margin):
264
+ """Size a buck stage (fixed rail → Vout range).
265
+
266
+ \b
267
+ D = Vout/Vrail
268
+ ΔI = Vout(1-D) / (L·fsw) (worst case at Vout = Vrail/2)
269
+ C_out = ΔI / (8·fsw·ΔV)
270
+ I_rail,rms = Iout·√(D(1-D)) (rail cap RMS, max at D=0.5)
271
+
272
+ Example (30 V rail → PD/PPS 3.3-21 V, 5 A):
273
+ ee-calc smps buck --vrail 30 --vout-min 3.3 --vout-max 21 --iout 5 --fsw 250k
274
+ """
275
+ vrail = parse_si(vrail_str)
276
+ vout_min = parse_si(vout_min_str)
277
+ vout_max = parse_si(vout_max_str)
278
+ iout = parse_si(iout_str)
279
+ fsw = parse_si(fsw_str)
280
+ dvout = parse_si(dvout_str)
281
+
282
+ if vout_max >= vrail:
283
+ raise click.UsageError(
284
+ f"Max output ({fmt_si(vout_max, 'V')}) must be below the rail "
285
+ f"({fmt_si(vrail, 'V')}) — a buck can only step down.")
286
+
287
+ v_star = worst_ripple_voltage(vout_min, vout_max, vrail)
288
+
289
+ if l_str:
290
+ l = parse_si(l_str)
291
+ l_note = f"{fmt_si(l, 'H')} (as supplied)"
292
+ else:
293
+ l_ideal = buck_inductance(vrail, v_star, ripple * iout, fsw)
294
+ l, l_note = _snap(l_ideal, 'H')
295
+
296
+ _hdr("Buck stage")
297
+ click.echo(f" Vrail = {fmt_si(vrail, 'V')}")
298
+ click.echo(f" Vout = {fmt_si(vout_min, 'V')} … {fmt_si(vout_max, 'V')} Iout = {fmt_si(iout, 'A')}")
299
+ click.echo(f" fsw = {fmt_si(fsw, 'Hz')}")
300
+ click.secho(f" L = {l_note}", fg='green', bold=True)
301
+
302
+ _hdr("\nOperating sweep")
303
+ widths = (10, 7, 10, 9, 10, 11)
304
+ _row(('Vout', 'Duty', 'ΔI(pp)', 'ripple', 'I_peak', 'I_rail,rms'), widths)
305
+ click.echo(" " + "─" * 64)
306
+ di_max = 0.0
307
+ irms_max = 0.0
308
+ for vout in (vout_min, v_star, vout_max):
309
+ d = buck_duty(vrail, vout)
310
+ di = buck_inductor_ripple(vrail, vout, l, fsw)
311
+ i_peak = iout + di / 2.0
312
+ i_rms = buck_input_rms(iout, d)
313
+ di_max = max(di_max, di)
314
+ irms_max = max(irms_max, i_rms)
315
+ marker = ' ◀ worst ripple' if abs(vout - v_star) < 1e-9 else ''
316
+ _row((f"{vout:.2f} V", f"{d:.3f}", f"{di:.2f} A",
317
+ f"{di / iout:.0%}", f"{i_peak:.2f} A", f"{i_rms:.2f} A"),
318
+ widths, marker=marker)
319
+
320
+ c_out_ideal = buck_output_cap(di_max, fsw, dvout)
321
+ _, c_note = _snap(c_out_ideal, 'F')
322
+
323
+ _hdr("\nComponent selection")
324
+ click.secho(f" Inductor : ≥ {fmt_si((iout + di_max / 2) * margin, 'A')} I_sat, "
325
+ f"≥ {fmt_si(iout, 'A')} I_rms", fg='green')
326
+ click.secho(f" Out cap : {c_note}", fg='green')
327
+ click.secho(f" Rail cap : carries ≥ {fmt_si(irms_max, 'A')} RMS from the buck side",
328
+ fg='green')
329
+ click.secho(" (don't oversize C_out — it slows PPS voltage slewing)",
330
+ fg='yellow')
ee_toolkit/units.py ADDED
@@ -0,0 +1,77 @@
1
+ """
2
+ Shared utilities: SI prefix parsing and pretty-printing.
3
+ """
4
+ import re
5
+
6
+ _PREFIX_MAP = {
7
+ 'G': 1e9,
8
+ 'M': 1e6,
9
+ 'k': 1e3,
10
+ 'K': 1e3,
11
+ '': 1.0,
12
+ 'm': 1e-3,
13
+ 'u': 1e-6,
14
+ 'µ': 1e-6,
15
+ 'n': 1e-9,
16
+ 'p': 1e-12,
17
+ 'f': 1e-15,
18
+ }
19
+
20
+ _SI_SCALE = [
21
+ (1e9, 'G'),
22
+ (1e6, 'M'),
23
+ (1e3, 'k'),
24
+ (1.0, ''),
25
+ (1e-3, 'm'),
26
+ (1e-6, 'µ'),
27
+ (1e-9, 'n'),
28
+ (1e-12,'p'),
29
+ ]
30
+
31
+ _UNIT_RE = re.compile(
32
+ r'^\s*([+-]?[0-9]*\.?[0-9]+(?:[eE][+-]?[0-9]+)?)'
33
+ r'\s*([GMkKmuµnpf]?)\s*$'
34
+ )
35
+
36
+
37
+ def parse_si(text: str) -> float:
38
+ """
39
+ Parse a string with an optional SI prefix into a float.
40
+
41
+ Examples
42
+ --------
43
+ >>> parse_si('10k') # 10 000
44
+ 10000.0
45
+ >>> parse_si('100n') # 100e-9
46
+ 1e-07
47
+ >>> parse_si('3.3M') # 3 300 000
48
+ 3300000.0
49
+ """
50
+ m = _UNIT_RE.match(text)
51
+ if not m:
52
+ raise ValueError(
53
+ f"Cannot parse '{text}' – expected a number with an optional SI prefix "
54
+ "(G M k m u µ n p f)."
55
+ )
56
+ number = float(m.group(1))
57
+ prefix = m.group(2)
58
+ return number * _PREFIX_MAP[prefix]
59
+
60
+
61
+ def fmt_si(value: float, unit: str = '', decimals: int = 4) -> str:
62
+ """
63
+ Format *value* with an SI prefix and optional unit string.
64
+
65
+ Examples
66
+ --------
67
+ >>> fmt_si(15920.0, 'Hz')
68
+ '15.92 kHz'
69
+ >>> fmt_si(1e-9, 'F')
70
+ '1.0 nF'
71
+ """
72
+ for scale, prefix in _SI_SCALE:
73
+ if abs(value) >= scale:
74
+ return f"{value / scale:.{decimals}g} {prefix}{unit}"
75
+ # Fallback for very small values
76
+ scale, prefix = _SI_SCALE[-1]
77
+ return f"{value / scale:.{decimals}g} {prefix}{unit}"
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: ee-toolkit
3
+ Version: 0.0.2
4
+ Project-URL: Documentation, https://git.sr.ht/~laplace/ee-toolkit#readme
5
+ Project-URL: Issues, https://git.sr.ht/~laplace/ee-toolkit/todo
6
+ Project-URL: Source, https://git.sr.ht/~laplace/ee-toolkit
7
+ Author-email: Alexander Becker <nabla.becker@mailbox.org>
8
+ License-Expression: GPL-3.0-or-later
9
+ License-File: LICENSE.txt
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: Implementation :: CPython
18
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
19
+ Requires-Python: >=3.8
20
+ Requires-Dist: click>=8.0
21
+ Description-Content-Type: text/markdown
22
+
23
+ # ee-toolkit
24
+
25
+ [![PyPI - Version](https://img.shields.io/pypi/v/ee-toolkit.svg)](https://pypi.org/project/ee-toolkit)
26
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/ee-toolkit.svg)](https://pypi.org/project/ee-toolkit)
27
+
28
+ A command-line EE calculator
29
+
30
+ This is essentially the cli version of many of the online 'electronics' focussed calculators
31
+ for all the usual things like filter cutoff frequencies or resonances or resistor approximations
32
+
33
+ ## License
34
+
35
+ `ee-toolkit` is licensed under the terms of the GPL-v3 license.
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ pip install ee-toolkit
41
+ ```
42
+
43
+ ## Commands
44
+
45
+ ### `filter`
46
+ ```bash
47
+ ee-calc filter rc --r 10k --c 100n # RC cutoff
48
+ ee-calc filter lc --l 10u --c 100p # LC resonance
49
+ ```
50
+
51
+ ### `eseries`
52
+ ```bash
53
+ ee-calc eseries find --value 10.5k # nearest E24 values
54
+ ee-calc eseries find --value 47n --series E96 --unit F # E96 capacitor
55
+ ee-calc eseries find --value 10u --series E12 --unit H # E12 inductor
56
+ ee-calc eseries decompose --value 10.5k --combo series # two-resistor series
57
+ ee-calc eseries decompose --value 10.5k --combo parallel # two-resistor parallel
58
+ ```
59
+ Value snapping is decade-independent — it works across the full pF…GΩ/µH range,
60
+ not just resistor decades.
61
+
62
+ ### `smps`
63
+ Switching-converter power-stage sizing (inductor, capacitor, frequency trade).
64
+ ```bash
65
+ # Boost stage: 3S-6S pack (9-25.2 V) → 30 V intermediate rail, 105 W
66
+ ee-calc smps boost --vin-min 9 --vin-max 25.2 --vrail 30 --pout 105 --fsw 250k
67
+
68
+ # Buck stage: 30 V rail → USB-PD/PPS output (3.3-21 V), 5 A
69
+ ee-calc smps buck --vrail 30 --vout-min 3.3 --vout-max 21 --iout 5 --fsw 250k
70
+
71
+ # Analyse a fixed inductor instead of sizing one
72
+ ee-calc smps buck --vrail 30 --vout-min 3.3 --vout-max 21 --iout 5 --fsw 250k --inductance 15u
73
+ ```
74
+ Each command reports the recommended inductance (snapped to E12), an operating
75
+ sweep across the voltage range (duty, ripple, peak current) marking the
76
+ worst-case ripple point, and the inductor/capacitor selection including
77
+ saturation-current and RMS-current targets. Override ripple/voltage-ripple
78
+ targets with `--ripple`, `--dvrail`/`--dvout`.
79
+
80
+ ### `oscillator`
81
+ ```bash
82
+ ee-calc oscillator lc --l 10u --c 100p # LC tank
83
+ ee-calc oscillator colpitts --l 10u --c1 100p --c2 100p # Colpitts
84
+ ee-calc oscillator hartley --l1 5u --l2 5u --c 100p # Hartley
85
+ ```
86
+
87
+ ### `opamp`
88
+ ```bash
89
+ ee-calc opamp noninv --rf 100k --r1 10k # non-inverting gain
90
+ ee-calc opamp inv --rf 100k --r1 10k # inverting gain
91
+ ee-calc opamp integrator --r 10k --c 100n # integrator corner freq
92
+ ee-calc opamp integrator --r 10k --c 100n --freq 500 # gain at 500 Hz
93
+ ee-calc opamp differentiator --r 10k --c 100n # differentiator corner freq
94
+ ee-calc opamp differentiator --r 10k --c 100n --freq 2k # gain at 2 kHz
95
+ ```
96
+
97
+ ## SI prefixes
98
+
99
+ All value inputs accept SI prefixes: `G M k m u µ n p f`
100
+
101
+ ```
102
+ 10k → 10 000
103
+ 100n → 100 × 10⁻⁹
104
+ 4.7M → 4 700 000
105
+ ```
106
+
107
+ ## Run tests
108
+
109
+ ```bash
110
+ python -m pytest tests/ -v
111
+ ```
@@ -0,0 +1,16 @@
1
+ ee_toolkit/__about__.py,sha256=KW48ZmlfhCUsX-7BLzk9VWpZljnynWruMdpV1Jmk7pI,138
2
+ ee_toolkit/__init__.py,sha256=Bl_M7kRF_a5EMK50H2KzTycOZvwxTYwX0QjDALGcV9U,107
3
+ ee_toolkit/cli.py,sha256=X3OJcqFnglJMpltlGEX59vlSWb0IMWRiS3BQCCm1tK8,1631
4
+ ee_toolkit/eseries.py,sha256=hnggGB_NsPYeO6qHVL9lFiy8wHlsomkvuTB7KU-eg7M,8510
5
+ ee_toolkit/filters.py,sha256=1VvJ94Q-0OoKOzuF-4kGbb80LVXT1A8_dUivAOXDLHY,4731
6
+ ee_toolkit/mosfet.py,sha256=8xSy4x7fNvF6kyF3OlSV7l1RWLavF3ZuLrLrLYUD0fM,3232
7
+ ee_toolkit/opamp.py,sha256=D-jhX1oBdTiQJdU_j7Lty7_SVo2Of6HI_xLt_F7gJl0,6819
8
+ ee_toolkit/oscillator.py,sha256=oaBM5-ATJt28qm091Ag3ZAeXCzYspJzqyt5IWEWb8Nc,3576
9
+ ee_toolkit/resistor.py,sha256=qzuCt6Zg5KRBDBZ9fU4QXyGJyUZQJrxfG_yTDg_y-EM,8680
10
+ ee_toolkit/smps.py,sha256=PP0ph2l4xEOqDaMrkDnYhNalxTMQDexSck970Kf0smA,13952
11
+ ee_toolkit/units.py,sha256=5j--iuTUnG3kllXeX8Z6VxgJQq1SiWg4h6Rq_EazM8k,1625
12
+ ee_toolkit-0.0.2.dist-info/METADATA,sha256=6OFWCorL6AlF7bRq-YgSYnt_yQD2rpiuhJ_B0GzSsHA,4005
13
+ ee_toolkit-0.0.2.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
14
+ ee_toolkit-0.0.2.dist-info/entry_points.txt,sha256=B1HnQx9H8BtZXlIGnluo5Skav4demPDscKPWhibrnWw,47
15
+ ee_toolkit-0.0.2.dist-info/licenses/LICENSE.txt,sha256=769qERkGHy-UfR4fBzJY83KqNle54WBx1PQxdrIDEww,34961
16
+ ee_toolkit-0.0.2.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ee-calc = ee_toolkit.cli:cli