diff --git a/README.md b/README.md index 839c144f..8d048d48 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,9 @@ resistorProps.parse({ resistance: "10k" } as ResistorPropsInput); | `` | [`AnalogAcSweepSimulationProps`](#analogacsweepsimulationprops-analogacsweepsimulation) | | `` | [`AnalogDcOperatingPointSimulationProps`](#analogdcoperatingpointsimulationprops-analogdcoperatingpointsimulation) | | `` | [`AnalogDcSweepSimulationProps`](#analogdcsweepsimulationprops-analogdcsweepsimulation) | +| `` | [`AnalogMeasurementProps`](#analogmeasurementprops-analogmeasurement) | | `` | [`AnalogSimulationProps`](#analogsimulationprops-analogsimulation) | -| `` | [`AnalogResistanceSweepParameterProps`](#analogresistancesweepparameterprops-analogsweepparameter) | +| `` | [`AnalogSweepCoordinatesProps`](#analogsweepcoordinatesprops-analogsweepparameter) | | `` | [`AnalogTransientSimulationProps`](#analogtransientsimulationprops-analogtransientsimulation) | | `` | [`AutoroutingPhaseProps`](#autoroutingphaseprops-autoroutingphase) | | `` | [`BatteryProps`](#batteryprops-battery) | @@ -280,6 +281,21 @@ export interface AnalogDcSweepSimulationProps extends AnalogAnalysisSimulationBa [Source](https://github.com/tscircuit/props/blob/main/lib/components/analogdcsweepsimulation.ts) +### AnalogMeasurementProps `` + +```ts +export interface AnalogMeasurementProps { + /** Stable name written to the simulation measurement result. */ + name: string; + /** Unit of the scalar returned by measureFn. */ + unit: string; + /** Computes one scalar for each transient simulation run. */ + measureFn: (context: AnalogTransientMeasurementContext) => number; +} +``` + +[Source](https://github.com/tscircuit/props/blob/main/lib/components/analogmeasurement.ts) + ### AnalogSimulationProps `` ```ts @@ -297,13 +313,24 @@ export interface AnalogSimulationProps { [Source](https://github.com/tscircuit/props/blob/main/lib/components/analogsimulation.ts) -### AnalogResistanceSweepParameterProps `` +### AnalogSweepCoordinatesProps `` ```ts -export interface AnalogResistanceSweepParameterProps extends AnalogSweepCoordinatesProps { - parameterType: "resistance"; - /** Selector for the resistor whose simulation-only resistance is swept. */ - resistorRef: string; +export interface AnalogSweepCoordinatesProps { + /** Stable identity for this sweep parameter. */ + name?: string; + /** Explicit parameter coordinates. Cannot be combined with start/stop/step. */ + values?: Array; + /** First generated parameter coordinate. Requires stop and step. */ + start?: number | string; + /** Last generated parameter coordinate. Requires start and step. */ + stop?: number | string; + /** Nonzero parameter increment directed from start toward stop. */ + step?: number | string; + /** Optional graph coordinates corresponding one-to-one with the physical sweep values. */ + displayValues?: number[]; + /** Unit for displayValues. Required when displayValues is provided. */ + displayUnit?: string; } ``` @@ -698,6 +725,8 @@ export interface CurrentSourceProps< acMagnitude?: number | string; /** Small-signal AC phase. Raw numbers are degrees. */ acPhase?: number | string; + /** Piecewise-linear transient source waveform. Cannot be combined with waveShape. */ + currentWaveform?: CurrentWaveformPoint[]; connections?: Connections; } ``` @@ -2191,6 +2220,8 @@ export interface VoltageSourceProps< acMagnitude?: number | string; /** Small-signal AC phase. Raw numbers are degrees. */ acPhase?: number | string; + /** Piecewise-linear transient source waveform. Cannot be combined with waveShape. */ + voltageWaveform?: VoltageWaveformPoint[]; connections?: Connections; } ``` diff --git a/generated/COMPONENT_TYPES.md b/generated/COMPONENT_TYPES.md index 1c9d7477..8d219954 100644 --- a/generated/COMPONENT_TYPES.md +++ b/generated/COMPONENT_TYPES.md @@ -957,6 +957,25 @@ export const schematicPinStyle = z.record( }), ``` +### simulation-waveform + +```typescript +export const validateStrictlyIncreasingWaveformTimes = ( + points: ReadonlyArray>, + context: z.RefinementCtx, +) => { + for (let index = 1; index < points.length; index++) { + if (points[index]!.time <= points[index - 1]!.time) { + context.addIssue({ + code: "custom", + path: [index, "time"], + message: "Waveform times must be strictly increasing", + }) + } + } +} +``` + ### url ```typescript @@ -1057,6 +1076,33 @@ export const analogDcSweepSimulationProps = z }) ``` +### analogmeasurement + +```typescript +export interface TransientMeasurementSeries { + timestampsMs: readonly number[] + values: readonly number[] +} +export interface AnalogTransientMeasurementContext { + getVoltage: (selector: string) => TransientMeasurementSeries + getCurrent: (selector: string) => TransientMeasurementSeries +} +export interface AnalogMeasurementProps { + name: string + unit: string + measureFn: (context: AnalogTransientMeasurementContext) => number +} +/** Computes one scalar for each transient simulation run. */ +export const analogMeasurementProps = z + .object({ + name: z.string().min(1), + unit: z.string().min(1), + measureFn: z.custom( + (value) => typeof value === "function", + ), + }) +``` + ### analogsimulation ```typescript @@ -1083,7 +1129,7 @@ export interface AnalogAnalysisSimulationBaseProps { graphIndependentAxes?: boolean children?: ReactNode } -/** Optional nested sweep parameter for repeated analysis runs. */ +/** Optional nested sweep parameters and transient measurements. */ export const analogAnalysisSimulationBaseProps = { name: z.string().optional(), spiceEngine: spiceEngine.optional(), @@ -1108,7 +1154,16 @@ export const analogSimulationProps = z.object({ ### analogsweepparameter ```typescript -/** Nonzero parameter increment directed from start toward stop. */ +export interface AnalogSweepCoordinatesProps { + name?: string + values?: Array + start?: number | string + stop?: number | string + step?: number | string + displayValues?: number[] + displayUnit?: string +} +/** Unit for displayValues. Required when displayValues is provided. */ export interface AnalogResistanceSweepParameterProps extends AnalogSweepCoordinatesProps { parameterType: "resistance" @@ -1787,6 +1842,11 @@ export const crystalProps = commonComponentProps.extend({ ### currentsource ```typescript +export interface CurrentWaveformPoint { + time: number | string + current: number | string +} +/** Source current at this point. Raw numbers are amperes. */ export interface CurrentSourceProps extends CommonComponentProps { current?: number | string @@ -1797,9 +1857,10 @@ export interface CurrentSourceProps dutyCycle?: number | string acMagnitude?: number | string acPhase?: number | string + currentWaveform?: CurrentWaveformPoint[] connections?: Connections } -/** Small-signal AC phase. Raw numbers are degrees. */ +/** Piecewise-linear transient source waveform. Cannot be combined with waveShape. */ export const currentSourceProps = commonComponentProps.extend({ current: current.optional(), frequency: frequency.optional(), @@ -1809,6 +1870,7 @@ export const currentSourceProps = commonComponentProps.extend({ dutyCycle: percentage.optional(), acMagnitude: current.optional(), acPhase: rotation.optional(), + currentWaveform: currentWaveform.optional(), connections: createConnectionsProp(currentSourcePinLabels).optional(), }) ``` @@ -4679,6 +4741,11 @@ export const voltageProbeProps = commonComponentProps ### voltagesource ```typescript +export interface VoltageWaveformPoint { + time: number | string + voltage: number | string +} +/** Source voltage at this point. Raw numbers are volts. */ export interface VoltageSourceProps extends CommonComponentProps { voltage?: number | string @@ -4694,9 +4761,10 @@ export interface VoltageSourceProps period?: number | string acMagnitude?: number | string acPhase?: number | string + voltageWaveform?: VoltageWaveformPoint[] connections?: Connections } -/** Small-signal AC phase. Raw numbers are degrees. */ +/** Piecewise-linear transient source waveform. Cannot be combined with waveShape. */ export const voltageSourceProps = commonComponentProps.extend({ voltage: voltage.optional(), frequency: frequency.optional(), @@ -4711,6 +4779,7 @@ export const voltageSourceProps = commonComponentProps.extend({ period: ms.optional(), acMagnitude: voltage.optional(), acPhase: rotation.optional(), + voltageWaveform: voltageWaveform.optional(), connections: createConnectionsProp(voltageSourcePinLabels).optional(), }) ``` diff --git a/generated/PROPS_OVERVIEW.md b/generated/PROPS_OVERVIEW.md index 013b7593..64aed1a1 100644 --- a/generated/PROPS_OVERVIEW.md +++ b/generated/PROPS_OVERVIEW.md @@ -53,7 +53,7 @@ export interface AnalogAnalysisSimulationBaseProps { spiceOptions?: SpiceOptions /** Render each probe with an independent vertical graph scale. */ graphIndependentAxes?: boolean - /** Optional nested sweep parameter for repeated analysis runs. */ + /** Optional nested sweep parameters and transient measurements. */ children?: ReactNode } @@ -95,6 +95,16 @@ export interface AnalogInductanceSweepParameterProps } +export interface AnalogMeasurementProps { + /** Stable name written to the simulation measurement result. */ + name: string + /** Unit of the scalar returned by measureFn. */ + unit: string + /** Computes one scalar for each transient simulation run. */ + measureFn: (context: AnalogTransientMeasurementContext) => number +} + + export interface AnalogResistanceSweepParameterProps extends AnalogSweepCoordinatesProps { parameterType: "resistance" @@ -115,6 +125,30 @@ export interface AnalogSimulationProps { } +export interface AnalogSweepCoordinatesProps { + /** Stable identity for this sweep parameter. */ + name?: string + /** Explicit parameter coordinates. Cannot be combined with start/stop/step. */ + values?: Array + /** First generated parameter coordinate. Requires stop and step. */ + start?: number | string + /** Last generated parameter coordinate. Requires start and step. */ + stop?: number | string + /** Nonzero parameter increment directed from start toward stop. */ + step?: number | string + /** Optional graph coordinates corresponding one-to-one with the physical sweep values. */ + displayValues?: number[] + /** Unit for displayValues. Required when displayValues is provided. */ + displayUnit?: string +} + + +export interface AnalogTransientMeasurementContext { + getVoltage: (selector: string) => TransientMeasurementSeries + getCurrent: (selector: string) => TransientMeasurementSeries +} + + export interface AnalogTransientSimulationProps extends AnalogAnalysisSimulationBaseProps { /** Simulation duration. Raw numbers are milliseconds. Defaults to 10ms. */ @@ -817,10 +851,20 @@ export interface CurrentSourceProps acMagnitude?: number | string /** Small-signal AC phase. Raw numbers are degrees. */ acPhase?: number | string + /** Piecewise-linear transient source waveform. Cannot be combined with waveShape. */ + currentWaveform?: CurrentWaveformPoint[] connections?: Connections } +export interface CurrentWaveformPoint { + /** Time from the start of the transient simulation. Raw numbers are milliseconds. */ + time: number | string + /** Source current at this point. Raw numbers are amperes. */ + current: number | string +} + + export interface CustomDrcCheckContext { select: CustomDrcSelect selectAll: CustomDrcSelectAll @@ -2373,6 +2417,11 @@ export interface SolderJumperProps extends JumperProps { export interface SpiceEngine { + /** + * Maximum number of independent simulations this engine can safely execute + * at once. Engines are treated as serial when this is omitted. + */ + maxConcurrentSimulations?: number simulate: (spiceString: string) => Promise } @@ -2549,6 +2598,12 @@ export interface ToolingrailProps { } +export interface TransientMeasurementSeries { + timestampsMs: readonly number[] + values: readonly number[] +} + + export interface TransistorProps extends CommonComponentProps { type: "npn" | "pnp" | "bjt" | "jfet" | "mosfet" | "igbt" @@ -2597,7 +2652,17 @@ export interface VoltageSourceProps acMagnitude?: number | string /** Small-signal AC phase. Raw numbers are degrees. */ acPhase?: number | string + /** Piecewise-linear transient source waveform. Cannot be combined with waveShape. */ + voltageWaveform?: VoltageWaveformPoint[] connections?: Connections } + +export interface VoltageWaveformPoint { + /** Time from the start of the transient simulation. Raw numbers are milliseconds. */ + time: number | string + /** Source voltage at this point. Raw numbers are volts. */ + voltage: number | string +} + ``` diff --git a/lib/common/simulation-waveform.ts b/lib/common/simulation-waveform.ts new file mode 100644 index 00000000..ea8fd757 --- /dev/null +++ b/lib/common/simulation-waveform.ts @@ -0,0 +1,16 @@ +import type { z } from "zod" + +export const validateStrictlyIncreasingWaveformTimes = ( + points: ReadonlyArray>, + context: z.RefinementCtx, +) => { + for (let index = 1; index < points.length; index++) { + if (points[index]!.time <= points[index - 1]!.time) { + context.addIssue({ + code: "custom", + path: [index, "time"], + message: "Waveform times must be strictly increasing", + }) + } + } +} diff --git a/lib/components/analogmeasurement.ts b/lib/components/analogmeasurement.ts new file mode 100644 index 00000000..a5217f99 --- /dev/null +++ b/lib/components/analogmeasurement.ts @@ -0,0 +1,36 @@ +import { expectTypesMatch } from "lib/typecheck" +import { z } from "zod" + +export interface TransientMeasurementSeries { + timestampsMs: readonly number[] + values: readonly number[] +} + +export interface AnalogTransientMeasurementContext { + getVoltage: (selector: string) => TransientMeasurementSeries + getCurrent: (selector: string) => TransientMeasurementSeries +} + +export interface AnalogMeasurementProps { + /** Stable name written to the simulation measurement result. */ + name: string + /** Unit of the scalar returned by measureFn. */ + unit: string + /** Computes one scalar for each transient simulation run. */ + measureFn: (context: AnalogTransientMeasurementContext) => number +} + +export const analogMeasurementProps = z + .object({ + name: z.string().min(1), + unit: z.string().min(1), + measureFn: z.custom( + (value) => typeof value === "function", + ), + }) + .strict() + +expectTypesMatch< + AnalogMeasurementProps, + z.input +>(true) diff --git a/lib/components/analogsimulation.ts b/lib/components/analogsimulation.ts index bbdc8b46..739b34a3 100644 --- a/lib/components/analogsimulation.ts +++ b/lib/components/analogsimulation.ts @@ -42,7 +42,7 @@ export interface AnalogAnalysisSimulationBaseProps { spiceOptions?: SpiceOptions /** Render each probe with an independent vertical graph scale. */ graphIndependentAxes?: boolean - /** Optional nested sweep parameter for repeated analysis runs. */ + /** Optional nested sweep parameters and transient measurements. */ children?: ReactNode } diff --git a/lib/components/analogsweepparameter.ts b/lib/components/analogsweepparameter.ts index 88646ca6..a94a22ca 100644 --- a/lib/components/analogsweepparameter.ts +++ b/lib/components/analogsweepparameter.ts @@ -8,7 +8,7 @@ import { import { expectTypesMatch } from "lib/typecheck" import { z } from "zod" -interface AnalogSweepCoordinatesProps { +export interface AnalogSweepCoordinatesProps { /** Stable identity for this sweep parameter. */ name?: string /** Explicit parameter coordinates. Cannot be combined with start/stop/step. */ @@ -19,6 +19,10 @@ interface AnalogSweepCoordinatesProps { stop?: number | string /** Nonzero parameter increment directed from start toward stop. */ step?: number | string + /** Optional graph coordinates corresponding one-to-one with the physical sweep values. */ + displayValues?: number[] + /** Unit for displayValues. Required when displayValues is provided. */ + displayUnit?: string } export interface AnalogResistanceSweepParameterProps @@ -70,6 +74,8 @@ const createAnalogSweepCoordinateProps = ( start: sweepQuantity.optional(), stop: sweepQuantity.optional(), step: sweepQuantity.optional(), + displayValues: z.array(z.number().finite()).min(1).optional(), + displayUnit: z.string().min(1).optional(), }) export const analogResistanceSweepParameterProps = z @@ -117,6 +123,8 @@ interface ParsedAnalogSweepCoordinates { start?: number stop?: number step?: number + displayValues?: number[] + displayUnit?: string } const validateAnalogSweepCoordinates = ( @@ -139,6 +147,26 @@ const validateAnalogSweepCoordinates = ( return } + const hasDisplayValues = sweepCoordinates.displayValues !== undefined + const hasDisplayUnit = sweepCoordinates.displayUnit !== undefined + if (hasDisplayValues !== hasDisplayUnit) { + context.addIssue({ + code: z.ZodIssueCode.custom, + message: "displayValues and displayUnit must be provided together", + }) + } + if ( + sweepCoordinates.values && + sweepCoordinates.displayValues && + sweepCoordinates.values.length !== sweepCoordinates.displayValues.length + ) { + context.addIssue({ + code: z.ZodIssueCode.custom, + path: ["displayValues"], + message: "displayValues and values must have the same length", + }) + } + if (rangeCoordinateCount !== 0 && rangeCoordinateCount !== 3) { context.addIssue({ code: z.ZodIssueCode.custom, diff --git a/lib/components/currentsource.ts b/lib/components/currentsource.ts index 0a2268d7..03b04caf 100644 --- a/lib/components/currentsource.ts +++ b/lib/components/currentsource.ts @@ -1,4 +1,4 @@ -import { frequency, rotation, current } from "circuit-json" +import { current, frequency, ms, rotation } from "circuit-json" import { type CommonComponentProps, commonComponentProps, @@ -9,10 +9,18 @@ import type { Connections } from "lib/utility-types/connections-and-selectors" import { expectTypesMatch } from "lib/typecheck" import { z } from "zod" import { type WaveShape } from "./voltagesource" +import { validateStrictlyIncreasingWaveformTimes } from "../common/simulation-waveform" export const currentSourcePinLabels = ["pin1", "pin2", "pos", "neg"] as const export type CurrentSourcePinLabels = (typeof currentSourcePinLabels)[number] +export interface CurrentWaveformPoint { + /** Time from the start of the transient simulation. Raw numbers are milliseconds. */ + time: number | string + /** Source current at this point. Raw numbers are amperes. */ + current: number | string +} + export interface CurrentSourceProps extends CommonComponentProps { current?: number | string @@ -25,6 +33,8 @@ export interface CurrentSourceProps acMagnitude?: number | string /** Small-signal AC phase. Raw numbers are degrees. */ acPhase?: number | string + /** Piecewise-linear transient source waveform. Cannot be combined with waveShape. */ + currentWaveform?: CurrentWaveformPoint[] connections?: Connections } @@ -46,6 +56,18 @@ const percentage = z .max(1, "Duty cycle cannot be greater than 100%"), ) +const currentWaveform = z + .array( + z + .object({ + time: ms.pipe(z.number().nonnegative()), + current, + }) + .strict(), + ) + .min(1) + .superRefine(validateStrictlyIncreasingWaveformTimes) + export const currentSourceProps = commonComponentProps.extend({ current: current.optional(), frequency: frequency.optional(), @@ -55,6 +77,7 @@ export const currentSourceProps = commonComponentProps.extend({ dutyCycle: percentage.optional(), acMagnitude: current.optional(), acPhase: rotation.optional(), + currentWaveform: currentWaveform.optional(), connections: createConnectionsProp(currentSourcePinLabels).optional(), }) diff --git a/lib/components/voltagesource.ts b/lib/components/voltagesource.ts index c0ffdf09..0e731476 100644 --- a/lib/components/voltagesource.ts +++ b/lib/components/voltagesource.ts @@ -8,12 +8,20 @@ import { createConnectionsProp } from "lib/common/connectionsProp" import type { Connections } from "lib/utility-types/connections-and-selectors" import { expectTypesMatch } from "lib/typecheck" import { z } from "zod" +import { validateStrictlyIncreasingWaveformTimes } from "../common/simulation-waveform" export type WaveShape = "sinewave" | "square" | "triangle" | "sawtooth" export const voltageSourcePinLabels = ["pin1", "pin2", "pos", "neg"] as const export type VoltageSourcePinLabels = (typeof voltageSourcePinLabels)[number] +export interface VoltageWaveformPoint { + /** Time from the start of the transient simulation. Raw numbers are milliseconds. */ + time: number | string + /** Source voltage at this point. Raw numbers are volts. */ + voltage: number | string +} + export interface VoltageSourceProps extends CommonComponentProps { voltage?: number | string @@ -31,6 +39,8 @@ export interface VoltageSourceProps acMagnitude?: number | string /** Small-signal AC phase. Raw numbers are degrees. */ acPhase?: number | string + /** Piecewise-linear transient source waveform. Cannot be combined with waveShape. */ + voltageWaveform?: VoltageWaveformPoint[] connections?: Connections } @@ -52,6 +62,18 @@ const percentage = z .max(1, "Duty cycle cannot be greater than 100%"), ) +const voltageWaveform = z + .array( + z + .object({ + time: ms.pipe(z.number().nonnegative()), + voltage, + }) + .strict(), + ) + .min(1) + .superRefine(validateStrictlyIncreasingWaveformTimes) + export const voltageSourceProps = commonComponentProps.extend({ voltage: voltage.optional(), frequency: frequency.optional(), @@ -66,6 +88,7 @@ export const voltageSourceProps = commonComponentProps.extend({ period: ms.optional(), acMagnitude: voltage.optional(), acPhase: rotation.optional(), + voltageWaveform: voltageWaveform.optional(), connections: createConnectionsProp(voltageSourcePinLabels).optional(), }) diff --git a/lib/index.ts b/lib/index.ts index 7a8f1f0f..9c81bcab 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -75,6 +75,7 @@ export * from "./components/analogdcoperatingpointsimulation" export * from "./components/analogdcsweepsimulation" export * from "./components/analogacsweepsimulation" export * from "./components/analogsweepparameter" +export * from "./components/analogmeasurement" export * from "./components/autoroutingphase" export * from "./components/spicemodel" export * from "./manual-edits" diff --git a/lib/platformConfig.ts b/lib/platformConfig.ts index 12d1a1c7..dace4963 100644 --- a/lib/platformConfig.ts +++ b/lib/platformConfig.ts @@ -28,6 +28,11 @@ export interface SpiceEngineSimulationResult { } export interface SpiceEngine { + /** + * Maximum number of independent simulations this engine can safely execute + * at once. Engines are treated as serial when this is omitted. + */ + maxConcurrentSimulations?: number simulate: (spiceString: string) => Promise } diff --git a/scripts/generate-readme-docs.ts b/scripts/generate-readme-docs.ts index 1641bcc7..e22cb278 100644 --- a/scripts/generate-readme-docs.ts +++ b/scripts/generate-readme-docs.ts @@ -9,6 +9,7 @@ const namespacedAnalogComponentNames = new Set([ "analogacsweepsimulation", "analogdcoperatingpointsimulation", "analogdcsweepsimulation", + "analogmeasurement", "analogsweepparameter", "analogtransientsimulation", ]) diff --git a/tests/analog-simulations.test.ts b/tests/analog-simulations.test.ts index 9f1f5f84..4640c47f 100644 --- a/tests/analog-simulations.test.ts +++ b/tests/analog-simulations.test.ts @@ -2,6 +2,7 @@ import { expect, test } from "bun:test" import { analogAcSweepSimulationProps } from "lib/components/analogacsweepsimulation" import { analogDcOperatingPointSimulationProps } from "lib/components/analogdcoperatingpointsimulation" import { analogDcSweepSimulationProps } from "lib/components/analogdcsweepsimulation" +import { analogMeasurementProps } from "lib/components/analogmeasurement" import { analogSweepParameterProps } from "lib/components/analogsweepparameter" import { analogTransientSimulationProps } from "lib/components/analogtransientsimulation" @@ -142,3 +143,38 @@ test("analog parameter sweep accepts values or a complete range", () => { }), ).toThrow("Provide either values or start/stop/step") }) + +test("analog measurement preserves the measurement function", () => { + const measureFn = () => 3.3 + const parsed = analogMeasurementProps.parse({ + name: "settled-output", + unit: "V", + measureFn, + }) + + expect(parsed.measureFn).toBe(measureFn) +}) + +test("analog parameter sweep accepts aligned display coordinates", () => { + const parsed = analogSweepParameterProps.parse({ + parameterType: "resistance", + resistorRef: ".R_FB_TOP", + values: ["236.6kΩ", "511kΩ", "855.4kΩ"], + displayValues: [1.8, 3.3, 5.2], + displayUnit: "V", + }) + + expect(parsed.displayValues).toEqual([1.8, 3.3, 5.2]) +}) + +test("analog parameter sweep rejects misaligned display coordinates", () => { + expect(() => + analogSweepParameterProps.parse({ + parameterType: "resistance", + resistorRef: ".R_FB_TOP", + values: ["236.6kΩ", "511kΩ"], + displayValues: [1.8], + displayUnit: "V", + }), + ).toThrow("displayValues and values must have the same length") +}) diff --git a/tests/currentsource.test.ts b/tests/currentsource.test.ts index b3df62de..d371f6cc 100644 --- a/tests/currentsource.test.ts +++ b/tests/currentsource.test.ts @@ -177,3 +177,32 @@ test("should allow optional connections", () => { const parsed = currentSourceProps.parse(rawProps) expect(parsed.connections).toBeUndefined() }) + +test("should parse a piecewise-linear current waveform", () => { + const parsed = currentSourceProps.parse({ + name: "ILOAD", + currentWaveform: [ + { time: "0ms", current: "100mA" }, + { time: "1ms", current: "100mA" }, + { time: "1.001ms", current: "1A" }, + ], + }) + + expect(parsed.currentWaveform).toEqual([ + { time: 0, current: 0.1 }, + { time: 1, current: 0.1 }, + { time: 1.001, current: 1 }, + ]) +}) + +test("should reject non-increasing current waveform times", () => { + expect(() => + currentSourceProps.parse({ + name: "ILOAD", + currentWaveform: [ + { time: "1ms", current: "100mA" }, + { time: "1ms", current: "1A" }, + ], + }), + ).toThrow("Waveform times must be strictly increasing") +}) diff --git a/tests/voltagesource.test.ts b/tests/voltagesource.test.ts index 7848cdef..10da050e 100644 --- a/tests/voltagesource.test.ts +++ b/tests/voltagesource.test.ts @@ -196,3 +196,20 @@ test("should allow optional connections", () => { const parsed = voltageSourceProps.parse(rawProps) expect(parsed.connections).toBeUndefined() }) + +test("should parse a piecewise-linear voltage waveform", () => { + const parsed = voltageSourceProps.parse({ + name: "VIN", + voltageWaveform: [ + { time: "0ms", voltage: "2.2V" }, + { time: "1ms", voltage: "2.2V" }, + { time: "1.001ms", voltage: "4.2V" }, + ], + }) + + expect(parsed.voltageWaveform).toEqual([ + { time: 0, voltage: 2.2 }, + { time: 1, voltage: 2.2 }, + { time: 1.001, voltage: 4.2 }, + ]) +})