velxio/frontend/src/__tests__/instruments.test.ts

107 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Voltmeter and Ammeter instrument tests.
*
* Voltmeter reads v(+) v() across a pair of probe terminals.
* Ammeter reports current through an inline 0 V sense source.
*/
import { describe, it, expect } from 'vitest';
import { buildNetlist } from '../simulation/spice/NetlistBuilder';
import { runNetlist } from '../simulation/spice/SpiceEngine';
describe('Voltmeter (instr-voltmeter)', () => {
it('reads ~3.33V across the midpoint of a 1k+2k divider', { timeout: 30_000 }, async () => {
const { netlist } = buildNetlist({
components: [
{ id: 'r1', metadataId: 'resistor', properties: { value: '1k' } },
{ id: 'r2', metadataId: 'resistor', properties: { value: '2k' } },
{ id: 'vm', metadataId: 'instr-voltmeter', properties: {} },
],
wires: [
{
id: 'w1',
start: { componentId: 'uno', pinName: '5V' },
end: { componentId: 'r1', pinName: '1' },
},
{
id: 'w2',
start: { componentId: 'r1', pinName: '2' },
end: { componentId: 'r2', pinName: '1' },
},
{
id: 'w3',
start: { componentId: 'r2', pinName: '2' },
end: { componentId: 'uno', pinName: 'GND' },
},
{
id: 'w4',
start: { componentId: 'vm', pinName: 'V+' },
end: { componentId: 'r1', pinName: '2' },
},
{
id: 'w5',
start: { componentId: 'vm', pinName: 'V-' },
end: { componentId: 'uno', pinName: 'GND' },
},
],
boards: [{ id: 'uno', vcc: 5, pins: {}, groundPinNames: ['GND'], vccPinNames: ['5V'] }],
analysis: { kind: 'op' },
});
expect(netlist).toMatch(/R_vm_vmR/);
const result = await runNetlist(netlist);
// Find any voltage variable whose value is ≈ 3.33 V (the midpoint)
const expected = (5 * 2) / 3;
let found = false;
for (const name of result.variableNames) {
if (!name.startsWith('v(')) continue;
const v = result.dcValue(name);
if (Math.abs(v - expected) < 0.05) {
found = true;
break;
}
}
expect(found, `No net with V≈3.33V. Vars: ${result.variableNames.join(',')}`).toBe(true);
});
});
describe('Ammeter (instr-ammeter)', () => {
it('reads ~22.7 mA through a 220 Ω load on 5V', { timeout: 30_000 }, async () => {
// Circuit:
// +5V ── R (220Ω) ── [A+ ammeter A-] ── GND
const { netlist } = buildNetlist({
components: [
{ id: 'r1', metadataId: 'resistor', properties: { value: '220' } },
{ id: 'am', metadataId: 'instr-ammeter', properties: {} },
],
wires: [
{
id: 'w1',
start: { componentId: 'uno', pinName: '5V' },
end: { componentId: 'r1', pinName: '1' },
},
{
id: 'w2',
start: { componentId: 'r1', pinName: '2' },
end: { componentId: 'am', pinName: 'A+' },
},
{
id: 'w3',
start: { componentId: 'am', pinName: 'A-' },
end: { componentId: 'uno', pinName: 'GND' },
},
],
boards: [{ id: 'uno', vcc: 5, pins: {}, groundPinNames: ['GND'], vccPinNames: ['5V'] }],
analysis: { kind: 'op' },
});
expect(netlist).toMatch(/V_am_sense/);
const result = await runNetlist(netlist);
// i(V_am_sense) — ngspice names the branch current for V-source "v_am_sense"
const idx = result.findVar('i(v_am_sense)');
expect(idx).toBeGreaterThanOrEqual(0);
const i = result.dcValue('i(v_am_sense)');
// Convention: positive current enters the + terminal, flows into the source
expect(Math.abs(i)).toBeCloseTo(5 / 220, 3);
});
});