@vessel-dsp/compiler 0.4.0 → 0.6.0

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.
Files changed (80) hide show
  1. package/README.md +4 -2
  2. package/dist/bbd-clock.d.ts.map +1 -1
  3. package/dist/bbd-clock.js +5 -0
  4. package/dist/bbd-clock.js.map +1 -1
  5. package/dist/compile.d.ts.map +1 -1
  6. package/dist/compile.js +305 -12
  7. package/dist/compile.js.map +1 -1
  8. package/dist/device-laws.d.ts.map +1 -1
  9. package/dist/device-laws.js +552 -97
  10. package/dist/device-laws.js.map +1 -1
  11. package/dist/fixtures/circuits.d.ts +23 -2
  12. package/dist/fixtures/circuits.d.ts.map +1 -1
  13. package/dist/fixtures/circuits.js +205 -2
  14. package/dist/fixtures/circuits.js.map +1 -1
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +1 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/lower.d.ts.map +1 -1
  20. package/dist/lower.js +4 -0
  21. package/dist/lower.js.map +1 -1
  22. package/dist/netlist.d.ts +52 -0
  23. package/dist/netlist.d.ts.map +1 -1
  24. package/dist/netlist.js +585 -13
  25. package/dist/netlist.js.map +1 -1
  26. package/dist/part-catalog.d.ts +35 -6
  27. package/dist/part-catalog.d.ts.map +1 -1
  28. package/dist/part-catalog.js +2544 -443
  29. package/dist/part-catalog.js.map +1 -1
  30. package/dist/registry-data/bjt.d.ts +3 -0
  31. package/dist/registry-data/bjt.d.ts.map +1 -0
  32. package/dist/registry-data/bjt.js +1350 -0
  33. package/dist/registry-data/bjt.js.map +1 -0
  34. package/dist/registry-data/charge-pump.d.ts +3 -0
  35. package/dist/registry-data/charge-pump.d.ts.map +1 -0
  36. package/dist/registry-data/charge-pump.js +204 -0
  37. package/dist/registry-data/charge-pump.js.map +1 -0
  38. package/dist/registry-data/diode.d.ts +3 -0
  39. package/dist/registry-data/diode.d.ts.map +1 -0
  40. package/dist/registry-data/diode.js +683 -0
  41. package/dist/registry-data/diode.js.map +1 -0
  42. package/dist/registry-data/index.d.ts +20 -0
  43. package/dist/registry-data/index.d.ts.map +1 -0
  44. package/dist/registry-data/index.js +100 -0
  45. package/dist/registry-data/index.js.map +1 -0
  46. package/dist/registry-data/jfet.d.ts +3 -0
  47. package/dist/registry-data/jfet.d.ts.map +1 -0
  48. package/dist/registry-data/jfet.js +605 -0
  49. package/dist/registry-data/jfet.js.map +1 -0
  50. package/dist/registry-data/loader.d.ts +137 -0
  51. package/dist/registry-data/loader.d.ts.map +1 -0
  52. package/dist/registry-data/loader.js +350 -0
  53. package/dist/registry-data/loader.js.map +1 -0
  54. package/dist/registry-data/mosfet.d.ts +3 -0
  55. package/dist/registry-data/mosfet.d.ts.map +1 -0
  56. package/dist/registry-data/mosfet.js +63 -0
  57. package/dist/registry-data/mosfet.js.map +1 -0
  58. package/dist/registry-data/opamp.d.ts +37 -0
  59. package/dist/registry-data/opamp.d.ts.map +1 -0
  60. package/dist/registry-data/opamp.js +669 -0
  61. package/dist/registry-data/opamp.js.map +1 -0
  62. package/dist/registry-data/regulator.d.ts +3 -0
  63. package/dist/registry-data/regulator.d.ts.map +1 -0
  64. package/dist/registry-data/regulator.js +189 -0
  65. package/dist/registry-data/regulator.js.map +1 -0
  66. package/dist/registry-data/schema.d.ts +56 -0
  67. package/dist/registry-data/schema.d.ts.map +1 -0
  68. package/dist/registry-data/schema.js +142 -0
  69. package/dist/registry-data/schema.js.map +1 -0
  70. package/dist/registry-data/zener.d.ts +3 -0
  71. package/dist/registry-data/zener.d.ts.map +1 -0
  72. package/dist/registry-data/zener.js +547 -0
  73. package/dist/registry-data/zener.js.map +1 -0
  74. package/dist/registry.d.ts +72 -5
  75. package/dist/registry.d.ts.map +1 -1
  76. package/dist/registry.js +65 -6
  77. package/dist/registry.js.map +1 -1
  78. package/dist/types.d.ts +255 -19
  79. package/dist/types.d.ts.map +1 -1
  80. package/package.json +1 -1
@@ -9,9 +9,10 @@
9
9
  // them. So the generator and the lowering JSON were deleted and this file kept, rather than the
10
10
  // reverse: the hand edits are the corrections. Recovery point for both: `892cdcf6`.
11
11
  //
12
- // The electrical parameters that come from the device database
13
- // (`web/assets/registry/component-*-chips.json`, still the product's canonical device data) are
14
- // held in agreement with this file by `scripts/report-part-store-drift.ts`, not by generation.
12
+ // The electrical parameters that come from the device database live in
13
+ // `registry-data/` as cited values (`{ value, basis, source }`), and this file builds the
14
+ // entries that carry them. `scripts/report-part-store-drift.ts` is gone (deleted with the
15
+ // lowering-JSON generator); nothing re-runs a generator over this file.
15
16
  //
16
17
  // `registry.ts` deliberately holds no parts -- it is the shape and the matching rules, so the
17
18
  // compiler itself knows about no specific chip and `emptyRegistry` stays a legitimate
@@ -29,7 +30,32 @@
29
30
  // The one thing this file needs from an op-amp datasheet is **open-loop gain**. `ideal-opamp` takes
30
31
  // `openLoopGain` plus rails, and the rails are the pedal's rather than the part's, so a dual op-amp
31
32
  // is a complete model from one published number and its pinout.
32
- import { foldPartId } from "./registry.js";
33
+ //
34
+ // **Gain-bandwidth product is the second figure, and it lives in the data registry**
35
+ // (`registry-data/opamp.ts`) rather than as a field on each entry, because one entry here
36
+ // often covers parts whose datasheets print different products (the TL072/NE5532/LM358 entry
37
+ // is one law over three datasheets). The TYP column is transcribed ("typical" here is that
38
+ // datasheet's own TYP column for that part, not a generic typical value), never MIN or MAX,
39
+ // never a graph read by eye, never a registry or curated-record number.
40
+ // A part whose datasheet prints no TYP gain-bandwidth row is listed in
41
+ // `opampGainBandwidthAbsent` with the reason and gets no figure, so its stamp is `null`
42
+ // and the compile names it (`opamp-gain-bandwidth-default`).
43
+ // Matching is by exact folded part id, as everywhere in this file: an id the datasheet
44
+ // does not print is not covered because a sibling is.
45
+ //
46
+ // **Output swing is the third figure, and lives there too** (`registry-data/opamp.ts`): the
47
+ // distance the output stops short of each rail, as a printed **pair** (positive side, negative
48
+ // side) with the condition it was printed at (total supply, load) and how it is carried to a
49
+ // pedal on another supply (`translation`, `"constant"` or `"proportional"`). TYP column only,
50
+ // printed rows only; an id with no such row is listed in `opampOutputSwingAbsent` with the
51
+ // reason and states no swing. Nearly every printed row is at +-15 V and nearly every pedal
52
+ // runs at 9 V, so the carry is a named choice, and `opamp-law-default-parameters` names
53
+ // every device it was carried for.
54
+ import { foldPartId, opampOutputSwingOf } from "./registry.js";
55
+ import { bjtRegistryRecords, chargePumpRegistryRecords, diodeRegistryRecords, findPartRegistryData, jfetRegistryRecords, mosfetRegistryRecords, opampGainBandwidthAbsent, opampOutputSwingAbsent, opampRegistryRecords, regulatorRegistryRecords, zenerRegistryRecords, communityProvenanceOf, } from "./registry-data/index.js";
56
+ // The absent lists and the merged op-amp records live in `registry-data/opamp.ts` and are
57
+ // re-exported here so their existing import sites keep working.
58
+ export { opampGainBandwidthAbsent, opampOutputSwingAbsent, opampRegistryRecords, } from "./registry-data/index.js";
33
59
  /**
34
60
  * An op-amp section as the registry states it: the part's own gain, and no rails.
35
61
  */
