699 lines
40 KiB
Markdown
699 lines
40 KiB
Markdown
|
|
# Fase 8 — Plan de Implementación en Velxio
|
|||
|
|
|
|||
|
|
> **Destino**: el código Velxio (`frontend/`). El sandbox `test/test_circuit/` queda como referencia y banco de pruebas congelado.
|
|||
|
|
> **Motor**: `eecircuit-engine` (ngspice-WASM real). **No** se inventa solver.
|
|||
|
|
> **Meta**: Velxio emula **circuitos digitales + analógicos conviviendo**, con sketches reales de Arduino/ESP32 interactuando con componentes discretos (resistencias, capacitores, transistores, op-amps, sensores, MOSFETs) y con instrumentos de medición (voltímetro, amperímetro, multímetro, osciloscopio analógico).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 0. Resumen ejecutivo
|
|||
|
|
|
|||
|
|
| | |
|
|||
|
|
|---|---|
|
|||
|
|
| **Motor de simulación eléctrica** | `eecircuit-engine@^1.7.0` (ngspice compilado a WASM, 39 MB) |
|
|||
|
|
| **Estrategia de carga** | Lazy-load tras toggle "⚡ Electrical" en la toolbar |
|
|||
|
|
| **Integración con MCU** | Co-simulación cuasi-estática: AVR/RP2040/ESP32 corre `N` ciclos, luego ngspice resuelve el netlist en `.op` o `.tran`; voltajes de nodos se inyectan en ADC; duty de PWM se traduce a fuente DC |
|
|||
|
|
| **Componentes nuevos (primera oleada)** | ≈ 30: pasivos genéricos, transistores reales (2N2222, 2N3055, BC547, TIP120), MOSFETs (2N7000, IRF540), op-amps (LM358, LM741, TL072, LM324), reguladores (78xx, LM317), Zener (1N4733), referencias (TL431), 555, puente rectificador, etc. |
|
|||
|
|
| **Instrumentos** | Voltímetro, amperímetro, multímetro DMM, probe de osciloscopio analógico (extiende el osciloscopio actual) |
|
|||
|
|
| **Modo "Electrical"** | Tres niveles: `off` (comportamiento actual), `spice` (ngspice-WASM — predeterminado del modo), `mna-fallback` (solver hand-rolled del sandbox) — reservado para entornos sin WASM |
|
|||
|
|
| **Plazo estimado** | 5 fases de 1–2 semanas cada una (ver §6). No bloquea el resto del roadmap: el feature flag permite merge seguro incremental |
|
|||
|
|
| **Validación** | Portar los **47 tests** del sandbox al `frontend/src/__tests__/` |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. Fuentes de verdad (leer antes de empezar)
|
|||
|
|
|
|||
|
|
Este plan **depende** de que el implementador haya leído:
|
|||
|
|
|
|||
|
|
- [`test/test_circuit/src/spice/SpiceEngine.js`](../src/spice/SpiceEngine.js) — wrapper de `eecircuit-engine`. A portar tal cual a `frontend/src/simulation/spice/`.
|
|||
|
|
- [`test/test_circuit/src/spice/AVRSpiceBridge.js`](../src/spice/AVRSpiceBridge.js) — puente cuasi-estático. Es el patrón exacto.
|
|||
|
|
- [`test/test_circuit/autosearch/04_ngspice_findings.md`](../autosearch/04_ngspice_findings.md) — trampas de ngspice ya identificadas (`&` vs `u()`, matriz singular, histéresis para memoria, etc.).
|
|||
|
|
- [`docs/wiki/circuit-emulation-gotchas.md`](../../../docs/wiki/circuit-emulation-gotchas.md) — debugging log completo.
|
|||
|
|
- [`docs/wiki/circuit-emulation-avr-bridge.md`](../../../docs/wiki/circuit-emulation-avr-bridge.md) — mapeo pin → puerto → ngspice.
|
|||
|
|
- El informe de survey de Velxio (ver el mensaje previo del agente Explore): **componentes son metadata-driven** con `components-metadata.json` generado en build; los **cables son visuales** (sin nodos); la **API de pines** está en `PinManager.onPinChange / onPwmChange / onAnalogChange`.
|
|||
|
|
|
|||
|
|
Si alguno de estos ha cambiado sustancialmente cuando se empiece la fase 1, parar y replantear.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Arquitectura objetivo
|
|||
|
|
|
|||
|
|
### 2.1 Diagrama de alto nivel
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────────────────────────────────────┐
|
|||
|
|
│ Velxio UI │
|
|||
|
|
│ │
|
|||
|
|
│ ComponentPicker Canvas Toolbar PropertyDialog Oscilloscope │
|
|||
|
|
│ │ │ │ │ │ │
|
|||
|
|
│ └─────────────┴────────┴───────────────┴──────────────┘ │
|
|||
|
|
│ │ │
|
|||
|
|
│ ▼ │
|
|||
|
|
│ ┌──────────────────────┐ │
|
|||
|
|
│ │ useSimulatorStore │ │
|
|||
|
|
│ │ components[] │ │
|
|||
|
|
│ │ wires[] │ │
|
|||
|
|
│ │ electrical: { │ │
|
|||
|
|
│ │ mode, │ │
|
|||
|
|
│ │ nodeVoltages, │ │
|
|||
|
|
│ │ componentI, │ │
|
|||
|
|
│ │ probes[] │ │
|
|||
|
|
│ │ } │ │
|
|||
|
|
│ └──────┬───────────────┘ │
|
|||
|
|
│ │ subscribe │
|
|||
|
|
│ ▼ │
|
|||
|
|
│ ┌──────────────────────┐ │
|
|||
|
|
│ │ ElectricalScheduler │ debounce 50 ms │
|
|||
|
|
│ │ (singleton) │ runs on wire / pin / prop change │
|
|||
|
|
│ └──────┬───────────────┘ │
|
|||
|
|
│ │ │
|
|||
|
|
│ ┌──────────────────┼──────────────────┐ │
|
|||
|
|
│ ▼ ▼ ▼ │
|
|||
|
|
│ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|||
|
|
│ │ Netlist │ │ AVR/ESP │ │ Instrument │ │
|
|||
|
|
│ │ Builder │ │ pin snapshot│ │ reader │ │
|
|||
|
|
│ └────┬─────┘ └──────┬───────┘ └──────┬───────┘ │
|
|||
|
|
│ │ │ │ │
|
|||
|
|
│ └────────┬───────┴──────────────────┘ │
|
|||
|
|
│ ▼ │
|
|||
|
|
│ ┌─────────────────────────┐ │
|
|||
|
|
│ │ SpiceEngine (lazy) │ if mode === 'spice' │
|
|||
|
|
│ │ eecircuit-engine (WASM)│ │
|
|||
|
|
│ │ │ if mode === 'mna-fallback' │
|
|||
|
|
│ │ CircuitSolverJS │ (hand-rolled, from sandbox) │
|
|||
|
|
│ └──────────┬──────────────┘ │
|
|||
|
|
│ │ │
|
|||
|
|
│ ▼ │
|
|||
|
|
│ { nodeVoltages, branchCurrents, converged, errors } │
|
|||
|
|
│ │ │
|
|||
|
|
│ └─► update store.electrical │
|
|||
|
|
│ └─► inject ADC voltages via PinManager.setAnalogVoltage │
|
|||
|
|
│ └─► update LED brightness on each LED DynamicComponent │
|
|||
|
|
│ └─► feed probes (voltmeter, ammeter, scope) │
|
|||
|
|
└─────────────────────────────────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2.2 Decisiones clave (no negociables)
|
|||
|
|
|
|||
|
|
| # | Decisión | Razón |
|
|||
|
|
|---|---|---|
|
|||
|
|
| D-1 | Usar `eecircuit-engine` SIEMPRE que `mode === 'spice'` | El sandbox validó que funciona; no inventar |
|
|||
|
|
| D-2 | Lazy-load del paquete (~39 MB) detrás del toggle | No penalizar tiempo de carga inicial |
|
|||
|
|
| D-3 | Scheduler con debounce 50 ms | Evitar re-solver por cada frame; user-perception: instantáneo |
|
|||
|
|
| D-4 | Co-simulación cuasi-estática (slice 1–10 ms) | Cycle-accurate es imposible; el sandbox probó que 1 ms alcanza para ADC + PWM |
|
|||
|
|
| D-5 | Cables siguen siendo visuales; un **NetlistBuilder** hace Union-Find al vuelo | Reutiliza el modelo de datos actual de Velxio; zero-risk |
|
|||
|
|
| D-6 | PWM se representa como fuente DC de `duty·Vcc` en modo `.op` | El sandbox mostró < 100 mV de error en filtros RC con esta simplificación |
|
|||
|
|
| D-7 | Componentes sin modelo eléctrico (LCD, NeoPixel, sensores I²C) se **omiten** del netlist | Siguen funcionando con su lógica actual |
|
|||
|
|
| D-8 | Instrumentos (voltímetro, amperímetro) son componentes Velxio normales con flag `isProbe=true` | NO se estampan en el netlist — son lectores de resultados |
|
|||
|
|
| D-9 | Modo por defecto: `off`. Activar con toggle explícito | Usuarios que no necesitan analógico no ven diferencia |
|
|||
|
|
| D-10 | Cada board Velxio (Uno, Mega, ESP32, RP2040) expone sus pines como nets nombrados `boardid_pin<N>` | Evita colisiones entre múltiples boards |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Estructura de archivos nuevos / modificados en `frontend/`
|
|||
|
|
|
|||
|
|
### 3.1 Archivos nuevos
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
frontend/src/simulation/spice/
|
|||
|
|
├── SpiceEngine.ts ★ wrapper de eecircuit-engine (port del sandbox)
|
|||
|
|
├── SpiceEngine.lazy.ts ★ dynamic-import wrapper, singleton
|
|||
|
|
├── NetlistBuilder.ts ★ wires[] + components[] → netlist string
|
|||
|
|
├── NetlistBuilder.unionFind.ts ★ auxiliar
|
|||
|
|
├── componentToSpice.ts ★ tabla metadataId → spice card
|
|||
|
|
├── subcircuits.ts ★ .subckt de op-amps, 555, etc.
|
|||
|
|
├── CircuitScheduler.ts ★ debouncer, orchestrator
|
|||
|
|
├── AVRSpiceBridge.ts ★ co-sim AVR ↔ ngspice (port del sandbox)
|
|||
|
|
├── RP2040SpiceBridge.ts ★ análogo para RP2040
|
|||
|
|
├── ESP32SpiceBridge.ts ★ análogo para ESP32 (según disponibilidad de ADC)
|
|||
|
|
└── CircuitSolverJS.ts ★ port del solver hand-rolled como fallback
|
|||
|
|
|
|||
|
|
frontend/src/simulation/spice/__tests__/
|
|||
|
|
├── netlistBuilder.test.ts
|
|||
|
|
├── componentToSpice.test.ts
|
|||
|
|
├── avr-ntc-readback.test.ts ← port directo del sandbox
|
|||
|
|
├── avr-pwm-rc.test.ts ← port
|
|||
|
|
├── avr-pot-cosim.test.ts ← port
|
|||
|
|
└── (… el resto de los 47 del sandbox …)
|
|||
|
|
|
|||
|
|
frontend/src/components/components-analog/
|
|||
|
|
├── GenericResistor.tsx ★ cualquier valor en Ω
|
|||
|
|
├── GenericCapacitor.tsx ★ cualquier valor en F, con polaridad opcional
|
|||
|
|
├── GenericInductor.tsx ★
|
|||
|
|
├── GenericDiode.tsx ★ modelo genérico, dropdown de partes 1N4148/1N4007/1N5819
|
|||
|
|
├── ZenerDiode.tsx ★ dropdown 1N4733/1N4742/etc. o voltaje custom
|
|||
|
|
├── BJTTransistor.tsx ★ NPN/PNP + part number (2N2222/2N3055/BC547/BC557/TIP120)
|
|||
|
|
├── MOSFETTransistor.tsx ★ N/P + parte (IRF540/IRF9540/2N7000/BS170)
|
|||
|
|
├── OpAmp.tsx ★ LM358/LM741/TL072/LM324 (package-pin-aware)
|
|||
|
|
├── VoltageRegulator.tsx ★ 7805/7812/LM317 + externals
|
|||
|
|
├── Timer555.tsx ★ chip NE555 en DIP-8
|
|||
|
|
├── BridgeRectifier.tsx ★ puente de 4 diodos
|
|||
|
|
└── GenericSwitch.tsx ★ SPST / SPDT / DPDT
|
|||
|
|
|
|||
|
|
frontend/src/components/components-instruments/
|
|||
|
|
├── Voltmeter.tsx ★ DC: lee V(node+) − V(node−); probe de 2 terminales
|
|||
|
|
├── Ammeter.tsx ★ DC: inserta 0.001 Ω en serie y lee i(Vsense)
|
|||
|
|
├── Multimeter.tsx ★ DMM: V/I/R/diode/continuity
|
|||
|
|
├── OscilloscopeAnalogProbe.tsx ★ señala un nodo ngspice como canal del scope
|
|||
|
|
└── SignalGenerator.tsx ★ fuente V controlada desde UI (sin, pulse, pwl)
|
|||
|
|
|
|||
|
|
frontend/src/components/analog-ui/
|
|||
|
|
├── ComponentValueEditor.tsx ★ input con unidades (k, M, u, n, p)
|
|||
|
|
├── ElectricalOverlay.tsx ★ SVG overlay: V de cada nodo, I de cada cable
|
|||
|
|
├── ElectricalModeToggle.tsx ★ toolbar button ⚡
|
|||
|
|
├── SpiceErrorBanner.tsx ★ banner de no-convergencia
|
|||
|
|
└── MeasurementPanel.tsx ★ lateral con todas las lecturas de probes
|
|||
|
|
|
|||
|
|
frontend/scripts/
|
|||
|
|
└── generate-analog-metadata.ts ★ genera components-metadata.json para analog/instruments
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.2 Archivos modificados
|
|||
|
|
|
|||
|
|
| Archivo | Cambio |
|
|||
|
|
|---|---|
|
|||
|
|
| `frontend/package.json` | + `"eecircuit-engine": "^1.7.0"` (como `dependencies`, marca de side-effect false para tree-shaking) |
|
|||
|
|
| `frontend/src/store/useSimulatorStore.ts` | + estado `electrical`, + acciones `runElectricalSolve`, `setElectricalMode`, `addProbe` |
|
|||
|
|
| `frontend/src/simulation/PinManager.ts` | + método `onAnyPinChange(cb)` — fires cuando cualquier pin (cualquier board) cambia; fires también en PWM update |
|
|||
|
|
| `frontend/src/simulation/AVRSimulator.ts` | + invocar `PinManager.notifyAnyPinChange()` dentro del callback existente |
|
|||
|
|
| `frontend/src/components/editor/EditorToolbar.tsx` | + botón `<ElectricalModeToggle />` |
|
|||
|
|
| `frontend/src/components/simulator/SimulatorCanvas.tsx` | + `<ElectricalOverlay />` encima del WireLayer; + render de instrumentos |
|
|||
|
|
| `frontend/src/components/DynamicComponent.tsx` | + leer `brightness` real de electrical.componentCurrents para LEDs |
|
|||
|
|
| `frontend/src/components/ComponentPickerModal.tsx` | + nuevas categorías `analog-passive`, `analog-active`, `instruments` |
|
|||
|
|
| `frontend/vite.config.ts` | si hace falta, marcar `eecircuit-engine` como `optimizeDeps.exclude` para que no bloquee dev server |
|
|||
|
|
| `frontend/components-metadata.json` | regenerado — ahora incluye los ~30 nuevos analog components + 5 instrumentos |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. Catálogo de componentes (primera oleada)
|
|||
|
|
|
|||
|
|
### 4.1 Pasivos genéricos
|
|||
|
|
|
|||
|
|
| Velxio `metadataId` | Tag web component | Propiedades | Netlist ngspice |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| `analog-resistor` | `<wokwi-resistor>` (existente) | `value` (Ω), `tolerance` (%) | `R{id} {net1} {net2} {value}` |
|
|||
|
|
| `analog-capacitor` | `<wokwi-capacitor>` o propio | `value` (F), `polarized` (bool), `voltage` (max V) | `C{id} {net+} {net-} {value} IC=0` |
|
|||
|
|
| `analog-electrolytic` | nuevo SVG | igual, siempre polarized=true | idem |
|
|||
|
|
| `analog-inductor` | nuevo SVG | `value` (H), `dcr` (Ω) | `L{id} {n1} {n2} {value}` + `R_{id}_dcr` en serie si `dcr > 0` |
|
|||
|
|
| `analog-potentiometer-generic` | extiende `Potentiometer.tsx` existente | `value` (Ω total), `curve` ("linear"\|"log"), `wiperPos` | dos resistores en serie |
|
|||
|
|
| `analog-trimmer` | SVG nuevo | igual; UI distinta (tornillo chico) | idem |
|
|||
|
|
|
|||
|
|
**Input de valor con prefijos SI** — `ComponentValueEditor.tsx`:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
"4.7k" → 4700
|
|||
|
|
"220" → 220
|
|||
|
|
"1Meg" → 1_000_000
|
|||
|
|
"10u" → 0.00001 (capacitor)
|
|||
|
|
"100n" → 1e-7
|
|||
|
|
"0.1u" → 1e-7 (alias)
|
|||
|
|
"22p" → 2.2e-11
|
|||
|
|
"10mH" → 0.01 (inductor)
|
|||
|
|
"1.5H" → 1.5
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Usar la misma sintaxis SPICE (`k`, `Meg`, `u`, `n`, `p`, `m`, `f`, `G`, `T`) para que el usuario aprenda de una vez.
|
|||
|
|
|
|||
|
|
### 4.2 Semiconductores discretos (reales, con part number)
|
|||
|
|
|
|||
|
|
| `metadataId` | Parte | SPICE model card |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `diode-1n4148` | 1N4148 small-signal | `.model D1N4148 D(Is=2.52n N=1.752 Rs=0.568 Ibv=0.1u Bv=100 Cjo=4p M=0.333 Vj=0.5)` |
|
|||
|
|
| `diode-1n4007` | 1N4007 rectifier | `.model D1N4007 D(Is=76.9n N=1.45 Rs=0.0342 Ikf=2.34 Bv=1000 Ibv=5u)` |
|
|||
|
|
| `diode-1n5819` | Schottky | `.model D1N5819 D(Is=31u N=1 Rs=0.043 Bv=40 Ibv=10m Cjo=110p)` |
|
|||
|
|
| `zener-1n4733` | 5.1 V zener | `.model D1N4733 D(Is=1n N=1 Rs=5 Bv=5.1 Ibv=50m)` |
|
|||
|
|
| `zener-1n4742` | 12 V zener | `.model D1N4742 D(Is=1n N=1 Rs=6 Bv=12 Ibv=20m)` |
|
|||
|
|
| `bjt-2n2222` | NPN general purpose | `.model Q2N2222 NPN(Is=14.34f Xti=3 Eg=1.11 Vaf=74.03 Bf=200 Ne=1.307 Ise=14.34f Ikf=0.2847 Xtb=1.5 Br=6.092 Nc=2 Isc=0 Ikr=0 Rc=1 Cjc=7.306p Mjc=0.3416 Vjc=0.75 Fc=0.5 Cje=22.01p Mje=0.377 Vje=0.75 Tr=46.91n Tf=411.1p Itf=0.6 Vtf=1.7 Xtf=3 Rb=10)` (estándar Motorola) |
|
|||
|
|
| `bjt-2n3055` | NPN power | modelo Motorola estándar (`Bf=70`, `Icmax=15 A`) |
|
|||
|
|
| `bjt-bc547` | NPN small-signal Europa | `.model QBC547B NPN(Is=7.049f Bf=378.6 Ikf=0.1393 Vaf=85 Br=7.202 Ne=1.25 Ise=92.22p Ikr=0.3 Rc=1.32 Cjc=6.033p Cje=8.063p Tf=575.8p Tr=1.0u Mje=0.3 Mjc=0.3 Vje=0.69 Vjc=0.69 Rb=10)` |
|
|||
|
|
| `bjt-bc557` | PNP | `.model QBC557B PNP(…)` |
|
|||
|
|
| `bjt-tip120` | NPN Darlington power | `.model QTIP120 NPN(Bf=1000 …)` |
|
|||
|
|
| `mosfet-2n7000` | N-ch small-signal | `.model M2N7000 NMOS(Level=3 Vto=1.6 Kp=0.1 L=2u W=0.1 Rd=1 Rs=0.5)` |
|
|||
|
|
| `mosfet-bs170` | N-ch TO-92 | similar |
|
|||
|
|
| `mosfet-irf540` | N-ch power | `.model MIRF540 NMOS(Level=3 Vto=3.0 Kp=20 L=2u W=1 Rd=0.044 Rs=0)` |
|
|||
|
|
| `mosfet-irf9540` | P-ch power | `.model MIRF9540 PMOS(Level=3 Vto=-3 Kp=15 …)` |
|
|||
|
|
|
|||
|
|
> **Fuente de los parámetros**: se copiarán de las librerías SPICE estándar distribuidas con LTspice/Orcad (dominio público por práctica de la industria, los fabricantes los publican). Los modelos viven en `subcircuits.ts` como strings constantes. Un test (`modelCompiles.test.ts`) verifica que cada `.model` line se parsee sin errores.
|
|||
|
|
|
|||
|
|
### 4.3 Integrados analógicos (subcircuitos)
|
|||
|
|
|
|||
|
|
| `metadataId` | Parte | Implementación |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `opamp-lm358` | LM358 (dual, single-supply) | `.subckt LM358 inv non_inv VEE OUT VCC` — macromodelo con slew-rate, GBW=1MHz, CMRR, etc. |
|
|||
|
|
| `opamp-lm741` | LM741 single | `.subckt LM741 …` |
|
|||
|
|
| `opamp-tl072` | TL072 JFET-input | `.subckt TL072 …` |
|
|||
|
|
| `opamp-lm324` | LM324 quad | `.subckt LM324 …` |
|
|||
|
|
| `reg-7805` | LM7805 +5V regulator | `.subckt LM7805 Vin Vout GND` |
|
|||
|
|
| `reg-7812` | LM7812 +12V | `.subckt LM7812 Vin Vout GND` |
|
|||
|
|
| `reg-lm317` | Adjustable | `.subckt LM317 Vin Vout ADJ` |
|
|||
|
|
| `ref-tl431` | 2.5V shunt reference | `.subckt TL431 cathode anode ref` |
|
|||
|
|
| `timer-555` | NE555 bipolar | `.subckt NE555 GND TRIG OUT RESET CTRL THR DIS VCC` |
|
|||
|
|
| `bridge-rect` | DF04 bridge rectifier | 4× `D1N4007` estampados juntos |
|
|||
|
|
|
|||
|
|
**Fuente de los `.subckt`**: igualmente dominio público; los que existen en manuales de fabricante (TI, ON Semi, Fairchild) y se distribuyen con spice libraries. Commitear en `frontend/src/simulation/spice/subcircuits/*.cir` y cargar con `import.meta.glob` o similar al primer uso.
|
|||
|
|
|
|||
|
|
### 4.4 Sensores y entradas
|
|||
|
|
|
|||
|
|
Ya existen como componentes Velxio (`Potentiometer`, `NTCThermistor`, `Photoresistor`, `Pushbutton`). Se añade una **capa de emisión SPICE** sin cambiar el wokwi-element:
|
|||
|
|
|
|||
|
|
| Existente | Emisión SPICE |
|
|||
|
|
|---|---|
|
|||
|
|
| `ntc-temperature-sensor` | `R{id} {net1} {net2} {R(T)}` calculado desde `temperature` slider |
|
|||
|
|
| `photoresistor` | `R{id} {net1} {net2} {R(lux)}` |
|
|||
|
|
| `pushbutton` | `R{id} {net1} {net2} {pressed ? 0.01 : 1G}` |
|
|||
|
|
| `slide-switch` | `R` con valor según posición |
|
|||
|
|
| `rotary-encoder` | pines digitales, sin estampa (fuera del solver) |
|
|||
|
|
|
|||
|
|
### 4.5 Instrumentos (componentes "probe", no se estampan)
|
|||
|
|
|
|||
|
|
| `metadataId` | Función | Cómo lee |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `instr-voltmeter-dc` | Mide V entre dos terminales | `result.nodeVoltage(net+) − result.nodeVoltage(net−)` |
|
|||
|
|
| `instr-ammeter-dc` | Mide I en serie con un cable | Inserta `V{id}_sense 0V` en serie; lee `i(V{id}_sense)` |
|
|||
|
|
| `instr-multimeter` | V / I / R / Diode / Continuity | V y I igual que arriba. Para R: inyecta 1 mA de prueba y lee ΔV |
|
|||
|
|
| `instr-scope-probe` | Agrega el nodo como canal del osciloscopio (modo analógico) | Durante `.tran`, samplea `v(node)` y lo publica al `useOscilloscopeStore` |
|
|||
|
|
| `instr-signal-generator` | Fuente de señal editable (sin, pulse, pwl) | Estampa una `V{id}` con la forma de onda configurada |
|
|||
|
|
|
|||
|
|
**Amperímetro — detalle de implementación crítica**: ngspice no puede medir corriente en un cable arbitrario directamente. Hay que **intercalar una fuente de voltaje 0 V** en el cable:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
; en vez de: R1 a b 1k
|
|||
|
|
; cable a → ammeter → R1 → cable b
|
|||
|
|
|
|||
|
|
V_amm_X pre_r1 a 0 ; inserta V 0V para medir i
|
|||
|
|
R1 pre_r1 b 1k
|
|||
|
|
|
|||
|
|
; y leemos: i(V_amm_X)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
El NetlistBuilder detecta cuando un cable pasa por un amperímetro y corta la net para insertar la fuente.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. Algoritmo del NetlistBuilder
|
|||
|
|
|
|||
|
|
Toma `{ components, wires, boards, pinStates }` y produce un netlist string para ngspice.
|
|||
|
|
|
|||
|
|
```typescript
|
|||
|
|
function buildNetlist(ctx: {
|
|||
|
|
components: Component[];
|
|||
|
|
wires: Wire[];
|
|||
|
|
boards: BoardInstance[];
|
|||
|
|
pinStates: Record<string, { v: 0 | 5 } | { duty: number }>;
|
|||
|
|
probes: Probe[];
|
|||
|
|
analysis: 'op' | 'tran' | 'ac';
|
|||
|
|
tranStep?: number;
|
|||
|
|
tranStop?: number;
|
|||
|
|
}): string {
|
|||
|
|
// 1. Build Union-Find of (componentId, pinName) pairs from wires.
|
|||
|
|
const uf = new UnionFind<string>();
|
|||
|
|
for (const w of ctx.wires) {
|
|||
|
|
const a = `${w.start.componentId}:${w.start.pinName}`;
|
|||
|
|
const b = `${w.end.componentId}:${w.end.pinName}`;
|
|||
|
|
uf.add(a); uf.add(b);
|
|||
|
|
uf.union(a, b);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. Canonicalize known special nets: any pin labeled GND / VSS / VEE → 'gnd' (= '0' in spice)
|
|||
|
|
// VCC / VDD / 5V / 3V3 → 'vcc_<voltage>'
|
|||
|
|
for (const comp of ctx.components) {
|
|||
|
|
for (const pin of pinsOf(comp)) {
|
|||
|
|
const key = `${comp.id}:${pin.name}`;
|
|||
|
|
if (isGroundPin(pin)) uf.setCanonical(key, '0');
|
|||
|
|
else if (isVccPin(pin)) uf.setCanonical(key, 'vcc_rail');
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
for (const board of ctx.boards) {
|
|||
|
|
for (const pin of boardGndPins(board)) uf.setCanonical(`${board.id}:${pin}`, '0');
|
|||
|
|
for (const pin of boardVccPins(board)) uf.setCanonical(`${board.id}:${pin}`, 'vcc_rail');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. Allocate auto-names for every other net. Use stable hashing so netlists are diffable.
|
|||
|
|
const netNames = assignNetNames(uf); // net0, net1, ... deterministic
|
|||
|
|
|
|||
|
|
// 4. Detect floating nodes: nets with only reactive connections (C, L).
|
|||
|
|
// Add an auto-pull to 0 with 100 MΩ to prevent ngspice singular-matrix hang.
|
|||
|
|
const floatingNets = detectFloatingNets(ctx.components, netNames);
|
|||
|
|
|
|||
|
|
// 5. Emit cards
|
|||
|
|
const lines = [`Velxio circuit @ ${new Date().toISOString()}`];
|
|||
|
|
const uses: Set<string> = new Set(); // which .model / .subckt we need
|
|||
|
|
|
|||
|
|
for (const comp of ctx.components) {
|
|||
|
|
if (isInstrument(comp)) continue; // probes don't stamp
|
|||
|
|
const card = componentToSpice(comp, netNames, uses);
|
|||
|
|
if (card) lines.push(card);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 6. Ammeters: rewrite — for every ammeter, insert V<id>_sense 0 V between its terminals
|
|||
|
|
// and re-point connected components accordingly. (Handled inside componentToSpice.)
|
|||
|
|
|
|||
|
|
// 7. Add board GPIO sources
|
|||
|
|
for (const board of ctx.boards) {
|
|||
|
|
for (const [pinName, state] of Object.entries(ctx.pinStates[board.id] ?? {})) {
|
|||
|
|
const net = netNames.get(`${board.id}:${pinName}`);
|
|||
|
|
if (!net) continue;
|
|||
|
|
const v = 'duty' in state ? state.duty * board.vcc : state.v;
|
|||
|
|
lines.push(`V_${board.id}_${pinName} ${net} 0 DC ${v}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 8. Vcc rail source
|
|||
|
|
if (uf.has('vcc_rail')) lines.push(`V_VCC vcc_rail 0 DC ${dominantBoardVcc(ctx.boards)}`);
|
|||
|
|
|
|||
|
|
// 9. Auto-pull-downs on floating nets
|
|||
|
|
for (const net of floatingNets) lines.push(`R_autopull_${net} ${net} 0 100Meg`);
|
|||
|
|
|
|||
|
|
// 10. Models and subcircuits
|
|||
|
|
for (const m of uses) lines.push(...modelOrSubcktText(m));
|
|||
|
|
|
|||
|
|
// 11. Analysis
|
|||
|
|
if (ctx.analysis === 'op') lines.push('.op');
|
|||
|
|
else if (ctx.analysis === 'tran') lines.push(`.tran ${ctx.tranStep} ${ctx.tranStop}`);
|
|||
|
|
else if (ctx.analysis === 'ac') lines.push('.ac dec 20 1 1Meg');
|
|||
|
|
|
|||
|
|
lines.push('.end');
|
|||
|
|
return lines.join('\n');
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.1 Detección de nodos flotantes
|
|||
|
|
|
|||
|
|
**Regla práctica del sandbox** (gotcha G-8): cualquier nodo que sólo se conecte a capacitores/inductores y/o diodos/transistores sin camino DC a 0 hará que ngspice se cuelgue 60 s en recovery. La solución barata es añadir `R 100 MΩ` a ground en cada uno.
|
|||
|
|
|
|||
|
|
Algoritmo: para cada net, itera sus conexiones; si hay al menos una `R` finita en el mismo camino UF hacia `0`, es seguro. Si no, etiquétalo como flotante y añade el pull-down. Implementación O(V+E) con BFS.
|
|||
|
|
|
|||
|
|
### 5.2 Cache del netlist
|
|||
|
|
|
|||
|
|
Si `components` y `wires` no han cambiado desde la última invocación, reutiliza el string anterior y sólo sustituye los valores de las fuentes (pin states). Evita 10–30 ms de re-construcción de string en cada solve.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. Fases de entrega
|
|||
|
|
|
|||
|
|
### Fase 8.1 — Fundamentos (1 semana)
|
|||
|
|
|
|||
|
|
**Goal**: `eecircuit-engine` funcionando en el proyecto de Velxio, sin UI. Tests pasan en `frontend/src/__tests__/`.
|
|||
|
|
|
|||
|
|
**Entregables**:
|
|||
|
|
- `npm install eecircuit-engine` + marca de lazy-load en Vite.
|
|||
|
|
- Port de `SpiceEngine.ts` y `SpiceEngine.lazy.ts` (copia fiel del sandbox con tipos TS).
|
|||
|
|
- Smoke test: `frontend/src/__tests__/spice-smoke.test.ts` ejecuta un divisor de voltaje y verifica `v(out) = 6`.
|
|||
|
|
- Port de los 22 tests `spice_*.test.js` del sandbox.
|
|||
|
|
- Telemetría: logs `[SpiceEngine] boot in Xms` y `[SpiceEngine] solve in Xms`.
|
|||
|
|
|
|||
|
|
**DoD (Definition of Done)**:
|
|||
|
|
- `npm test` en `frontend/` pasa los 22 tests.
|
|||
|
|
- Bundle de dev (`npm run dev`) funciona sin errores de ESM/WASM.
|
|||
|
|
- Bundle de prod (`npm run build:docker`) no crece (el lazy chunk es separado).
|
|||
|
|
|
|||
|
|
### Fase 8.2 — Netlist Builder + mapeo de componentes existentes (1 semana)
|
|||
|
|
|
|||
|
|
**Goal**: NetlistBuilder puede tomar un escenario de Velxio (con R, LED, pot, NTC) y emitir un netlist válido. No hay UI todavía.
|
|||
|
|
|
|||
|
|
**Entregables**:
|
|||
|
|
- `NetlistBuilder.ts` con algoritmo de §5, UnionFind.
|
|||
|
|
- `componentToSpice.ts` — soporta los 4 pasivos de Velxio hoy: `resistor`, `led`, `capacitor`, `potentiometer`, + NTC, photoresistor, pushbutton.
|
|||
|
|
- Tests: `netlistBuilder.test.ts` con 10+ escenarios (divisor, RC charging, LED+R, pot+ADC).
|
|||
|
|
- Integración con `useSimulatorStore`: acción `runElectricalSolve` (no-op si `mode === 'off'`).
|
|||
|
|
|
|||
|
|
**DoD**:
|
|||
|
|
- Creamos un escenario programáticamente (no por UI), llamamos `runElectricalSolve`, verificamos que `store.electrical.nodeVoltages` tiene los valores correctos.
|
|||
|
|
- Port de los tests end-to-end del sandbox: `e2e_pot_pwm_led` y `e2e_thermistor` funcionan dentro de Velxio.
|
|||
|
|
|
|||
|
|
### Fase 8.3 — UI: toggle y overlay de voltajes (1 semana)
|
|||
|
|
|
|||
|
|
**Goal**: usuario puede activar modo eléctrico y ver voltajes en el canvas.
|
|||
|
|
|
|||
|
|
**Entregables**:
|
|||
|
|
- `ElectricalModeToggle.tsx` en toolbar (icono ⚡).
|
|||
|
|
- `ElectricalOverlay.tsx` — SVG overlay que etiqueta cada nodo con `V(n) ≈ 2.50 V`.
|
|||
|
|
- `SpiceErrorBanner.tsx` — si `!converged`, muestra banner con mensaje de ngspice (`getError()`).
|
|||
|
|
- `LED.tsx` — usar `brightness = electrical.componentCurrents[id] / 0.020` en lugar del bool actual.
|
|||
|
|
- Scheduler con debounce 50 ms.
|
|||
|
|
- Hook en `PinManager.onAnyPinChange` → solve.
|
|||
|
|
|
|||
|
|
**DoD**:
|
|||
|
|
- Activar toggle con un sketch "Blink" cargado → pin 13 alterna → LED se enciende con brillo calculado real.
|
|||
|
|
- Activar toggle con circuito NTC → `analogRead(A0)` refleja el voltaje real del divisor.
|
|||
|
|
- Probar con 5+ circuitos típicos del tutorial Velxio; todos convergen.
|
|||
|
|
|
|||
|
|
### Fase 8.4 — Nuevos componentes analógicos (2 semanas)
|
|||
|
|
|
|||
|
|
**Goal**: las 30 piezas nuevas del §4 aparecen en el ComponentPicker, se pueden arrastrar al canvas, tienen editor de valores.
|
|||
|
|
|
|||
|
|
**Entregables**:
|
|||
|
|
- Todos los React wrappers en `components-analog/` (15+ componentes con SVG, properties, pinInfo).
|
|||
|
|
- Metadata regenerada (`generate-analog-metadata.ts`) y agregada a `components-metadata.json`.
|
|||
|
|
- `ComponentValueEditor.tsx` con parser SI (`k`, `Meg`, `u`, `n`, `p`).
|
|||
|
|
- `componentToSpice.ts` expandido con todos los part numbers + `.subckt` para IC.
|
|||
|
|
- Biblioteca `subcircuits/` con los macromodelos (LM358, LM741, 555, 7805, LM317, TL431).
|
|||
|
|
- Tests: un test por parte (`bjt-2n3055`, `opamp-lm358` Schmitt trigger, `timer-555` astable real, etc.).
|
|||
|
|
|
|||
|
|
**DoD**:
|
|||
|
|
- Usuario puede construir un amplificador con LM358, medir la ganancia y coincide con cálculo manual.
|
|||
|
|
- Usuario puede construir un 555 astable, verificar la frecuencia con osciloscopio.
|
|||
|
|
- Usuario puede construir un regulador 7805 con capacitores y ver la salida estabilizada.
|
|||
|
|
- Todos los nuevos componentes aparecen en el picker bajo `analog-passive` / `analog-active`.
|
|||
|
|
|
|||
|
|
### Fase 8.5 — Instrumentos de medición (1 semana)
|
|||
|
|
|
|||
|
|
**Goal**: voltímetro, amperímetro, multímetro, signal generator, osciloscopio analógico.
|
|||
|
|
|
|||
|
|
**Entregables**:
|
|||
|
|
- `Voltmeter.tsx` — 2 terminales; display con unidades (mV/V/kV según magnitud).
|
|||
|
|
- `Ammeter.tsx` — inserta V sense en el cable; display con unidades (nA/µA/mA/A).
|
|||
|
|
- `Multimeter.tsx` — dial con modos V/I/R/Continuity/Diode.
|
|||
|
|
- `OscilloscopeAnalogProbe.tsx` — clip que se conecta a un nodo; extiende `useOscilloscopeStore` con canales analógicos.
|
|||
|
|
- Modificación de `Oscilloscope.tsx`: renderiza waveforms analógicos además de digitales. Usa `.tran` de ngspice para samplear.
|
|||
|
|
- `SignalGenerator.tsx` — UI con selector de forma de onda (DC/sine/square/triangle/PWL), amplitud, frecuencia, offset.
|
|||
|
|
- `MeasurementPanel.tsx` — panel lateral con tabla de todas las lecturas.
|
|||
|
|
|
|||
|
|
**DoD**:
|
|||
|
|
- Usuario pone voltímetro en un divisor → lee `3.33 V`, refresca al mover slider.
|
|||
|
|
- Usuario pone amperímetro en serie con un LED → lee `~13 mA`.
|
|||
|
|
- Usuario conecta signal generator (1 kHz sine, 1 Vpp) a filtro RC, probe del osciloscopio en la salida → ve Bode manual (atenuación visible al 10 kHz).
|
|||
|
|
|
|||
|
|
### Fase 8.6 — Validación + rollout (1–2 semanas)
|
|||
|
|
|
|||
|
|
**Goal**: release behind feature flag, telemetría, docs de usuario.
|
|||
|
|
|
|||
|
|
**Entregables**:
|
|||
|
|
- Feature flag: `VITE_ELECTRICAL_SIM=true` (prod), `false` (si bugs).
|
|||
|
|
- Docs de usuario: `docs/wiki/electrical-simulation-user-guide.md`.
|
|||
|
|
- Telemetría: opt-in, envía `solve_ms`, `component_count`, `converged` a un endpoint interno.
|
|||
|
|
- Tests de regresión: ningún sketch del examples gallery debe romperse.
|
|||
|
|
- Checklist de QA (en el PR final):
|
|||
|
|
- [ ] LED blink con nueva simulación eléctrica → brillo correcto
|
|||
|
|
- [ ] Pot + ADC → monotónico
|
|||
|
|
- [ ] NTC + ADC → temperatura recuperable
|
|||
|
|
- [ ] 555 astable → frecuencia correcta ±10 %
|
|||
|
|
- [ ] Op-amp inverter → ganancia −10
|
|||
|
|
- [ ] MOSFET switch → V_drain conmuta
|
|||
|
|
- [ ] Voltímetro / amperímetro → lecturas coinciden con cálculo manual
|
|||
|
|
- [ ] Toggle on/off → performance acceptable (< 100 ms solve)
|
|||
|
|
- [ ] Bundle size production → lazy chunk no bloquea initial load
|
|||
|
|
- [ ] 0 regresiones en examples gallery
|
|||
|
|
|
|||
|
|
**DoD**:
|
|||
|
|
- PR merged a `master`.
|
|||
|
|
- Blog post / release notes.
|
|||
|
|
- Roadmap actualizado.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. Mapeo de boards (múltiples MCUs conviviendo)
|
|||
|
|
|
|||
|
|
El survey reveló que Velxio tiene `boards[]`: múltiples Arduinos, RP2040, ESP32 en el mismo canvas. El NetlistBuilder debe manejar esto:
|
|||
|
|
|
|||
|
|
- Cada board **declara sus propios nets** con prefijo `{boardId}_{pinName}`. Ej: `uno1_d13`, `esp32a_gpio5`.
|
|||
|
|
- El **VCC rail no se comparte automáticamente** entre boards (pueden alimentarse a 5 V y 3.3 V).
|
|||
|
|
- Un cable entre `uno1:5V` y `esp32a:5V` los une en la misma net vía Union-Find.
|
|||
|
|
- Un cable entre `uno1:D2` y `esp32a:GPIO5` es legal (y común para buses UART/I²C). Ambos lados se modelan como `V<source>` pero sólo uno puede ser "driver" en un instante dado. En caso de conflicto (ambos OUTPUT diferente valor) → banner rojo "bus conflict" y ngspice resolverá con el promedio via GMIN.
|
|||
|
|
|
|||
|
|
### 7.1 RP2040 — ADC y PWM
|
|||
|
|
|
|||
|
|
- RP2040 tiene 3 canales ADC (GP26, GP27, GP28) + sensor de temperatura interno (GP4 hidden).
|
|||
|
|
- `RP2040SpiceBridge.ts` es prácticamente igual al de AVR, con:
|
|||
|
|
- Tensión de referencia 3.3 V
|
|||
|
|
- 12-bit ADC en lugar de 10
|
|||
|
|
- PWM: hasta 16 canales, cualquier pin; leer duty via `rp2040.getPWMDuty(pin)` (ya existe en `RP2040Simulator.ts`).
|
|||
|
|
|
|||
|
|
### 7.2 ESP32 — limitaciones
|
|||
|
|
|
|||
|
|
- ESP32 ADC es notoriamente no-lineal; el modelo SPICE de su ADC necesita una curva de corrección. MVP: lineal 0–3.3 V, 12-bit.
|
|||
|
|
- PWM ("LEDC") con 16 canales, resolución configurable hasta 20 bits. Leer duty via `esp32.getPWMDuty(channel)` (disponible en el emulador QEMU).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. Riesgos y mitigaciones
|
|||
|
|
|
|||
|
|
| Riesgo | Impacto | Mitigación |
|
|||
|
|
|---|---|---|
|
|||
|
|
| **Bundle +39 MB** | Usuarios en red lenta se frustran | Lazy-load detrás de toggle. Chunk separado. Cache agresivo. Primer click del toggle: splash "Downloading simulator (39 MB)…" |
|
|||
|
|
| **ngspice cuelga 60 s por matriz singular** | Freeze de UI | Validación pre-solve: detectar nodos flotantes con BFS; añadir pull-down 100 MΩ automáticamente. Logs claros si ocurre. Timeout 3 s con abort. |
|
|||
|
|
| **Co-sim > 100 ms por slice con circuitos grandes** | Lag visible | 1) debounce 50 ms en scheduler, 2) mover a Web Worker si > 20 componentes, 3) modo "DC only" por defecto, 4) UI indicador "computing…" si > 200 ms |
|
|||
|
|
| **Conflicto entre Velxio pin simulado y SPICE** | Resultados incorrectos | SPICE es la fuente de verdad para tensiones analógicas. Las mutaciones manuales vía UI (click en LED) se convierten en modo "sim off". |
|
|||
|
|
| **Modelos SPICE de terceros con licencia unclear** | Bloqueo legal | Usar sólo modelos **publicados por fabricantes** (TI, ON, Microchip) y distribuir como strings en el source bajo cláusula de fair use educativa. Documentar origen por cada `.subckt` en un comentario. |
|
|||
|
|
| **PWM cuasi-estático incorrecto para circuitos sensibles a ripple** | Mala UX en audio / switching power | Detectar: si hay capacitor < 1 µF en el camino del PWM → escalar automáticamente a modo `.tran` con edges reales y aviso "switching detail enabled, may be slower" |
|
|||
|
|
| **Regresión del emulador digital** | Tests existentes fallan | Toggle off es zero-risk (nada cambia en el solver). Tests de regresión de `frontend/src/__tests__/` deben seguir pasando con `VITE_ELECTRICAL_SIM=false`. |
|
|||
|
|
| **Modelos BJT/MOSFET no convergen en esquemas exotic** | Usuario frustrado | Modo "Beginner": modelos ideales (VCVS para op-amp, switch para transistor). Modo "Expert": modelos reales con `.model`. Toggle en opciones. |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. Criterios de éxito / métricas
|
|||
|
|
|
|||
|
|
**Cuantitativos**:
|
|||
|
|
|
|||
|
|
| Métrica | Objetivo |
|
|||
|
|
|---|---|
|
|||
|
|
| Tests de portabilidad desde sandbox | **47/47 pasando** en `frontend/` |
|
|||
|
|
| Tiempo de boot de ngspice (primera vez) | < 800 ms en laptop estándar |
|
|||
|
|
| Tiempo de solve para circuito típico (20 componentes) | < 50 ms |
|
|||
|
|
| Tamaño del chunk lazy | ≤ 45 MB comprimido |
|
|||
|
|
| Precisión en tests E2E vs analítico | < 2 % error |
|
|||
|
|
| Zero regresiones en modo off | 100 % de tests previos pasan |
|
|||
|
|
|
|||
|
|
**Cualitativos**:
|
|||
|
|
|
|||
|
|
- Un usuario con conocimiento de electrónica básica puede armar un amplificador BJT y ver la ganancia correcta en el osciloscopio.
|
|||
|
|
- Un usuario puede armar un 555 astable y medir la frecuencia con un probe.
|
|||
|
|
- Un usuario puede conectar un termistor a un Arduino y leer la temperatura real convertida desde ADC.
|
|||
|
|
- Un tutorial de "control de brillo de LED con potenciómetro" funciona end-to-end sin intervención manual.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 10. Fuera de alcance (futuras fases)
|
|||
|
|
|
|||
|
|
Se documentan aquí para que no se cuelen en esta fase por scope creep.
|
|||
|
|
|
|||
|
|
- **Análisis `.noise`** — ruido Johnson/shot/flicker. ngspice lo soporta; lo dejamos para cuando haya UI de audio.
|
|||
|
|
- **Análisis Monte Carlo de tolerancias** — simular 100 copias con ±5 % en resistencias.
|
|||
|
|
- **Temperatura ambiente global** — afecta parámetros de todos los componentes.
|
|||
|
|
- **Análisis `.pz`** (pole-zero) — diseño de filtros avanzados.
|
|||
|
|
- **Análisis S-parameter** — RF.
|
|||
|
|
- **Emulación de ICs digitales discretos** (74HC00, 74HC595, 4017) con timing real. Actualmente se podrían hacer con B-sources, pero se dejará para fase 9.
|
|||
|
|
- **Transformadores acoplados** (ngspice `K`) — para SMPS.
|
|||
|
|
- **Modelos térmicos** — acoplar disipación de potencia con perfil térmico.
|
|||
|
|
- **Biblioteca de "circuitos ejemplo"** — pre-cargados en el picker (Darlington, diff-amp, current mirror, etc.).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 11. Apéndices
|
|||
|
|
|
|||
|
|
### 11.1 Ejemplo concreto de netlist generado
|
|||
|
|
|
|||
|
|
Escenario: Arduino Uno con sketch que hace `analogWrite(9, 127)`, conectado a una red RC (10 kΩ + 1 µF) y un voltímetro en la salida.
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Velxio circuit @ 2026-04-15T12:00:00Z
|
|||
|
|
* Board pin sources
|
|||
|
|
V_uno1_d9 uno1_d9 0 DC 2.5 ; duty=0.5 × 5V
|
|||
|
|
* Components
|
|||
|
|
R_r1 uno1_d9 net0 10k ; R1 from D9 to intermediate net
|
|||
|
|
C_c1 net0 0 1u IC=0 ; C1 from intermediate to ground
|
|||
|
|
* Auto-pull (net0 has cap-to-ground, so it has a DC path via V_uno1_d9 → R_r1)
|
|||
|
|
* Vcc rail
|
|||
|
|
V_VCC vcc_rail 0 DC 5
|
|||
|
|
* Analysis
|
|||
|
|
.op
|
|||
|
|
.end
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Tras `runSim`: `v(net0) ≈ 2.5 V`. El voltímetro (que conoce sus 2 terminales = `net0` y `0`) muestra `2.50 V`.
|
|||
|
|
|
|||
|
|
### 11.2 Ejemplo con transistor
|
|||
|
|
|
|||
|
|
Escenario: 2N2222 en common-emitter, señal de entrada 10 mV AC a 1 kHz desde un signal generator, R_C=4.7k, R_E=1k bypasseado con 100 µF, osciloscopio en colector.
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
Velxio amplifier
|
|||
|
|
V_VCC vcc_rail 0 DC 12
|
|||
|
|
V_sg1 sg1_out 0 SIN(0 0.01 1k)
|
|||
|
|
C_cin sg1_out net_base 1u
|
|||
|
|
R_rb1 vcc_rail net_base 47k
|
|||
|
|
R_rb2 net_base 0 10k
|
|||
|
|
R_rc vcc_rail net_coll 4.7k
|
|||
|
|
R_re net_em 0 1k
|
|||
|
|
C_ce net_em 0 100u
|
|||
|
|
Q_q1 net_coll net_base net_em Q2N2222
|
|||
|
|
.model Q2N2222 NPN(Is=14.34f Bf=200 Vaf=74.03 …)
|
|||
|
|
C_cout net_coll scope_probe1 1u
|
|||
|
|
R_scope_input scope_probe1 0 1Meg ; high-Z scope input (1 MΩ)
|
|||
|
|
.tran 10u 6m
|
|||
|
|
.end
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
El osciloscopio samplea `v(scope_probe1)`; muestra la onda invertida y amplificada.
|
|||
|
|
|
|||
|
|
### 11.3 Tabla de conversión wokwi-elements existentes → SPICE
|
|||
|
|
|
|||
|
|
Extensión del mapeo del §4; fuente: survey de Velxio.
|
|||
|
|
|
|||
|
|
| wokwi tagName | `metadataId` actual | Emite SPICE | Notas |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| `<wokwi-resistor>` | `resistor` | **sí** | R lineal |
|
|||
|
|
| `<wokwi-resistor-us>` | `resistor-us` | **sí** | idem |
|
|||
|
|
| `<wokwi-led>` | `led` | **sí** | D con Is/n por color |
|
|||
|
|
| `<wokwi-rgb-led>` | `rgb-led` | parcialmente | 3 LEDs independientes |
|
|||
|
|
| `<wokwi-pushbutton>` | `pushbutton` | **sí** | R conmutable |
|
|||
|
|
| `<wokwi-slide-switch>` | `slide-switch` | **sí** | |
|
|||
|
|
| `<wokwi-potentiometer>` | `slide-potentiometer` | **sí** | dos R |
|
|||
|
|
| `<wokwi-photoresistor>` | `photoresistor` | **sí** | R(lux) |
|
|||
|
|
| `<wokwi-ntc-temperature-sensor>` | `ntc-temperature-sensor` | **sí** | R(T) |
|
|||
|
|
| `<wokwi-7segment>` | `7-segment` | **sí** | 8 LEDs |
|
|||
|
|
| `<wokwi-buzzer>` | `buzzer` | como R de 100 Ω | sonido fuera del solver |
|
|||
|
|
| `<wokwi-servo>` | `servo-horn` | R=50 Ω consumo | ángulo fuera del solver |
|
|||
|
|
| `<wokwi-dc-motor>` | `dc-motor` | R + L serie | back-EMF opcional |
|
|||
|
|
| `<wokwi-dht22>` | `dht22` | **no** | protocolo 1-wire, fuera |
|
|||
|
|
| `<wokwi-lcd1602>` | `lcd1602` | **no** | display, fuera |
|
|||
|
|
| `<wokwi-ssd1306>` | `ssd1306` | **no** | I²C, fuera |
|
|||
|
|
| `<wokwi-neopixel-matrix>` | `neopixel-matrix` | **no** | protocolo WS2812, fuera |
|
|||
|
|
| `<wokwi-mpu6050>` | `mpu6050` | **no** | I²C, fuera |
|
|||
|
|
|
|||
|
|
### 11.4 Mapeo de pines de part numbers reales
|
|||
|
|
|
|||
|
|
Ejemplo: **LM358** (DIP-8 dual op-amp).
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
+──┐ ┐──+
|
|||
|
|
OUT1─1 \_/ 8─VCC
|
|||
|
|
IN−1─2 7─OUT2
|
|||
|
|
IN+1─3 6─IN−2
|
|||
|
|
GND─4 5─IN+2
|
|||
|
|
+───────+
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
En Velxio, el `<wokwi-lm358>` (nuevo) expondrá 8 pines con esos nombres. El `componentToSpice` para el metadataId `opamp-lm358` emite:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
X_{id} {inp1} {inn1} {vcc} {out1} {gnd} {inp2} {inn2} {out2} LM358
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Y el `.subckt LM358` (importado de la biblioteca) hace el macromodelo completo.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 12. Checklist final antes de mergear cada PR
|
|||
|
|
|
|||
|
|
- [ ] Todos los tests del sandbox siguen pasando en su ubicación actual (baseline preservado)
|
|||
|
|
- [ ] Nuevos tests en `frontend/src/__tests__/` pasan
|
|||
|
|
- [ ] `npm run build:docker` exitoso
|
|||
|
|
- [ ] `npm run lint` clean
|
|||
|
|
- [ ] Feature flag `VITE_ELECTRICAL_SIM` respeta default off en prod hasta Fase 8.6
|
|||
|
|
- [ ] Bundle analyzer: el chunk de `eecircuit-engine` es `import()` separado
|
|||
|
|
- [ ] QA manual: al menos 3 circuitos representativos validados
|
|||
|
|
- [ ] Docs actualizados: entrada en `docs/wiki/` para cada nuevo componente
|
|||
|
|
- [ ] CLAUDE.md actualizado: sección "Electrical simulation"
|
|||
|
|
- [ ] Changelog: release notes preparadas
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 13. Referencias cruzadas
|
|||
|
|
|
|||
|
|
- Sandbox madre: [`test/test_circuit/`](../..)
|
|||
|
|
- Plan inicial (baseline): [`plan1.md`](./plan1.md)
|
|||
|
|
- Findings del sandbox: [`../autosearch/`](../autosearch/)
|
|||
|
|
- Wiki docs: [`docs/wiki/circuit-emulation.md`](../../../docs/wiki/circuit-emulation.md)
|
|||
|
|
- Plan anterior de integración (superseded): [`docs/wiki/circuit-emulation-velxio-integration.md`](../../../docs/wiki/circuit-emulation-velxio-integration.md) → este documento lo reemplaza con más detalle de componentes e instrumentos.
|