circuit-json-to-spice 0.0.25 → 0.0.27
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 +1 -0
- package/dist/index.js +106 -40
- package/lib/circuitJsonToSpice.ts +107 -18
- package/package.json +3 -3
- package/tests/examples/assets/switch.json +999 -0
- package/tests/examples/switch.test.tsx +29 -0
- package/tests/unit/voltage-probe.test.ts +75 -22
|
@@ -3,6 +3,7 @@ import { SpiceComponent } from "./spice-classes/SpiceComponent"
|
|
|
3
3
|
import { ResistorCommand } from "./spice-commands/ResistorCommand"
|
|
4
4
|
import { CapacitorCommand } from "./spice-commands/CapacitorCommand"
|
|
5
5
|
import { VoltageSourceCommand } from "./spice-commands/VoltageSourceCommand"
|
|
6
|
+
import { BJTCommand } from "./spice-commands/BJTCommand"
|
|
6
7
|
import { DiodeCommand } from "./spice-commands/DiodeCommand"
|
|
7
8
|
import { InductorCommand } from "./spice-commands/InductorCommand"
|
|
8
9
|
import { VoltageControlledSwitchCommand } from "./spice-commands/VoltageControlledSwitchCommand"
|
|
@@ -119,12 +120,23 @@ export function circuitJsonToSpice(
|
|
|
119
120
|
if (simulationProbes.length > 0) {
|
|
120
121
|
for (const probe of simulationProbes) {
|
|
121
122
|
if (!probe.name) continue
|
|
123
|
+
|
|
124
|
+
if (
|
|
125
|
+
probe.reference_input_source_port_id ||
|
|
126
|
+
probe.reference_input_source_net_id
|
|
127
|
+
) {
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
|
|
122
131
|
let net: string | undefined | null
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
132
|
+
const signal_port_id = probe.signal_input_source_port_id
|
|
133
|
+
const signal_net_id = probe.signal_input_source_net_id
|
|
134
|
+
|
|
135
|
+
if (signal_port_id) {
|
|
136
|
+
net = connMap.getNetConnectedToId(signal_port_id)
|
|
137
|
+
} else if (signal_net_id) {
|
|
126
138
|
const trace = sourceTraces.find((t) =>
|
|
127
|
-
t.connected_source_net_ids.includes(
|
|
139
|
+
t.connected_source_net_ids.includes(signal_net_id!),
|
|
128
140
|
)
|
|
129
141
|
if (trace && trace.connected_source_port_ids.length > 0) {
|
|
130
142
|
const portId = trace.connected_source_port_ids[0]
|
|
@@ -136,10 +148,10 @@ export function circuitJsonToSpice(
|
|
|
136
148
|
if (!netToNodeName.has(net)) {
|
|
137
149
|
netToNodeName.set(net, probe.name)
|
|
138
150
|
}
|
|
139
|
-
} else if (
|
|
151
|
+
} else if (signal_port_id && probe.name) {
|
|
140
152
|
// It's a floating port with a probe, so we map it directly. This port
|
|
141
153
|
// will now be skipped in the second-pass for unconnected ports.
|
|
142
|
-
nodeMap.set(
|
|
154
|
+
nodeMap.set(signal_port_id, probe.name)
|
|
143
155
|
}
|
|
144
156
|
}
|
|
145
157
|
}
|
|
@@ -368,6 +380,59 @@ export function circuitJsonToSpice(
|
|
|
368
380
|
}
|
|
369
381
|
break
|
|
370
382
|
}
|
|
383
|
+
case "simple_transistor": {
|
|
384
|
+
if ("name" in component) {
|
|
385
|
+
const collectorPort = componentPorts.find(
|
|
386
|
+
(p) =>
|
|
387
|
+
p.name?.toLowerCase() === "collector" ||
|
|
388
|
+
p.port_hints?.includes("collector"),
|
|
389
|
+
)
|
|
390
|
+
const basePort = componentPorts.find(
|
|
391
|
+
(p) =>
|
|
392
|
+
p.name?.toLowerCase() === "base" ||
|
|
393
|
+
p.port_hints?.includes("base"),
|
|
394
|
+
)
|
|
395
|
+
const emitterPort = componentPorts.find(
|
|
396
|
+
(p) =>
|
|
397
|
+
p.name?.toLowerCase() === "emitter" ||
|
|
398
|
+
p.port_hints?.includes("emitter"),
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
if (!collectorPort || !basePort || !emitterPort) {
|
|
402
|
+
throw new Error(
|
|
403
|
+
`Transistor ${component.name} is missing required ports (collector, base, emitter)`,
|
|
404
|
+
)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const collectorNode =
|
|
408
|
+
nodeMap.get(collectorPort.source_port_id) || "0"
|
|
409
|
+
const baseNode = nodeMap.get(basePort.source_port_id) || "0"
|
|
410
|
+
const emitterNode = nodeMap.get(emitterPort.source_port_id) || "0"
|
|
411
|
+
|
|
412
|
+
const transistor_type = (component as any).transistor_type ?? "npn"
|
|
413
|
+
const modelName = transistor_type.toUpperCase()
|
|
414
|
+
if (!netlist.models.has(modelName)) {
|
|
415
|
+
netlist.models.set(
|
|
416
|
+
modelName,
|
|
417
|
+
`.MODEL ${modelName} ${transistor_type.toUpperCase()}`,
|
|
418
|
+
)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const bjtCmd = new BJTCommand({
|
|
422
|
+
name: component.name,
|
|
423
|
+
collector: collectorNode,
|
|
424
|
+
base: baseNode,
|
|
425
|
+
emitter: emitterNode,
|
|
426
|
+
model: modelName,
|
|
427
|
+
})
|
|
428
|
+
spiceComponent = new SpiceComponent(component.name, bjtCmd, [
|
|
429
|
+
collectorNode,
|
|
430
|
+
baseNode,
|
|
431
|
+
emitterNode,
|
|
432
|
+
])
|
|
433
|
+
}
|
|
434
|
+
break
|
|
435
|
+
}
|
|
371
436
|
}
|
|
372
437
|
|
|
373
438
|
if (spiceComponent) {
|
|
@@ -486,22 +551,46 @@ export function circuitJsonToSpice(
|
|
|
486
551
|
if (simulationProbes.length > 0) {
|
|
487
552
|
const nodesToProbe = new Set<string>()
|
|
488
553
|
|
|
554
|
+
const getPortIdFromNetId = (netId: string) => {
|
|
555
|
+
const trace = sourceTraces.find((t) =>
|
|
556
|
+
t.connected_source_net_ids.includes(netId),
|
|
557
|
+
)
|
|
558
|
+
return trace?.connected_source_port_ids[0]
|
|
559
|
+
}
|
|
560
|
+
|
|
489
561
|
for (const probe of simulationProbes) {
|
|
490
|
-
let
|
|
491
|
-
if (
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
t.connected_source_net_ids.includes(probe.source_net_id!),
|
|
496
|
-
)
|
|
497
|
-
if (trace && trace.connected_source_port_ids.length > 0) {
|
|
498
|
-
const portId = trace.connected_source_port_ids[0]
|
|
499
|
-
nodeName = nodeMap.get(portId)
|
|
562
|
+
let signalPortId = probe.signal_input_source_port_id
|
|
563
|
+
if (!signalPortId) {
|
|
564
|
+
const signalNetId = probe.signal_input_source_net_id
|
|
565
|
+
if (signalNetId) {
|
|
566
|
+
signalPortId = getPortIdFromNetId(signalNetId)
|
|
500
567
|
}
|
|
501
568
|
}
|
|
502
569
|
|
|
503
|
-
if (
|
|
504
|
-
|
|
570
|
+
if (!signalPortId) continue
|
|
571
|
+
|
|
572
|
+
const signalNodeName = nodeMap.get(signalPortId)
|
|
573
|
+
if (!signalNodeName) continue
|
|
574
|
+
|
|
575
|
+
let referencePortId = probe.reference_input_source_port_id
|
|
576
|
+
if (!referencePortId && probe.reference_input_source_net_id) {
|
|
577
|
+
referencePortId = getPortIdFromNetId(
|
|
578
|
+
probe.reference_input_source_net_id,
|
|
579
|
+
)
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if (referencePortId) {
|
|
583
|
+
const referenceNodeName = nodeMap.get(referencePortId)
|
|
584
|
+
if (referenceNodeName && referenceNodeName !== "0") {
|
|
585
|
+
nodesToProbe.add(`V(${signalNodeName},${referenceNodeName})`)
|
|
586
|
+
} else if (signalNodeName !== "0") {
|
|
587
|
+
nodesToProbe.add(`V(${signalNodeName})`)
|
|
588
|
+
}
|
|
589
|
+
} else {
|
|
590
|
+
// Single-ended probe
|
|
591
|
+
if (signalNodeName !== "0") {
|
|
592
|
+
nodesToProbe.add(`V(${signalNodeName})`)
|
|
593
|
+
}
|
|
505
594
|
}
|
|
506
595
|
}
|
|
507
596
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "circuit-json-to-spice",
|
|
3
3
|
"main": "dist/index.js",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.27",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "tsup-node ./lib/index.ts --dts --format esm --sourcemap inline -d dist",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"@tscircuit/circuit-json-util": "^0.0.72",
|
|
17
17
|
"@types/bun": "^1.2.15",
|
|
18
18
|
"bun-match-svg": "^0.0.13",
|
|
19
|
-
"circuit-json": "^0.0.
|
|
19
|
+
"circuit-json": "^0.0.322",
|
|
20
20
|
"eecircuit-engine": "^1.5.2",
|
|
21
|
-
"tscircuit": "^0.0.
|
|
21
|
+
"tscircuit": "^0.0.936",
|
|
22
22
|
"tsup": "^8.4.0"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|