@@ -77,11 +103,13 @@ function tubeLaw(partIds, law) {
77
103
  * So a newly covered zener renders its old forward behaviour exactly and only gains a
78
104
  * reverse knee; deriving the forward law is plan step 2's work, not this entry's.
79
105
  */
80
- function zenerLaw(partIds, breakdownVolts) {
106
+ function zenerLaw(record) {
107
+ const breakdownVolts = zenerBreakdownVoltsOf(record);
81
108
  return {
82
- partIds,
109
+ partIds: [...record.partIds],
83
110
  declaredTypes: [],
84
111
  terminalRoleGroups: [],
112
+ ...provisionalArmOf(record),
85
113
  model: {
86
114
  kind: "law",
87
115
  law: {
@@ -96,118 +124,430 @@ function zenerLaw(partIds, breakdownVolts) {
96
124
  };
97
125
  }
98
126
  /**
99
- * Every part id a `zenerLaw` entry covers, folded for comparison. `report-part-store-drift.ts`
100
- * keys its forward-law exclusion on this set -- a catalog product -- rather than on the
101
- * registry row's `type`, so a hand-fitted diode law that happens to sit on a zener id is
102
- * still graded, and a zener entry is never mistaken for a forward claim.
127
+ * The `provisionalParams` arm of a law entry built from a data record: the record's
128
+ * `community`-basis parameters with their source URLs, or absent when the record holds
129
+ * none. Spread into the built entry, so an entry with no provisional value is shaped
130
+ * exactly as before (the field stays absent, not empty).
103
131
  */
104
- // --- Switch JFETs: per-part silicon gate data, channel deliberately unrefined -----------------
105
- //
106
- // The JFET class default (`device-laws.ts`'s `case "jfet"`) is avowedly no part's values, and
107
- // its gate prefactor (`gateSaturationCurrent: 1e-5`, reused from the triode grid) parks a
108
- // series switch whose gate sits ~0.4 V above its channel in the junction's exponential foot,
109
- // rectifying microamp signal currents into the audio path. Measured 2026-10-06 on
110
- // `ibanez-ts9` Q102 (Vgs +0.392, gate peaks 5 uA): the engine's H2 sits at -29.6 dB there
111
- // against -57 to -64 dB on the physical reissue specimen and -77.9 dB with the FET replaced
112
- // by 100 ohm; killing only the gate terms reproduces the resistor swap to the printed digit,
113
- // and ngspice with the stock-card-equivalent NJF mapping agrees with the engine to 0.1/0.8 dB,
114
- // so the defect is the parameter value, not the solver. The TS9 H2 is channel-inert (a
115
- // 52-to-836-ohm Rds sweep moves it 0.2 dB), so the entry below refines the gate term only.
132
+ function provisionalArmOf(record) {
133
+ const provisional = communityProvenanceOf(record);
134
+ return provisional.length === 0 ? {} : { provisionalParams: provisional };
135
+ }
136
+ /**
137
+ * Every part id a `zenerLaw` entry covers, folded for comparison: a hand-fitted diode law
138
+ * that happens to sit on a zener id is still graded as a forward claim, and a zener entry
139
+ * is never mistaken for one.
140
+ */
141
+ // --- Wave-1 discrete builders: cited record -> law entry --------------------------------------
116
142
  //
117
- // The entry carries the class channel by construction, not by measurement:
118
- // `thresholdVolts`, `transconductance`, `channelLengthModulation` and `subthresholdVolts`
119
- // repeat the class default, and `device-laws.ts` reads only the gate term from a part
120
- // entry. The channel is rank-dependent (an unbinned 2SK30A spans IDSS 0.3-6.5 mA across
121
- // R/O/Y/GR), and the database row's beta.typ disagrees with `boss-sg-1-slow-gear`'s
122
- // recorded gain-cell island (report 4: the island's own Beta=1.2m reproduces its 59.9x,
123
- // beta.typ 0.0004664 would halve it), so refining the channel here would trade one
124
- // packet's fix for another's regression on evidence neither side owns. A rank-binned
125
- // channel fit belongs in the packet or a rank-suffixed entry, not here.
143
+ // Stage-1 wired the bjt, mosfet, diode-forward and jfet-channel cases to read the registry
144
+ // (document-typed > registry > class default, exact folded part id). These builders turn the
145
+ // wave-1 `registry-data` records into the law entries those cases read. Every builder maps the
146
+ // record's PRESENT parameters and gap-fills the rest with the class default the unregistered
147
+ // path stamps -- the literals below repeat `device-laws.ts` (`SILICON_SATURATION_CURRENT`,
148
+ // `SILICON_FORWARD_BETA`, reverseBeta 1, `THERMAL_VOLTAGE`, leakageAmps 0; the JFET/MOSFET
149
+ // channel and gate constants; the diode forward defaults) BY VALUE, deliberately: importing
150
+ // them would drag the stamping path into the catalog, and a drift between the two copies is
151
+ // caught by the sparse-record test (`registry-discrete-wiring`), which stamps a one-parameter
152
+ // entry and requires every omitted field to equal the empty-registry stamp.
126
153
  //
127
- // Provenance and uncertainty, stated rather than fitted: the gate transcribes the device
128
- // database's silicon-junction convention (`saturationCurrent.typ` 1e-14, the same constant
129
- // as `SILICON_SATURATION_CURRENT`); onset/scale stay class. The Toshiba 2SK30A sheet
130
- // (`parts/jfets/2SK30A/datasheets/toshiba-2sk30a.pdf`, curated in `parts/jfets/2SK30A.md`)
131
- // prints IDSS ranks, VGS(off) and Yfs, and no forward gate current at all (only reverse
132
- // IGSS). The gate IS is therefore a silicon-junction order choice, not a measured 2SK30A
133
- // forward current: it puts the foot current at switch biases three orders below signal
134
- // currents (verified H2-free for gateSaturationCurrent in {1e-7, 1e-8, 1e-9, 0}; 1e-14 sits
135
- // inside that verified-flat region at the repo's silicon constant). Never fitted to a specimen.
154
+ // Bin and package spellings merge into the entry's `partIds`: `registryLawFor` matches whole
155
+ // folded ids against `partIds` only, so an alias that stayed on the record would never match.
156
+ // The data loader's uniqueness scope already covers ids plus aliases, so the merge cannot
157
+ // introduce a collision the loader missed.
136
158
  //
137
- // Coverage is deliberately a single exact id: the part `ibanez-ts9` declares.
138
- // The match is `registryLawFor`'s whole folded id (`2sk30a`), with no prefix or
139
- // substring rule anywhere on the path, so `2SK30ATM`, `2SK30ATM-Y` and `2SK44SPC
140
- // do not match. `boss-cs-2` (2SK30ATM-Y), `boss-ds-1` (2SK30ATM), `boss-dm-2`
141
- // (2SK30ATM-Y) and `ibanez-ts808` (2SK44SPC) each need their own packet study
142
- // before their behaviour may move, so they stay on the class default until then.
143
- // `boss-sg-1-slow-gear` declares the same id; its gate sits 0.5 V below onset in
144
- // every state, so the gate refinement is inert there (a ~0.02 dB Vgs shift from the
145
- // removed 4.5 nA foot current, measured in the report-5 rerun).
146
- function fetLaw(partIds, law) {
159
+ // Structural fields stay at stamp time, never in the entry: a bjt entry's `polarity` and a
160
+ // fet entry's `channel` are inert placeholders (`npn` / `n`) that `device-laws.ts` always
161
+ // overwrites from the device's own `pnp` / `pChannel`. A bjt entry therefore serves NPN and
162
+ // PNP parts alike, and no entry asserts a polarity or channel about anyone's part.
163
+ /** One record's cited value for a schema parameter, or absent when the record omits it. */
164
+ function citedValueOf(record, name) {
165
+ const param = record.params[name];
166
+ return param === undefined ? undefined : param.value;
167
+ }
168
+ /** The entry's id list: canonical ids plus typed alias spellings, in listing order. */
169
+ function entryIdsOf(record) {
170
+ return [...record.partIds, ...(record.aliases ?? [])];
171
+ }
172
+ /** A bjt record's law entry. Throws when the record is not class `bjt` or states nothing. */
173
+ export function bjtLawEntry(record) {
174
+ if (record.class !== "bjt") {
175
+ throw new Error(`${record.partIds.join(",")}: a bjt entry needs class "bjt", not "${record.class}"`);
176
+ }
177
+ if (Object.keys(record.params).length === 0) {
178
+ throw new Error(`${record.partIds.join(",")}: a bjt record states no parameters`);
179
+ }
147
180
  return {
148
- partIds,
181
+ partIds: entryIdsOf(record),
149
182
  declaredTypes: [],
150
183
  terminalRoleGroups: [],
151
- model: { kind: "law", law },
184
+ ...provisionalArmOf(record),
185
+ model: {
186
+ kind: "law",
187
+ law: {
188
+ kind: "bjt",
189
+ polarity: "npn",
190
+ saturationCurrent: citedValueOf(record, "saturationCurrent") ?? 1e-14,
191
+ forwardBeta: citedValueOf(record, "forwardBeta") ?? 100,
192
+ reverseBeta: citedValueOf(record, "reverseBeta") ?? 1,
193
+ thermalVoltage: citedValueOf(record, "thermalVoltage") ?? 0.025_852,
194
+ leakageAmps: citedValueOf(record, "leakageAmps") ?? 0,
195
+ },
196
+ },
197
+ };
198
+ }
199
+ /**
200
+ * A signal-diode record's law entry. Requires the forward pair together: the only
201
+ * derivation the registry admits (two printed (If, Vf) points) yields IS and n as one
202
+ * working, so a lone IS without n is a broken derivation and throws rather than shipping
203
+ * half a knee. Gap-filled breakdownVolts 0 keeps the no-knee reading the unregistered path
204
+ * stamps (a zener knee belongs to class `zener` and the existing `zenerLaw` path, not here).
205
+ */
206
+ export function diodeLawEntry(record) {
207
+ if (record.class !== "diode") {
208
+ throw new Error(`${record.partIds.join(",")}: a diode entry needs class "diode", not "${record.class}"`);
209
+ }
210
+ const saturationCurrent = citedValueOf(record, "saturationCurrent");
211
+ const emissionCoefficient = citedValueOf(record, "emissionCoefficient");
212
+ if (saturationCurrent === undefined || emissionCoefficient === undefined) {
213
+ throw new Error(`${record.partIds.join(",")}: a diode record states no forward pair ` +
214
+ `(saturationCurrent ${saturationCurrent === undefined ? "absent" : "present"}, ` +
215
+ `emissionCoefficient ${emissionCoefficient === undefined ? "absent" : "present"})`);
216
+ }
217
+ return {
218
+ partIds: entryIdsOf(record),
219
+ declaredTypes: [],
220
+ terminalRoleGroups: [],
221
+ ...provisionalArmOf(record),
222
+ model: {
223
+ kind: "law",
224
+ law: {
225
+ kind: "diode",
226
+ saturationCurrent,
227
+ emissionCoefficient,
228
+ thermalVoltage: citedValueOf(record, "thermalVoltage") ?? 0.025_852,
229
+ breakdownVolts: citedValueOf(record, "breakdownVolts") ?? 0,
230
+ seriesResistance: citedValueOf(record, "seriesResistance") ?? 1.0,
231
+ isLed: false,
232
+ },
233
+ },
234
+ };
235
+ }
236
+ /**
237
+ * A JFET- or MOSFET-channel record's law entry. The class selects the gap-fill constants
238
+ * (JFET depletion defaults with the silicon gate; MOSFET enhancement defaults with the
239
+ * insulated gate), so a JFET record can never stamp MOSFET gate zeros and the reverse.
240
+ * Throws on any other class and on an empty record.
241
+ */
242
+ export function fetLawEntry(record, cls) {
243
+ if (record.class !== cls) {
244
+ throw new Error(`${record.partIds.join(",")}: a ${cls} entry needs class "${cls}", not "${record.class}"`);
245
+ }
246
+ if (Object.keys(record.params).length === 0) {
247
+ throw new Error(`${record.partIds.join(",")}: a ${cls} record states no parameters`);
248
+ }
249
+ const channelDefaults = cls === "jfet"
250
+ ? {
251
+ thresholdVolts: -2,
252
+ transconductance: 1e-3,
253
+ channelLengthModulation: 0,
254
+ subthresholdVolts: 0.07,
255
+ gateSaturationCurrent: 1e-5,
256
+ gateOnsetVolts: 0.5,
257
+ gateScaleVolts: 0.06,
258
+ }
259
+ : {
260
+ thresholdVolts: 2,
261
+ transconductance: 1e-3,
262
+ channelLengthModulation: 0,
263
+ subthresholdVolts: 0,
264
+ gateSaturationCurrent: 0,
265
+ gateOnsetVolts: 0,
266
+ gateScaleVolts: 1,
267
+ };
268
+ return {
269
+ partIds: entryIdsOf(record),
270
+ declaredTypes: [],
271
+ terminalRoleGroups: [],
272
+ ...provisionalArmOf(record),
273
+ model: {
274
+ kind: "law",
275
+ law: {
276
+ kind: "fet",
277
+ channel: "n",
278
+ thresholdVolts: citedValueOf(record, "thresholdVolts") ??
279
+ channelDefaults.thresholdVolts,
280
+ transconductance: citedValueOf(record, "transconductance") ??
281
+ channelDefaults.transconductance,
282
+ channelLengthModulation: citedValueOf(record, "channelLengthModulation") ??
283
+ channelDefaults.channelLengthModulation,
284
+ subthresholdVolts: citedValueOf(record, "subthresholdVolts") ??
285
+ channelDefaults.subthresholdVolts,
286
+ gateSaturationCurrent: citedValueOf(record, "gateSaturationCurrent") ??
287
+ channelDefaults.gateSaturationCurrent,
288
+ gateOnsetVolts: citedValueOf(record, "gateOnsetVolts") ??
289
+ channelDefaults.gateOnsetVolts,
290
+ gateScaleVolts: citedValueOf(record, "gateScaleVolts") ??
291
+ channelDefaults.gateScaleVolts,
292
+ },
293
+ },
152
294
  };
153
295
  }
154
- // --- Zener diodes: reverse breakdown from the device database ------------------------------------
296
+ // --- JFET channel records: built by `fetLawEntry` (wave-1 builders above) -------------------
297
+ //
298
+ // The JFET class default is avowedly no part's values; see `registry-data/jfet.ts` for the
299
+ // measured gate-defect note, the rank argument for keeping the 2SK30A class channel, and the
300
+ // provenance. The generic builder maps each record's present parameters and gap-fills the
301
+ // class channel, so 2SK30A (gate term only) stamps byte-identically to before while full
302
+ // channels (2N5457) stamp their cited values.
303
+ // --- Zener diodes: reverse breakdown from the data registry -------------------------------------
155
304
  //
156
305
  // `breakdownVolts` only; forward behaviour repeats the class default by construction (see
157
306
  // `zenerLaw` above). A device that declares its own breakdown keeps it: `device-laws.ts`
158
- // overlays the declared value on a catalog match, so the 26 corpus diodes that already
307
+ // overlays the declared value on a catalog match, so the corpus diodes that already
159
308
  // declare one (e.g. `boss-ph-1r` D1 11 V against the registry's 10.99) render unchanged.
309
+ // The rows live in `registry-data/zener.ts` as cited values; the entries array below maps
310
+ // them through `zenerLaw`, so the membership set below and the shipped entries cannot
311
+ // drift apart: there is one list, not two.
312
+ /** Read a zener record's cited breakdown, loudly. The loader guarantees the citation. */
313
+ function zenerBreakdownVoltsOf(record) {
314
+ const breakdown = record.params["breakdownVolts"];
315
+ if (breakdown === undefined || !Number.isFinite(breakdown.value)) {
316
+ throw new Error(`${record.partIds.join(",")}: a zener record states no breakdownVolts`);
317
+ }
318
+ return breakdown.value;
319
+ }
320
+ export const zenerCatalogPartIds = new Set(zenerRegistryRecords.flatMap((record) => record.partIds.map((id) => foldPartId(id))));
321
+ // --- Fixed-rail regulators: wiring skeletons over registry-data voltages ---------------------------
322
+ // The rail numbers live in `registry-data/regulator.ts` as cited values; this table holds only
323
+ // the terminal wiring (role groups, section terminals) each rail's entry shares. Keyed by the
324
+ // rail volts the record states, so a record always lands on its own wiring and a skeleton row
325
+ // no record consumes throws at load (see the check below). `pinout` is `[null, null, null]`
326
+ // for all of them: no regulator entry asserts a pin order.
327
+ const regulatorTerminalStructures = {
328
+ // UNRESOLVED: this entry conflates two packages with OPPOSITE pin orders. The TO-92/SOT-89 78L05 is pin1=VOUT, pin2=GND, pin3=VIN (verified: TI LM78L, SNVS754O, Figure 4-2 SOT-89 top view and Figure 4-3 LP 3-pin TO-92 bottom view). The TO-220 7805 is pin1=VIN, pin2=GND, pin3=VOUT. The role groups below assert the 7805 order, because `pin1` sits in the vin group and `pin3` in the vout group.
329
+ // Consequence: a document that declares a 78L05's terminals as pin1/pin2/pin3 binds its input and output the wrong way round. No corpus packet does today -- all seven three-pin regulators declare typed roles as of 2026-09-05 -- so this is latent, not live. Fixing it means SPLITTING the entry by package before a `pinout` can be written, because one pin map cannot serve both.
330
+ 5: {
331
+ terminalRoleGroups: [["vin", "in", "input", "pin1"], ["gnd", "pin2"], ["vout", "out", "output", "pin3"]],
332
+ terminals: [2, 1],
333
+ },
334
+ 9: {
335
+ terminalRoleGroups: [["vin", "in", "input", "pin1"], ["gnd", "pin2"], ["vout", "out", "output", "pin3"]],
336
+ terminals: [2, 1],
337
+ },
338
+ // Wave 5: 12 V and 3.3 V 78Lxx rails (LM78L12/LM78L33, TI SNVS754O). Same wiring
339
+ // shape as the 5 V/9 V entries, and the same latent TO-92-vs-TO-220 pin-order
340
+ // caveat as the 5 V entry above: no corpus packet names these rails today, so the
341
+ // order is asserted for parity, not verified against a packet.
342
+ 12: {
343
+ terminalRoleGroups: [["vin", "in", "input", "pin1"], ["gnd", "pin2"], ["vout", "out", "output", "pin3"]],
344
+ terminals: [2, 1],
345
+ },
346
+ 3.3: {
347
+ terminalRoleGroups: [["vin", "in", "input", "pin1"], ["gnd", "pin2"], ["vout", "out", "output", "pin3"]],
348
+ terminals: [2, 1],
349
+ },
350
+ // electro-harmonix-q-tron's U6 (PartNumber 78L18AC) is the 24V-to-+18V rail; it matches by part id on the first rung, so the role groups below are the standard in/gnd/out family spelling kept for parity with the 5V/9V entries, not a new contract.
351
+ 18: {
352
+ terminalRoleGroups: [["vin", "in", "input", "pin1"], ["gnd", "ground", "pin2"], ["vout", "out", "output", "pin3"]],
353
+ terminals: [2, 1],
354
+ },
355
+ // `volts` here is SIGNED, which contradicts the convention its consumer documents: `declaredRegulatorTerminals` says "the law carries a magnitude" and reverses the source's sense for an output declared `negative`. Declaring `negative` against a signed -15 therefore double-negates and puts +15V on the rail -- measured 2026-09-05. Until this is settled, a negative regulator using this entry must declare its output `positive`, which is why deluxe-memory-man does.
356
+ [-15]: {
357
+ terminalRoleGroups: [["gnd", "pin1"], ["vin", "in", "input", "pin2"], ["vout", "out", "output", "pin3"]],
358
+ terminals: [2, 0],
359
+ },
360
+ // `terminalRoleGroups` is left empty rather than copying the existing regulators' generic `in`/`gnd`/`out` aliases: `VR1` already resolves through the exact part id, and this repo's own corpus shows that generic pinout-signature fallback misidentifying an unrelated part with similarly generic terminal names (`boss-oc-2`'s `BA634-5`, five terminals including `in`/`gnd`/`out`, incidentally satisfies the existing 78L05 groups and is only saved from a wrong law by the later terminal-count guard). No reason to add a second entry to that same weak ladder when the exact part id already does the job.
361
+ 15: {
362
+ terminalRoleGroups: [],
363
+ terminals: [2, 1],
364
+ },
365
+ };
366
+ /** Build one regulator entry from its registry record and the wiring skeleton for its rail. */
367
+ function regulatorEntry(record) {
368
+ const volts = record.params["volts"];
369
+ const sourceOhms = record.params["sourceOhms"];
370
+ if (volts === undefined || sourceOhms === undefined) {
371
+ throw new Error(`${record.partIds.join(",")}: a regulator record states no volts or sourceOhms`);
372
+ }
373
+ const structure = regulatorTerminalStructures[volts.value];
374
+ if (structure === undefined) {
375
+ throw new Error(`${record.partIds.join(",")}: no regulator wiring skeleton for ${String(volts.value)} V`);
376
+ }
377
+ return {
378
+ partIds: [...record.partIds],
379
+ declaredTypes: [],
380
+ terminalRoleGroups: structure.terminalRoleGroups,
381
+ ...provisionalArmOf(record),
382
+ model: {
383
+ kind: "sections",
384
+ sections: [
385
+ {
386
+ law: {
387
+ kind: "voltage-source",
388
+ volts: volts.value,
389
+ sourceOhms: sourceOhms.value,
390
+ },
391
+ terminals: [...structure.terminals],
392
+ },
393
+ ],
394
+ pinout: [null, null, null],
395
+ },
396
+ };
397
+ }
398
+ /** The regulator entry for one rail, by the volts its registry record states. Position-preserving. */
399
+ function regulatorEntryForVolts(volts) {
400
+ const record = regulatorRegistryRecords.find((candidate) => candidate.params["volts"]?.value === volts);
401
+ if (record === undefined) {
402
+ throw new Error(`no regulator registry record states ${String(volts)} V`);
403
+ }
404
+ return regulatorEntry(record);
405
+ }
406
+ // Every skeleton row is consumed by exactly one record, and every record lands on a row:
407
+ // a rail added to one side without the other throws here rather than silently defaulting.
408
+ for (const key of Object.keys(regulatorTerminalStructures)) {
409
+ if (!regulatorRegistryRecords.some((record) => record.params["volts"]?.value === Number(key))) {
410
+ throw new Error(`regulator wiring skeleton for ${key} V is consumed by no registry record`);
411
+ }
412
+ }
413
+ // --- Charge pumps: wiring skeletons over registry-data loss/slope ---------------------------------
414
+ // The two numbers the derivation reads (`sourceOhms`, `chargePumpLossVolts`) live in
415
+ // `registry-data/charge-pump.ts` as cited values; this table holds only the terminal wiring
416
+ // (role groups, package-order pinout) each pump's entry shares. The 8-pin parts are NOT
417
+ // pin-identical: pin 1 is BOOST (TC1044S, ICL7660S, TC7662B, MAX1044), NC (ICL7660, ICL7660A),
418
+ // FB/SD (LT1054) or SHDN (LTC1144). Keyed by the record's first part id, so a record always
419
+ // lands on its own wiring; a record with no skeleton row, or a skeleton row no record
420
+ // consumes, throws at load (see the check below).
160
421
  //
161
- // Only real manufacturer part numbers appear here: no prose aliases (`3V`, `5V6 zener`,
162
- // `zener/protection diode`) and no `SOURCE-*-ZENER` placeholder ids. A packet that prints a
163
- // voltage instead of a part number states no part, so the catalog must not answer it; those
164
- // values belong in the packet as declared `breakdownVolts`.
165
- //
166
- // Each row is `[partIds, breakdownVolts]`; the entries array below maps the table through
167
- // `zenerLaw`, so the drift script's membership set above and the shipped entries cannot drift
168
- // apart: there is one list, not two.
169
- const zenerBreakdownVolts = [
170
- [["1N5230"], 4.7],
171
- [["1N961B"], 10],
172
- [["1N965B"], 15],
173
- [["1N969B"], 22],
174
- [["1N4744A"], 15],
175
- [["1N4751A", "1N4751A 30V", "1N4751A-30V", "1N4751A 30 V"], 30],
176
- [["1N4753A"], 36],
177
- [["RD5.1EB3", "RD5.1EB 3", "RD5.1EB-3"], 5.235],
178
- // Bare RD5.1EB spans every bin but only B3 is in the registry (NEC RD5.1E B3:
179
- // Vz 5.12–5.35 V, typ 5.235; vendored at parts/diodes/RD5_1EB3). Stamping B3's
180
- // typical for the unbinned part is a derived stand-in, disclosed, not a printed
181
- // unbinned value.
182
- [["RD5.1EB"], 5.235],
183
- // RD3.0EB1/RD3.0EB3 are real part numbers whose registry value lives on the
184
- // SOURCE-3V-ZENER row (BV 3); the placeholder id and the prose aliases stay out.
185
- [["RD3.0EB3", "RD3.0EB1", "RD3.0EB-1", "RD3.0EB-3"], 3],
186
- [["RD4.7EB", "02BZ4.7A", "02BZ4_7A", "02BZ4-7A"], 4.7],
187
- [["RD5.6EB3", "RD-5.6EB3", "RD5.6EB 3", "RD5.6EB-3"], 5.76],
188
- [["RD5.6EB2", "RD-5.6EB2", "RD5.6EB 2", "RD5.6EB-2", "RD5.6JB2", "RD5.6J B2", "RD5.6J-B2"], 5.58],
189
- [["RD6.6EB3", "RD6.6EB 3", "RD6.6EB-3"], 6.62],
190
- [["RD8.2EB3", "RD8.2EB 3", "RD8.2EB-3"], 8.18],
191
- // MTZ5.6B is the real part behind the SOURCE-5V6-ZENER row (BV 5.6).
192
- [["MTZ5.6B"], 5.6],
193
- [["RD11EB3", "RD11EB 3", "RD11EB-3", "RD11E B3", "RD11E-B3"], 10.99],
194
- // RD11EB is the unbinned part with its own registry row (NEC suffix-B range
195
- // 10.18–11.26 V, typ 11): not the B3 typical under a shorter name.
196
- [["RD11EB"], 11],
197
- [["0Z11A", "05Z11A"], 11],
198
- [["1N4733A"], 5.1],
199
- [["1N5331"], 5.1],
200
- [["1N5239B"], 9.1],
201
- [["1N4742A", "1N4742"], 12],
202
- [["RD5.6S", "RD5.6S B3", "RD5.6SB3", "RD5.6S-B3"], 5.6],
203
- [["RD11FB3", "RD11FB-3"], 11.15],
204
- [["PESD12VL1BA"], 12],
205
- // The flat `component-zener-diode-chips.json` rows with no diode-file entry.
206
- [["1N5231"], 5.1],
207
- [["1N5231B"], 5.1],
208
- [["1N4733"], 5.1],
209
- ];
210
- export const zenerCatalogPartIds = new Set(zenerBreakdownVolts.flatMap(([partIds]) => partIds.map(foldPartId)));
422
+ // `volts: 0` in the built law is an unused placeholder: the pump rail is derived per circuit
423
+ // from the input node (see `pumpDerivation` in device-laws.ts), and pump parts bypass generic
424
+ // section expansion, `registrySuppliedVoltages`, and `declaredRegulatorTerminals`, so this
425
+ // number is never stamped.
426
+ const chargePumpTerminalStructures = {
427
+ // Pinout verified against Microchip DS21348A (2001), p1 pin-configuration diagram and general
428
+ // description: 1 BOOST, 2 CAP+, 3 GND, 4 CAP-, 5 VOUT, 6 LV, 7 OSC, 8 V+.
429
+ BOOST_7660: {
430
+ terminalRoleGroups: [
431
+ ["boost"],
432
+ ["capp", "capplus"],
433
+ ["gnd", "ground"],
434
+ ["capm", "capminus"],
435
+ ["vout", "negativeoutput", "voutnegative", "out", "output"],
436
+ ["lv", "lowvoltage"],
437
+ ["osc", "oscillator"],
438
+ ["vplus", "vin", "vdd", "vcc", "input"],
439
+ ],
440
+ pinout: ["boost", "capp", "gnd", "capm", "vout", "lv", "osc", "vplus"],
441
+ },
442
+ // Pinout verified against Intersil FN3072 Rev 7.00, p1 pinout diagram (8 Ld PDIP/SOIC top view):
443
+ // 1 NC, 2 CAP+, 3 GND, 4 CAP-, 5 VOUT, 6 LV, 7 OSC, 8 V+.
444
+ NC_7660: {
445
+ terminalRoleGroups: [
446
+ ["nc", "noconnect"],
447
+ ["capp", "capplus"],
448
+ ["gnd", "ground"],
449
+ ["capm", "capminus"],
450
+ ["vout", "negativeoutput", "voutnegative", "out", "output"],
451
+ ["lv", "lowvoltage"],
452
+ ["osc", "oscillator"],
453
+ ["vplus", "vin", "vdd", "vcc", "input"],
454
+ ],
455
+ pinout: ["nc", "capp", "gnd", "capm", "vout", "lv", "osc", "vplus"],
456
+ },
457
+ // Pinout verified against TI SLVS033G, p3 pin config + pin-functions table (PDIP-8): 1 FB/SD
458
+ // (shutdown/error-amp input), 2 CAP+, 3 GND, 4 CAP-, 5 VOUT (output), 6 VREF (2.5 V reference
459
+ // output), 7 OSC (input), 8 VCC (supply). This is NOT the 7660 pin order: pin 1 is an input and
460
+ // pin 6 an output, so the part is footprint-compatible but not a drop-in substitute.
461
+ FBSD_LT1054: {
462
+ terminalRoleGroups: [
463
+ ["fbsd", "fb", "sd", "shutdown"],
464
+ ["capp", "capplus"],
465
+ ["gnd", "ground"],
466
+ ["capm", "capminus"],
467
+ ["vout", "negativeoutput", "voutnegative", "out", "output"],
468
+ ["vref", "reference"],
469
+ ["osc", "oscillator"],
470
+ ["vcc", "vin", "vplus", "vdd", "input"],
471
+ ],
472
+ pinout: ["fbsd", "capp", "gnd", "capm", "vout", "vref", "osc", "vcc"],
473
+ },
474
+ // Pinout verified against Linear Technology 1144fa, p2 config + p5 Pin Functions: 1 BOOST
475
+ // (x10 when tied high), 2 CAP+, 3 GND, 4 CAP-, 5 VOUT, 6 SHDN (float/V+ = run, GND = shutdown),
476
+ // 7 OSC, 8 V+. No LV pin and no low-voltage mode split.
477
+ SHDN_LTC1144: {
478
+ terminalRoleGroups: [
479
+ ["boost"],
480
+ ["capp", "capplus"],
481
+ ["gnd", "ground"],
482
+ ["capm", "capminus"],
483
+ ["vout", "negativeoutput", "voutnegative", "out", "output"],
484
+ ["shdn", "shutdown"],
485
+ ["osc", "oscillator"],
486
+ ["vplus", "vin", "vdd", "vcc", "input"],
487
+ ],
488
+ pinout: ["boost", "capp", "gnd", "capm", "vout", "shdn", "osc", "vplus"],
489
+ },
490
+ };
491
+ /** Which wiring skeleton each charge-pump record builds on, by the record's first part id. */
492
+ const chargePumpStructureFor = {
493
+ TC1044S: "BOOST_7660",
494
+ ICL7660: "NC_7660",
495
+ ICL7660A: "NC_7660",
496
+ ICL7660S: "BOOST_7660",
497
+ TC7662B: "BOOST_7660",
498
+ LT1054: "FBSD_LT1054",
499
+ LTC1144: "SHDN_LTC1144",
500
+ MAX1044: "BOOST_7660",
501
+ };
502
+ /** Build one charge-pump entry from its registry record and its wiring skeleton. */
503
+ function chargePumpEntry(record) {
504
+ const sourceOhms = record.params["sourceOhms"];
505
+ const loss = record.params["chargePumpLossVolts"];
506
+ if (sourceOhms === undefined || loss === undefined) {
507
+ throw new Error(`${record.partIds.join(",")}: a charge-pump record states no sourceOhms or chargePumpLossVolts`);
508
+ }
509
+ const key = record.partIds[0];
510
+ const skeletonName = key === undefined ? undefined : chargePumpStructureFor[key];
511
+ const skeleton = skeletonName === undefined ? undefined : chargePumpTerminalStructures[skeletonName];
512
+ if (skeleton === undefined) {
513
+ throw new Error(`${record.partIds.join(",")}: no charge-pump wiring skeleton for ${String(key)}`);
514
+ }
515
+ return {
516
+ partIds: [...record.partIds],
517
+ declaredTypes: [],
518
+ terminalRoleGroups: skeleton.terminalRoleGroups,
519
+ ...provisionalArmOf(record),
520
+ model: {
521
+ kind: "sections",
522
+ sections: [
523
+ {
524
+ law: {
525
+ kind: "voltage-source",
526
+ volts: 0,
527
+ sourceOhms: sourceOhms.value,
528
+ },
529
+ terminals: [4, 2],
530
+ },
531
+ ],
532
+ pinout: skeleton.pinout,
533
+ },
534
+ chargePumpLossVolts: loss.value,
535
+ };
536
+ }
537
+ /** The charge-pump entry for one family, by the record's first part id. Position-preserving. */
538
+ function chargePumpEntryFor(firstPartId) {
539
+ const record = chargePumpRegistryRecords.find((candidate) => candidate.partIds[0] === firstPartId);
540
+ if (record === undefined) {
541
+ throw new Error(`no charge-pump registry record starts with ${firstPartId}`);
542
+ }
543
+ return chargePumpEntry(record);
544
+ }
545
+ // Every skeleton row is consumed by exactly one record, and every record lands on a row.
546
+ for (const key of Object.keys(chargePumpStructureFor)) {
547
+ if (!chargePumpRegistryRecords.some((record) => record.partIds[0] === key)) {
548
+ throw new Error(`charge-pump wiring skeleton for ${key} is consumed by no registry record`);
549
+ }
550
+ }
211
551
  const entries = [
212
552
  // uPC4558, from the artifacts library's own vendored copy:
213
553
  // `parts/op-amps/UPC4558/datasheets/renesas-upc258-upc4558.pdf` (Renesas `UPC258, UPC4558`,
@@ -287,12 +627,20 @@ const entries = [
287
627
  // 100 dB is 10^(100/20) = 100,000, which is what the part record's "100 k" states in the other
288
628
  // unit. `boss-ce-2b` says `NJM4558DD`; the datasheet's own numbers differ from it only in
289
629
  // package/rank suffix and are listed alongside.
630
+ // `JRC4558D` is the one knowingly non-printed id here: the Nisshinbo sheet prints NJM4558D,
631
+ // not JRC4558D, and the identification of JRC4558D as the NJR/JRC marking of the NJM4558D
632
+ // rests on `parts/op-amps/JRC4558D.md` ("NJR/JRC version. Same datasheet specs as NJM4558"),
633
+ // not on this sheet. It is carried anyway per owner decision 2026-10-15, because a real
634
+ // Boss SD-1 specimen carries a chip marked "JRC 4558D" and three corpus packets name it.
290
635
  // **Pin labels corrected and role groups added 2026-09-02 (W2).** The pinout read `OUT_B, IN_B_MINUS, IN_B_PLUS, VEE, IN_A_PLUS, IN_A_MINUS, OUT_A, VCC` -- the datasheet's A and B swapped. Electrically it made no difference, because both sections are the same `ideal-opamp` law and each section's triple was internally consistent, so this is a documentation correction: the positions are physical DIP-8 pins either way and the `sections` indices are unchanged.
291
636
  // The role groups carry **only** the pin-numbered spellings, deliberately. Generic aliases (`noninverting`, `out`) would let one declared role satisfy two different groups -- A's and B's -- and `matchesTerminalRoles` would then identify any 5-terminal section as this whole package. Pin-numbered tokens are unambiguous per position. `boss-dm-3`'s `IC1` is the only corpus device declaring a 4558 as an 8-pin package rather than as sections.
292
637
  {
293
638
  "partIds": [
294
639
  "NJM4558DD",
295
640
  "NJM4558D",
641
+ "JRC4558D",
642
+ "JRC4558",
643
+ "JRC4558DD",
296
644
  "NJM4558M",
297
645
  "NJM4558E",
298
646
  "NJM4558V",
@@ -381,18 +729,12 @@ const entries = [
381
729
  //
382
730
  // VOM Maximum peak output voltage swing RL = 10 kOhm, TA = 25 C, VCC+- = +-15 V
383
731
  // Min +-10 V Typ +-13.5 V (table 6.7)
384
- // Figure 6-1, VOM vs |VCC+-| (RL = 10 kOhm), is roughly linear: VOM is about 3.3 V at
385
- // |VCC+-| = 4.5 V, the +-4.5 V a 9 V single supply gives.
386
- //
387
- // `outputSwingDropVolts` is 1.2 V: the distance the output stops short of each rail at that
388
- // 9 V operating point (4.5 - 3.3). **That figure is read by eye from the plotted line, not
389
- // printed.** The printed figure is the 1.5 V (15 - 13.5) of table 6.7, at +-15 V. 1.2 was
390
- // chosen because the pedals fitted with this part run on 9 V, and it is disclosed here rather
391
- // than presented as a datasheet number. Open-loop gain stays the house 100000 the family
392
- // entry it was split from carried; it is not a sourced TL06x figure.
393
732
  //
394
- // The drop also reaches a *declared* `opamp` device (the ElectroSmash script-logo Phase 90's
395
- // TL061s) through `registryOpampSwingDrop`, which reads the first section's law.
733
+ // Open-loop gain stays the house 100000 the family entry it was split from carried; it is not a
734
+ // sourced TL06x figure. The output swing is not in this entry: it has its own table,
735
+ // `registry-data/opamp.ts`, which stamps the printed pair (1.5 V a side, `constant`) onto these
736
+ // ids, for a declared `opamp` device and a package alike. The graph-read 1.2 V this entry carried
737
+ // until row 16 (Figure 6-1 at +-4.5 V) was not a printed figure and is gone.
396
738
  {
397
739
  "partIds": [
398
740
  "TL062",
@@ -420,8 +762,7 @@ const entries = [
420
762
  "kind": "ideal-opamp",
421
763
  "railHigh": null,
422
764
  "railLow": null,
423
- "openLoopGain": 100000,
424
- "outputSwingDropVolts": 1.2
765
+ "openLoopGain": 100000
425
766
  },
426
767
  "terminals": [
427
768
  2,
@@ -434,8 +775,7 @@ const entries = [
434
775
  "kind": "ideal-opamp",
435
776
  "railHigh": null,
436
777
  "railLow": null,
437
- "openLoopGain": 100000,
438
- "outputSwingDropVolts": 1.2
778
+ "openLoopGain": 100000
439
779
  },
440
780
  "terminals": [
441
781
  4,
@@ -478,8 +818,7 @@ const entries = [
478
818
  "kind": "ideal-opamp",
479
819
  "railHigh": null,
480
820
  "railLow": null,
481
- "openLoopGain": 100000,
482
- "outputSwingDropVolts": 1.2
821
+ "openLoopGain": 100000
483
822
  },
484
823
  "terminals": [
485
824
  0,
@@ -3068,18 +3407,10 @@ const entries = [
3068
3407
  },
3069
3408
  // Texas Instruments OP07x, from "OP07x Precision Operational Amplifiers" SLOS099H (rev. March
3070
3409
  // 2023), `parts/op-amps/OP07/datasheets/op07-ti.pdf` in vessel-dsp/artifacts, section 6.5.
3071
- //
3072
- // VOM Output voltage swing, VS = +-15 V RL = 10 kOhm: min +-12 typ +-13
3073
- // RL = 2 kOhm: min +-11.5 typ +-12.8
3074
- //
3075
- // `outputSwingDropVolts` is 2.2 V: 15 - 12.8, the table's own default condition (RL = 2 kOhm,
3076
- // typ). **That is a +-15 V figure carried to a 9 V single supply on the assumption that a
3077
- // bipolar output stage's headroom is constant, not proportional to the supply.** The datasheet
3078
- // states nothing at 9 V, and the proportional reading would give 0.66 V. The measured pin 6
3079
- // swing on a specimen has not been taken: the RAT specimen study
3080
- // (`packet-study/pro-co-rat-2-v4b-op07cp`) scored this law UNDECIDED, its band exceeding 20 dB,
3081
- // and it is shipped on the datasheet alone, as a typical and never a measured part. The datasheet
3082
- // bracket across load current is 2.0 (typ, 10 kOhm) to 3.5 (min spec, 2 kOhm).
3410
+ // Open-loop gain stays the house 100000. The output swing is not in this entry: it has its own
3411
+ // registry-data record (`registry-data/opamp.ts`), which types the printed +-12.8 V (RL = 2 kOhm, VS = +-15 V)
3412
+ // as a `proportional` pair for OP07CP, OP07DP and OP07DN, and lists the bare `OP07` id absent (the
3413
+ // sheet's characteristics apply to the C and D grades only).
3083
3414
  {
3084
3415
  "partIds": [
3085
3416
  "OP07",
@@ -3113,8 +3444,7 @@ const entries = [
3113
3444
  "kind": "ideal-opamp",
3114
3445
  "railHigh": null,
3115
3446
  "railLow": null,
3116
- "openLoopGain": 100000,
3117
- "outputSwingDropVolts": 2.2
3447
+ "openLoopGain": 100000
3118
3448
  },
3119
3449
  "terminals": [
3120
3450
  0,
@@ -3131,6 +3461,11 @@ const entries = [
3131
3461
  }
3132
3462
  },
3133
3463
  // --- Single Op-Amps (LM741, TL071, NJM5534D) --------------------------------------------------------------------------
3464
+ // Wave 5: LM741CN rides this entry. It is the exact PDIP orderable spelling printed
3465
+ // in the TI LM741 SNOSC25D (May 1998, rev Oct 2015) Package Option Addendum:
3466
+ // 'LM741CN/NOPB | Active | Production | PDIP (P) | 8', part marking 'LM 741CN'.
3467
+ // The specs sit under the LM741C column family; open-loop gain stays the house
3468
+ // 100000 and no GBW row is quoted (see opampGainBandwidthAbsent).
3134
3469
  {
3135
3470
  "partIds": [
3136
3471
  "LM741",
@@ -3142,7 +3477,9 @@ const entries = [
3142
3477
  "NJM5534",
3143
3478
  "NJM5534D",
3144
3479
  "NE5534",
3145
- "NJM5534M"
3480
+ "NJM5534M",
3481
+ "JRC5534",
3482
+ "LM741CN"
3146
3483
  ],
3147
3484
  "declaredTypes": [],
3148
3485
  "terminalRoleGroups": [
@@ -3220,170 +3557,16 @@ const entries = [
3220
3557
  ]
3221
3558
  }
3222
3559
  },
3223
- // --- 5V Voltage Regulators (78L05, uPC78L05, 78M05, TA78L05F) - Causal input-dependent physical modeling! ------------------
3224
- // UNRESOLVED: this entry conflates two packages with OPPOSITE pin orders. The TO-92/SOT-89 78L05 is pin1=VOUT, pin2=GND, pin3=VIN (verified: TI LM78L, SNVS754O, Figure 4-2 SOT-89 top view and Figure 4-3 LP 3-pin TO-92 bottom view). The TO-220 7805 is pin1=VIN, pin2=GND, pin3=VOUT. The role groups below assert the 7805 order, because `pin1` sits in the vin group and `pin3` in the vout group.
3225
- // Consequence: a document that declares a 78L05's terminals as pin1/pin2/pin3 binds its input and output the wrong way round. No corpus packet does today -- all seven three-pin regulators declare typed roles as of 2026-09-05 -- so this is latent, not live. Fixing it means SPLITTING the entry by package before a `pinout` can be written, because one pin map cannot serve both.
3226
- {
3227
- "partIds": [
3228
- "uPC78L05",
3229
- "78L05",
3230
- "7805",
3231
- "78M05",
3232
- "TA78L05F",
3233
- // Toshiba's TA78L05S (product page detail.TA78L05S): the same 5 V TA78L die in TO-92.
3234
- // Without it, `boss-dd-5`'s IC5 fell to the pin-name rung and matched the 78L18 entry,
3235
- // solving the pedal's 5 V rail at 18 V.
3236
- "TA78L05S",
3237
- "2930L05",
3238
- "BA78L05",
3239
- "uPC78L05T"
3240
- ],
3241
- "declaredTypes": [],
3242
- "terminalRoleGroups": [
3243
- [
3244
- "vin",
3245
- "in",
3246
- "input",
3247
- "pin1"
3248
- ],
3249
- [
3250
- "gnd",
3251
- "pin2"
3252
- ],
3253
- [
3254
- "vout",
3255
- "out",
3256
- "output",
3257
- "pin3"
3258
- ]
3259
- ],
3260
- "model": {
3261
- "kind": "sections",
3262
- "sections": [
3263
- {
3264
- "law": {
3265
- "kind": "voltage-source",
3266
- "volts": 5,
3267
- "sourceOhms": 1
3268
- },
3269
- "terminals": [
3270
- 2,
3271
- 1
3272
- ]
3273
- }
3274
- ],
3275
- "pinout": [
3276
- null,
3277
- null,
3278
- null
3279
- ]
3280
- }
3281
- },
3282
- // --- 9V Voltage Regulators (7809, 78L09, 78M09, uPC78L09) - Causal input-dependent physical modeling! ------------------
3283
- {
3284
- "partIds": [
3285
- "uPC78L09",
3286
- "78L09",
3287
- "7809",
3288
- "78M09",
3289
- "TA78L09F",
3290
- "2930L09",
3291
- "BA78L09",
3292
- "uPC78L09T"
3293
- ],
3294
- "declaredTypes": [],
3295
- "terminalRoleGroups": [
3296
- [
3297
- "vin",
3298
- "in",
3299
- "input",
3300
- "pin1"
3301
- ],
3302
- [
3303
- "gnd",
3304
- "pin2"
3305
- ],
3306
- [
3307
- "vout",
3308
- "out",
3309
- "output",
3310
- "pin3"
3311
- ]
3312
- ],
3313
- "model": {
3314
- "kind": "sections",
3315
- "sections": [
3316
- {
3317
- "law": {
3318
- "kind": "voltage-source",
3319
- "volts": 9,
3320
- "sourceOhms": 1
3321
- },
3322
- "terminals": [
3323
- 2,
3324
- 1
3325
- ]
3326
- }
3327
- ],
3328
- "pinout": [
3329
- null,
3330
- null,
3331
- null
3332
- ]
3333
- }
3334
- },
3335
- // --- 18V Voltage Regulators (78L18, 78L18AC, 78L18T) - same 3-pin voltage-source shape as 5V/9V above ----------------------
3336
- // electro-harmonix-q-tron's U6 (PartNumber 78L18AC) is the 24V-to-+18V rail; it matches by part id on the first rung, so the
3337
- // role groups below are the standard in/gnd/out family spelling kept for parity with the 5V/9V entries, not a new contract.
3338
- {
3339
- "partIds": [
3340
- "78L18",
3341
- "78L18AC",
3342
- "78L18T",
3343
- "7818"
3344
- ],
3345
- "declaredTypes": [],
3346
- "terminalRoleGroups": [
3347
- [
3348
- "vin",
3349
- "in",
3350
- "input",
3351
- "pin1"
3352
- ],
3353
- [
3354
- "gnd",
3355
- "ground",
3356
- "pin2"
3357
- ],
3358
- [
3359
- "vout",
3360
- "out",
3361
- "output",
3362
- "pin3"
3363
- ]
3364
- ],
3365
- "model": {
3366
- "kind": "sections",
3367
- "sections": [
3368
- {
3369
- "law": {
3370
- "kind": "voltage-source",
3371
- "volts": 18,
3372
- "sourceOhms": 1
3373
- },
3374
- "terminals": [
3375
- 2,
3376
- 1
3377
- ]
3378
- }
3379
- ],
3380
- "pinout": [
3381
- null,
3382
- null,
3383
- null
3384
- ]
3385
- }
3386
- },
3560
+ // --- 5V Voltage Regulators: ids and rail from registry-data/regulator.ts; wiring above --------
3561
+ regulatorEntryForVolts(5),
3562
+ // --- 9V Voltage Regulators: ids and rail from registry-data/regulator.ts; wiring above --------
3563
+ regulatorEntryForVolts(9),
3564
+ // --- 12V Voltage Regulators (wave 5: LM78L12): ids and rail from registry-data/regulator.ts; wiring above --------
3565
+ regulatorEntryForVolts(12),
3566
+ // --- 3.3V Voltage Regulators (wave 5: LM78L33): ids and rail from registry-data/regulator.ts; wiring above --------
3567
+ regulatorEntryForVolts(3.3),
3568
+ // --- 18V Voltage Regulators: ids and rail from registry-data/regulator.ts; wiring above --------
3569
+ regulatorEntryForVolts(18),
3387
3570
  // --- Active Physical Analog Comparators (LM311, NJM311M, LM319, NJM319M) ----------------------------------------------------
3388
3571
  // --- Active Physical Analog Comparators (LM311, NJM311M, LM319, NJM319M, LM393, LM339) ------------------------------------
3389
3572
  {
@@ -5193,80 +5376,50 @@ const entries = [
5193
5376
  ]
5194
5377
  }
5195
5378
  },
5196
- // --- Negative Voltage Regulators (LM7915, LM7912) - Causal input-dependent physical modeling! --------------------------------
5197
- // UNRESOLVED, two separate problems. (1) `7912`/`LM7912` are -12V parts sharing an entry hardcoded to volts: -15, so a 7912 would be modelled 3V low. Only one corpus device uses this entry (deluxe-memory-man's U12_LM7915), so the defect is latent.
5198
- // (2) `volts` here is SIGNED, which contradicts the convention its consumer documents: `declaredRegulatorTerminals` says "the law carries a magnitude" and reverses the source's sense for an output declared `negative`. Declaring `negative` against a signed -15 therefore double-negates and puts +15V on the rail -- measured 2026-09-05. Until this is settled, a negative regulator using this entry must declare its output `positive`, which is why deluxe-memory-man does.
5379
+ // --- Negative Voltage Regulators: ids and rail from registry-data/regulator.ts; wiring above --------
5380
+ // The 7912/LM7912 sharing and the signed-volts convention are recorded on the record; the
5381
+ // wiring skeleton for -15 V is shared. Until the convention is settled, a negative regulator
5382
+ // using this entry must declare its output `positive`, which is why deluxe-memory-man does.
5383
+ regulatorEntryForVolts(-15),
5384
+ // --- TC1044S charge-pump inverter (Microchip): ids and loss/slope from registry-data/charge-pump.ts; wiring above ---
5385
+ chargePumpEntryFor("TC1044S"),
5386
+ // --- ICL7660 charge-pump inverter (Intersil/Renesas): ids and loss/slope from registry-data/charge-pump.ts; wiring above ---
5387
+ chargePumpEntryFor("ICL7660"),
5388
+ // --- ICL7660A charge-pump inverter (Intersil/Renesas): ids and loss/slope from registry-data/charge-pump.ts; wiring above ---
5389
+ chargePumpEntryFor("ICL7660A"),
5390
+ // --- ICL7660S charge-pump inverter (Renesas): ids and loss/slope from registry-data/charge-pump.ts; wiring above ---
5391
+ chargePumpEntryFor("ICL7660S"),
5392
+ // --- TC7662B charge-pump inverter (Microchip): ids and loss/slope from registry-data/charge-pump.ts; wiring above ---
5393
+ chargePumpEntryFor("TC7662B"),
5394
+ // --- LT1054 switched-capacitor converter (TI second source): ids and loss/slope from registry-data/charge-pump.ts; wiring above ---
5395
+ chargePumpEntryFor("LT1054"),
5396
+ // --- LTC1144 wide-input converter (Linear Tech): ids and loss/slope from registry-data/charge-pump.ts; wiring above ---
5397
+ chargePumpEntryFor("LTC1144"),
5398
+ // --- MAX1044 switched-capacitor converter (Maxim): ids and loss/slope from registry-data/charge-pump.ts; wiring above ---
5399
+ chargePumpEntryFor("MAX1044"),
5400
+ // --- AK4552VT8 8-pin codec body alternative ----------------------------------------------------------------------------------
5199
5401
  {
5200
5402
  "partIds": [
5201
- "LM7915",
5202
- "7915",
5203
- "7912",
5204
- "LM7912"
5403
+ "AK4552VT8",
5404
+ "AK4552VT",
5405
+ "AK4552"
5205
5406
  ],
5206
5407
  "declaredTypes": [],
5207
- "terminalRoleGroups": [
5208
- [
5209
- "gnd",
5210
- "pin1"
5211
- ],
5212
- [
5213
- "vin",
5214
- "in",
5215
- "input",
5216
- "pin2"
5217
- ],
5218
- [
5219
- "vout",
5220
- "out",
5221
- "output",
5222
- "pin3"
5223
- ]
5224
- ],
5408
+ "terminalRoleGroups": [],
5225
5409
  "model": {
5226
5410
  "kind": "sections",
5227
5411
  "sections": [
5228
5412
  {
5229
5413
  "law": {
5230
- "kind": "voltage-source",
5231
- "volts": -15,
5232
- "sourceOhms": 1
5414
+ "kind": "ideal-opamp",
5415
+ "railHigh": null,
5416
+ "railLow": null,
5417
+ "openLoopGain": 10
5233
5418
  },
5234
5419
  "terminals": [
5420
+ 0,
5235
5421
  2,
5236
- 0
5237
- ]
5238
- }
5239
- ],
5240
- "pinout": [
5241
- null,
5242
- null,
5243
- null
5244
- ]
5245
- }
5246
- },
5247
- // --- AK4552VT8 8-pin codec body alternative ----------------------------------------------------------------------------------
5248
- {
5249
- "partIds": [
5250
- "AK4552VT8",
5251
- "AK4552VT",
5252
- "AK4552"
5253
- ],
5254
- "declaredTypes": [],
5255
- "terminalRoleGroups": [],
5256
- "model": {
5257
- "kind": "sections",
5258
- "sections": [
5259
- {
5260
- "law": {
5261
- "kind": "ideal-opamp",
5262
- "railHigh": null,
5263
- "railLow": null,
5264
- "openLoopGain": 10
5265
- },
5266
- "terminals": [
5267
- 0,
5268
- 2,
5269
- 2
5422
+ 2
5270
5423
  ]
5271
5424
  },
5272
5425
  {
@@ -5354,10 +5507,11 @@ const entries = [
5354
5507
  // output=pinout[0] -- the `ideal-opamp` law reads only those three; vplus/vminus are not
5355
5508
  // wired to the law (rails come from the circuit, exactly as the CD4049 template above).
5356
5509
  // No MC33178 datasheet is vendored in the artifacts library, so the open-loop gain is the
5357
- // class-general 100 dB (1e4) rather than a sourced MC33178 figure. `outputSwingDropVolts` 1.1 is
5358
- // the registry's (`component-opamp-chips.json`) figure, 15 V - 13.9 V from the onsemi MC33178-D
5359
- // output swing table at +-15 V, RL = 2 kOhm; that datasheet is likewise not vendored here, so the
5360
- // figure is carried from the registry's citation and not re-read.
5510
+ // class-general 100 dB (1e4) rather than a sourced MC33178 figure. It states **no output swing**:
5511
+ // the 1.1 V this entry carried until row 16 was the registry's (`component-opamp-chips.json`)
5512
+ // citation of the onsemi MC33178-D table at +-15 V, never re-read here because the datasheet is not
5513
+ // vendored, which does not meet the printed-TYP-only rule; the id is listed in
5514
+ // `opampOutputSwingAbsent` with that reason.
5361
5515
  {
5362
5516
  "partIds": [
5363
5517
  "MC33178",
@@ -5374,8 +5528,7 @@ const entries = [
5374
5528
  "kind": "ideal-opamp",
5375
5529
  "railHigh": null,
5376
5530
  "railLow": null,
5377
- "openLoopGain": 100000,
5378
- "outputSwingDropVolts": 1.1
5531
+ "openLoopGain": 100000
5379
5532
  },
5380
5533
  "terminals": [
5381
5534
  2,
@@ -5707,52 +5860,14 @@ const entries = [
5707
5860
  ]
5708
5861
  }
5709
5862
  },
5710
- // --- LM340T-15 +15V linear regulator (mxr-m117r-flanger) --------------------------------------------------------------------
5711
- //
5712
- // Not a CMOS/analog-switch part either, but the sole remaining blocker on `mxr-m117r-flanger`
5713
- // after the `CD4013AE` fix above: its `VR1` names PartNumber `LM340T-15`, an unregistered fixed
5714
- // +15V linear regulator. Same shape and same `voltage-source` law as the existing 78L05/78L09
5715
- // entries above (input/ground/output, output held at the regulator's fixed rail relative to
5716
- // ground) -- only the voltage differs, read directly off the part number and the component's own
5717
- // `Description: "15 V regulator from the selected factory schematic."` `VR1` declares exactly
5718
- // three terminals, `input, ground, output`, in that order, matching the existing regulator
5719
- // entries' `terminals: [2, 1]` (output relative to ground) convention directly.
5720
- //
5721
- // `terminalRoleGroups` is left empty rather than copying the existing regulator entries'
5722
- // generic `in`/`gnd`/`out` aliases: `VR1` already resolves through the exact-partId rung, and
5723
- // this repo's own corpus shows that generic pinout-signature fallback misidentifying an unrelated
5724
- // part with similarly-generic terminal names (`boss-oc-2`'s `BA634-5`, five terminals including
5725
- // `in`/`gnd`/`out`, incidentally satisfies the existing 78L05 entry's groups and is only saved
5726
- // from a wrong law by the later terminal-count guard). No reason to add a second entry to that
5727
- // same weak ladder when the exact part id already does the job.
5728
- {
5729
- "partIds": [
5730
- "LM340T-15"
5731
- ],
5732
- "declaredTypes": [],
5733
- "terminalRoleGroups": [],
5734
- "model": {
5735
- "kind": "sections",
5736
- "sections": [
5737
- {
5738
- "law": {
5739
- "kind": "voltage-source",
5740
- "volts": 15,
5741
- "sourceOhms": 1
5742
- },
5743
- "terminals": [
5744
- 2,
5745
- 1
5746
- ]
5747
- }
5748
- ],
5749
- "pinout": [
5750
- null,
5751
- null,
5752
- null
5753
- ]
5754
- }
5755
- },
5863
+ // --- LM340T-15 +15V linear regulator (mxr-m117r-flanger): ids and rail from registry-data/regulator.ts; wiring above ----
5864
+ // The sole remaining blocker on `mxr-m117r-flanger` after the `CD4013AE` fix above: its `VR1`
5865
+ // names PartNumber `LM340T-15`, an unregistered fixed +15V linear regulator. Same shape and same
5866
+ // `voltage-source` law as the 78L05/78L09 entries (input/ground/output, output held at the
5867
+ // regulator's fixed rail relative to ground) -- `VR1` declares exactly three terminals,
5868
+ // `input, ground, output`, in that order, matching the regulator entries' `terminals: [2, 1]`
5869
+ // (output relative to ground) convention directly.
5870
+ regulatorEntryForVolts(15),
5756
5871
  // --- Tube rectifiers ------------------------------------------------------------------
5757
5872
  //
5758
5873
  // A vacuum rectifier drops tens of volts where a silicon diode drops less than one, and that
@@ -6680,33 +6795,2019 @@ const entries = [
6680
6795
  ]
6681
6796
  }
6682
6797
  },
6683
- ...zenerBreakdownVolts.map(([partIds, breakdownVolts]) => zenerLaw(partIds, breakdownVolts)),
6684
- // 2SK30A series/shunt switches (`ibanez-ts9` Q102/Q104, the only packet with
6685
- // specimen evidence; `boss-sg-1-slow-gear` declares the same id and its gate is
6686
- // inert there). Channel repeats the class default by construction (see above);
6687
- // only `gateSaturationCurrent` carries part data.
6688
- fetLaw(["2SK30A"], {
6689
- kind: "fet",
6690
- channel: "n",
6691
- thresholdVolts: -2,
6692
- transconductance: 1e-3,
6693
- channelLengthModulation: 0,
6694
- subthresholdVolts: 0.07,
6695
- gateSaturationCurrent: 1e-14,
6696
- gateOnsetVolts: 0.5,
6697
- gateScaleVolts: 0.06,
6698
- }),
6699
- ];
6700
- /**
6701
- * Every part id whose `fet` entry refines the gate term only, folded for comparison.
6702
- * `report-part-store-drift.ts` keys its channel-field exclusion on this set -- a catalog
6703
- * product -- rather than on the registry row, so a gate-only entry's avowed class channel
6704
- * is never graded against the database's beta/lambda/Vp the way a transcribed channel
6705
- * would be. Mirrors `zenerCatalogPartIds` above.
6706
- */
6707
- export const gateOnlyFetPartIds = new Set(["2sk30a"]);
6708
- /**
6709
- * The catalog the corpus-facing scripts inject.
6710
- */
6711
- export const pedalPartCatalog = { entries };
6798
+ // Wave-1 op-amp entries: sections whose gain-bandwidth product the data registry
6799
+ // stamps (see `registry-data/opamp.ts` and `withDatasheetGainBandwidth` below). The
6800
+ // open-loop gain is the sheet's own TYP AOL transcribed; the swing pair is deliberately
6801
+ // absent (owner D4 freeze, upheld for this wave).
6802
+ // TI LM833 dual audio op-amp, from `parts/op-amps/LM833/datasheets/lm833-ti.pdf` (TI
6803
+ // SLOS481B, rev Oct 2014). Pin diagram and Pin Functions table, 6 p3 (D/SOIC, DGK/MSOP
6804
+ // and P/PDIP packages, TOP VIEW): 1 OUT1, 2 IN1-, 3 IN1+, 4 VCC-, 5 IN2+, 6 IN2-,
6805
+ // 7 OUT2, 8 VCC+ -- the standard dual order, read off the sheet's own table, with the
6806
+ // same pin-numbered role convention as the 4558 entry above (pin-numbered tokens only;
6807
+ // no generic alias may satisfy two sections at once).
6808
+ //
6809
+ // Electrical characteristics 7.5/7.6 p5 at VCC+- = +-15 V, TA = 25 C:
6810
+ // Large-signal differential voltage amplification AVD, RL >= 2 kOhm, VO = +-10 V:
6811
+ // MIN 90 TYP 110 dB. Used as `openLoopGain: decibelGain(110)`, the typ figure.
6812
+ // GBW is not in this entry: it lives in the data-registry row (TYP 16 MHz at f = 100 kHz),
6813
+ // stamped by the pass below. Wave 4: LM833N rides this entry for open-loop gain
6814
+ // (TI LM833-N sheet SNOSBD8E prints LM833N/NOPB as the PDIP orderable of the same
6815
+ // die with the same AV 110 dB TYP) but carries its own GBW row: the LM833-N sheet
6816
+ // prints GBW TYP 15 MHz, not the LM833 sheet's 16, so the pass splits the entry.
6817
+ {
6818
+ "partIds": [
6819
+ "LM833",
6820
+ "LM833N"
6821
+ ],
6822
+ "declaredTypes": [],
6823
+ "terminalRoleGroups": [
6824
+ [
6825
+ "OUT_A_pin1"
6826
+ ],
6827
+ [
6828
+ "INVERTING_A_pin2"
6829
+ ],
6830
+ [
6831
+ "NONINVERTING_A_pin3"
6832
+ ],
6833
+ [
6834
+ "V_MINUS_pin4"
6835
+ ],
6836
+ [
6837
+ "NONINVERTING_B_pin5"
6838
+ ],
6839
+ [
6840
+ "INVERTING_B_pin6"
6841
+ ],
6842
+ [
6843
+ "OUT_B_pin7"
6844
+ ],
6845
+ [
6846
+ "V_PLUS_pin8"
6847
+ ]
6848
+ ],
6849
+ "model": {
6850
+ "kind": "sections",
6851
+ "pinout": [
6852
+ "OUT_A",
6853
+ "IN_A_MINUS",
6854
+ "IN_A_PLUS",
6855
+ "VEE",
6856
+ "IN_B_PLUS",
6857
+ "IN_B_MINUS",
6858
+ "OUT_B",
6859
+ "VCC"
6860
+ ],
6861
+ "sections": [
6862
+ {
6863
+ "law": {
6864
+ "kind": "ideal-opamp",
6865
+ "railHigh": null,
6866
+ "railLow": null,
6867
+ "openLoopGain": 316227.7660168379
6868
+ },
6869
+ "terminals": [
6870
+ 2,
6871
+ 1,
6872
+ 0
6873
+ ]
6874
+ },
6875
+ {
6876
+ "law": {
6877
+ "kind": "ideal-opamp",
6878
+ "railHigh": null,
6879
+ "railLow": null,
6880
+ "openLoopGain": 316227.7660168379
6881
+ },
6882
+ "terminals": [
6883
+ 4,
6884
+ 5,
6885
+ 6
6886
+ ]
6887
+ }
6888
+ ]
6889
+ }
6890
+ },
6891
+ // Wave-3 op-amp entries: sections whose open-loop gain is the sheet's own TYP AOL
6892
+ // transcribed (the gain-bandwidth product lives in `registry-data/opamp.ts`, stamped by
6893
+ // the passes below; the swing pair stays absent per the D4 freeze). GBW rows and catalog
6894
+ // entries are 1:1 by part id, so no entry splits. LM1458/N carry AOL only (no GBW row
6895
+ // exists; see `opampGainBandwidthAbsent`).
6896
+ // NJR NJM4580 dual audio op-amp, from `parts/op-amps/JRC4580/datasheets/njm4580d-njr.pdf`
6897
+ // (NJR Ver. 2012-09-14). PIN FUNCTION table, p1 (DIP-8 TOP VIEW): 1 A OUTPUT, 2 A -INPUT,
6898
+ // 3 A +INPUT, 4 V-, 5 B +INPUT, 6 B -INPUT, 7 B OUTPUT, 8 V+ -- the standard dual order,
6899
+ // with the same pin-numbered role convention as the LM833 entry above (pin-numbered
6900
+ // tokens only; no generic alias may satisfy two sections at once).
6901
+ //
6902
+ // Electrical characteristics p2 at V+/V- = +-15 V, Ta = 25 C:
6903
+ // Voltage Gain AV, RL >= 2 kOhm, VO = +-10 V: MIN 90 TYP 110 dB.
6904
+ // Used as `openLoopGain: decibelGain(110)`, the typ figure (the D rank changes the noise
6905
+ // row only). The JRC ids rest on `parts/op-amps/JRC4580.md` ('JRC4580D / NJM4580D',
6906
+ // primary local datasheet this sheet) -- the JRC4558D precedent (owner decision
6907
+ // 2026-10-15: the NJR/JRC marking rides the NJM sheet). GBW is not in this entry: it
6908
+ // lives in the data-registry row (TYP 15 MHz at f = 10 kHz), stamped by the pass below.
6909
+ {
6910
+ "partIds": [
6911
+ "JRC4580D",
6912
+ "JRC4580",
6913
+ "NJM4580D",
6914
+ "NJM4580"
6915
+ ],
6916
+ "declaredTypes": [],
6917
+ "terminalRoleGroups": [
6918
+ [
6919
+ "OUT_A_pin1"
6920
+ ],
6921
+ [
6922
+ "INVERTING_A_pin2"
6923
+ ],
6924
+ [
6925
+ "NONINVERTING_A_pin3"
6926
+ ],
6927
+ [
6928
+ "V_MINUS_pin4"
6929
+ ],
6930
+ [
6931
+ "NONINVERTING_B_pin5"
6932
+ ],
6933
+ [
6934
+ "INVERTING_B_pin6"
6935
+ ],
6936
+ [
6937
+ "OUT_B_pin7"
6938
+ ],
6939
+ [
6940
+ "V_PLUS_pin8"
6941
+ ]
6942
+ ],
6943
+ "model": {
6944
+ "kind": "sections",
6945
+ "pinout": [
6946
+ "OUT_A",
6947
+ "IN_A_MINUS",
6948
+ "IN_A_PLUS",
6949
+ "VEE",
6950
+ "IN_B_PLUS",
6951
+ "IN_B_MINUS",
6952
+ "OUT_B",
6953
+ "VCC"
6954
+ ],
6955
+ "sections": [
6956
+ {
6957
+ "law": {
6958
+ "kind": "ideal-opamp",
6959
+ "railHigh": null,
6960
+ "railLow": null,
6961
+ "openLoopGain": 316227.7660168379
6962
+ },
6963
+ "terminals": [
6964
+ 2,
6965
+ 1,
6966
+ 0
6967
+ ]
6968
+ },
6969
+ {
6970
+ "law": {
6971
+ "kind": "ideal-opamp",
6972
+ "railHigh": null,
6973
+ "railLow": null,
6974
+ "openLoopGain": 316227.7660168379
6975
+ },
6976
+ "terminals": [
6977
+ 4,
6978
+ 5,
6979
+ 6
6980
+ ]
6981
+ }
6982
+ ]
6983
+ }
6984
+ },
6985
+ // TI OPA2134 dual audio op-amp, from `parts/op-amps/OPA2134/datasheets/opa2134-ti.pdf`
6986
+ // (TI SBOS058B, Dec 1997, Rev. B Nov 2024). Table 4-1 Pin Functions: OPA2134, p3 (D/SOIC
6987
+ // and P/PDIP 8-pin, top view Fig. 4-2): OUT A-1, -IN A-2, +IN A-3, V--4, +IN B-5, -IN B-6,
6988
+ // OUT B-7, V+-8 -- the standard dual order, same pin-numbered role convention.
6989
+ // OPA2134PA is a printed PDIP-8 orderable of the same die (orderable table, marking
6990
+ // OPA2134PA); the specs below are family-wide with no per-orderable split.
6991
+ //
6992
+ // Electrical characteristics 5.7 p7 at TA = 25 C, VS = +-15 V:
6993
+ // AOL Open-loop voltage gain, RL = 2 kOhm, -13.8 V <= VO <= 13.5 V:
6994
+ // MIN 104 TYP 120 dB (a second row at RL = 10 kOhm reads the same 104/120).
6995
+ // Used as `openLoopGain: decibelGain(120)`, the typ figure. GBW is not in this entry:
6996
+ // it lives in the data-registry row (TYP 8 MHz), stamped by the pass below.
6997
+ {
6998
+ "partIds": [
6999
+ "OPA2134",
7000
+ "OPA2134PA"
7001
+ ],
7002
+ "declaredTypes": [],
7003
+ "terminalRoleGroups": [
7004
+ [
7005
+ "OUT_A_pin1"
7006
+ ],
7007
+ [
7008
+ "INVERTING_A_pin2"
7009
+ ],
7010
+ [
7011
+ "NONINVERTING_A_pin3"
7012
+ ],
7013
+ [
7014
+ "V_MINUS_pin4"
7015
+ ],
7016
+ [
7017
+ "NONINVERTING_B_pin5"
7018
+ ],
7019
+ [
7020
+ "INVERTING_B_pin6"
7021
+ ],
7022
+ [
7023
+ "OUT_B_pin7"
7024
+ ],
7025
+ [
7026
+ "V_PLUS_pin8"
7027
+ ]
7028
+ ],
7029
+ "model": {
7030
+ "kind": "sections",
7031
+ "pinout": [
7032
+ "OUT_A",
7033
+ "IN_A_MINUS",
7034
+ "IN_A_PLUS",
7035
+ "VEE",
7036
+ "IN_B_PLUS",
7037
+ "IN_B_MINUS",
7038
+ "OUT_B",
7039
+ "VCC"
7040
+ ],
7041
+ "sections": [
7042
+ {
7043
+ "law": {
7044
+ "kind": "ideal-opamp",
7045
+ "railHigh": null,
7046
+ "railLow": null,
7047
+ "openLoopGain": 1000000
7048
+ },
7049
+ "terminals": [
7050
+ 2,
7051
+ 1,
7052
+ 0
7053
+ ]
7054
+ },
7055
+ {
7056
+ "law": {
7057
+ "kind": "ideal-opamp",
7058
+ "railHigh": null,
7059
+ "railLow": null,
7060
+ "openLoopGain": 1000000
7061
+ },
7062
+ "terminals": [
7063
+ 4,
7064
+ 5,
7065
+ 6
7066
+ ]
7067
+ }
7068
+ ]
7069
+ }
7070
+ },
7071
+ // TI LM1458 dual op-amp, from `parts/op-amps/LM1458/datasheets/lm1458.pdf` (TI SNOSBU4D,
7072
+ // Apr 1998, Rev. D Mar 2013, ex-National; NOT the ST sheet `MC1458.md` points at).
7073
+ // Connection Diagram Fig. 2 Dual-In-Line Package (Top View), p1: 1 OUTPUT A,
7074
+ // 2 INVERTING INPUT A, 3 NON-INVERTING INPUT A, 4 V-, 5 NON-INVERTING INPUT B,
7075
+ // 6 INVERTING INPUT B, 7 OUTPUT B, 8 V+ -- the standard dual order, same pin-numbered
7076
+ // role convention. LM1458N is printed (Abs Max 'LM1458N 400 mW' p2; orderables
7077
+ // LM1458N/NOPB p5, PDIP-8); the AC specs sit under the generic LM1458 column, which
7078
+ // covers it (the CA1458 entry above is the neighbouring point not jumped to: Harris
7079
+ // 200,000 vs TI 160,000 for the same row, so this comes from the document that names
7080
+ // LM1458).
7081
+ //
7082
+ // Electrical characteristics p2 at TA = 25 C, VS = +-15 V, VOUT = +-10 V, RL >= 2 kOhm:
7083
+ // Large Signal Voltage Gain: LM1458 MIN 20 TYP 160 V/mV (LM1558 50/160 is the sibling
7084
+ // column). Used as `openLoopGain: 160000`, the typ figure. The sheet prints no GBW row
7085
+ // at all (see `opampGainBandwidthAbsent`); the swing pair stays absent per the D4 freeze
7086
+ // (see `opampOutputSwingAbsent`).
7087
+ {
7088
+ "partIds": [
7089
+ "LM1458",
7090
+ "LM1458N"
7091
+ ],
7092
+ "declaredTypes": [],
7093
+ "terminalRoleGroups": [
7094
+ [
7095
+ "OUT_A_pin1"
7096
+ ],
7097
+ [
7098
+ "INVERTING_A_pin2"
7099
+ ],
7100
+ [
7101
+ "NONINVERTING_A_pin3"
7102
+ ],
7103
+ [
7104
+ "V_MINUS_pin4"
7105
+ ],
7106
+ [
7107
+ "NONINVERTING_B_pin5"
7108
+ ],
7109
+ [
7110
+ "INVERTING_B_pin6"
7111
+ ],
7112
+ [
7113
+ "OUT_B_pin7"
7114
+ ],
7115
+ [
7116
+ "V_PLUS_pin8"
7117
+ ]
7118
+ ],
7119
+ "model": {
7120
+ "kind": "sections",
7121
+ "pinout": [
7122
+ "OUT_A",
7123
+ "IN_A_MINUS",
7124
+ "IN_A_PLUS",
7125
+ "VEE",
7126
+ "IN_B_PLUS",
7127
+ "IN_B_MINUS",
7128
+ "OUT_B",
7129
+ "VCC"
7130
+ ],
7131
+ "sections": [
7132
+ {
7133
+ "law": {
7134
+ "kind": "ideal-opamp",
7135
+ "railHigh": null,
7136
+ "railLow": null,
7137
+ "openLoopGain": 160000
7138
+ },
7139
+ "terminals": [
7140
+ 2,
7141
+ 1,
7142
+ 0
7143
+ ]
7144
+ },
7145
+ {
7146
+ "law": {
7147
+ "kind": "ideal-opamp",
7148
+ "railHigh": null,
7149
+ "railLow": null,
7150
+ "openLoopGain": 160000
7151
+ },
7152
+ "terminals": [
7153
+ 4,
7154
+ 5,
7155
+ 6
7156
+ ]
7157
+ }
7158
+ ]
7159
+ }
7160
+ },
7161
+ // Wave-4 op-amp entries: sections whose open-loop gain is the sheet's own TYP AOL
7162
+ // transcribed (the gain-bandwidth product lives in `registry-data/opamp.ts`, stamped by
7163
+ // the passes below; the swing pair stays absent per the D4 freeze). GBW rows and catalog
7164
+ // entries are 1:1 by part id, so no entry splits. LM308 and UA741CP carry AOL only (no
7165
+ // GBW row exists; see `opampGainBandwidthAbsent`).
7166
+ // TI LM108/LM208/LM308 single precision op-amp, from SNOSBS5A (May 2004, rev Sept
7167
+ // 2011, OBSOLETE): 5.x Electrical Characteristics (TA = 25C, VS = +-15 V, VOUT = +-10 V,
7168
+ // RL >= 10 kOhm): 'Large Signal Voltage Gain | MIN 50 (LM108/208) / 25 (LM308) |
7169
+ // TYP 300 | V/mV'. Used as `openLoopGain: 300000`. No GBW row is transcribed (the
7170
+ // sheet prints compensation values and open-loop response graphs only; the Fig. 6
7171
+ // 250 kHz/3.5 MHz numbers belong to the LM101A-boosted application circuit).
7172
+ // Pinout: Figure 8 Dual-In-Line Package Top View (8-Pin) p11 (1 COMP, 2 INPUT-,
7173
+ // 3 INPUT+, 4 V-, 5 NC, 6 OUTPUT, 7 V+, 8 COMP 2); the entry keeps the LF351
7174
+ // three-terminal capture (IN+ pin3, IN- pin2, OUT pin6), which is the same
7175
+ // LM741-compatible single layout.
7176
+ {
7177
+ "partIds": [
7178
+ "LM308"
7179
+ ],
7180
+ "declaredTypes": [],
7181
+ "terminalRoleGroups": [
7182
+ [
7183
+ "vin+",
7184
+ "in+",
7185
+ "pin3"
7186
+ ],
7187
+ [
7188
+ "vin-",
7189
+ "in-",
7190
+ "pin2"
7191
+ ],
7192
+ [
7193
+ "vout",
7194
+ "out",
7195
+ "pin6"
7196
+ ]
7197
+ ],
7198
+ "model": {
7199
+ "kind": "sections",
7200
+ "sections": [
7201
+ {
7202
+ "law": {
7203
+ "kind": "ideal-opamp",
7204
+ "railHigh": null,
7205
+ "railLow": null,
7206
+ "openLoopGain": 300000
7207
+ },
7208
+ "terminals": [
7209
+ 0,
7210
+ 1,
7211
+ 2
7212
+ ]
7213
+ }
7214
+ ],
7215
+ "pinout": [
7216
+ "vin+",
7217
+ "vin-",
7218
+ "vout"
7219
+ ]
7220
+ }
7221
+ },
7222
+ // TI LM324 quad op-amp, from SLOS066AE (Aug 1975, rev Sept 2025): 6.7 Electrical
7223
+ // Characteristics for LM324/LM324K (VCC = 15 V, VO = 1 V to 11 V, RL >= 2 kOhm, 25C):
7224
+ // 'Large-signal differential voltage amplification AVD | MIN 25 TYP 100 | V/mV'.
7225
+ // Used as `openLoopGain: 100000`. GBW is not in this entry: it lives in the
7226
+ // data-registry row (B1 unity-gain bandwidth TYP 1.2 MHz, 6.10 Operating Conditions),
7227
+ // stamped by the pass below. Pinout: Table 5-1 Pin Functions N (PDIP) p3, the
7228
+ // standard quad order (1OUT, 1IN-, 1IN+, VCC+, 2IN+, 2IN-, 2OUT, 3OUT, 3IN-, 3IN+,
7229
+ // VCC-, 4IN+, 4IN-, 4OUT), same A/B/C/D convention as the TL074 entry above.
7230
+ {
7231
+ "partIds": [
7232
+ "LM324"
7233
+ ],
7234
+ "declaredTypes": [],
7235
+ "terminalRoleGroups": [
7236
+ [
7237
+ "OUT_A_pin1"
7238
+ ],
7239
+ [
7240
+ "INVERTING_A_pin2"
7241
+ ],
7242
+ [
7243
+ "NONINVERTING_A_pin3"
7244
+ ],
7245
+ [
7246
+ "V_PLUS_pin4"
7247
+ ],
7248
+ [
7249
+ "NONINVERTING_B_pin5"
7250
+ ],
7251
+ [
7252
+ "INVERTING_B_pin6"
7253
+ ],
7254
+ [
7255
+ "OUT_B_pin7"
7256
+ ],
7257
+ [
7258
+ "OUT_C_pin8"
7259
+ ],
7260
+ [
7261
+ "INVERTING_C_pin9"
7262
+ ],
7263
+ [
7264
+ "NONINVERTING_C_pin10"
7265
+ ],
7266
+ [
7267
+ "V_MINUS_pin11"
7268
+ ],
7269
+ [
7270
+ "NONINVERTING_D_pin12"
7271
+ ],
7272
+ [
7273
+ "INVERTING_D_pin13"
7274
+ ],
7275
+ [
7276
+ "OUT_D_pin14"
7277
+ ]
7278
+ ],
7279
+ "model": {
7280
+ "kind": "sections",
7281
+ "pinout": [
7282
+ "OUT_A",
7283
+ "IN_A_MINUS",
7284
+ "IN_A_PLUS",
7285
+ "VCC",
7286
+ "IN_B_PLUS",
7287
+ "IN_B_MINUS",
7288
+ "OUT_B",
7289
+ "OUT_C",
7290
+ "IN_C_MINUS",
7291
+ "IN_C_PLUS",
7292
+ "VEE",
7293
+ "IN_D_PLUS",
7294
+ "IN_D_MINUS",
7295
+ "OUT_D"
7296
+ ],
7297
+ "sections": [
7298
+ {
7299
+ "law": {
7300
+ "kind": "ideal-opamp",
7301
+ "railHigh": null,
7302
+ "railLow": null,
7303
+ "openLoopGain": 100000
7304
+ },
7305
+ "terminals": [
7306
+ 2,
7307
+ 1,
7308
+ 0
7309
+ ]
7310
+ },
7311
+ {
7312
+ "law": {
7313
+ "kind": "ideal-opamp",
7314
+ "railHigh": null,
7315
+ "railLow": null,
7316
+ "openLoopGain": 100000
7317
+ },
7318
+ "terminals": [
7319
+ 4,
7320
+ 5,
7321
+ 6
7322
+ ]
7323
+ },
7324
+ {
7325
+ "law": {
7326
+ "kind": "ideal-opamp",
7327
+ "railHigh": null,
7328
+ "railLow": null,
7329
+ "openLoopGain": 100000
7330
+ },
7331
+ "terminals": [
7332
+ 9,
7333
+ 8,
7334
+ 7
7335
+ ]
7336
+ },
7337
+ {
7338
+ "law": {
7339
+ "kind": "ideal-opamp",
7340
+ "railHigh": null,
7341
+ "railLow": null,
7342
+ "openLoopGain": 100000
7343
+ },
7344
+ "terminals": [
7345
+ 11,
7346
+ 12,
7347
+ 13
7348
+ ]
7349
+ }
7350
+ ]
7351
+ }
7352
+ },
7353
+ // TI uA741C single op-amp in the PDIP-8 UA741CP orderable, from SLOS094H (Nov 1970,
7354
+ // rev Feb 2026): 5.4 Electrical Characteristics uA741C (VCC+- = +-15 V, VO = +-10 V,
7355
+ // RL >= 2 kOhm, 25C): 'Large-signal differential voltage amplification AVD |
7356
+ // MIN 20 TYP 200 | V/mV'. Used as `openLoopGain: 200000`. The sheet prints the
7357
+ // orderable as UA741CP (PDIP P|8) and the device table as uA741CP; the ASCII id is
7358
+ // the orderable spelling. No GBW row is transcribed (switching tables print only
7359
+ // rise time, overshoot and SR). Pinout: Figure 4-2 D-or-P 8-pin Top View + Table 4-2
7360
+ // Pin Functions p3 (1 NC, 2 IN-, 3 IN+, 4 VCC-, 5 NC, 6 OUT, 7 VCC+, 8 NC); the entry
7361
+ // keeps the LF351 three-terminal capture (IN+ pin3, IN- pin2, OUT pin6).
7362
+ {
7363
+ "partIds": [
7364
+ "UA741CP"
7365
+ ],
7366
+ "declaredTypes": [],
7367
+ "terminalRoleGroups": [
7368
+ [
7369
+ "vin+",
7370
+ "in+",
7371
+ "pin3"
7372
+ ],
7373
+ [
7374
+ "vin-",
7375
+ "in-",
7376
+ "pin2"
7377
+ ],
7378
+ [
7379
+ "vout",
7380
+ "out",
7381
+ "pin6"
7382
+ ]
7383
+ ],
7384
+ "model": {
7385
+ "kind": "sections",
7386
+ "sections": [
7387
+ {
7388
+ "law": {
7389
+ "kind": "ideal-opamp",
7390
+ "railHigh": null,
7391
+ "railLow": null,
7392
+ "openLoopGain": 200000
7393
+ },
7394
+ "terminals": [
7395
+ 0,
7396
+ 1,
7397
+ 2
7398
+ ]
7399
+ }
7400
+ ],
7401
+ "pinout": [
7402
+ "vin+",
7403
+ "vin-",
7404
+ "vout"
7405
+ ]
7406
+ }
7407
+ },
7408
+ // TI TLC2262 dual LinCMOS op-amp, from SLOS177D (Feb 1997, rev Mar 2001): TLC2262C
7409
+ // operating characteristics (VDD+- = +-5 V, the 10 V table closest to a 9 V pedal):
7410
+ // 'Large-signal differential voltage amplification, RL = 50 kOhm, VO = +-4 V:
7411
+ // MIN 80 TYP 200 V/mV'. Used as `openLoopGain: 200000`. Coordinator standing rule
7412
+ // 2026-10-19: the bare id the sheet names in its description takes the commercial (C)
7413
+ // grade column, disclosed here; no suffixed grade is aliased. GBW is not in this
7414
+ // entry: it lives in the data-registry row (TYP 0.73 MHz, same table), stamped by the
7415
+ // pass below. Pinout: D/P/PW 8-pin Top View p3, the standard dual order
7416
+ // (1OUT, 1IN-, 1IN+, VDD-/GND, 2IN+, 2IN-, 2OUT, VDD+), same A/B convention as the
7417
+ // LM833 entry above.
7418
+ {
7419
+ "partIds": [
7420
+ "TLC2262"
7421
+ ],
7422
+ "declaredTypes": [],
7423
+ "terminalRoleGroups": [
7424
+ [
7425
+ "OUT_A_pin1"
7426
+ ],
7427
+ [
7428
+ "INVERTING_A_pin2"
7429
+ ],
7430
+ [
7431
+ "NONINVERTING_A_pin3"
7432
+ ],
7433
+ [
7434
+ "V_MINUS_pin4"
7435
+ ],
7436
+ [
7437
+ "NONINVERTING_B_pin5"
7438
+ ],
7439
+ [
7440
+ "INVERTING_B_pin6"
7441
+ ],
7442
+ [
7443
+ "OUT_B_pin7"
7444
+ ],
7445
+ [
7446
+ "V_PLUS_pin8"
7447
+ ]
7448
+ ],
7449
+ "model": {
7450
+ "kind": "sections",
7451
+ "pinout": [
7452
+ "OUT_A",
7453
+ "IN_A_MINUS",
7454
+ "IN_A_PLUS",
7455
+ "VEE",
7456
+ "IN_B_PLUS",
7457
+ "IN_B_MINUS",
7458
+ "OUT_B",
7459
+ "VCC"
7460
+ ],
7461
+ "sections": [
7462
+ {
7463
+ "law": {
7464
+ "kind": "ideal-opamp",
7465
+ "railHigh": null,
7466
+ "railLow": null,
7467
+ "openLoopGain": 200000
7468
+ },
7469
+ "terminals": [
7470
+ 2,
7471
+ 1,
7472
+ 0
7473
+ ]
7474
+ },
7475
+ {
7476
+ "law": {
7477
+ "kind": "ideal-opamp",
7478
+ "railHigh": null,
7479
+ "railLow": null,
7480
+ "openLoopGain": 200000
7481
+ },
7482
+ "terminals": [
7483
+ 4,
7484
+ 5,
7485
+ 6
7486
+ ]
7487
+ }
7488
+ ]
7489
+ }
7490
+ },
7491
+ // onsemi MC33174 quad op-amp, from MC33171/D Rev. 14 Jul 2025: DC table (VCC = +15 V,
7492
+ // VEE = -15 V, RL to ground, TA = 25C) p3: 'Large Signal Voltage Gain (VO = +-10 V,
7493
+ // RL = 10 k), AVOL MIN 50 TYP 500 V/mV'. Used as `openLoopGain: 500000`. GBW is not
7494
+ // in this entry: it lives in the data-registry row (TYP 1.8 MHz at f = 100 kHz, AC
7495
+ // table p4), stamped by the pass below. Pinout: PIN CONNECTIONS quad diagram p2
7496
+ // incl. PDIP-14 (Output 1 = 1, Inputs 1, VCC, Inputs 2, Output 2, Output 3,
7497
+ // Inputs 3, VEE, Inputs 4, Output 4), the standard quad order, same A/B/C/D
7498
+ // convention as the TL074 entry.
7499
+ {
7500
+ "partIds": [
7501
+ "MC33174"
7502
+ ],
7503
+ "declaredTypes": [],
7504
+ "terminalRoleGroups": [
7505
+ [
7506
+ "OUT_A_pin1"
7507
+ ],
7508
+ [
7509
+ "INVERTING_A_pin2"
7510
+ ],
7511
+ [
7512
+ "NONINVERTING_A_pin3"
7513
+ ],
7514
+ [
7515
+ "V_PLUS_pin4"
7516
+ ],
7517
+ [
7518
+ "NONINVERTING_B_pin5"
7519
+ ],
7520
+ [
7521
+ "INVERTING_B_pin6"
7522
+ ],
7523
+ [
7524
+ "OUT_B_pin7"
7525
+ ],
7526
+ [
7527
+ "OUT_C_pin8"
7528
+ ],
7529
+ [
7530
+ "INVERTING_C_pin9"
7531
+ ],
7532
+ [
7533
+ "NONINVERTING_C_pin10"
7534
+ ],
7535
+ [
7536
+ "V_MINUS_pin11"
7537
+ ],
7538
+ [
7539
+ "NONINVERTING_D_pin12"
7540
+ ],
7541
+ [
7542
+ "INVERTING_D_pin13"
7543
+ ],
7544
+ [
7545
+ "OUT_D_pin14"
7546
+ ]
7547
+ ],
7548
+ "model": {
7549
+ "kind": "sections",
7550
+ "pinout": [
7551
+ "OUT_A",
7552
+ "IN_A_MINUS",
7553
+ "IN_A_PLUS",
7554
+ "VCC",
7555
+ "IN_B_PLUS",
7556
+ "IN_B_MINUS",
7557
+ "OUT_B",
7558
+ "OUT_C",
7559
+ "IN_C_MINUS",
7560
+ "IN_C_PLUS",
7561
+ "VEE",
7562
+ "IN_D_PLUS",
7563
+ "IN_D_MINUS",
7564
+ "OUT_D"
7565
+ ],
7566
+ "sections": [
7567
+ {
7568
+ "law": {
7569
+ "kind": "ideal-opamp",
7570
+ "railHigh": null,
7571
+ "railLow": null,
7572
+ "openLoopGain": 500000
7573
+ },
7574
+ "terminals": [
7575
+ 2,
7576
+ 1,
7577
+ 0
7578
+ ]
7579
+ },
7580
+ {
7581
+ "law": {
7582
+ "kind": "ideal-opamp",
7583
+ "railHigh": null,
7584
+ "railLow": null,
7585
+ "openLoopGain": 500000
7586
+ },
7587
+ "terminals": [
7588
+ 4,
7589
+ 5,
7590
+ 6
7591
+ ]
7592
+ },
7593
+ {
7594
+ "law": {
7595
+ "kind": "ideal-opamp",
7596
+ "railHigh": null,
7597
+ "railLow": null,
7598
+ "openLoopGain": 500000
7599
+ },
7600
+ "terminals": [
7601
+ 9,
7602
+ 8,
7603
+ 7
7604
+ ]
7605
+ },
7606
+ {
7607
+ "law": {
7608
+ "kind": "ideal-opamp",
7609
+ "railHigh": null,
7610
+ "railLow": null,
7611
+ "openLoopGain": 500000
7612
+ },
7613
+ "terminals": [
7614
+ 11,
7615
+ 12,
7616
+ 13
7617
+ ]
7618
+ }
7619
+ ]
7620
+ }
7621
+ },
7622
+ // Wave-5 op-amp entries: sections whose open-loop gain is the sheet's own TYP AOL
7623
+ // transcribed (the gain-bandwidth product lives in `registry-data/opamp.ts`, stamped by
7624
+ // the passes below; the swing pair stays absent per the D4 freeze). GBW rows and catalog
7625
+ // entries are 1:1 by part id, so no entry splits. TL072C carries AOL only (no single
7626
+ // GBW figure exists; see `opampGainBandwidthAbsent`).
7627
+ // TI TL072C dual JFET-input op-amp, from SLOS080W (Sep 1978, rev Jul 2025): 5.8 DC
7628
+ // (VS = +-15 V, TA = 25C, VO = 0 V): 'AOL | Open-loop voltage gain | TL07xC |
7629
+ // MIN 25 TYP 200 V/mV'. Used as `openLoopGain: 200000` (bare id takes the commercial
7630
+ // C-grade column, disclosed; no suffixed alias). Pinout: Table 4-3 Pin Functions
7631
+ // TL072x (1 1OUT, 2 1IN-, 3 1IN+, 4 VCC-, 5 2IN+, 6 2IN-, 7 2OUT, 8 VCC+), the
7632
+ // standard dual order, same pin-numbered role convention as the NJM4580 entry.
7633
+ {
7634
+ "partIds": [
7635
+ "TL072C"
7636
+ ],
7637
+ "declaredTypes": [],
7638
+ "terminalRoleGroups": [
7639
+ [
7640
+ "OUT_A_pin1"
7641
+ ],
7642
+ [
7643
+ "INVERTING_A_pin2"
7644
+ ],
7645
+ [
7646
+ "NONINVERTING_A_pin3"
7647
+ ],
7648
+ [
7649
+ "V_MINUS_pin4"
7650
+ ],
7651
+ [
7652
+ "NONINVERTING_B_pin5"
7653
+ ],
7654
+ [
7655
+ "INVERTING_B_pin6"
7656
+ ],
7657
+ [
7658
+ "OUT_B_pin7"
7659
+ ],
7660
+ [
7661
+ "V_PLUS_pin8"
7662
+ ]
7663
+ ],
7664
+ "model": {
7665
+ "kind": "sections",
7666
+ "pinout": [
7667
+ "OUT_A",
7668
+ "IN_A_MINUS",
7669
+ "IN_A_PLUS",
7670
+ "VEE",
7671
+ "IN_B_PLUS",
7672
+ "IN_B_MINUS",
7673
+ "OUT_B",
7674
+ "VCC"
7675
+ ],
7676
+ "sections": [
7677
+ {
7678
+ "law": {
7679
+ "kind": "ideal-opamp",
7680
+ "railHigh": null,
7681
+ "railLow": null,
7682
+ "openLoopGain": 200000
7683
+ },
7684
+ "terminals": [
7685
+ 2,
7686
+ 1,
7687
+ 0
7688
+ ]
7689
+ },
7690
+ {
7691
+ "law": {
7692
+ "kind": "ideal-opamp",
7693
+ "railHigh": null,
7694
+ "railLow": null,
7695
+ "openLoopGain": 200000
7696
+ },
7697
+ "terminals": [
7698
+ 4,
7699
+ 5,
7700
+ 6
7701
+ ]
7702
+ }
7703
+ ]
7704
+ }
7705
+ },
7706
+ // TI OPA1678 dual audio op-amp, from SBOS855E (Jan 2017 - Dec 2022): 6.7 Electrical
7707
+ // Characteristics (VS = +-15 V, TA = 25C, RL = 2 kOhm, VCM = VOUT = midsupply):
7708
+ // 'AOL | Open-loop voltage gain | (V-) + 0.8 V <= VO <= (V+) - 0.8 V | MIN 106
7709
+ // TYP 114 | dB'. Used as `openLoopGain: decibelGain(114)` = 501187.2336272725.
7710
+ // GBW lives in the data-registry row (TYP 16 MHz at G = 1). Pinout: 5 Pin
7711
+ // Configuration and Functions, Pin Functions OPA1678 (OUT A-1, -IN A-2, +IN A-3,
7712
+ // V--4, +IN B-5, -IN B-6, OUT B-7, V+-8), the standard dual order.
7713
+ {
7714
+ "partIds": [
7715
+ "OPA1678"
7716
+ ],
7717
+ "declaredTypes": [],
7718
+ "terminalRoleGroups": [
7719
+ [
7720
+ "OUT_A_pin1"
7721
+ ],
7722
+ [
7723
+ "INVERTING_A_pin2"
7724
+ ],
7725
+ [
7726
+ "NONINVERTING_A_pin3"
7727
+ ],
7728
+ [
7729
+ "V_MINUS_pin4"
7730
+ ],
7731
+ [
7732
+ "NONINVERTING_B_pin5"
7733
+ ],
7734
+ [
7735
+ "INVERTING_B_pin6"
7736
+ ],
7737
+ [
7738
+ "OUT_B_pin7"
7739
+ ],
7740
+ [
7741
+ "V_PLUS_pin8"
7742
+ ]
7743
+ ],
7744
+ "model": {
7745
+ "kind": "sections",
7746
+ "pinout": [
7747
+ "OUT_A",
7748
+ "IN_A_MINUS",
7749
+ "IN_A_PLUS",
7750
+ "VEE",
7751
+ "IN_B_PLUS",
7752
+ "IN_B_MINUS",
7753
+ "OUT_B",
7754
+ "VCC"
7755
+ ],
7756
+ "sections": [
7757
+ {
7758
+ "law": {
7759
+ "kind": "ideal-opamp",
7760
+ "railHigh": null,
7761
+ "railLow": null,
7762
+ "openLoopGain": 501187.2336272725
7763
+ },
7764
+ "terminals": [
7765
+ 2,
7766
+ 1,
7767
+ 0
7768
+ ]
7769
+ },
7770
+ {
7771
+ "law": {
7772
+ "kind": "ideal-opamp",
7773
+ "railHigh": null,
7774
+ "railLow": null,
7775
+ "openLoopGain": 501187.2336272725
7776
+ },
7777
+ "terminals": [
7778
+ 4,
7779
+ 5,
7780
+ 6
7781
+ ]
7782
+ }
7783
+ ]
7784
+ }
7785
+ },
7786
+ // TI (Burr-Brown) OPA134 single audio op-amp, from SBOS058B (Dec 1997, Rev. B
7787
+ // Nov 2024): 5.7 Electrical Characteristics (TA = 25C, VS = +-15 V): 'AOL |
7788
+ // Open-loop voltage gain | RL = 2 kOhm, -13.8 V <= VO <= 13.5 V | MIN 104
7789
+ // TYP 120 | dB' (a second row at RL = 10 kOhm reads the same 104/120). Used as
7790
+ // `openLoopGain: decibelGain(120)` = 1000000. GBW lives in the data-registry row
7791
+ // (TYP 8 MHz). OPA134PA is the printed PDIP-8 orderable of the same die
7792
+ // (orderable table: 'OPA134PA | Active | Production | PDIP (P) | 8'; family specs
7793
+ // with no per-orderable split). Pinout: Figure 4-1 + Pin Functions OPA134
7794
+ // (+IN-3, -IN-2, OUT-6); the entry keeps the LF351 three-terminal capture.
7795
+ {
7796
+ "partIds": [
7797
+ "OPA134PA"
7798
+ ],
7799
+ "declaredTypes": [],
7800
+ "terminalRoleGroups": [
7801
+ [
7802
+ "vin+",
7803
+ "in+",
7804
+ "pin3"
7805
+ ],
7806
+ [
7807
+ "vin-",
7808
+ "in-",
7809
+ "pin2"
7810
+ ],
7811
+ [
7812
+ "vout",
7813
+ "out",
7814
+ "pin6"
7815
+ ]
7816
+ ],
7817
+ "model": {
7818
+ "kind": "sections",
7819
+ "sections": [
7820
+ {
7821
+ "law": {
7822
+ "kind": "ideal-opamp",
7823
+ "railHigh": null,
7824
+ "railLow": null,
7825
+ "openLoopGain": 1000000
7826
+ },
7827
+ "terminals": [
7828
+ 0,
7829
+ 1,
7830
+ 2
7831
+ ]
7832
+ }
7833
+ ],
7834
+ "pinout": [
7835
+ "vin+",
7836
+ "vin-",
7837
+ "vout"
7838
+ ]
7839
+ }
7840
+ },
7841
+ // TI (Burr-Brown) OPA604 single op-amp, from SBOS019A (Jan 1992 - Sep 2003):
7842
+ // ELECTRICAL CHARACTERISTICS (TA = +25C, VS = +-15 V, MIN/TYP/MAX columns):
7843
+ // 'OPEN-LOOP GAIN | Open-Loop Voltage Gain, VO = +-10 V, RL = 1 kOhm: MIN 80
7844
+ // TYP 100 dB'. Used as `openLoopGain: decibelGain(100)` = 100000. GBW lives in
7845
+ // the data-registry row (TYP 20 MHz at G = 100, quoted with its condition).
7846
+ // OPA604AP is named by the specs header ('OPA604AP, AU'); the PDIP suffix follows
7847
+ // the Burr-Brown AP convention (the Package Option Addendum lists AU active).
7848
+ // Pinout: the DIP/SOIC diagram (-In-2, +In-3, OUT-6); the LF351 three-terminal
7849
+ // capture, same as the UA741CP entry.
7850
+ {
7851
+ "partIds": [
7852
+ "OPA604AP"
7853
+ ],
7854
+ "declaredTypes": [],
7855
+ "terminalRoleGroups": [
7856
+ [
7857
+ "vin+",
7858
+ "in+",
7859
+ "pin3"
7860
+ ],
7861
+ [
7862
+ "vin-",
7863
+ "in-",
7864
+ "pin2"
7865
+ ],
7866
+ [
7867
+ "vout",
7868
+ "out",
7869
+ "pin6"
7870
+ ]
7871
+ ],
7872
+ "model": {
7873
+ "kind": "sections",
7874
+ "sections": [
7875
+ {
7876
+ "law": {
7877
+ "kind": "ideal-opamp",
7878
+ "railHigh": null,
7879
+ "railLow": null,
7880
+ "openLoopGain": 100000
7881
+ },
7882
+ "terminals": [
7883
+ 0,
7884
+ 1,
7885
+ 2
7886
+ ]
7887
+ }
7888
+ ],
7889
+ "pinout": [
7890
+ "vin+",
7891
+ "vin-",
7892
+ "vout"
7893
+ ]
7894
+ }
7895
+ },
7896
+ // Wave-2 op-amp entries: sections whose open-loop gain is the sheet's own TYP AOL
7897
+ // transcribed (the gain-bandwidth product lives in `registry-data/opamp.ts`, stamped by
7898
+ // the passes below; the swing pair stays absent per the D4 freeze). GBW rows and catalog
7899
+ // entries are 1:1 by part id, so no entry splits.
7900
+ // ADI AD712 dual precision BiFET op-amp, from the ADI AD712 datasheet Rev. B
7901
+ // (SPECIFICATIONS at VS = +-15 V @ TA = +25C): OPEN-LOOP GAIN MIN 150 TYP 400 V/mV
7902
+ // (J/A/S grades; K/B/T and C print MIN 200 TYP 400 -- TYP 400 in every grade, so the
7903
+ // bare id carries it). Used as `openLoopGain: 400000`. The sheet's frequency row reads
7904
+ // 'Small Signal Bandwidth 3.0 MIN 4.0 TYP MHz' with no closed-loop gain stated and never
7905
+ // uses the term gain-bandwidth, so no GBW figure is transcribed (see
7906
+ // `opampGainBandwidthAbsent`). Pinout: 'pinned out in a standard op amp configuration'
7907
+ // (dual DIP-8 order, same pin-numbered role convention as the LM833 entry above).
7908
+ {
7909
+ "partIds": [
7910
+ "AD712"
7911
+ ],
7912
+ "declaredTypes": [],
7913
+ "terminalRoleGroups": [
7914
+ [
7915
+ "OUT_A_pin1"
7916
+ ],
7917
+ [
7918
+ "INVERTING_A_pin2"
7919
+ ],
7920
+ [
7921
+ "NONINVERTING_A_pin3"
7922
+ ],
7923
+ [
7924
+ "V_MINUS_pin4"
7925
+ ],
7926
+ [
7927
+ "NONINVERTING_B_pin5"
7928
+ ],
7929
+ [
7930
+ "INVERTING_B_pin6"
7931
+ ],
7932
+ [
7933
+ "OUT_B_pin7"
7934
+ ],
7935
+ [
7936
+ "V_PLUS_pin8"
7937
+ ]
7938
+ ],
7939
+ "model": {
7940
+ "kind": "sections",
7941
+ "pinout": [
7942
+ "OUT_A",
7943
+ "IN_A_MINUS",
7944
+ "IN_A_PLUS",
7945
+ "VEE",
7946
+ "IN_B_PLUS",
7947
+ "IN_B_MINUS",
7948
+ "OUT_B",
7949
+ "VCC"
7950
+ ],
7951
+ "sections": [
7952
+ {
7953
+ "law": {
7954
+ "kind": "ideal-opamp",
7955
+ "railHigh": null,
7956
+ "railLow": null,
7957
+ "openLoopGain": 400000
7958
+ },
7959
+ "terminals": [
7960
+ 2,
7961
+ 1,
7962
+ 0
7963
+ ]
7964
+ },
7965
+ {
7966
+ "law": {
7967
+ "kind": "ideal-opamp",
7968
+ "railHigh": null,
7969
+ "railLow": null,
7970
+ "openLoopGain": 400000
7971
+ },
7972
+ "terminals": [
7973
+ 4,
7974
+ 5,
7975
+ 6
7976
+ ]
7977
+ }
7978
+ ]
7979
+ }
7980
+ },
7981
+ // TI LF347 quad JFET-input op-amp, from SLOS013C (Mar 1987, rev Mar 2016):
7982
+ // 6.5 AVD 'Large signal differential voltage, VO = +-10 V, RL = 2 kOhm, 25C: MIN 25
7983
+ // TYP 100 V/mV' (6.6 LF347B: MIN 50 TYP 100). Used as `openLoopGain: 100000`.
7984
+ // LF347N is the PDIP-14 of the LF347 grade. Pinout: the sheet's own Pin Functions
7985
+ // table (1 1OUT, 2 1IN-, 3 1IN+, 4 VCC+, 5 2IN+, 6 2IN-, 7 2OUT, 8 3OUT, 9 3IN-,
7986
+ // 10 3IN+, 11 VCC-, 12 4IN+, 13 4IN-, 14 4OUT) -- the standard quad order.
7987
+ {
7988
+ "partIds": [
7989
+ "LF347",
7990
+ "LF347N"
7991
+ ],
7992
+ "declaredTypes": [],
7993
+ "terminalRoleGroups": [
7994
+ [
7995
+ "OUT_A_pin1"
7996
+ ],
7997
+ [
7998
+ "INVERTING_A_pin2"
7999
+ ],
8000
+ [
8001
+ "NONINVERTING_A_pin3"
8002
+ ],
8003
+ [
8004
+ "V_PLUS_pin4"
8005
+ ],
8006
+ [
8007
+ "NONINVERTING_B_pin5"
8008
+ ],
8009
+ [
8010
+ "INVERTING_B_pin6"
8011
+ ],
8012
+ [
8013
+ "OUT_B_pin7"
8014
+ ],
8015
+ [
8016
+ "OUT_C_pin8"
8017
+ ],
8018
+ [
8019
+ "INVERTING_C_pin9"
8020
+ ],
8021
+ [
8022
+ "NONINVERTING_C_pin10"
8023
+ ],
8024
+ [
8025
+ "V_MINUS_pin11"
8026
+ ],
8027
+ [
8028
+ "NONINVERTING_D_pin12"
8029
+ ],
8030
+ [
8031
+ "INVERTING_D_pin13"
8032
+ ],
8033
+ [
8034
+ "OUT_D_pin14"
8035
+ ]
8036
+ ],
8037
+ "model": {
8038
+ "kind": "sections",
8039
+ "pinout": [
8040
+ "OUT_A",
8041
+ "IN_A_MINUS",
8042
+ "IN_A_PLUS",
8043
+ "VCC",
8044
+ "IN_B_PLUS",
8045
+ "IN_B_MINUS",
8046
+ "OUT_B",
8047
+ "OUT_C",
8048
+ "IN_C_MINUS",
8049
+ "IN_C_PLUS",
8050
+ "VEE",
8051
+ "IN_D_PLUS",
8052
+ "IN_D_MINUS",
8053
+ "OUT_D"
8054
+ ],
8055
+ "sections": [
8056
+ {
8057
+ "law": {
8058
+ "kind": "ideal-opamp",
8059
+ "railHigh": null,
8060
+ "railLow": null,
8061
+ "openLoopGain": 100000
8062
+ },
8063
+ "terminals": [
8064
+ 2,
8065
+ 1,
8066
+ 0
8067
+ ]
8068
+ },
8069
+ {
8070
+ "law": {
8071
+ "kind": "ideal-opamp",
8072
+ "railHigh": null,
8073
+ "railLow": null,
8074
+ "openLoopGain": 100000
8075
+ },
8076
+ "terminals": [
8077
+ 4,
8078
+ 5,
8079
+ 6
8080
+ ]
8081
+ },
8082
+ {
8083
+ "law": {
8084
+ "kind": "ideal-opamp",
8085
+ "railHigh": null,
8086
+ "railLow": null,
8087
+ "openLoopGain": 100000
8088
+ },
8089
+ "terminals": [
8090
+ 9,
8091
+ 8,
8092
+ 7
8093
+ ]
8094
+ },
8095
+ {
8096
+ "law": {
8097
+ "kind": "ideal-opamp",
8098
+ "railHigh": null,
8099
+ "railLow": null,
8100
+ "openLoopGain": 100000
8101
+ },
8102
+ "terminals": [
8103
+ 11,
8104
+ 12,
8105
+ 13
8106
+ ]
8107
+ }
8108
+ ]
8109
+ }
8110
+ },
8111
+ // National Semiconductor LF351 single JFET-input op-amp, from DS005648 Rev. 3 (1998):
8112
+ // AVOL 'Large Signal Voltage Gain, VS = +-15 V, TA = 25C, VO = +-10 V, RL = 2 kOhm:
8113
+ // MIN 25 TYP 100 V/mV'. Used as `openLoopGain: 100000`. LF351N is the DIP orderable
8114
+ // (NS Package N08E, sheet p11 'Order Number LF351M or LF351N'). Pinout: 'pin compatible
8115
+ // with the standard LM741' (single DIP-8: IN+ pin3, IN- pin2, OUT pin6), the same
8116
+ // three-terminal capture as the TL061 entry above.
8117
+ {
8118
+ "partIds": [
8119
+ "LF351",
8120
+ "LF351N"
8121
+ ],
8122
+ "declaredTypes": [],
8123
+ "terminalRoleGroups": [
8124
+ [
8125
+ "vin+",
8126
+ "in+",
8127
+ "pin3"
8128
+ ],
8129
+ [
8130
+ "vin-",
8131
+ "in-",
8132
+ "pin2"
8133
+ ],
8134
+ [
8135
+ "vout",
8136
+ "out",
8137
+ "pin6"
8138
+ ]
8139
+ ],
8140
+ "model": {
8141
+ "kind": "sections",
8142
+ "sections": [
8143
+ {
8144
+ "law": {
8145
+ "kind": "ideal-opamp",
8146
+ "railHigh": null,
8147
+ "railLow": null,
8148
+ "openLoopGain": 100000
8149
+ },
8150
+ "terminals": [
8151
+ 0,
8152
+ 1,
8153
+ 2
8154
+ ]
8155
+ }
8156
+ ],
8157
+ "pinout": [
8158
+ "vin+",
8159
+ "vin-",
8160
+ "vout"
8161
+ ]
8162
+ }
8163
+ },
8164
+ // SGS-THOMSON L272 dual power op-amp, from the L272 datasheet (Jan 1995):
8165
+ // ELECTRICAL CHARACTERISTICS (Vs = 24 V, Tamb = 25C): 'Gv | O. L. Voltage Gain |
8166
+ // f = 100 Hz | MIN 60 TYP 70 dB' (f = 1 kHz TYP 50 dB). Used as
8167
+ // `openLoopGain: decibelGain(70)` = 3162.2776601683795. Pinout: the sheet's PIN
8168
+ // CONNECTIONS are diagrams only (no text pin table could be extracted), so this entry
8169
+ // asserts NO pin order (neutral null pinout, M5218 precedent) and serves
8170
+ // section-grouped declarations; a packet study must verify the order before pin
8171
+ // positions are typed. One family table for L272/L272M/L272D.
8172
+ {
8173
+ "partIds": [
8174
+ "L272",
8175
+ "L272M",
8176
+ "L272D"
8177
+ ],
8178
+ "declaredTypes": [],
8179
+ "terminalRoleGroups": [],
8180
+ "model": {
8181
+ "kind": "sections",
8182
+ "sections": [
8183
+ {
8184
+ "law": {
8185
+ "kind": "ideal-opamp",
8186
+ "railHigh": null,
8187
+ "railLow": null,
8188
+ "openLoopGain": 3162.2776601683795
8189
+ },
8190
+ "terminals": [
8191
+ 2,
8192
+ 1,
8193
+ 0
8194
+ ]
8195
+ },
8196
+ {
8197
+ "law": {
8198
+ "kind": "ideal-opamp",
8199
+ "railHigh": null,
8200
+ "railLow": null,
8201
+ "openLoopGain": 3162.2776601683795
8202
+ },
8203
+ "terminals": [
8204
+ 4,
8205
+ 5,
8206
+ 6
8207
+ ]
8208
+ }
8209
+ ],
8210
+ "pinout": [
8211
+ null,
8212
+ null,
8213
+ null,
8214
+ null,
8215
+ null,
8216
+ null,
8217
+ null,
8218
+ null
8219
+ ]
8220
+ }
8221
+ },
8222
+ // TI TL074 quad JFET-input op-amp, from SLOS080W (Sep 1978, rev Jul 2025):
8223
+ // 5.8 DC (VS = +-15 V, TA = 25C, VO = 0 V): 'AOL | Open-loop voltage gain | TL07xC |
8224
+ // MIN 25 TYP 200 V/mV' (full-range row MIN-only 15; AC/BC 50/200, M 35/200, I MIN-only
8225
+ // 25 -- the I grade is not covered). Used as `openLoopGain: 200000`. TL074CN (PDIP-N)
8226
+ // and TL074CD (SOIC-D) are C-grade 'all other devices' for the 5.25 MHz GBW row.
8227
+ // Pinout: the sheet's own Table 4-6 Pin Functions TL074x (1 1OUT, 2 1IN-, 3 1IN+,
8228
+ // 4 VCC+, 5 2IN+, 6 2IN-, 7 2OUT, 8 3OUT, 9 3IN-, 10 3IN+, 11 VCC-, 12 4IN+,
8229
+ // 13 4IN-, 14 4OUT).
8230
+ {
8231
+ "partIds": [
8232
+ "TL074",
8233
+ "TL074CN",
8234
+ "TL074CD"
8235
+ ],
8236
+ "declaredTypes": [],
8237
+ "terminalRoleGroups": [
8238
+ [
8239
+ "OUT_A_pin1"
8240
+ ],
8241
+ [
8242
+ "INVERTING_A_pin2"
8243
+ ],
8244
+ [
8245
+ "NONINVERTING_A_pin3"
8246
+ ],
8247
+ [
8248
+ "V_PLUS_pin4"
8249
+ ],
8250
+ [
8251
+ "NONINVERTING_B_pin5"
8252
+ ],
8253
+ [
8254
+ "INVERTING_B_pin6"
8255
+ ],
8256
+ [
8257
+ "OUT_B_pin7"
8258
+ ],
8259
+ [
8260
+ "OUT_C_pin8"
8261
+ ],
8262
+ [
8263
+ "INVERTING_C_pin9"
8264
+ ],
8265
+ [
8266
+ "NONINVERTING_C_pin10"
8267
+ ],
8268
+ [
8269
+ "V_MINUS_pin11"
8270
+ ],
8271
+ [
8272
+ "NONINVERTING_D_pin12"
8273
+ ],
8274
+ [
8275
+ "INVERTING_D_pin13"
8276
+ ],
8277
+ [
8278
+ "OUT_D_pin14"
8279
+ ]
8280
+ ],
8281
+ "model": {
8282
+ "kind": "sections",
8283
+ "pinout": [
8284
+ "OUT_A",
8285
+ "IN_A_MINUS",
8286
+ "IN_A_PLUS",
8287
+ "VCC",
8288
+ "IN_B_PLUS",
8289
+ "IN_B_MINUS",
8290
+ "OUT_B",
8291
+ "OUT_C",
8292
+ "IN_C_MINUS",
8293
+ "IN_C_PLUS",
8294
+ "VEE",
8295
+ "IN_D_PLUS",
8296
+ "IN_D_MINUS",
8297
+ "OUT_D"
8298
+ ],
8299
+ "sections": [
8300
+ {
8301
+ "law": {
8302
+ "kind": "ideal-opamp",
8303
+ "railHigh": null,
8304
+ "railLow": null,
8305
+ "openLoopGain": 200000
8306
+ },
8307
+ "terminals": [
8308
+ 2,
8309
+ 1,
8310
+ 0
8311
+ ]
8312
+ },
8313
+ {
8314
+ "law": {
8315
+ "kind": "ideal-opamp",
8316
+ "railHigh": null,
8317
+ "railLow": null,
8318
+ "openLoopGain": 200000
8319
+ },
8320
+ "terminals": [
8321
+ 4,
8322
+ 5,
8323
+ 6
8324
+ ]
8325
+ },
8326
+ {
8327
+ "law": {
8328
+ "kind": "ideal-opamp",
8329
+ "railHigh": null,
8330
+ "railLow": null,
8331
+ "openLoopGain": 200000
8332
+ },
8333
+ "terminals": [
8334
+ 9,
8335
+ 8,
8336
+ 7
8337
+ ]
8338
+ },
8339
+ {
8340
+ "law": {
8341
+ "kind": "ideal-opamp",
8342
+ "railHigh": null,
8343
+ "railLow": null,
8344
+ "openLoopGain": 200000
8345
+ },
8346
+ "terminals": [
8347
+ 11,
8348
+ 12,
8349
+ 13
8350
+ ]
8351
+ }
8352
+ ]
8353
+ }
8354
+ },
8355
+ // Wave 2 (JED): TI CD4016B quad bilateral switch. Its own entry (NOT an alias of the
8356
+ // CD4066 entry above): the 4016 is the high-Ron predecessor and the 4066 its
8357
+ // pin-compatible low-Ron replacement (onsemi MC14066B: 'designed to be pin-for-pin
8358
+ // compatible with the MC14016B, but has much lower ON resistance'; ST HCF4066B: 'pin
8359
+ // for pin compatible with HCF4016B, but exhibits a much lower ON resistance').
8360
+ // On-resistance from TI CD4016B SCHS026D Rev. E (Aug 2024), Features p1: '280 Ohm
8361
+ // typical on-state resistance for 15 V operation' (switch matching 'within 10 Ohm typ
8362
+ // over 15 V signal-input range'). The 4.5 table's at-rail TYP at 15 V is 200 Ohm
8363
+ // (MAX 400; mid-supply worst-case MAX-only 850) -- the 280 headline is the over-range
8364
+ // figure and is the transcribed value. offOhms/thresholdVolts repeat the 4066 entry
8365
+ // (not newly cited). Only the ids the TI sheet owns: the unbuffered CD4016, HEF/HCF/MC
8366
+ // variants are not covered (Nexperia's HEF4016B specs a different test: 115 Ohm
8367
+ // peak-typ at 15 V, 100 uA, sharing no figure with this row).
8368
+ {
8369
+ "partIds": [
8370
+ "4016B",
8371
+ "CD4016B"
8372
+ ],
8373
+ "declaredTypes": [],
8374
+ "terminalRoleGroups": [
8375
+ [
8376
+ "switch_a",
8377
+ "in",
8378
+ "a",
8379
+ "sig_in",
8380
+ "inout1",
8381
+ "1in/out",
8382
+ "in/out"
8383
+ ],
8384
+ [
8385
+ "switch_b",
8386
+ "out",
8387
+ "b",
8388
+ "sig_out",
8389
+ "inout2",
8390
+ "1out/in",
8391
+ "out/in"
8392
+ ],
8393
+ [
8394
+ "control",
8395
+ "ctrl",
8396
+ "c",
8397
+ "gate",
8398
+ "ctl",
8399
+ "1control"
8400
+ ],
8401
+ [
8402
+ "vcc",
8403
+ "vdd",
8404
+ "vplus",
8405
+ "v+"
8406
+ ],
8407
+ [
8408
+ "vss",
8409
+ "gnd",
8410
+ "vminus",
8411
+ "v-",
8412
+ "ground"
8413
+ ]
8414
+ ],
8415
+ "model": {
8416
+ "kind": "sections",
8417
+ "sections": [
8418
+ {
8419
+ "law": {
8420
+ "kind": "analog-switch",
8421
+ "onOhms": 280,
8422
+ "offOhms": 1000000000,
8423
+ "thresholdVolts": 2
8424
+ },
8425
+ "terminals": [
8426
+ 0,
8427
+ 1,
8428
+ 2
8429
+ ]
8430
+ }
8431
+ ],
8432
+ "pinout": [
8433
+ "switch_a",
8434
+ "switch_b",
8435
+ "control",
8436
+ "vcc",
8437
+ "vss"
8438
+ ]
8439
+ }
8440
+ },
8441
+ // Wave 2 (JED): Cabintech CT3101 BBD clock generator. Its own entry mirroring the
8442
+ // MN3101 above (same 8-pin DIL footprint, same terminal order), NOT an unsourced alias:
8443
+ // the CT3101's own datasheet ('CT3101 BBD Clock Generator - Datasheet', Rev 1,
8444
+ // Aug 2023, Cabintech Global LLC, https://cabintechglobal.com/pdf/CABINTECH_CT3101.pdf)
8445
+ // states it 'is pin-for-pin compatible with the discontinued Panasonic MN3101' and 'can
8446
+ // be used as a drop-in replacement for the MN3101', with the pin table 1 GND, 2 CP1,
8447
+ // 3 Vdd (-3 V to -18 V), 4 CP2, 5 OX3, 6 OX2, 7 OX1, 8 Vgg (14/15 Vdd output).
8448
+ // No quiescentSupplyAmps: the sheet says only 'about 1/2 of MN3101' (no figure).
8449
+ // It is a hybrid module (CD4047-based), not a monolithic second source.
8450
+ {
8451
+ "partIds": [
8452
+ "CT3101"
8453
+ ],
8454
+ "declaredTypes": [],
8455
+ "terminalRoleGroups": [
8456
+ [
8457
+ "gnd",
8458
+ "v-",
8459
+ "vss",
8460
+ "pin1",
8461
+ "gndpin1"
8462
+ ],
8463
+ [
8464
+ "cp1",
8465
+ "clk1",
8466
+ "clock1",
8467
+ "phi1",
8468
+ "pin2",
8469
+ "cp1pin2"
8470
+ ],
8471
+ [
8472
+ "vdd",
8473
+ "v+",
8474
+ "vcc",
8475
+ "pin3",
8476
+ "vddpin3"
8477
+ ],
8478
+ [
8479
+ "cp2",
8480
+ "clk2",
8481
+ "clock2",
8482
+ "phi2",
8483
+ "pin4",
8484
+ "cp2pin4"
8485
+ ],
8486
+ [
8487
+ "ox3",
8488
+ "osc3",
8489
+ "oscctrl",
8490
+ "pin5",
8491
+ "ox3pin5"
8492
+ ],
8493
+ [
8494
+ "ox2",
8495
+ "osc2",
8496
+ "oscout",
8497
+ "pin6",
8498
+ "ox2pin6"
8499
+ ],
8500
+ [
8501
+ "ox1",
8502
+ "osc1",
8503
+ "oscin",
8504
+ "osc",
8505
+ "pin7",
8506
+ "ox1pin7"
8507
+ ],
8508
+ [
8509
+ "vgg",
8510
+ "vggout",
8511
+ "pin8",
8512
+ "vggoutpin8"
8513
+ ]
8514
+ ],
8515
+ "model": {
8516
+ "kind": "sections",
8517
+ "sections": [
8518
+ {
8519
+ "law": {
8520
+ "kind": "clock-driver",
8521
+ "defaultFrequency": 10000
8522
+ },
8523
+ "terminals": [
8524
+ 1,
8525
+ 3,
8526
+ 7,
8527
+ 2,
8528
+ 6,
8529
+ 0
8530
+ ]
8531
+ }
8532
+ ],
8533
+ "pinout": [
8534
+ null,
8535
+ null,
8536
+ null,
8537
+ null,
8538
+ null,
8539
+ null,
8540
+ null,
8541
+ null
8542
+ ]
8543
+ }
8544
+ },
8545
+ ...zenerRegistryRecords.map((record) => zenerLaw(record)),
8546
+ // Wave-1 discrete fill: bjt, diode-forward, mosfet and jfet-channel records become
8547
+ // law entries through the builders above -- one list per class, so the entries cannot
8548
+ // drift from the records. 2SK30A rides the same generic path as every other JFET: its
8549
+ // record carries only gateSaturationCurrent, so the builder gap-fills the class channel
8550
+ // and the stamp is byte-identical to the bespoke mapping this replaces.
8551
+ ...bjtRegistryRecords.map((record) => bjtLawEntry(record)),
8552
+ ...diodeRegistryRecords.map((record) => diodeLawEntry(record)),
8553
+ ...mosfetRegistryRecords.map((record) => fetLawEntry(record, "mosfet")),
8554
+ ...jfetRegistryRecords.map((record) => fetLawEntry(record, "jfet")),
8555
+ ];
8556
+ // --- Op-amp gain-bandwidth product: stamped from the data registry ------------------------------
8557
+ /**
8558
+ * Merge a stamped row's `community`-basis parameters into the entry's provisional arm.
8559
+ * A row whose parameters are all better-sourced adds nothing, and an entry with no
8560
+ * provisional value keeps the field absent rather than gaining an empty one.
8561
+ */
8562
+ function withRowProvisional(entry, row) {
8563
+ const merged = [...(entry.provisionalParams ?? [])];
8564
+ row.params.forEach((param, index) => {
8565
+ if (param?.basis !== "community" || typeof param.url !== "string")
8566
+ return;
8567
+ const name = row.names[index];
8568
+ if (!merged.some((held) => held.name === name)) {
8569
+ merged.push({ name, url: param.url });
8570
+ }
8571
+ });
8572
+ return merged.length === 0 ? {} : { provisionalParams: merged };
8573
+ }
8574
+ //
8575
+ // The figures live in `registry-data/opamp.ts` as cited values (datasheet TYP rows, quoted).
8576
+ // This pass stamps each record's product onto the op-amp sections of exactly the ids it
8577
+ // covers. An entry whose ids have different products (or none) is split into one entry per
8578
+ // product, keeping every other field, so no id changes behaviour except by gaining its own
8579
+ // figure.
8580
+ //
8581
+ // Loud on any mismatch, as before: a record or absent id the catalog holds no op-amp entry
8582
+ // for, an id in both lists, or an id in two records is a table error and throws at load
8583
+ // rather than being silently skipped.
8584
+ function withDatasheetGainBandwidth(base) {
8585
+ const rowOf = new Map();
8586
+ for (const row of opampRegistryRecords) {
8587
+ if (row.gainBandwidthHz === undefined)
8588
+ continue;
8589
+ for (const id of row.partIds) {
8590
+ const folded = foldPartId(id);
8591
+ if (rowOf.has(folded))
8592
+ throw new Error(`gain-bandwidth table names ${id} twice`);
8593
+ rowOf.set(folded, row);
8594
+ }
8595
+ }
8596
+ const absent = new Set();
8597
+ for (const { partIds } of opampGainBandwidthAbsent) {
8598
+ for (const id of partIds) {
8599
+ const folded = foldPartId(id);
8600
+ if (rowOf.has(folded))
8601
+ throw new Error(`${id} is both given a gain-bandwidth figure and listed absent`);
8602
+ absent.add(folded);
8603
+ }
8604
+ }
8605
+ const seen = new Set();
8606
+ const out = [];
8607
+ for (const entry of base) {
8608
+ const sections = entry.model.kind === "sections" ? entry.model.sections : null;
8609
+ const isOpamp = sections !== null && sections.length > 0 && sections.every((section) => section.law.kind === "ideal-opamp");
8610
+ if (!isOpamp || sections === null || entry.model.kind !== "sections") {
8611
+ out.push(entry);
8612
+ continue;
8613
+ }
8614
+ const groups = new Map();
8615
+ for (const id of entry.partIds) {
8616
+ const folded = foldPartId(id);
8617
+ seen.add(folded);
8618
+ const row = rowOf.get(folded) ?? null;
8619
+ const group = groups.get(row);
8620
+ if (group === undefined)
8621
+ groups.set(row, [id]);
8622
+ else
8623
+ group.push(id);
8624
+ }
8625
+ const model = entry.model;
8626
+ for (const [row, partIds] of groups) {
8627
+ if (row === null) {
8628
+ out.push(groups.size === 1 ? entry : { ...entry, partIds });
8629
+ continue;
8630
+ }
8631
+ const hertz = row.gainBandwidthHz?.value;
8632
+ if (hertz === undefined || !(hertz > 0)) {
8633
+ throw new Error(`${partIds.join(",")}: gain-bandwidth record states no positive product`);
8634
+ }
8635
+ out.push({
8636
+ ...entry,
8637
+ partIds,
8638
+ ...withRowProvisional(entry, {
8639
+ params: [row.gainBandwidthHz],
8640
+ names: ["gainBandwidthHz"],
8641
+ }),
8642
+ model: {
8643
+ ...model,
8644
+ sections: model.sections.map((section) => section.law.kind === "ideal-opamp"
8645
+ ? { ...section, law: { ...section.law, gainBandwidthHz: hertz } }
8646
+ : section),
8647
+ },
8648
+ });
8649
+ }
8650
+ }
8651
+ for (const folded of [...rowOf.keys(), ...absent]) {
8652
+ if (!seen.has(folded))
8653
+ throw new Error(`gain-bandwidth table names ${folded}, which no op-amp entry holds`);
8654
+ }
8655
+ return out;
8656
+ }
8657
+ // --- Op-amp output swing: stamped from the data registry -------------------------------------------------
8658
+ //
8659
+ // The same rules as the gain-bandwidth pass above. A record states the drop from each rail at
8660
+ // the printed condition; `translation` is how that pair is carried to a pedal on another supply:
8661
+ // "constant" the printed drops are applied as printed (a headroom independent of the supply);
8662
+ // "proportional" both are multiplied by the op-amp's supply span over `supplyVolts`.
8663
+ // The datasheet states neither: it is the owner's reading, recorded per record, and the table does not
8664
+ // say it is shown right. Only the OP07 reading has a held-out hardware test behind it (T-a1, row 16),
8665
+ // and a test of a rule written first is not a measurement of the rule.
8666
+ /**
8667
+ * The catalog's op-amp entries with each registry record's swing pair stamped onto the sections
8668
+ * of exactly the ids it covers. An entry whose ids have different rows (or none) is split into
8669
+ * one entry per row, keeping every other field, so no id changes behaviour except by gaining
8670
+ * its own pair.
8671
+ *
8672
+ * Loud on any mismatch, as `withDatasheetGainBandwidth` is: a record or absent id the catalog
8673
+ * holds no op-amp entry for, an id in both lists, an id in two records, a base entry that
8674
+ * already carries a swing field (every figure comes through the registry), or a record whose
8675
+ * numbers do not validate (`opampOutputSwingOf`) throws at load.
8676
+ */
8677
+ export function withDatasheetOutputSwing(base, rows = opampRegistryRecords, absentRows = opampOutputSwingAbsent) {
8678
+ const rowOf = new Map();
8679
+ for (const row of rows) {
8680
+ const high = row.outputSwingDropHighVolts;
8681
+ const low = row.outputSwingDropLowVolts;
8682
+ const printed = row.outputSwingPrinted;
8683
+ if (high === undefined && low === undefined && printed === undefined)
8684
+ continue;
8685
+ if (high === undefined || low === undefined || printed === undefined) {
8686
+ throw new Error(`${row.partIds.join(",")}: an op-amp swing record states a half pair (high ${high === undefined ? "absent" : "present"}, low ${low === undefined ? "absent" : "present"}, printed ${printed === undefined ? "absent" : "present"})`);
8687
+ }
8688
+ for (const id of row.partIds) {
8689
+ const folded = foldPartId(id);
8690
+ if (rowOf.has(folded))
8691
+ throw new Error(`output-swing table names ${id} twice`);
8692
+ rowOf.set(folded, row);
8693
+ }
8694
+ }
8695
+ const absent = new Set();
8696
+ for (const { partIds, reason } of absentRows) {
8697
+ if (reason.length === 0)
8698
+ throw new Error(`output-swing absent row for ${partIds.join(",")} states no reason`);
8699
+ for (const id of partIds) {
8700
+ const folded = foldPartId(id);
8701
+ if (rowOf.has(folded))
8702
+ throw new Error(`${id} is both given an output-swing pair and listed absent`);
8703
+ absent.add(folded);
8704
+ }
8705
+ }
8706
+ const seen = new Set();
8707
+ const out = [];
8708
+ for (const entry of base) {
8709
+ const sections = entry.model.kind === "sections" ? entry.model.sections : null;
8710
+ const isOpamp = sections !== null && sections.length > 0 && sections.every((section) => section.law.kind === "ideal-opamp");
8711
+ if (!isOpamp || sections === null || entry.model.kind !== "sections") {
8712
+ out.push(entry);
8713
+ continue;
8714
+ }
8715
+ for (const section of sections) {
8716
+ const law = section.law;
8717
+ if (law.kind === "ideal-opamp" &&
8718
+ (law.outputSwingDropHighVolts !== undefined ||
8719
+ law.outputSwingDropLowVolts !== undefined ||
8720
+ law.outputSwingPrinted !== undefined)) {
8721
+ throw new Error(`${entry.partIds.join(",")}: an op-amp entry states an output swing in place; it must come through the registry`);
8722
+ }
8723
+ }
8724
+ const groups = new Map();
8725
+ for (const id of entry.partIds) {
8726
+ const folded = foldPartId(id);
8727
+ seen.add(folded);
8728
+ const row = rowOf.get(folded) ?? null;
8729
+ const group = groups.get(row);
8730
+ if (group === undefined)
8731
+ groups.set(row, [id]);
8732
+ else
8733
+ group.push(id);
8734
+ }
8735
+ const model = entry.model;
8736
+ for (const [row, partIds] of groups) {
8737
+ if (row === null) {
8738
+ out.push(groups.size === 1 ? entry : { ...entry, partIds });
8739
+ continue;
8740
+ }
8741
+ const high = row.outputSwingDropHighVolts?.value;
8742
+ const low = row.outputSwingDropLowVolts?.value;
8743
+ const printed = row.outputSwingPrinted;
8744
+ if (high === undefined || low === undefined || printed === undefined) {
8745
+ throw new Error(`${partIds.join(",")}: an op-amp swing record states a half pair`);
8746
+ }
8747
+ const stamped = {
8748
+ ...entry,
8749
+ partIds,
8750
+ ...withRowProvisional(entry, {
8751
+ params: [row.outputSwingDropHighVolts, row.outputSwingDropLowVolts],
8752
+ names: ["outputSwingDropHighVolts", "outputSwingDropLowVolts"],
8753
+ }),
8754
+ model: {
8755
+ ...model,
8756
+ sections: model.sections.map((section) => section.law.kind === "ideal-opamp"
8757
+ ? {
8758
+ ...section,
8759
+ law: {
8760
+ ...section.law,
8761
+ outputSwingDropHighVolts: high,
8762
+ outputSwingDropLowVolts: low,
8763
+ outputSwingPrinted: {
8764
+ supplyVolts: printed.supplyVolts,
8765
+ loadOhms: printed.loadOhms,
8766
+ translation: printed.translation,
8767
+ },
8768
+ },
8769
+ }
8770
+ : section),
8771
+ },
8772
+ };
8773
+ // A malformed record (a non-positive figure, an unknown translation) throws here, naming the ids.
8774
+ for (const section of stamped.model.kind === "sections" ? stamped.model.sections : []) {
8775
+ if (section.law.kind === "ideal-opamp")
8776
+ opampOutputSwingOf(section.law, partIds.join(","));
8777
+ }
8778
+ out.push(stamped);
8779
+ }
8780
+ }
8781
+ for (const folded of [...rowOf.keys(), ...absent]) {
8782
+ if (!seen.has(folded))
8783
+ throw new Error(`output-swing table names ${folded}, which no op-amp entry holds`);
8784
+ }
8785
+ return out;
8786
+ }
8787
+ /**
8788
+ * Every part id whose `fet` entry refines the gate term only, folded for comparison.
8789
+ * Derived from the jfet registry records that state NO channel parameter (a record that
8790
+ * states thresholdVolts or transconductance carries a full channel and is not gate-only):
8791
+ * such an entry keeps its avowed class channel, which is never graded against the database's
8792
+ * beta/lambda/Vp the way a transcribed channel would be. Mirrors `zenerCatalogPartIds`.
8793
+ */
8794
+ const GATE_ONLY_EXCLUDED_PARAMS = new Set([
8795
+ "thresholdVolts",
8796
+ "transconductance",
8797
+ "channelLengthModulation",
8798
+ "subthresholdVolts",
8799
+ ]);
8800
+ export const gateOnlyFetPartIds = new Set(jfetRegistryRecords
8801
+ .filter((record) => Object.keys(record.params).every((name) => !GATE_ONLY_EXCLUDED_PARAMS.has(name)))
8802
+ .flatMap((record) => [
8803
+ ...record.partIds,
8804
+ ...(record.aliases ?? []),
8805
+ ])
8806
+ .map((id) => foldPartId(id)));
8807
+ /**
8808
+ * The catalog the corpus-facing scripts inject.
8809
+ */
8810
+ export const pedalPartCatalog = {
8811
+ entries: withDatasheetOutputSwing(withDatasheetGainBandwidth(entries)),
8812
+ };
6712
8813
  //# sourceMappingURL=part-catalog.js.map