velxio/docs/wiki/circuit-emulation-tests.md

8.9 KiB
Raw Permalink Blame History

Test Catalog — All 47 Tests

Every test in test/test_circuit/test/ enumerated: the circuit under test, what physics it validates, and the numerical expectation.

Baseline — hand-rolled MNA solver (25 tests)

passive.test.js — 11 tests

Tests the linear DC solver on textbook passives.

# Test Circuit Expected Tolerance
1 Voltage divider 9V → 1k + 2k V(out) = 6.0 V 3 decimals
2 Equal-R divider 5V → 1k + 1k V(b) = 2.5 V 3 decimals
3 Ohm's law 5V, 220Ω I = 22.7 mA 5 decimals
4 Three 3kΩ parallel series with 1k V(p) = 1.5 V 3 decimals
5 Current source + R 1 mA into 2.2kΩ V = 2.2 V 3 decimals
6 Pot wiper at 50% 10k pot, 5V V = 2.5 V 2 decimals
7 Pot sweep 0→1 5 steps monotonic increase 10 mV slack
8 NTC resistance @ 25°C β=3950 R = 10 000 Ω within 1 Ω
9 NTC temperature monotonicity T ∈ {0, 25, 50} R(0) > R(25) > R(50) textbook ranges
10 NTC + 10k divider T ∈ {0, 25, 50} V_A0 matches expected per table per-point tolerance
11 Switch open/closed 5V → 1k → switch → GND open: 4.95.0 V, closed: < 50 mV

transient_rc.test.js — 3 tests

# Test Circuit Expected Tolerance
1 RC charging @ τ 10k, 100µF, 5V V(τ) ≈ 3.16 V ±5%
2 5τ settling 1k, 1µF, 3.3V V(5τ) > 99% V hard bound
3 Capacitor discharge pre-charged 5V → 10k V(τ) in [1.5, 2.2] V manual bounds

diodes.test.js — 7 tests

# Test Circuit Expected
1 Forward diode + 1k + 5V Shockley default V_d ∈ [0.55, 0.75] V, I ∈ [4, 5] mA
2 Reverse diode diode blocks V_a < 10 mV
3 Red LED + 220Ω + 5V V_f ∈ [1.8, 2.3] V, I ∈ [10, 16] mA, brightness > 0.5
4 Blue LED higher Vf than red V_blue > V_red, V_blue ∈ [2.8, 3.6] V
5 LED brightness with supply V ∈ {2.0, 3.0, 5.0} monotonic, dim at 2V, bright at 5V
6 BJT switch ON V_b = 5V, R_b = 10k V_CE < 0.8 V
7 BJT switch OFF V_b = 0 V_CE > 4.9 V

avr_blink.test.js — 2 tests

