diff --git a/frontend/public/components-metadata.json b/frontend/public/components-metadata.json index 9581e207..c69842fc 100644 --- a/frontend/public/components-metadata.json +++ b/frontend/public/components-metadata.json @@ -517,6 +517,66 @@ "category": "analog", "description": "Single-channel optocoupler. CTR 80–600% (typ. 100%). Higher drive than 4N25, commonly used for MCU-to-mains isolation." }, + { + "thumbnail": "\n \n \n 5.00V\n 1.00A\n \n \n PSU\n ", + "tags": [ + "power-supply", + "regulated", + "source", + "psu", + "bench", + "lab", + "dc", + "ac", + "analog" + ], + "properties": [ + { + "name": "mode", + "type": "string", + "defaultValue": "dc", + "control": "select", + "options": [ + "dc", + "ac" + ], + "description": "DC for fixed rails, AC for sine waveform output" + }, + { + "name": "voltage", + "type": "number", + "defaultValue": 5, + "control": "text", + "description": "Output voltage in V (DC mode) or peak amplitude (AC mode)" + }, + { + "name": "frequency", + "type": "number", + "defaultValue": 50, + "control": "text", + "description": "Frequency in Hz (AC mode only)" + }, + { + "name": "currentLimit", + "type": "number", + "defaultValue": 1, + "control": "text", + "description": "Maximum sourced current in A. The verifier warns when exceeded." + } + ], + "defaultValues": { + "mode": "dc", + "voltage": 5, + "frequency": 50, + "currentLimit": 1 + }, + "pinCount": 2, + "id": "power-supply", + "tagName": "wokwi-signal-generator", + "name": "Regulated Power Supply", + "category": "analog", + "description": "Bench DC/AC power supply with optional current limit. DC mode for fixed rails; AC mode for sine. Verifier enforces the current limit per instance." + }, { "thumbnail": "\n \n \n SIGNAL-GENERATOR\n \n ", "tags": [ diff --git a/frontend/src/simulation/spice/componentToSpice.ts b/frontend/src/simulation/spice/componentToSpice.ts index 95149309..8face639 100644 --- a/frontend/src/simulation/spice/componentToSpice.ts +++ b/frontend/src/simulation/spice/componentToSpice.ts @@ -548,6 +548,41 @@ const MAPPERS: Record = { }; }, + // ── Regulated power supply (DC / AC, current-limit aware) ─────────────── + // Properties: + // mode: 'dc' | 'ac' (default 'dc') + // voltage: V (default 5) + // frequency: Hz (default 50, only used in AC mode) + // currentLimit: A (default 1; enforced by circuitVerifier, + // NOT SPICE — ngspice has no native + // foldback limiter) + // The visual element reuses wokwi-signal-generator (no new Web Component + // needed). ESR is derived from the currentLimit so a near-short reads + // as I ≈ 1.5·limit, which the verifier then flags as source-overload. + 'power-supply': (comp, netLookup) => { + const pos = netLookup('+') ?? netLookup('SIG') ?? netLookup('VCC'); + const neg = netLookup('−') ?? netLookup('-') ?? netLookup('GND'); + if (!pos || !neg) return null; + const mode = String(comp.properties.mode ?? 'dc').toLowerCase(); + const voltage = Number(comp.properties.voltage ?? 5); + const currentLimit = Math.max(0.01, Number(comp.properties.currentLimit ?? 1)); + const esr = Math.max(0.01, voltage / (currentLimit * 1.5)); + let source: string; + if (mode === 'ac') { + const freq = Number(comp.properties.frequency ?? 50); + source = `SIN(0 ${voltage} ${freq})`; + } else { + source = `DC ${voltage}`; + } + return { + cards: [ + `V_${comp.id} ${pos} ${comp.id}_int ${source}`, + `R_${comp.id}_esr ${comp.id}_int ${neg} ${esr}`, + ], + modelsUsed: new Set(), + }; + }, + // ── Signal generator (AC / pulse / DC) ─────────────────────────────────── // Properties: // waveform: 'sine' | 'square' | 'dc' (default 'sine') diff --git a/frontend/src/simulation/verify/circuitVerifier.ts b/frontend/src/simulation/verify/circuitVerifier.ts index acd0ea56..7ce172d6 100644 --- a/frontend/src/simulation/verify/circuitVerifier.ts +++ b/frontend/src/simulation/verify/circuitVerifier.ts @@ -128,22 +128,36 @@ export async function verifyCircuit( const branchCurrents = solve.branchCurrents; // ── Rule 1: short circuit / power source overload ────────────────────── - // Every voltage source (battery / signal-generator) emits a branch current - // `i(v_)`. SPICE convention: V-source's current is measured + → − - // INTERNALLY, so external current draw is the absolute value. + // Every voltage source (battery / signal-generator / power-supply) emits + // a branch current `i(v_)`. SPICE convention: V-source's current is + // measured + → − INTERNALLY, so external current draw is the absolute + // value. + // + // power-supply components carry a per-instance `currentLimit` property + // that overrides the global short-circuit threshold — that matches what + // a real bench supply does: a 100mA-limited supply trips at 100mA, a + // 5A-limited supply tolerates up to 5A before flagging fault. const sourceComponents = input.components.filter((c) => - /^(battery|signal-generator)/.test(c.metadataId), + /^(battery|signal-generator|power-supply)/.test(c.metadataId), ); for (const src of sourceComponents) { const i = Math.abs(branchCurrents[`v_${src.id}`] ?? 0); - if (i >= config.shortCircuitAmps) { + const perInstanceLimit = + src.metadataId === 'power-supply' + ? Number(src.properties?.currentLimit ?? config.shortCircuitAmps) + : config.shortCircuitAmps; + const threshold = Number.isFinite(perInstanceLimit) && perInstanceLimit > 0 + ? perInstanceLimit + : config.shortCircuitAmps; + if (i >= threshold) { + const isPsu = src.metadataId === 'power-supply'; errors.push({ severity: 'error', - code: 'short-circuit', + code: isPsu ? 'source-overload' : 'short-circuit', componentId: src.id, - message: `Possible short circuit — ${src.metadataId} ${src.id} is delivering ${formatAmps( - i, - )} (threshold ${formatAmps(config.shortCircuitAmps)}). Check for 5 V tied directly to GND.`, + message: isPsu + ? `Power supply ${src.id} is being asked for ${formatAmps(i)} — past its ${formatAmps(threshold)} current limit. A real bench supply would foldback or cut out. Raise the currentLimit or add more series resistance to the load.` + : `Possible short circuit — ${src.metadataId} ${src.id} is delivering ${formatAmps(i)} (threshold ${formatAmps(threshold)}). Check for power tied directly to GND.`, metric: i, }); } diff --git a/scripts/component-overrides.json b/scripts/component-overrides.json index 4761901e..243b24ae 100644 --- a/scripts/component-overrides.json +++ b/scripts/component-overrides.json @@ -1044,6 +1044,65 @@ "analog" ] }, + { + "id": "power-supply", + "tagName": "wokwi-signal-generator", + "name": "Regulated Power Supply", + "category": "analog", + "description": "Bench DC/AC power supply with optional current limit. DC mode for fixed rails (5V, 12V, etc.); AC mode for sine sources. Visually shares the signal-generator chassis so no new Web Component is needed. The current limit is enforced by the circuit verifier — if any load pulls more than the configured amperage, Run is blocked with a 'source-overload' warning, mimicking a real bench supply's foldback behavior.", + "properties": [ + { + "name": "mode", + "type": "string", + "defaultValue": "dc", + "control": "select", + "options": [ + "dc", + "ac" + ], + "description": "DC for fixed rails, AC for sine waveform output" + }, + { + "name": "voltage", + "type": "number", + "defaultValue": 5, + "control": "text", + "description": "Output voltage in V (DC mode) or peak amplitude (AC mode)" + }, + { + "name": "frequency", + "type": "number", + "defaultValue": 50, + "control": "text", + "description": "Frequency in Hz (AC mode only)" + }, + { + "name": "currentLimit", + "type": "number", + "defaultValue": 1, + "control": "text", + "description": "Maximum sourced current in A. The verifier warns when exceeded." + } + ], + "defaultValues": { + "mode": "dc", + "voltage": 5, + "frequency": 50, + "currentLimit": 1 + }, + "pinCount": 2, + "tags": [ + "power-supply", + "regulated", + "source", + "psu", + "bench", + "lab", + "dc", + "ac", + "analog" + ] + }, { "id": "signal-generator", "tagName": "wokwi-signal-generator",