velxio/test/test_circuit/src/avr/intelHex.js

37 lines
1.2 KiB
JavaScript
Raw Normal View History

feat: electrical simulation via ngspice-WASM (eecircuit-engine) Adds full SPICE-accurate electrical simulation to Velxio, behind a lazy- loaded ⚡ toolbar toggle. Arduino / ESP32 / RP2040 sketches now co-simulate with real analog behaviour: correct voltages on wires, real I–V curves on LEDs, working potentiometers, NTC thermistors read by analogRead(), PWM driving RC filters, transistors, op-amps, diodes, MOSFETs, etc. Engine: eecircuit-engine (ngspice compiled to WebAssembly). Main bundle stays at 2.4 MB; the 20 MB SPICE chunk only loads when the user activates electrical mode. Disabled at build time via VITE_ELECTRICAL_SIM=false. Frontend additions: - simulation/spice/: SpiceEngine wrapper + lazy entry, NetlistBuilder with UnionFind over wires, componentToSpice mapping (24 metadataIds incl. real part numbers: 2N2222, 2N3055, BC547, IRF540, 2N7000, 1N4148, 1N4007, 1N4733, LEDs, NTC, op-amp ideal), CircuitScheduler with debounced coalescing, AVRSpiceBridge for quasi-static co-simulation. - store/useElectricalStore: Zustand slice, feature-flag aware. - components/analog-ui/: ⚡ toolbar toggle + SVG voltage overlay. - components/components-instruments/: Voltmeter, Ammeter probes. - 62 tests (spice-*, netlist-builder, component-to-spice, instruments). Sandbox (test/test_circuit/): 47-test validation sandbox that proved the approach (hand-rolled MNA baseline + ngspice pipeline) before porting to the app. Kept as reference. Docs: docs/wiki/circuit-emulation-*.md (13 engineering pages covering architecture, solvers, components, AVR bridge, gotchas, performance, integration plan, API reference, appendix) + electrical-simulation- user-guide.md (end-user facing). Reference plan: test/test_circuit/plan/phase_8_velxio_implementation.md Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:11:54 +07:00
/** Parse Intel HEX text into a program Uint8Array (byte-addressed). */
export function parseIntelHex(text) {
const bytes = [];
let highAddr = 0;
for (const rawLine of text.split('\n')) {
const line = rawLine.trim();
if (!line.startsWith(':')) continue;
const byteCount = parseInt(line.slice(1, 3), 16);
const addr = parseInt(line.slice(3, 7), 16);
const type = parseInt(line.slice(7, 9), 16);
if (type === 0) {
const fullAddr = (highAddr << 16) | addr;
for (let i = 0; i < byteCount; i++) {
const b = parseInt(line.slice(9 + i * 2, 11 + i * 2), 16);
bytes[fullAddr + i] = b;
}
} else if (type === 1) {
break;
} else if (type === 4) {
highAddr = parseInt(line.slice(9, 13), 16);
}
}
const maxAddr = bytes.length;
const out = new Uint8Array(maxAddr);
for (let i = 0; i < maxAddr; i++) out[i] = bytes[i] ?? 0;
return out;
}
/** Convert byte array into avr8js 16-bit word array (little-endian). */
export function bytesToProgramWords(bytes, wordCount = 0x8000 / 2) {
const prog = new Uint16Array(wordCount);
for (let i = 0; i < bytes.length; i += 2) {
prog[i >> 1] = (bytes[i] || 0) | ((bytes[i + 1] || 0) << 8);
}
return prog;
}