Uses the real fixtures/blink.hex (copied from Velxio's test fixtures).

# Test What it validates
1 Toggles pin 13 After 2 s of AVR time, observed ≥ 3 edge transitions on D13; both 0 and 1 states visible
2 Drives simulated LED Pin-13 voltage fed into a VoltageSource, 220 Ω + red LED; LED brightness switches between < 1% and > 50%

e2e_pot_pwm_led.test.js — 1 test

Uses potToPwmProgram() (hand-assembled: reads A0, writes to OCR0A for PWM on D6).

Circuit: +5V → pot (10k) → A0 / GND plus D6 → 220Ω → red LED → GND.

Sweep wiper 0.0 → 0.25 → 0.50 → 0.75 → 1.00:

Wiper V_A0 PWM duty I_LED Brightness
0.00 0.000 V 0.0 % 0.00 mA 0.000
0.25 1.250 V 24.7 % 0.00 mA 0.000
0.50 2.500 V 49.8 % 3.26 mA 0.163
0.75 3.750 V 74.9 % 8.77 mA 0.439
1.00 5.000 V 100.0 % 14.38 mA 0.719

Assertions: duty at 0% < 5%, duty at 100% > 90%, brightness monotonic, final > 0.5, initial < 0.05.

e2e_thermistor.test.js — 1 test

Uses adcReadProgram() + NTC + pullup divider.

Circuit: +5V → 10kΩ → A0 → NTC → GND.

Per temperature T ∈ {0, 15, 25, 35, 50} °C:

T_set V_A0 ADC raw R_ntc (true) R_meas (recovered) T_meas
0 °C 3.854 V 789 33 621 Ω 33 718 Ω 0.05 °C
15 °C 3.065 V 627 15 837 Ω 15 833 Ω 15.01 °C
25 °C 2.500 V 511 10 000 Ω 9 980 Ω 25.04 °C
35 °C 1.971 V 403 6 506 Ω 6 500 Ω 35.02 °C
50 °C 1.320 V 270 3 588 Ω 3 586 Ω 50.02 °C

Assertion: |T_meas T_set| < 1 °C at every point. Achieved: < 0.05 °C.

ngspice — spice_*.test.js (22 tests)

ngspice_smoke.test.js — 1 test

The minimal possible smoke test.

# Test Netlist Expected
1 Boot + voltage divider V 9 → R 1k → R 2k → GND, .op V(out) = 6.0

spice_passive.test.js — 3 tests

All run with .op:

# Circuit Expected
1 9V → 1k + 2k V(out) = 6 V
2 Three 3kΩ parallel + 1k series from 3V V(p) = 1.5 V
3 1 mA current source into 2.2kΩ V(a) = 2.2 V

spice_transient.test.js — 2 tests

# Test .tran Expected
1 RC charging 10m 3 V(τ) ≈ 3.16 V, V(5τ) > 90%
2 RLC underdamped oscillation 10u 30m, R=1Ω, L=10mH, C=10µF zero-crossings imply f ≈ 503 Hz, ±15 %

spice_ac.test.js — 2 tests

# Test .ac Expected
1 RC low-pass Bode 20 ppd, 10 Hz 1 MHz f₋₃dB ∈ [900, 1100] Hz; low-freq gain ≈ 0 dB; 20 dB/decade rolloff; phase @ cutoff ≈ 45°
2 Parallel-tank RLC bandpass 30 ppd, 10 Hz 1 MHz peak near 5033 Hz (±20%), passive so max gain ≈ 0 dB; 10×f₀ rejection > 15 dB below peak

spice_active.test.js — 6 tests

# Test Analysis Expected
1 Diode forward (1N4148 model) .op V_a ∈ [0.55, 0.80] V
2 Bridge rectifier (4 diodes + sine 6V/50Hz) .tran 0.1m 40m peak output 4.25.4 V, always positive
3 Common-emitter amp (2N2222, 12V, ~1kHz input, bypassed R_E) .tran 10u 6m voltage gain > 30
4 N-MOS Level-1 switch, V_gate=5V .op V(drain) < 1.0 V
5 N-MOS Level-1 off, V_gate=0 .op V(drain) > 4.9 V
6 Op-amp inverting amp (E-source, R_in=1k, R_f=10k) .op V(out) ≈ 2.0 V

spice_digital.test.js — 4 tests

All gates implemented with B-sources using u() step function.

# Gate Rows tested
1 AND 4 (truth table)
2 NAND 4
3 XOR 4
4 AND → RC low-pass .tran 10u 20m — verifies filter ripple < 1.5 V, raw AND swings 05 V, filtered mean in (0, 5)

spice_555_astable.test.js — 1 test

RC relaxation oscillator built from a Schmitt-switch (S-element with hysteresis).

  • R = 10 kΩ, C = 10 nF
  • SMOD: Vt=2.5, Vh=0.833, Ron=100, Roff=1G
  • .tran 0.5u 2m

Expected: output toggles, ≥ 3 rising edges detected in the steady-state window; measured frequency between 500 Hz and 50 kHz (wide range because behavioral model differs from ideal 555 math).

Typical measured: ~7 kHz.

spice_avr_mixed.test.js — 3 tests

The flagship mixed-signal demonstrator.

Test 1 — NTC → ngspice → ADC → sketch

For each T ∈ {0, 25, 50} °C:

  1. ngspice .op solves 10k pullup + R_ntc(T)
  2. avr.setAnalogVoltage(0, V(a0))
  3. avr.runCycles(500_000) runs adcReadProgram()
  4. Read ADCH/ADCL registers directly → reconstruct 10-bit result

Expected results match textbook within ±5 LSB:

T V(a0) from ngspice ADC raw from avr8js
0 °C 3.854 V 789
25 °C 2.500 V 512
50 °C 1.320 V 270

Test 2 — Sketch → PWM → ngspice RC → filtered DC

For V_A0 ∈ {1.0, 2.5, 4.0} V:

  1. Run potToPwmProgram() with the given analog voltage
  2. Read PWM duty (must match V_A0 / 5 ± 0.05)
  3. Feed duty × 5 as DC source into R=10k, R_load=10MΩ in ngspice
  4. .op gives filtered DC output — should match duty × 5 within 0.1 V

Measured:

V_A0 duty V_filt expected
1.0 V 20.0 % 0.999 V 1.00 V
2.5 V 50.2 % 2.507 V 2.51 V
4.0 V 80.0 % 3.996 V 4.00 V

Test 3 — Co-simulation loop (AVRSpiceBridge)

The bridge runs the AVR in 1 ms slices, re-solving ngspice between each. Pot wiper is moved mid-test.

  • wiper = 0.25 → 5 slices run → expected ADC ≈ 256 (0.25 × 1023). Measured: 256. Tolerance ±20 LSB.
  • wiper moves to 0.75 → 5 more slices → expected ADC ≈ 767. Measured: 768.

Demonstrates that ngspice + avr8js can be cleanly co-simulated quasi-statically.

Pass/fail history

All 47 tests passed as of 2026-04-15. Running npm test from test/test_circuit/ is the canonical verification. Expected wall-clock: ~5 seconds on an i7.

Full command output:

 ✓ test/ngspice_smoke.test.js        (1 test)  372ms
 ✓ test/spice_passive.test.js        (3 tests) 358ms
 ✓ test/spice_transient.test.js      (2 tests) 629ms
 ✓ test/spice_ac.test.js             (2 tests) 340ms
 ✓ test/spice_active.test.js         (6 tests) 600ms
 ✓ test/spice_digital.test.js        (4 tests) 420ms
 ✓ test/spice_555_astable.test.js    (1 test)  609ms
 ✓ test/spice_avr_mixed.test.js      (3 tests) 800ms
 ✓ test/passive.test.js             (11 tests)   8ms
 ✓ test/transient_rc.test.js         (3 tests)  22ms
 ✓ test/diodes.test.js               (7 tests)  15ms
 ✓ test/avr_blink.test.js            (2 tests) 2400ms
 ✓ test/e2e_pot_pwm_led.test.js      (1 test)   90ms
 ✓ test/e2e_thermistor.test.js       (1 test)  121ms

 Test Files  14 passed (14)
 Tests       47 passed (47)
 Duration    ~5 s