molecule
AFS_Adom flight computer molecule
Public Unreviewedby ae007
Active fin stabilization flight computer for a sounding rocket, a clean-room AI redesign of the Rice Eclipse AFS board: RP2350B, ICM-45686, KX134-1211, MMC5983MA, MS5611, TMP117, INA219A, 8 servos, 2S LiPo, USB-C; 4-layer Adom molecule, designed end to end by Claude through KiCad and adom-aiflow
main
ae007
Project page: AFS_Adom rev A (Rice Eclipse AFS clean-room AI redesign): highlight reel and narrated process video with…
cbe460e
7d ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
"""AFS_Adom servo-rail transient (ngspice): an 8 A, 20 ms servo stall pulse (all eight servos) drawn from the 2S pack through
the XT30, the 5 mOhm shunt and the 2x220 uF bulk capacitors, with the INA219 input filter (10 Ohm + 100 nF) on the shunt.
Checks: the INA219 differential input stays inside the chosen PGA/2 range (80 mV), the shunt dissipation stays inside its
rating, the servo rail droop (pack + cable + shunt) stays under 0.5 V, and VSYS behind the ideal diode keeps the 3.3 V buck
above its 3.8 V UVLO. Own model (pack = 7.4 V behind 25 mOhm + 1 uH cable). Writes servo_pulse.json + PNG.
"""
import json, subprocess, os
here = os.path.dirname(os.path.abspath(__file__))
calcs = json.load(open(os.path.join(here, '..', 'design', 'calcs.json')))
R_SHUNT = calcs['blocks']['shunt_ina219']['R_ohm']
net = f"""* servo stall pulse through the shunt
Vpack pack 0 DC 7.4
Rpack pack cab 0.025
Lcab cab vbat_in 1u
Rshunt vbat_in vsh {R_SHUNT}
Vsh vsh vbat DC 0
Cbulk1 vbat b1 220u ic=7.4
Resr1 b1 0 0.25
Cbulk2 vbat b2 220u ic=7.4
Resr2 b2 0 0.25
* INA219 input filter 10 R / 100 nF across the shunt
Rf1 vbat_in isp 10
Rf2 vbat isn 10
Cf isp isn 100n
* servo load: 0.3 A baseline, 8 A pulse 5..25 ms with 50 us edges
Bservo vbat 0 I = 0.3 + 7.7*(time > 5m && time < 25m)
* ideal diode to VSYS with the buck input current
Bbat vbat vsys I = (v(vbat) - v(vsys) > 0.03) ? (v(vbat) - v(vsys) - 0.03)/0.09 : 0
Cvsys vsys 0 20u ic=7.4
Bin vsys 0 I = (v(vsys) > 3.8) ? 6.6/(v(vsys)*0.88) : 0
.tran 5u 40m uic
.option method=gear reltol=1e-3
.control
run
wrdata {os.path.join(here, 'servo_pulse.dat')} v(vbat_in) v(vbat) v(isp,isn) v(vsys) i(Vsh)
.endc
.end
"""
cir = os.path.join(here, 'servo_pulse.cir'); open(cir, 'w').write(net)
r = subprocess.run(['ngspice', '-b', cir], capture_output=True, text=True, timeout=300)
open(os.path.join(here, 'servo_pulse.log'), 'w').write(r.stdout + r.stderr)
rows = [[float(x) for x in l.split()] for l in open(os.path.join(here, 'servo_pulse.dat')) if len(l.split()) >= 10]
t = [x[0] for x in rows]; vin = [x[1] for x in rows]; vbat = [x[3] for x in rows]; vsns = [x[5] for x in rows]; vsys = [x[7] for x in rows]; ish = [x[9] for x in rows]
def win(a, t0, t1): return [v for tt, v in zip(t, a) if t0 <= tt <= t1]
vsns_max = max(win(vsns, 6e-3, 24e-3)); vbat_min = min(win(vbat, 4e-3, 30e-3)); vsys_min = min(win(vsys, 4e-3, 30e-3))
i_max = max(abs(x) for x in win(ish, 6e-3, 24e-3)); p_shunt = i_max ** 2 * R_SHUNT
checks = [
{"name": "ina219_shunt_input_mV_at_8A", "value": round(1e3 * vsns_max, 2), "limit": 80.0, "pass": 1e3 * vsns_max <= 80.0},
{"name": "shunt_dissipation_W_at_8A", "value": round(p_shunt, 3), "limit": 2.0, "pass": p_shunt <= 2.0},
{"name": "servo_rail_droop_V", "value": round(7.4 - vbat_min, 3), "limit": 1.0, "pass": (7.4 - vbat_min) <= 1.0, "note": "self-set budget: 8 A x (25 mOhm pack + 5 mOhm shunt) = 0.24 V DC plus the cable L di/dt; servo rail stays above 5 V from a 6 V pack; the design item is VSYS and the INA219 range"},
{"name": "vsys_min_V_during_pulse", "value": round(vsys_min, 3), "limit": {"min": 3.8}, "pass": vsys_min >= 3.8},
]
res = {"model": "own ngspice model: 2S pack 7.4 V behind 25 mOhm and 1 uH of cable, 5 mOhm shunt, 2x220 uF (0.25 Ohm ESR), INA219 RC input filter, ideal diode and buck input load",
"scenario": "0.3 A baseline, 8 A servo stall pulse from 5 to 25 ms (all eight servos)", "checks": checks, "pass": all(c["pass"] for c in checks), "ngspice": r.returncode}
json.dump(res, open(os.path.join(here, 'servo_pulse.json'), 'w'), indent=1); print(json.dumps(res, indent=1))
import sys; sys.path.insert(0, here); from svgplot import plot
tm = [x * 1e3 for x in t]
plot(os.path.join(here, 'servo_pulse.svg'), 'AFS_Adom servo rail: 8 A / 20 ms stall pulse through the 5 mOhm shunt (own ngspice model)', 'ms', 'V',
[('VBAT_IN', tm, vin), ('VBAT servo rail', tm, vbat), ('VSYS', tm, vsys)])
plot(os.path.join(here, 'servo_pulse_shunt.svg'), 'INA219 differential input across the 5 mOhm shunt during the pulse', 'ms', 'mV', [('IN+ - IN-', tm, [1e3 * v for v in vsns])])