LTspice reference corpus
This directory holds the LTspice schematics that later phases null the DK-method circuit solver against. Nothing in it is required to build or test SushiDSP: CI reads the committed CSVs and never runs LTspice.
Commit 2bdbf91 simulated all eight blocks and committed their CSVs; jcm_fmv was
regenerated on 2026-09-06 after its treble-pot return node was corrected and its master
volume removed. Each block README lists the judgment calls behind its netlist.
Layout
docs/reference/spice/
README.md this file
models/
12ax7_koren.sub Koren triode + Norman grid current (D6b Triode12AX7)
el34_koren.sub Koren pentode + Norman grid current (D6b PentodeEL34)
d1n914.sub .model D1N914 (D6b SiliconDiode)
<block>/
<block>.asc the schematic
README.md components, values, probe, stepped control, judgment calls
<block>_ac.csv generated by tools/spice/export.py, committed
<block>_tran.csv generated by tools/spice/export.py, committed
The eight blocks, each with its own README:
| Block | Circuit |
|---|---|
ts_clip |
Tube Screamer, spec D6c |
ts_tone |
Tube Screamer, spec D6c |
jcm_v1a |
JCM800 2203, spec D6d |
jcm_v1b_cold |
JCM800 2203, spec D6d |
jcm_cf |
JCM800 2203, spec D6d |
jcm_fmv |
JCM800 2203, spec D6d |
jcm_ltp |
JCM800 2203, spec D6d |
jcm_pp_ot_nfb |
JCM800 2203, spec D6d |
Device models
The .sub files use the same equations and the same parameter values as the C++ models in
D6b, so a null test measures the solver, not model disagreement. Parameters are spec
D6b.3 verbatim:
| Model | Parameters |
|---|---|
12AX7_KOREN |
mu = 100, kp = 600, kvb = 300, kg1 = 1060, kx = 1.4 |
EL34_KOREN |
mu = 11, kp = 60, kvb = 24, kg1 = 650, kg2 = 4500, kx = 1.35 |
D1N914 |
Is = 2.52 nA, N = 1.752, Rs = 0.568 Ω, Cjo = 4 pF |
Both tubes use Norman’s soft grid-current law
Ig = Ig0 * ln(1 + exp((Vgk - 0.5) / 0.15)) with Ig0 = 1 uA. D6b’s C++ models must use
that same scale factor; a different Ig0 shifts every grid-conduction knee and the null
tests will blame the solver for it.
Pin order, which the schematics’ X lines depend on:
12AX7_KOREN:P G KEL34_KOREN:P G2 G1 K
How the schematics are written
Each .asc carries its whole circuit as SPICE directive text on the sheet (a TEXT ... !
block) rather than as placed symbols joined by wires. LTspice netlists those lines verbatim,
so node names are explicit and the netlist cannot silently depend on symbol pin geometry –
which could not have been checked without running LTspice. The sheets open and simulate
normally; they are just not pretty.
Every schematic carries both analyses as comment lines, and tools/spice/export.py enables
exactly one per run:
;ac dec 40 10 20k;tran 0 40m 20m 1u(a 40 ms run whose last 20 ms is saved as steady state)
.options plotwinsize=0 keeps the raw file uncompressed so the ASCII parse is exact. Every
block probes a node named out, and the stimulus is always
V1 <node> 0 SINE(0 {AMP} 1k) AC 1.
Blocks with a control (Drive, Tone, Preamp, Middle, Presence) step that control over the pot
fractions 0.1, 0.5 and 0.9. Blocks without one step AMP over 10 mV, 100 mV and 1 V. Either
way the CSV’s setting column is 0, 1 or 2 in that order.
CSV contracts
<block>_ac.csv – 200 log-spaced points from 10 Hz to 20 kHz per setting, interpolated from
the raw sweep:
frequency_hz,setting,magnitude_db,phase_degrees
<block>_tran.csv – the final 20 ms resampled onto a uniform 192 kHz grid (3840 rows per
setting):
time_s,setting,input_v,output_v
tests/measure/SpiceCsv.hpp reads both. A missing file yields an empty result rather than an
error, so a test can skip cleanly while the corpus is still ungenerated.
The setting column is meaningless in the AC CSVs of jcm_v1b_cold, jcm_cf and
jcm_ltp. Those three blocks have no control, so they step AMP – which scales only the
SINE() stimulus, while the AC 1 magnitude stays fixed. All three of their AC sweeps are
therefore identical and only setting = 0 carries information. Their transient CSVs do
carry three genuinely different runs, and a transient null is the acceptance criterion for
all three. Every other block steps a real control and its AC setting column means what it
says.
Things to watch in the results
All eight blocks have been simulated. These are the specific failures to look for when a block is regenerated:
EL34_KORENconvergence nearVg2k = 0. The Koren pentode’sE1divides byVg2k:E1 = (Vg2k/kp)*ln(1 + exp(kp*(1/mu + Vg1k/Vg2k))). At the start of a run, before the screen supply has come up,Vg2kpasses through zero and that term is singular. Ifjcm_pp_ot_nfbfails to find an operating point or aborts with a timestep-too-small error, this is the first suspect. The usual fixes, in order of preference: give the screen node a.ic, addstartupto the source, or clamp the divisor in the.sub(limit(V(G2,K), 1, 1e6)) – but if the divisor is clamped, D6b’s C++PentodeEL34must clamp it identically or the null test will measure the clamp instead of the solver.- The
ts_tonetopology, which is reconstructed from D6c’s prose rather than traced from a board. It should be monotonic between 300 Hz and 2 kHz with no notch (D6c’s acceptance criterion).jcm_fmvwas reconstructed the same way and its treble-pot return node was found wrong on 2026-09-06 (commit b3badb1); the corrected deck shows the characteristic mid scoop at noon and reads 0 dB at 10 Hz. jcm_pp_ot_nfbfeedback polarity, which depends on transformer dot orientation. If the loop turns out positive (oscillation, or gain rising asRnfbis reduced), swap the secondary: writeLs 0 secinstead ofLs sec 0.- Zero-valued pot sections at
LEVEL = 1(ts_tone). LTspice shorts a 0 Ω resistor and warns; that is the intended behaviour, not an error.jcm_fmvno longer has a master volume, so it no longer reaches this case.
Checklist for generating the CSVs
- Install LTspice: https://www.analog.com/en/resources/design-tools-and-calculators/ltspice-simulator.html
- Run
python tools/spice/export.py --all(add--ltspice <path>if it is not at the default location). - Sanity-check the results against the watch list above and against each block README’s open questions before trusting them.
- Commit the generated
<block>_ac.csvand<block>_tran.csvfiles. - Replace “not yet generated” in every block README’s LTspice version line with the version actually used, and drop the “not yet simulated” banner from that README and from this one. Done for all eight blocks on 2026-09-06.

