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 },
+ ])
+})