@vessel-dsp/compiler 0.0.0-stage → 0.2.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.
- package/README.md +314 -2
- package/dist/bake.d.ts +38 -0
- package/dist/bake.d.ts.map +1 -0
- package/dist/bake.js +101 -0
- package/dist/bake.js.map +1 -0
- package/dist/bbd-clock.d.ts +171 -0
- package/dist/bbd-clock.d.ts.map +1 -0
- package/dist/bbd-clock.js +1750 -0
- package/dist/bbd-clock.js.map +1 -0
- package/dist/block-row.d.ts +51 -0
- package/dist/block-row.d.ts.map +1 -0
- package/dist/block-row.js +17 -0
- package/dist/block-row.js.map +1 -0
- package/dist/bypass.d.ts +3 -0
- package/dist/bypass.d.ts.map +1 -0
- package/dist/bypass.js +228 -0
- package/dist/bypass.js.map +1 -0
- package/dist/compile.d.ts +24 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +344 -0
- package/dist/compile.js.map +1 -0
- package/dist/converter-scale.d.ts +14 -0
- package/dist/converter-scale.d.ts.map +1 -0
- package/dist/converter-scale.js +133 -0
- package/dist/converter-scale.js.map +1 -0
- package/dist/couple.d.ts +28 -0
- package/dist/couple.d.ts.map +1 -0
- package/dist/couple.js +567 -0
- package/dist/couple.js.map +1 -0
- package/dist/dangling-active-terminal.d.ts +47 -0
- package/dist/dangling-active-terminal.d.ts.map +1 -0
- package/dist/dangling-active-terminal.js +542 -0
- package/dist/dangling-active-terminal.js.map +1 -0
- package/dist/declared-program.d.ts +23 -0
- package/dist/declared-program.d.ts.map +1 -0
- package/dist/declared-program.js +387 -0
- package/dist/declared-program.js.map +1 -0
- package/dist/denomination.d.ts +48 -0
- package/dist/denomination.d.ts.map +1 -0
- package/dist/denomination.js +27 -0
- package/dist/denomination.js.map +1 -0
- package/dist/device-laws.d.ts +237 -0
- package/dist/device-laws.d.ts.map +1 -0
- package/dist/device-laws.js +3497 -0
- package/dist/device-laws.js.map +1 -0
- package/dist/diagnostics.d.ts +13 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +19 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/emit.d.ts +8 -0
- package/dist/emit.d.ts.map +1 -0
- package/dist/emit.js +43 -0
- package/dist/emit.js.map +1 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +39 -0
- package/dist/errors.js.map +1 -0
- package/dist/firmware-class.d.ts +69 -0
- package/dist/firmware-class.d.ts.map +1 -0
- package/dist/firmware-class.js +20 -0
- package/dist/firmware-class.js.map +1 -0
- package/dist/fixtures/circuits.d.ts +1721 -0
- package/dist/fixtures/circuits.d.ts.map +1 -0
- package/dist/fixtures/circuits.js +13404 -0
- package/dist/fixtures/circuits.js.map +1 -0
- package/dist/fixtures/expected.d.ts +257 -0
- package/dist/fixtures/expected.d.ts.map +1 -0
- package/dist/fixtures/expected.js +311 -0
- package/dist/fixtures/expected.js.map +1 -0
- package/dist/fixtures/index.d.ts +4 -0
- package/dist/fixtures/index.d.ts.map +1 -0
- package/dist/fixtures/index.js +8 -0
- package/dist/fixtures/index.js.map +1 -0
- package/dist/fixtures/registry.d.ts +52 -0
- package/dist/fixtures/registry.d.ts.map +1 -0
- package/dist/fixtures/registry.js +341 -0
- package/dist/fixtures/registry.js.map +1 -0
- package/dist/generic-tube-fit.d.ts +12 -0
- package/dist/generic-tube-fit.d.ts.map +1 -0
- package/dist/generic-tube-fit.js +45 -0
- package/dist/generic-tube-fit.js.map +1 -0
- package/dist/grounded-clock-supply.d.ts +13 -0
- package/dist/grounded-clock-supply.d.ts.map +1 -0
- package/dist/grounded-clock-supply.js +45 -0
- package/dist/grounded-clock-supply.js.map +1 -0
- package/dist/identify.d.ts +50 -0
- package/dist/identify.d.ts.map +1 -0
- package/dist/identify.js +319 -0
- package/dist/identify.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/inert-controls.d.ts +15 -0
- package/dist/inert-controls.d.ts.map +1 -0
- package/dist/inert-controls.js +266 -0
- package/dist/inert-controls.js.map +1 -0
- package/dist/latch-seed.d.ts +53 -0
- package/dist/latch-seed.d.ts.map +1 -0
- package/dist/latch-seed.js +257 -0
- package/dist/latch-seed.js.map +1 -0
- package/dist/link.d.ts +7 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +270 -0
- package/dist/link.js.map +1 -0
- package/dist/lower.d.ts +79 -0
- package/dist/lower.d.ts.map +1 -0
- package/dist/lower.js +1855 -0
- package/dist/lower.js.map +1 -0
- package/dist/netlist.d.ts +79 -0
- package/dist/netlist.d.ts.map +1 -0
- package/dist/netlist.js +4746 -0
- package/dist/netlist.js.map +1 -0
- package/dist/non-executable-clock-drivers.d.ts +8 -0
- package/dist/non-executable-clock-drivers.d.ts.map +1 -0
- package/dist/non-executable-clock-drivers.js +33 -0
- package/dist/non-executable-clock-drivers.js.map +1 -0
- package/dist/open-category.d.ts +98 -0
- package/dist/open-category.d.ts.map +1 -0
- package/dist/open-category.js +111 -0
- package/dist/open-category.js.map +1 -0
- package/dist/over-driven-node.d.ts +33 -0
- package/dist/over-driven-node.d.ts.map +1 -0
- package/dist/over-driven-node.js +188 -0
- package/dist/over-driven-node.js.map +1 -0
- package/dist/part-catalog.d.ts +7 -0
- package/dist/part-catalog.d.ts.map +1 -0
- package/dist/part-catalog.js +6639 -0
- package/dist/part-catalog.js.map +1 -0
- package/dist/part-number.d.ts +32 -0
- package/dist/part-number.d.ts.map +1 -0
- package/dist/part-number.js +80 -0
- package/dist/part-number.js.map +1 -0
- package/dist/partition.d.ts +7 -0
- package/dist/partition.d.ts.map +1 -0
- package/dist/partition.js +242 -0
- package/dist/partition.js.map +1 -0
- package/dist/port-full-scale.d.ts +70 -0
- package/dist/port-full-scale.d.ts.map +1 -0
- package/dist/port-full-scale.js +235 -0
- package/dist/port-full-scale.js.map +1 -0
- package/dist/pot-orientation.d.ts +114 -0
- package/dist/pot-orientation.d.ts.map +1 -0
- package/dist/pot-orientation.js +551 -0
- package/dist/pot-orientation.js.map +1 -0
- package/dist/power-domain-control.d.ts +40 -0
- package/dist/power-domain-control.d.ts.map +1 -0
- package/dist/power-domain-control.js +142 -0
- package/dist/power-domain-control.js.map +1 -0
- package/dist/proxy-declaration.d.ts +121 -0
- package/dist/proxy-declaration.d.ts.map +1 -0
- package/dist/proxy-declaration.js +50 -0
- package/dist/proxy-declaration.js.map +1 -0
- package/dist/registry.d.ts +323 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +192 -0
- package/dist/registry.js.map +1 -0
- package/dist/sparse-schedule.d.ts +65 -0
- package/dist/sparse-schedule.d.ts.map +1 -0
- package/dist/sparse-schedule.js +421 -0
- package/dist/sparse-schedule.js.map +1 -0
- package/dist/speaker-load.d.ts +38 -0
- package/dist/speaker-load.d.ts.map +1 -0
- package/dist/speaker-load.js +97 -0
- package/dist/speaker-load.js.map +1 -0
- package/dist/stage-coverage.d.ts +21 -0
- package/dist/stage-coverage.d.ts.map +1 -0
- package/dist/stage-coverage.js +124 -0
- package/dist/stage-coverage.js.map +1 -0
- package/dist/stamp-partition.d.ts +314 -0
- package/dist/stamp-partition.d.ts.map +1 -0
- package/dist/stamp-partition.js +472 -0
- package/dist/stamp-partition.js.map +1 -0
- package/dist/supply-reference.d.ts +19 -0
- package/dist/supply-reference.d.ts.map +1 -0
- package/dist/supply-reference.js +66 -0
- package/dist/supply-reference.js.map +1 -0
- package/dist/supply-short.d.ts +32 -0
- package/dist/supply-short.d.ts.map +1 -0
- package/dist/supply-short.js +118 -0
- package/dist/supply-short.js.map +1 -0
- package/dist/supply-stamps.d.ts +51 -0
- package/dist/supply-stamps.d.ts.map +1 -0
- package/dist/supply-stamps.js +363 -0
- package/dist/supply-stamps.js.map +1 -0
- package/dist/terminal-role.d.ts +109 -0
- package/dist/terminal-role.d.ts.map +1 -0
- package/dist/terminal-role.js +46 -0
- package/dist/terminal-role.js.map +1 -0
- package/dist/terminal-roles.d.ts +59 -0
- package/dist/terminal-roles.d.ts.map +1 -0
- package/dist/terminal-roles.js +92 -0
- package/dist/terminal-roles.js.map +1 -0
- package/dist/transformer.d.ts +181 -0
- package/dist/transformer.d.ts.map +1 -0
- package/dist/transformer.js +476 -0
- package/dist/transformer.js.map +1 -0
- package/dist/types.d.ts +3157 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/dist/unbounded-operating-point.d.ts +35 -0
- package/dist/unbounded-operating-point.d.ts.map +1 -0
- package/dist/unbounded-operating-point.js +181 -0
- package/dist/unbounded-operating-point.js.map +1 -0
- package/dist/unexecuted-region.d.ts +3 -0
- package/dist/unexecuted-region.d.ts.map +1 -0
- package/dist/unexecuted-region.js +122 -0
- package/dist/unexecuted-region.js.map +1 -0
- package/dist/unimplemented-control-role.d.ts +3 -0
- package/dist/unimplemented-control-role.d.ts.map +1 -0
- package/dist/unimplemented-control-role.js +61 -0
- package/dist/unimplemented-control-role.js.map +1 -0
- package/dist/unreachable-output.d.ts +3 -0
- package/dist/unreachable-output.d.ts.map +1 -0
- package/dist/unreachable-output.js +97 -0
- package/dist/unreachable-output.js.map +1 -0
- package/dist/unreadable-terminal-role.d.ts +67 -0
- package/dist/unreadable-terminal-role.d.ts.map +1 -0
- package/dist/unreadable-terminal-role.js +409 -0
- package/dist/unreadable-terminal-role.js.map +1 -0
- package/dist/unverified-pinout.d.ts +27 -0
- package/dist/unverified-pinout.d.ts.map +1 -0
- package/dist/unverified-pinout.js +121 -0
- package/dist/unverified-pinout.js.map +1 -0
- package/package.json +75 -5
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
// Which end of a potentiometer is the quiet one, split out of `lower.ts` (R7, 2026-09-01).
|
|
2
|
+
//
|
|
3
|
+
// A pot's law is a track resistance split by its 0..1 position, so lowering needs to know
|
|
4
|
+
// which declared end the position counts *from*.
|
|
5
|
+
//
|
|
6
|
+
// **The format CAN state it, and since 2026-09-22 the declaration is read first.**
|
|
7
|
+
// `@vessel-dsp/core` owns `PotentiometerTerminalRole = ccw | wiper | cw` -- rotation, not
|
|
8
|
+
// position, so it survives mirroring -- and its resolver refuses rather than guessing, saying
|
|
9
|
+
// of an incomplete one: *"the source does not carry the sweep direction. Do not infer it."*
|
|
10
|
+
// This module inferred it for every pot regardless, including the 15 in the corpus that
|
|
11
|
+
// declare both ends, and disagreed with 4 of them. See `declaredEnds`.
|
|
12
|
+
//
|
|
13
|
+
// The inference remains, because 348 of the corpus's 363 pots declare `end` -- core's
|
|
14
|
+
// deliberately ambiguous token -- and something has to orient them: a BFS over DC-conducting
|
|
15
|
+
// devices measures each end's distance to ground and to an op-amp input, and the closer-to-
|
|
16
|
+
// ground end is the quiet one. That is inference over inference, and every one of those 348 is
|
|
17
|
+
// a packet backfill waiting to retire a branch of it -- see R1 in
|
|
18
|
+
// `thoughts/shared/plans/archive/2026-08-31-v2-complexity-reduction-plan.md`.
|
|
19
|
+
import { LoweringError } from "./errors.js";
|
|
20
|
+
import { dcConductingKinds } from "./netlist.js";
|
|
21
|
+
import { GROUND } from "./types.js";
|
|
22
|
+
/**
|
|
23
|
+
* The one terminal index whose **declared** role is `wiper`, or null when the terminals disagree.
|
|
24
|
+
*
|
|
25
|
+
* Several terminals may declare it and still be unambiguous: `boss-dm-3`'s `VR1` writes
|
|
26
|
+
* `lug1,lug2,lug3,wiper` with `lug2` and `wiper` on one node -- the same physical point named
|
|
27
|
+
* twice -- so what matters is that every `wiper` resolves to the same node, not that only one
|
|
28
|
+
* terminal says so. Two *different* nodes claiming it is a document this stage cannot read.
|
|
29
|
+
*/
|
|
30
|
+
function declaredWiperIndex(device) {
|
|
31
|
+
const declared = device.identity.declaredTerminalRoles;
|
|
32
|
+
const indices = declared.flatMap((role, index) => role === "wiper" ? [index] : []);
|
|
33
|
+
if (indices.length === 0) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
const nodes = new Set(indices.map((index) => device.nodes[index]));
|
|
37
|
+
return nodes.size === 1 ? (indices[0] ?? null) : null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The two nodes a device declares as its `ccw` and `cw` ends, or null when it does not say.
|
|
41
|
+
*
|
|
42
|
+
* **`@vessel-dsp/core` owns this vocabulary and everything below is the guess it replaces.**
|
|
43
|
+
* `PotentiometerTerminalRole` is `ccw | wiper | cw`, rotation and not position, so it survives
|
|
44
|
+
* mirroring and board rotation; the resolver refuses rather than completes, and its own doc
|
|
45
|
+
* comment says of an incomplete resolution: *"False means the source does not carry the sweep
|
|
46
|
+
* direction. Do not infer it."* Inferring it is exactly what this file did, for every pot,
|
|
47
|
+
* including the ones that said.
|
|
48
|
+
*
|
|
49
|
+
* Measured over the corpus 2026-09-22: **363 pots, 15 declare both ends, 348 declare
|
|
50
|
+
* `end`** -- core's ambiguous token, which resolves to no role by design. So the quiet-distance
|
|
51
|
+
* inference below is still what almost every pot gets, and that is the *second* half of the
|
|
52
|
+
* typed-source-evidence test: the source can state the fact and does not, which makes those 348
|
|
53
|
+
* a packet backfill in `vessel-dsp/artifacts` rather than anything to fix here.
|
|
54
|
+
*
|
|
55
|
+
* On the 15 that do declare, the inference **agreed on 11 and disagreed on 4** --
|
|
56
|
+
* `electro-harmonix-frequency-analyzer`'s `P1_BLEND` and `P2_FINE_TUNE`, and
|
|
57
|
+
* `moogerfooger-mf-102`'s `P1` and `VR2`. Those four knobs swept backwards, and the packets had
|
|
58
|
+
* said so all along. A conflict is resolved in favour of the declaration, because the
|
|
59
|
+
* alternative is this repository overruling a typed source fact with a circuit guess.
|
|
60
|
+
*
|
|
61
|
+
* Both ends must resolve to one node each and the two must differ; anything else is not a
|
|
62
|
+
* declaration this stage can read, and falls through to the inference.
|
|
63
|
+
*/
|
|
64
|
+
function declaredEnds(device) {
|
|
65
|
+
const declared = device.identity.declaredTerminalRoles;
|
|
66
|
+
const nodesFor = (role) => new Set(declared.flatMap((declaredRole, index) => declaredRole === role ? [device.nodes[index]] : []));
|
|
67
|
+
const ccw = nodesFor("ccw");
|
|
68
|
+
const cw = nodesFor("cw");
|
|
69
|
+
if (ccw.size !== 1 || cw.size !== 1) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const [ccwNode] = [...ccw];
|
|
73
|
+
const [cwNode] = [...cw];
|
|
74
|
+
if (ccwNode === undefined || cwNode === undefined || ccwNode === cwNode) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return [ccwNode, cwNode];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Adjacency among nodes joined by a DC-conducting device -- the closed `dcConductingKinds`
|
|
81
|
+
* vocabulary (resistor, potentiometer, rheostat, inductor, switch, selector) `netlist.ts`'s
|
|
82
|
+
* `voltagePortRails` also uses. Shared by every quiet-distance BFS in this file, computed once
|
|
83
|
+
* per netlist rather than once per seed set: the graph does not change between them.
|
|
84
|
+
*/
|
|
85
|
+
function dcConductingAdjacency(netlist,
|
|
86
|
+
/**
|
|
87
|
+
* Kinds to conduct in addition to `dcConductingKinds`. Empty by default (pure DC graph,
|
|
88
|
+
* the behaviour every existing caller relies on); a caller asking an AC-connectivity
|
|
89
|
+
* question passes the kinds it wants added, e.g. `"capacitor"`.
|
|
90
|
+
*/
|
|
91
|
+
extraKinds = new Set()) {
|
|
92
|
+
const conducts = (kind) => dcConductingKinds.has(kind) || extraKinds.has(kind);
|
|
93
|
+
const adjacency = new Map();
|
|
94
|
+
for (const device of netlist.devices) {
|
|
95
|
+
if (!conducts(device.kind)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
for (let i = 0; i < device.nodes.length; i += 1) {
|
|
99
|
+
for (let j = i + 1; j < device.nodes.length; j += 1) {
|
|
100
|
+
const a = device.nodes[i];
|
|
101
|
+
const b = device.nodes[j];
|
|
102
|
+
if (a === b) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
adjacency.set(a, [...(adjacency.get(a) ?? []), b]);
|
|
106
|
+
adjacency.set(b, [...(adjacency.get(b) ?? []), a]);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return adjacency;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Multi-source BFS over a DC-conducting adjacency graph: every seed starts at distance 0, so the
|
|
114
|
+
* result is each reachable node's distance to its *nearest* seed.
|
|
115
|
+
*/
|
|
116
|
+
function bfsDistances(adjacency, seeds) {
|
|
117
|
+
const distance = new Map();
|
|
118
|
+
const queue = [];
|
|
119
|
+
for (const node of seeds) {
|
|
120
|
+
distance.set(node, 0);
|
|
121
|
+
queue.push(node);
|
|
122
|
+
}
|
|
123
|
+
let head = 0;
|
|
124
|
+
while (head < queue.length) {
|
|
125
|
+
const at = queue[head];
|
|
126
|
+
head += 1;
|
|
127
|
+
const atDistance = distance.get(at);
|
|
128
|
+
for (const next of adjacency.get(at) ?? []) {
|
|
129
|
+
if (!distance.has(next)) {
|
|
130
|
+
distance.set(next, atDistance + 1);
|
|
131
|
+
queue.push(next);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return distance;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* How far each node sits, in hops through DC-conducting devices, from a quiet DC reference --
|
|
139
|
+
* ground, or a supply's own terminal.
|
|
140
|
+
*
|
|
141
|
+
* This is the connectivity evidence a pot's orientation needs, in the same closed vocabulary
|
|
142
|
+
* `netlist.ts`'s `voltagePortRails` already uses to ask a related question ("is this node's
|
|
143
|
+
* potential already generated"): `dcConductingKinds` -- resistor, potentiometer, rheostat,
|
|
144
|
+
* inductor, switch, selector -- conduct a steady current, and nothing else in this set does, so
|
|
145
|
+
* an active device (a transistor, an op-amp, a diode) is never crossed. A quiet reference is
|
|
146
|
+
* ground itself or any terminal of a `voltage-source` or `rail` device -- **not** an op-amp
|
|
147
|
+
* output, which is actively driven with the signal and is the opposite of quiet.
|
|
148
|
+
*
|
|
149
|
+
* Computed once per netlist and passed down, not per pot: `boss-ds-1` alone lowers 82 regions,
|
|
150
|
+
* and the graph does not change between them.
|
|
151
|
+
*/
|
|
152
|
+
export function quietDistances(netlist) {
|
|
153
|
+
const quietReferences = new Set([GROUND]);
|
|
154
|
+
for (const device of netlist.devices) {
|
|
155
|
+
if (device.kind === "voltage-source" || device.kind === "rail") {
|
|
156
|
+
for (const node of device.nodes) {
|
|
157
|
+
quietReferences.add(node);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return bfsDistances(dcConductingAdjacency(netlist), quietReferences);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* An op-amp input seeds the quiet-distance BFS this file uses for pot orientation, and it is read
|
|
165
|
+
* from the declared role rather than from a folded terminal name.
|
|
166
|
+
*
|
|
167
|
+
* The name vocabulary this replaced had to be kept in step with
|
|
168
|
+
* `scripts/lib/source-to-spice.ts`, which uses the same seeds for the from-source parity deck: a
|
|
169
|
+
* stale copy there makes the deck disagree with the pipeline about which end of a pot is quiet,
|
|
170
|
+
* and the parity report reads that tool artefact as a source defect. One field, read the same way
|
|
171
|
+
* in both, removes that coupling.
|
|
172
|
+
*/
|
|
173
|
+
function isOpampInput(role) {
|
|
174
|
+
return role === "nonInverting" || role === "inverting";
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* How far each node sits, in hops through DC-conducting devices, from an op-amp's own input
|
|
178
|
+
* terminal.
|
|
179
|
+
*
|
|
180
|
+
* A second, narrower quiet-reference class than `quietDistances`, kept separate rather than
|
|
181
|
+
* folded into it. An op-amp input under closed-loop negative feedback sits at a virtual
|
|
182
|
+
* reference regardless of the feedback network's own resistances -- a property of the feedback
|
|
183
|
+
* *topology*, not of any one element's value -- so seeding it is not the same evidence-standing-
|
|
184
|
+
* on-itself risk `netlist.ts`'s `generated()` was found to have (there, an op-amp's *output* was
|
|
185
|
+
* treated as an independent reference, which a feedback path can make circular: the output's own
|
|
186
|
+
* potential depends on the input it would be certifying). Measured 2026-08-14 (the rheostat-nine
|
|
187
|
+
* census, `docs/todo-archive/2026-08-14-the-rheostat-nine.md`): applied unconditionally, this
|
|
188
|
+
* evidence moves 24 pots, 15 of them reversing an orientation `quietDistances` alone already
|
|
189
|
+
* decided -- including undoing a same-day correction and overturning a pot where declaration
|
|
190
|
+
* order and connectivity previously agreed unanimously. `potTerminals` below therefore only
|
|
191
|
+
* consults this map when `quietDistances` leaves a pot's two ends **fully unreachable** (both
|
|
192
|
+
* infinite) -- never to override an already-finite decision, even a finite tie. Scoped that way,
|
|
193
|
+
* it moves exactly 6 pots and reverses none.
|
|
194
|
+
*/
|
|
195
|
+
export function opampInputDistances(netlist) {
|
|
196
|
+
const seeds = new Set();
|
|
197
|
+
for (const device of netlist.devices) {
|
|
198
|
+
if (device.kind !== "opamp") {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
device.identity.declaredTerminalRoles.forEach((role, index) => {
|
|
202
|
+
if (isOpampInput(role)) {
|
|
203
|
+
const node = device.nodes[index];
|
|
204
|
+
if (node !== undefined) {
|
|
205
|
+
seeds.add(node);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
return bfsDistances(dcConductingAdjacency(netlist), seeds);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* How far each node sits, in hops through DC-conducting devices, from an op-amp's declared
|
|
214
|
+
* **output** terminal.
|
|
215
|
+
*
|
|
216
|
+
* The companion of `opampInputDistances` for the negative-feedback gain-stage class: a rheostat
|
|
217
|
+
* wired from an op-amp's declared output, through its track, to the same op-amp's declared
|
|
218
|
+
* inverting input is a gain-setting feedback element, and for that class a rising control must
|
|
219
|
+
* mean *more* feedback resistance (non-inverting gain is `1 + Rf/Rg`, so more `Rf` is more
|
|
220
|
+
* gain). `boss-os-2`'s `DRIVE` sits exactly there on both its 270 k sections: the wiper shares
|
|
221
|
+
* its node with the `output` terminal, the free end reaches the `inverting` terminal through a
|
|
222
|
+
* single series resistor, and the whole pocket is DC-isolated from ground, so
|
|
223
|
+
* `quietDistances` has no claim and the `opampInputDistances` tie above falls through to
|
|
224
|
+
* declaration order -- which oriented `VR3b` so its live resistance *fell* as the knob rose,
|
|
225
|
+
* running the pedal's Drive backwards (rendered: 6.3e-1 rms at 0.0 against 2.9e-1 at 1.0).
|
|
226
|
+
* The output-side distance below is the discriminating typed fact: which end the wiper is
|
|
227
|
+
* strapped to relative to the declared `output` role, not how close either end happens to sit
|
|
228
|
+
* to an input.
|
|
229
|
+
*/
|
|
230
|
+
export function opampOutputDistances(netlist) {
|
|
231
|
+
const seeds = new Set();
|
|
232
|
+
for (const device of netlist.devices) {
|
|
233
|
+
if (device.kind !== "opamp") {
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
device.identity.declaredTerminalRoles.forEach((role, index) => {
|
|
237
|
+
if (role === "output") {
|
|
238
|
+
const node = device.nodes[index];
|
|
239
|
+
if (node !== undefined) {
|
|
240
|
+
seeds.add(node);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
return bfsDistances(dcConductingAdjacency(netlist), seeds);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* The set of nodes that are a declared op-amp **inverting** input, read from the declared
|
|
249
|
+
* `inverting` role rather than a folded terminal name (the same closed-vocabulary read
|
|
250
|
+
* `opampInputDistances` uses for its seeds, narrowed to the inverting half).
|
|
251
|
+
*
|
|
252
|
+
* Kept as a node set, not a distance map, because the one consumer below asks a yes/no
|
|
253
|
+
* question -- "does this pot's wiper sit on an inverting input?" -- not "how far is it from
|
|
254
|
+
* one". `opampInputDistances` cannot answer that: it seeds from *both* input roles, so a zero
|
|
255
|
+
* there does not say which input a node sits on.
|
|
256
|
+
*/
|
|
257
|
+
export function opampInvertingInputNodes(netlist) {
|
|
258
|
+
const nodes = new Set();
|
|
259
|
+
for (const device of netlist.devices) {
|
|
260
|
+
if (device.kind !== "opamp") {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
device.identity.declaredTerminalRoles.forEach((role, index) => {
|
|
264
|
+
if (role === "inverting") {
|
|
265
|
+
const node = device.nodes[index];
|
|
266
|
+
if (node !== undefined) {
|
|
267
|
+
nodes.add(node);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
return nodes;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* How far each node sits, in hops through the **AC** graph (DC-conducting devices *plus*
|
|
276
|
+
* capacitors), from an op-amp's declared output terminal.
|
|
277
|
+
*
|
|
278
|
+
* The companion of `opampOutputDistances` for the one shape it cannot reach: a non-inverting
|
|
279
|
+
* gain pot whose wiper sits *on* the op-amp's declared inverting input. For that class the wiper
|
|
280
|
+
* is the DC hub -- both track ends reach ground, the inverting input, and the output through the
|
|
281
|
+
* wiper in exactly the same number of DC hops -- so every DC distance map this file has is a
|
|
282
|
+
* tie and is silent about which end the live half is. Capacitors are the one passive element
|
|
283
|
+
* that separates the two ends in that shape (the output couples to one end, ground to the
|
|
284
|
+
* other), so the discrimination has to follow them. `boss-ds-1`'s `VR1` is the corpus's sole
|
|
285
|
+
* instance: its two ends are a 2/2 DC-quiet tie, the wiper is on the inverting input, and
|
|
286
|
+
* only `AC` separates the output-coupled end (one cap, `C6`, from the output) from the
|
|
287
|
+
* ground-coupled end.
|
|
288
|
+
*
|
|
289
|
+
* Computed over the AC graph on purpose, not by adding a capacitor-aware variant to the DC
|
|
290
|
+
* maps: the DC maps are a validated "moves exactly 6 pots, reverses none" instrument and must
|
|
291
|
+
* stay DC, and folding a capacitor into them would change what every existing caller reads.
|
|
292
|
+
*/
|
|
293
|
+
export function opampOutputAcDistances(netlist) {
|
|
294
|
+
const seeds = new Set();
|
|
295
|
+
for (const device of netlist.devices) {
|
|
296
|
+
if (device.kind !== "opamp") {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
device.identity.declaredTerminalRoles.forEach((role, index) => {
|
|
300
|
+
if (role === "output") {
|
|
301
|
+
const node = device.nodes[index];
|
|
302
|
+
if (node !== undefined) {
|
|
303
|
+
seeds.add(node);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
return bfsDistances(dcConductingAdjacency(netlist, new Set(["capacitor"])), seeds);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Control roles that name a time constant rather than a level.
|
|
312
|
+
*
|
|
313
|
+
* `potTerminals`' quiet-distance rule exists to make a rising control mean "louder", which is
|
|
314
|
+
* the right goal for a gain or volume pot and an empty one for a pot setting an RC. Measured on
|
|
315
|
+
* `boss-nf-1-noise-gate`'s `DECAY`: the two orientations render the *same* output level at both
|
|
316
|
+
* ends of the sweep, so the loudness evidence says nothing and the rule picks arbitrarily -- it
|
|
317
|
+
* picked the side that runs the knob backwards, giving a 240 ms release at full clockwise where
|
|
318
|
+
* the ET-45C service note's ADJUSTMENT block sets DECAY full clockwise for its 1.5-2 s decay.
|
|
319
|
+
*
|
|
320
|
+
* **Why a positive list and not "every rheostat".** Letting declaration order win for all 64 of
|
|
321
|
+
* the corpus's rheostat-shaped pots was measured and is worse: it inverts three knobs that are
|
|
322
|
+
* currently right to fix one that is wrong. `boss-fa-1`'s `VOLUME` goes from 6.4e-7 -> 9.3e-1
|
|
323
|
+
* across its sweep today and 9.3e-1 -> 6.4e-7 under that change, so turning the volume up would
|
|
324
|
+
* mute the pedal; `bk-butler-tube-driver`'s `TUBE_DRIVE` and `boss-od-1`'s `OVER_DRIVE` invert
|
|
325
|
+
* the same way. The quiet-distance rule is load-bearing for those and must keep them.
|
|
326
|
+
*
|
|
327
|
+
* So the exemption is scoped to roles that name a time constant, where "louder" is not what the
|
|
328
|
+
* knob is for. Roles are free text in this corpus and most pots declare none at all -- including
|
|
329
|
+
* `TUBE_DRIVE` and every Marshall tone pot -- so an unrecognised or absent role keeps today's
|
|
330
|
+
* behaviour, which is the safe default.
|
|
331
|
+
*/
|
|
332
|
+
const TIME_CONSTANT_CONTROL_ROLES = new Set([
|
|
333
|
+
"decay",
|
|
334
|
+
"attack",
|
|
335
|
+
"release",
|
|
336
|
+
"time",
|
|
337
|
+
"d.time",
|
|
338
|
+
"r.time",
|
|
339
|
+
"rate",
|
|
340
|
+
"lfo-rate",
|
|
341
|
+
"speed",
|
|
342
|
+
"rise",
|
|
343
|
+
"rise/fall",
|
|
344
|
+
]);
|
|
345
|
+
function namesATimeConstant(role) {
|
|
346
|
+
return role == null
|
|
347
|
+
? false
|
|
348
|
+
: TIME_CONSTANT_CONTROL_ROLES.has(role.trim().toLowerCase());
|
|
349
|
+
}
|
|
350
|
+
export function potTerminals(device, quietDistance, opampInputDistance,
|
|
351
|
+
/**
|
|
352
|
+
* Distance to an op-amp's declared output terminal, for the negative-feedback
|
|
353
|
+
* gain-stage tie below. Empty by default: a call site that has no netlist in hand
|
|
354
|
+
* (the inert-control and operating-point screens) passes nothing and gets today's
|
|
355
|
+
* behaviour exactly, as with the other two maps.
|
|
356
|
+
*/
|
|
357
|
+
opampOutputDistance = new Map(),
|
|
358
|
+
/**
|
|
359
|
+
* The declared role of the control that varies this pot, when the document states one.
|
|
360
|
+
* Only consulted for the rheostat exemption below; every other path ignores it, and a
|
|
361
|
+
* call site that has no role passes `null` and gets today's behaviour exactly.
|
|
362
|
+
*/
|
|
363
|
+
controlRole = null,
|
|
364
|
+
/**
|
|
365
|
+
* Nodes that are a declared op-amp inverting input, for the negative-feedback gain-pot
|
|
366
|
+
* tie below. Empty by default: a call site with no netlist in hand (the inert-control and
|
|
367
|
+
* operating-point screens) passes nothing and gets today's behaviour exactly, as with the
|
|
368
|
+
* other evidence maps.
|
|
369
|
+
*/
|
|
370
|
+
invertingInputNodes = new Set(),
|
|
371
|
+
/**
|
|
372
|
+
* AC distance (DC + capacitors) to an op-amp's declared output, for the same gain-pot
|
|
373
|
+
* tie. Empty by default for the same reason.
|
|
374
|
+
*/
|
|
375
|
+
acOutputDistance = new Map()) {
|
|
376
|
+
// Find the wiper by role, not by sitting in the middle. 81 of the corpus's 331
|
|
377
|
+
// three-terminal pots declare it first or last -- `anode,cathode,wiper` alone is
|
|
378
|
+
// 64 -- and taking position 2 regardless makes whatever is there the wiper. When
|
|
379
|
+
// that is the grounded end, both halves of the track short to ground, the output
|
|
380
|
+
// is cut off from the input, and the pedal renders **exact silence**.
|
|
381
|
+
// **Read from the declared role, and refuse rather than fall back.** This matched a folded
|
|
382
|
+
// terminal *name* against `{wiper, wipe, w}` and took declaration order when nothing matched,
|
|
383
|
+
// which is the silent half of the role-vocabulary problem: a document naming its wiper
|
|
384
|
+
// anything else got position 2 regardless, and nothing said so. Measured across the corpus
|
|
385
|
+
// after the roles were backfilled: 523 of 523 pots reaching this resolve by declared role, 0
|
|
386
|
+
// declare none, 0 are ambiguous -- so the positional path had no remaining legitimate case
|
|
387
|
+
// and is a refusal instead.
|
|
388
|
+
const wiperIndex = declaredWiperIndex(device);
|
|
389
|
+
if (wiperIndex === null) {
|
|
390
|
+
const declared = device.identity.declaredTerminalRoles;
|
|
391
|
+
throw new LoweringError(`potentiometer ${device.id} does not identify its wiper: ${declared.some((role) => role === "wiper")
|
|
392
|
+
? "several terminals declare `wiper` on different nodes"
|
|
393
|
+
: "no terminal declares `wiper`"} (roles [${declared.join(", ")}]) -- and taking the middle terminal instead shorts ` +
|
|
394
|
+
"both halves of the track to whatever sits there, which renders exact silence", device.id);
|
|
395
|
+
}
|
|
396
|
+
const ends = device.nodes.filter((_, index) => index !== wiperIndex);
|
|
397
|
+
const wiper = device.nodes[wiperIndex];
|
|
398
|
+
const [declaredFirst, declaredSecond] = ends;
|
|
399
|
+
if (declaredFirst === undefined ||
|
|
400
|
+
wiper === undefined ||
|
|
401
|
+
declaredSecond === undefined) {
|
|
402
|
+
throw new LoweringError(`potentiometer ${device.id} needs three terminals`, device.id);
|
|
403
|
+
}
|
|
404
|
+
// The stamps below put `end1`-to-wiper on the side whose share *shrinks* toward the wiper's
|
|
405
|
+
// travel and `end2`-to-wiper on the side whose share *grows* with it (see `lowerRegion`'s
|
|
406
|
+
// `controlled-conductance` case and the runtime's identical `fraction` comment) -- so `end2`
|
|
407
|
+
// has to be the electrically quiet end for a rising control position to mean "louder",
|
|
408
|
+
// which is a fact about the circuit, not about which terminal a schematic happened to list
|
|
409
|
+
// first.
|
|
410
|
+
//
|
|
411
|
+
// Evidence, never declaration order: whichever declared end sits closer to a quiet DC
|
|
412
|
+
// reference (ground, or a supply's own terminal) becomes `end2`. A tie -- both equidistant,
|
|
413
|
+
// including both unreachable -- is not evidence either way, so it falls back to declaration
|
|
414
|
+
// order exactly as before, which is what the corpus's other pots that already work this way
|
|
415
|
+
// (248 of them, before this change) rely on unchanged.
|
|
416
|
+
// **A declaration outranks every inference below.** `end2` is the end the wiper sits at when
|
|
417
|
+
// the control reads 0 -- it takes the `lower` stamp, whose share is `fraction` itself, so its
|
|
418
|
+
// half collapses to the residual at position 0 -- and position 0 is the shaft fully
|
|
419
|
+
// counter-clockwise. So `end2` is the `ccw` lug whenever the document names it, and nothing
|
|
420
|
+
// after this point is consulted. See `declaredEnds`.
|
|
421
|
+
//
|
|
422
|
+
// **Including a rheostat**, whose wiper is strapped to one of its own declared ends. That
|
|
423
|
+
// shape has no divider and the existing fallback for it reads the *control's* free-text role
|
|
424
|
+
// against a spelling table (`TIME_CONSTANT_CONTROL_ROLES`), which is prose matching on the
|
|
425
|
+
// lowering spine. A declared rotation is the typed fact that table stands in for, so it wins
|
|
426
|
+
// here too, and the arithmetic comes out right either way: with the wiper on `cw`, `end2` is
|
|
427
|
+
// `ccw` and the live half takes `share = fraction`, so its resistance grows as the shaft
|
|
428
|
+
// turns clockwise; with the wiper on `ccw`, the live half is `end1`'s and takes
|
|
429
|
+
// `1 - fraction`, and it shrinks. Both are what the shaft does.
|
|
430
|
+
// `electro-harmonix-frequency-analyzer`'s `P2_FINE_TUNE` and `moogerfooger-mf-102`'s `P1`
|
|
431
|
+
// are the corpus's two, and both ran backwards under the inference.
|
|
432
|
+
const declaredRotation = declaredEnds(device);
|
|
433
|
+
if (declaredRotation !== null) {
|
|
434
|
+
const [ccwNode, cwNode] = declaredRotation;
|
|
435
|
+
return [cwNode, wiper, ccwNode];
|
|
436
|
+
}
|
|
437
|
+
// A rheostat -- wiper strapped to one of its own ends -- has no divider to orient, and when
|
|
438
|
+
// its control names a time constant the quiet-distance rule below has no loudness signal to
|
|
439
|
+
// read. Which declared end the wiper is strapped to is then the only evidence there is, so
|
|
440
|
+
// declaration order wins here and nowhere else. See `TIME_CONSTANT_CONTROL_ROLES`.
|
|
441
|
+
if ((wiper === declaredFirst || wiper === declaredSecond) &&
|
|
442
|
+
namesATimeConstant(controlRole)) {
|
|
443
|
+
return [declaredFirst, wiper, declaredSecond];
|
|
444
|
+
}
|
|
445
|
+
const firstDistance = quietDistance.get(declaredFirst) ?? Number.POSITIVE_INFINITY;
|
|
446
|
+
const secondDistance = quietDistance.get(declaredSecond) ?? Number.POSITIVE_INFINITY;
|
|
447
|
+
if (Number.isFinite(firstDistance) || Number.isFinite(secondDistance)) {
|
|
448
|
+
if (firstDistance < secondDistance) {
|
|
449
|
+
return [declaredSecond, wiper, declaredFirst];
|
|
450
|
+
}
|
|
451
|
+
if (secondDistance < firstDistance) {
|
|
452
|
+
return [declaredFirst, wiper, declaredSecond];
|
|
453
|
+
}
|
|
454
|
+
// A finite tie (both ends the same non-infinite distance, e.g. `boss-ph-1r`'s `VR2` at
|
|
455
|
+
// 3/3) is still real evidence that *something* about the graph already reaches both ends
|
|
456
|
+
// equally -- narrower and more reliable than "nothing reaches either", so it is left on
|
|
457
|
+
// declaration order rather than handed to the op-amp-input fallback below, which is
|
|
458
|
+
// scoped to the fully-unreachable case only.
|
|
459
|
+
//
|
|
460
|
+
// One exception, and only one: a non-inverting gain pot whose wiper sits on the op-amp's
|
|
461
|
+
// declared inverting input. For that shape the wiper is the DC hub, so the quiet, input,
|
|
462
|
+
// and output DC distances all tie exactly as here, and every DC map this file has is
|
|
463
|
+
// silent about which end the live half is -- declaration order decides, and it orients
|
|
464
|
+
// the live half backwards when the schematic lists the output-coupled end first.
|
|
465
|
+
// `boss-ds-1`'s `VR1` is the corpus's sole instance: rendered 3.0e-2 rms at 0.0 against
|
|
466
|
+
// 1.9e-2 at 1.0, its Drive running backwards. The one typed fact that separates the two
|
|
467
|
+
// ends there is AC connectivity -- the output couples (through a capacitor) to one end,
|
|
468
|
+
// ground to the other -- so when the wiper is on a declared inverting input and the ends
|
|
469
|
+
// differ in AC output distance, the end nearer the output becomes `end2`: its live half
|
|
470
|
+
// is the `lower` stamp (`share = fraction`), so a rising control adds feedback resistance
|
|
471
|
+
// (non-inverting gain is `1 + Rf/Rg`) and means more gain. A plain tie with no such
|
|
472
|
+
// discrimination stays on declaration order, as before; a tie where the wiper is not on
|
|
473
|
+
// an inverting input is left alone here and falls to its own existing evidence.
|
|
474
|
+
if (invertingInputNodes.has(wiper)) {
|
|
475
|
+
const acFirst = acOutputDistance.get(declaredFirst) ?? Number.POSITIVE_INFINITY;
|
|
476
|
+
const acSecond = acOutputDistance.get(declaredSecond) ?? Number.POSITIVE_INFINITY;
|
|
477
|
+
if (acFirst < acSecond) {
|
|
478
|
+
return [declaredSecond, wiper, declaredFirst];
|
|
479
|
+
}
|
|
480
|
+
if (acSecond < acFirst) {
|
|
481
|
+
return [declaredFirst, wiper, declaredSecond];
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return [declaredFirst, wiper, declaredSecond];
|
|
485
|
+
}
|
|
486
|
+
// Both ends fully unreachable from ground/rail: `quietDistances` has made no claim at all
|
|
487
|
+
// about this pot. Op-amp-input evidence is trustworthy here only for a pot that is *also*
|
|
488
|
+
// structurally wired as a rheostat -- its wiper coincident with one of its own ends
|
|
489
|
+
// (`boss-fa-1`'s `VR1` shape), or the wiper declared at the quiet reference itself
|
|
490
|
+
// (`boss-hm-2`'s `VR4_DIST` shape) -- the same working detector the 2026-08-14 rheostat-nine
|
|
491
|
+
// census validated. An ordinary three-terminal divider that merely floats in a DC-isolated
|
|
492
|
+
// pocket for unrelated reasons (a real, separate wiper node, no rheostat shape at all) does
|
|
493
|
+
// not get this evidence: measured against `jhs-morning-glory`'s `Drive` (exactly this
|
|
494
|
+
// ordinary-divider shape, not wiper-coincident), letting it through moved the pot's own
|
|
495
|
+
// already-marginal Newton operating point from a bounded reading at one end of its travel to
|
|
496
|
+
// a diverging one (peak ~8e3 against a 0.1 V input) at the other -- a measured regression,
|
|
497
|
+
// not a structural inference, and the reason this gate exists rather than trusting "both
|
|
498
|
+
// ends unreachable" alone.
|
|
499
|
+
const wiperIsRheostatShaped = wiper === declaredFirst ||
|
|
500
|
+
wiper === declaredSecond ||
|
|
501
|
+
quietDistance.get(wiper) === 0;
|
|
502
|
+
if (!wiperIsRheostatShaped) {
|
|
503
|
+
return [declaredFirst, wiper, declaredSecond];
|
|
504
|
+
}
|
|
505
|
+
const firstOpampDistance = opampInputDistance.get(declaredFirst) ?? Number.POSITIVE_INFINITY;
|
|
506
|
+
const secondOpampDistance = opampInputDistance.get(declaredSecond) ?? Number.POSITIVE_INFINITY;
|
|
507
|
+
if (firstOpampDistance < secondOpampDistance) {
|
|
508
|
+
return [declaredSecond, wiper, declaredFirst];
|
|
509
|
+
}
|
|
510
|
+
if (secondOpampDistance < firstOpampDistance) {
|
|
511
|
+
return [declaredFirst, wiper, declaredSecond];
|
|
512
|
+
}
|
|
513
|
+
// The input distances tie, so the nearest-input evidence is silent about which end is
|
|
514
|
+
// quiet, and what used to be left was declaration order. Before falling back to it, ask
|
|
515
|
+
// the one typed fact the format does state for the negative-feedback gain-stage class:
|
|
516
|
+
// a rheostat whose wiper is strapped to the end sitting on an op-amp's declared
|
|
517
|
+
// **output**, with the free end reaching that op-amp's declared **inverting** input, is
|
|
518
|
+
// a gain-setting feedback element (non-inverting gain is `1 + Rf/Rg`), and for that
|
|
519
|
+
// class a rising control must mean *more* feedback resistance -- so the live half has
|
|
520
|
+
// to be the `lower` stamp, `share = fraction`. Declaration order got this backwards for
|
|
521
|
+
// `boss-os-2`'s `VR3b` (wiper strapped to the `output` node, `inverting` one hop away
|
|
522
|
+
// through R22): its live half landed on `upper`, where `share = 1 - fraction`, so the
|
|
523
|
+
// pedal's Drive ran backwards -- 6.3e-1 rms at 0.0 against 2.9e-1 at 1.0 -- while its
|
|
524
|
+
// gang twin `VR3a`, decided by `quietDistances` above, was already right.
|
|
525
|
+
//
|
|
526
|
+
// Scoped to the tie on purpose, like the input-distance evidence it guards: the
|
|
527
|
+
// asymmetric case above is the validated "moves exactly 6 pots and reverses none"
|
|
528
|
+
// evidence, and this rule does not override a finite decision it already made. The
|
|
529
|
+
// asymmetry guards (`outEout < outEfree`, `inEfree <= inEout`) keep the class narrow:
|
|
530
|
+
// the wiper-strapped end must be the one nearer a declared output, and the free end at
|
|
531
|
+
// least as near a declared inverting input. Both distances read declared roles and
|
|
532
|
+
// connectivity only; nothing here reads a name.
|
|
533
|
+
if (wiper === declaredFirst || wiper === declaredSecond) {
|
|
534
|
+
const outputEnd = wiper === declaredFirst ? declaredFirst : declaredSecond;
|
|
535
|
+
const freeEnd = wiper === declaredFirst ? declaredSecond : declaredFirst;
|
|
536
|
+
if (outputEnd !== freeEnd) {
|
|
537
|
+
const outEout = opampOutputDistance.get(outputEnd) ?? Number.POSITIVE_INFINITY;
|
|
538
|
+
const outFree = opampOutputDistance.get(freeEnd) ?? Number.POSITIVE_INFINITY;
|
|
539
|
+
const inFree = opampInputDistance.get(freeEnd) ?? Number.POSITIVE_INFINITY;
|
|
540
|
+
const inEout = opampInputDistance.get(outputEnd) ?? Number.POSITIVE_INFINITY;
|
|
541
|
+
if (Number.isFinite(outEout) &&
|
|
542
|
+
outEout < outFree &&
|
|
543
|
+
Number.isFinite(inFree) &&
|
|
544
|
+
inFree <= inEout) {
|
|
545
|
+
return [outputEnd, wiper, freeEnd];
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
return [declaredFirst, wiper, declaredSecond];
|
|
550
|
+
}
|
|
551
|
+
//# sourceMappingURL=pot-orientation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pot-orientation.js","sourceRoot":"","sources":["../src/pot-orientation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { CompileWarning, Netlist, NodeId } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* A device exposed as a sweepable user control that is **power-domain hardware**: it sits outside
|
|
4
|
+
* the audio graph and no panel entry claims it.
|
|
5
|
+
*
|
|
6
|
+
* **Why this is reported rather than decided.** Structure cannot tell a user control from fixed
|
|
7
|
+
* hardware on this side of the boundary. A standby switch and a mains fuse are both switches in
|
|
8
|
+
* the supply, and one belongs on the front panel while the other belongs inside the chassis --
|
|
9
|
+
* only the document can say which. So the compiler names the ambiguity and the fix is a
|
|
10
|
+
* `deviceInterface.controls` entry, or a kind that is not a switch.
|
|
11
|
+
*
|
|
12
|
+
* **A rule was attempted and it does not exist.** The promising one was "a switch declaring a
|
|
13
|
+
* `FuseRating` is a fuse, therefore not a control" -- legal evidence, since it reads the presence
|
|
14
|
+
* of a typed property rather than a name, and 26 corpus switches declare one. It fails on its own
|
|
15
|
+
* data: `mesa-boogie-dual-rectifier`'s `SRC_POWER_SWITCH_FUSE_4A`, `orange-rockerverb`'s
|
|
16
|
+
* `SRC_PRIMARY_POWER_SWITCH_AND_FUSE` and `sunn-beta-lead`'s `SRC_POWER_SWITCH_FUSE` are each a
|
|
17
|
+
* power switch *and* a fuse in one two-terminal component, and a power switch is exactly the user
|
|
18
|
+
* control the rule would have suppressed. Narrowing it to "two terminals and no switch-shaped
|
|
19
|
+
* property" keeps all three, because a fuse and a combined switch-and-fuse are structurally
|
|
20
|
+
* identical: two terminals, one closed contact. The difference is whether a human operates it,
|
|
21
|
+
* which is not a fact about the circuit.
|
|
22
|
+
*
|
|
23
|
+
* **Measured 2026-09-04: 67 of the corpus's 958 program controls**, across the packets that
|
|
24
|
+
* model their own power supply. It read 80 before `boss-bf-2`'s three service trims were
|
|
25
|
+
* declared, and 76 before every signal jack became a seed -- see the note in the function. They are mains switches,
|
|
26
|
+
* voltage selectors, and mains, HT and heater fuses -- `vox-ac30-top-boost` alone contributes
|
|
27
|
+
* `SW_MAINS`, a 230/120 selector and four fuses. A fuse offered to a player as a knob to sweep is
|
|
28
|
+
* the shape of the problem.
|
|
29
|
+
*
|
|
30
|
+
* **The traversal's one known imprecision** is at the B+ boundary: it stops at the transformer but
|
|
31
|
+
* not at the rectifier and filter, so B+ nodes stay reachable through a tube's plate. That keeps
|
|
32
|
+
* `SW1_STANDBY` and `S_STANDBY_SWITCH` out of this warning, which is right -- a standby switch is
|
|
33
|
+
* a front-panel control -- and also keeps `F2_HT_FUSE` out, which is not.
|
|
34
|
+
*/
|
|
35
|
+
export declare function findPowerDomainControls(netlist: Netlist, controls: readonly {
|
|
36
|
+
readonly id: string;
|
|
37
|
+
readonly role?: string | null;
|
|
38
|
+
readonly label?: string | null;
|
|
39
|
+
}[], ports: readonly NodeId[]): readonly CompileWarning[];
|
|
40
|
+
//# sourceMappingURL=power-domain-control.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"power-domain-control.d.ts","sourceRoot":"","sources":["../src/power-domain-control.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAU,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAgEvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,uBAAuB,CACtC,OAAO,EAAE,OAAO,EAIhB,QAAQ,EAAE,SAAS;IAClB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,EAAE,EACH,KAAK,EAAE,SAAS,MAAM,EAAE,GACtB,SAAS,cAAc,EAAE,CAkD3B"}
|