@photivra/engine 2.17.0 → 2.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +116 -1
- package/CITATION.cff +1 -1
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/dist/camera/sensor-equivalence.d.ts +156 -0
- package/dist/camera/sensor-equivalence.js +208 -0
- package/dist/camera/sensor-equivalence.js.map +1 -0
- package/dist/capture/focus-control.js +2 -75
- package/dist/capture/focus-control.js.map +1 -1
- package/dist/capture/native-environment-lean.d.ts +1 -0
- package/dist/capture/native-environment-lean.js +1 -1
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-environment-optical-stage.d.ts +1 -0
- package/dist/capture/native-environment-optical-stage.js +2 -5
- package/dist/capture/native-environment-optical-stage.js.map +1 -1
- package/dist/capture/native-environment-raw.js +1 -5
- package/dist/capture/native-environment-raw.js.map +1 -1
- package/dist/capture/native-environment-tabulated.d.ts +5 -0
- package/dist/capture/native-environment-tabulated.js +1 -1
- package/dist/capture/native-environment-tabulated.js.map +1 -1
- package/dist/capture/release-sequence.js +2 -47
- package/dist/capture/release-sequence.js.map +1 -1
- package/dist/color/white-balance.d.ts +2 -0
- package/dist/color/white-balance.js +3 -56
- package/dist/color/white-balance.js.map +1 -1
- package/dist/composition/image-formation-plan.d.ts +1 -0
- package/dist/composition/image-formation-plan.js +3 -49
- package/dist/composition/image-formation-plan.js.map +1 -1
- package/dist/core/evidence-provenance.js +1 -16
- package/dist/core/evidence-provenance.js.map +1 -1
- package/dist/core/record-validation.d.ts +54 -0
- package/dist/core/record-validation.js +176 -0
- package/dist/core/record-validation.js.map +1 -1
- package/dist/core/scientific-assurance.js +1 -18
- package/dist/core/scientific-assurance.js.map +1 -1
- package/dist/core/shared-helpers.d.ts +33 -0
- package/dist/core/shared-helpers.js +34 -0
- package/dist/core/shared-helpers.js.map +1 -0
- package/dist/core/spectral-composition.js +1 -36
- package/dist/core/spectral-composition.js.map +1 -1
- package/dist/core/spectral.js +1 -19
- package/dist/core/spectral.js.map +1 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/equipment/exposure-capabilities.d.ts +1 -0
- package/dist/equipment/exposure-capabilities.js +2 -36
- package/dist/equipment/exposure-capabilities.js.map +1 -1
- package/dist/equipment/ideal-lens.d.ts +25 -0
- package/dist/equipment/ideal-lens.js +21 -0
- package/dist/equipment/ideal-lens.js.map +1 -0
- package/dist/equipment/iso-capabilities.js +2 -49
- package/dist/equipment/iso-capabilities.js.map +1 -1
- package/dist/equipment/metering-capabilities.js +1 -26
- package/dist/equipment/metering-capabilities.js.map +1 -1
- package/dist/equipment/release-capabilities.js +1 -53
- package/dist/equipment/release-capabilities.js.map +1 -1
- package/dist/exposure/auto-iso-slowest-shutter.js +1 -7
- package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
- package/dist/exposure/exposure-duration-control.js +1 -16
- package/dist/exposure/exposure-duration-control.js.map +1 -1
- package/dist/exposure/exposure-mode-resolver.js +1 -17
- package/dist/exposure/exposure-mode-resolver.js.map +1 -1
- package/dist/exposure/filtered-exposure-time.d.ts +93 -0
- package/dist/exposure/filtered-exposure-time.js +129 -0
- package/dist/exposure/filtered-exposure-time.js.map +1 -0
- package/dist/exposure/flash-ttl.d.ts +1 -0
- package/dist/exposure/flash-ttl.js +1 -1
- package/dist/exposure/flash-ttl.js.map +1 -1
- package/dist/exposure/flash.js +1 -49
- package/dist/exposure/flash.js.map +1 -1
- package/dist/exposure/metering-scene-radiance.js +1 -25
- package/dist/exposure/metering-scene-radiance.js.map +1 -1
- package/dist/exposure/metering-target.js +1 -30
- package/dist/exposure/metering-target.js.map +1 -1
- package/dist/exposure/metering-temporal.js +1 -8
- package/dist/exposure/metering-temporal.js.map +1 -1
- package/dist/exposure/metering.js +1 -72
- package/dist/exposure/metering.js.map +1 -1
- package/dist/exposure/program-line.js +1 -45
- package/dist/exposure/program-line.js.map +1 -1
- package/dist/facade/catalogue-lens-signal.d.ts +94 -0
- package/dist/facade/catalogue-lens-signal.js +362 -0
- package/dist/facade/catalogue-lens-signal.js.map +1 -0
- package/dist/facade/defocus-image-space-provider.js +5 -2
- package/dist/facade/defocus-image-space-provider.js.map +1 -1
- package/dist/facade/develop-joined-parts.d.ts +29 -0
- package/dist/facade/develop-joined-parts.js +33 -0
- package/dist/facade/develop-joined-parts.js.map +1 -0
- package/dist/facade/display-view-lens-signal.d.ts +32 -1
- package/dist/facade/display-view-lens-signal.js +85 -10
- package/dist/facade/display-view-lens-signal.js.map +1 -1
- package/dist/facade/expected-signal-setup.d.ts +8 -0
- package/dist/facade/expected-signal-setup.js +7 -0
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/expected-signal.d.ts +5 -0
- package/dist/facade/expected-signal.js +5 -1
- package/dist/facade/expected-signal.js.map +1 -1
- package/dist/facade/generic-lens-capture.d.ts +5 -2
- package/dist/facade/generic-lens-capture.js +5 -2
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +67 -8
- package/dist/facade/simulate-capture.js +262 -126
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.js +1 -1
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.js +1 -3
- package/dist/gpu/chart-composite-gpu.js.map +1 -1
- package/dist/gpu/chart-radiance-gpu.js +1 -5
- package/dist/gpu/chart-radiance-gpu.js.map +1 -1
- package/dist/gpu/display-view-quality-gpu.d.ts +13 -1
- package/dist/gpu/display-view-quality-gpu.js +4 -8
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.js +3 -25
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/gpu/sensor-stage-gpu.js +1 -5
- package/dist/gpu/sensor-stage-gpu.js.map +1 -1
- package/dist/gpu/webgpu-runtime.d.ts +2 -0
- package/dist/gpu/webgpu-runtime.js +3 -0
- package/dist/gpu/webgpu-runtime.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/motion/camera-rotation.js +1 -6
- package/dist/motion/camera-rotation.js.map +1 -1
- package/dist/motion/capture-rotation-inverse-mapping.d.ts +12 -0
- package/dist/motion/capture-rotation-inverse-mapping.js +2 -2
- package/dist/motion/capture-rotation-inverse-mapping.js.map +1 -1
- package/dist/motion/capture-rotation-temporal-quadrature.d.ts +1 -0
- package/dist/motion/capture-rotation-temporal-quadrature.js +1 -1
- package/dist/motion/capture-rotation-temporal-quadrature.js.map +1 -1
- package/dist/motion/capture-rotation-trajectory.js +2 -17
- package/dist/motion/capture-rotation-trajectory.js.map +1 -1
- package/dist/motion/capture-translation-parallax-temporal-quadrature.d.ts +1 -0
- package/dist/motion/capture-translation-parallax-temporal-quadrature.js +3 -14
- package/dist/motion/capture-translation-parallax-temporal-quadrature.js.map +1 -1
- package/dist/motion/extended-object-projection.js +5 -40
- package/dist/motion/extended-object-projection.js.map +1 -1
- package/dist/optics/atmospheric-haze.js +1 -7
- package/dist/optics/atmospheric-haze.js.map +1 -1
- package/dist/optics/brown-conrady-distortion.js +1 -7
- package/dist/optics/brown-conrady-distortion.js.map +1 -1
- package/dist/optics/complex-pupil-psf.d.ts +1 -0
- package/dist/optics/complex-pupil-psf.js +2 -84
- package/dist/optics/complex-pupil-psf.js.map +1 -1
- package/dist/optics/focus-state.js +1 -11
- package/dist/optics/focus-state.js.map +1 -1
- package/dist/optics/front-of-lens-filter.d.ts +5 -0
- package/dist/optics/front-of-lens-filter.js +3 -65
- package/dist/optics/front-of-lens-filter.js.map +1 -1
- package/dist/optics/generic-nd-filter-presets.d.ts +12 -0
- package/dist/optics/generic-nd-filter-presets.js +48 -0
- package/dist/optics/generic-nd-filter-presets.js.map +1 -0
- package/dist/optics/illumination-vignetting.js +1 -6
- package/dist/optics/illumination-vignetting.js.map +1 -1
- package/dist/optics/lens-psf-profile.d.ts +4 -1
- package/dist/optics/lens-psf-profile.js +12 -111
- package/dist/optics/lens-psf-profile.js.map +1 -1
- package/dist/optics/longitudinal-chromatic-aberration.js +1 -7
- package/dist/optics/longitudinal-chromatic-aberration.js.map +1 -1
- package/dist/optics/pupil-vignetting.js +1 -7
- package/dist/optics/pupil-vignetting.js.map +1 -1
- package/dist/optics/radial-distortion.js +1 -6
- package/dist/optics/radial-distortion.js.map +1 -1
- package/dist/optics/scene-to-sensor-irradiance.js +4 -96
- package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
- package/dist/optics/stray-light.d.ts +1 -0
- package/dist/optics/stray-light.js +1 -1
- package/dist/optics/stray-light.js.map +1 -1
- package/dist/optics/zernike-wavefront.js +1 -7
- package/dist/optics/zernike-wavefront.js.map +1 -1
- package/dist/output/capture-geometry.js +1 -8
- package/dist/output/capture-geometry.js.map +1 -1
- package/dist/output/developed-lens-corrections.d.ts +62 -0
- package/dist/output/developed-lens-corrections.js +135 -0
- package/dist/output/developed-lens-corrections.js.map +1 -0
- package/dist/output/generic-lens-corrections.d.ts +2 -6
- package/dist/output/generic-lens-corrections.js +3 -6
- package/dist/output/generic-lens-corrections.js.map +1 -1
- package/dist/output/geometric-transforms.js +1 -4
- package/dist/output/geometric-transforms.js.map +1 -1
- package/dist/output/native-capture-sdr.d.ts +2 -2
- package/dist/output/native-capture-sdr.js +3 -2
- package/dist/output/native-capture-sdr.js.map +1 -1
- package/dist/processing/development-detail.d.ts +29 -0
- package/dist/processing/development-detail.js +128 -107
- package/dist/processing/development-detail.js.map +1 -1
- package/dist/schema/illumination-temporal.js +3 -72
- package/dist/schema/illumination-temporal.js.map +1 -1
- package/dist/schema/illumination.d.ts +1 -0
- package/dist/schema/illumination.js +2 -65
- package/dist/schema/illumination.js.map +1 -1
- package/dist/schema/scene-radiance-temporal.js +2 -14
- package/dist/schema/scene-radiance-temporal.js.map +1 -1
- package/dist/schema/scene-radiance.js +2 -60
- package/dist/schema/scene-radiance.js.map +1 -1
- package/dist/schema/validation.d.ts +2 -0
- package/dist/schema/validation.js +2 -17
- package/dist/schema/validation.js.map +1 -1
- package/dist/sensor/accumulated-charge.js +2 -59
- package/dist/sensor/accumulated-charge.js.map +1 -1
- package/dist/sensor/architecture.js +1 -16
- package/dist/sensor/architecture.js.map +1 -1
- package/dist/sensor/camera-saturation-capacity.d.ts +1 -0
- package/dist/sensor/camera-saturation-capacity.js +3 -55
- package/dist/sensor/camera-saturation-capacity.js.map +1 -1
- package/dist/sensor/capture-color-sampling-binding.js +1 -26
- package/dist/sensor/capture-color-sampling-binding.js.map +1 -1
- package/dist/sensor/capture-mode-timing.js +1 -39
- package/dist/sensor/capture-mode-timing.js.map +1 -1
- package/dist/sensor/capture-mode.js +1 -23
- package/dist/sensor/capture-mode.js.map +1 -1
- package/dist/sensor/color-sampling.js +1 -26
- package/dist/sensor/color-sampling.js.map +1 -1
- package/dist/sensor/constant-rate-temporal-integration.js +2 -46
- package/dist/sensor/constant-rate-temporal-integration.js.map +1 -1
- package/dist/sensor/dark-current.js +2 -45
- package/dist/sensor/dark-current.js.map +1 -1
- package/dist/sensor/eqe-electron-rate.js +2 -35
- package/dist/sensor/eqe-electron-rate.js.map +1 -1
- package/dist/sensor/exposure-window.d.ts +5 -0
- package/dist/sensor/exposure-window.js +7 -22
- package/dist/sensor/exposure-window.js.map +1 -1
- package/dist/sensor/iso-gain.js +1 -7
- package/dist/sensor/iso-gain.js.map +1 -1
- package/dist/sensor/iso-signal-chain.js +1 -43
- package/dist/sensor/iso-signal-chain.js.map +1 -1
- package/dist/sensor/optical-stack.js +1 -25
- package/dist/sensor/optical-stack.js.map +1 -1
- package/dist/sensor/photon-energy.js +1 -26
- package/dist/sensor/photon-energy.js.map +1 -1
- package/dist/sensor/physical-charge-capacity.js +3 -77
- package/dist/sensor/physical-charge-capacity.js.map +1 -1
- package/dist/sensor/radiometry-readiness.js +2 -23
- package/dist/sensor/radiometry-readiness.js.map +1 -1
- package/dist/sensor/raw-readout.d.ts +3 -0
- package/dist/sensor/raw-readout.js +4 -66
- package/dist/sensor/raw-readout.js.map +1 -1
- package/dist/sensor/raw-reconstruction.js +2 -64
- package/dist/sensor/raw-reconstruction.js.map +1 -1
- package/dist/sensor/readout-exposure-linkage.js +1 -9
- package/dist/sensor/readout-exposure-linkage.js.map +1 -1
- package/dist/sensor/readout-timing.js +3 -57
- package/dist/sensor/readout-timing.js.map +1 -1
- package/dist/sensor/response-application-compatibility.d.ts +2 -0
- package/dist/sensor/response-application-compatibility.js +3 -29
- package/dist/sensor/response-application-compatibility.js.map +1 -1
- package/dist/sensor/response-operating-range.d.ts +1 -0
- package/dist/sensor/response-operating-range.js +3 -79
- package/dist/sensor/response-operating-range.js.map +1 -1
- package/dist/sensor/responsivity-photocurrent.js +4 -108
- package/dist/sensor/responsivity-photocurrent.js.map +1 -1
- package/dist/sensor/sampling-aperture.js +1 -25
- package/dist/sensor/sampling-aperture.js.map +1 -1
- package/dist/sensor/spatial-sample-reduction.js +2 -19
- package/dist/sensor/spatial-sample-reduction.js.map +1 -1
- package/dist/sensor/spatial-sampling-quadrature.js +1 -3
- package/dist/sensor/spatial-sampling-quadrature.js.map +1 -1
- package/dist/sensor/spatio-spectral-reduction.js +1 -18
- package/dist/sensor/spatio-spectral-reduction.js.map +1 -1
- package/dist/sensor/spectral-coverage.d.ts +10 -1
- package/dist/sensor/spectral-coverage.js +1 -1
- package/dist/sensor/spectral-coverage.js.map +1 -1
- package/dist/sensor/spectral-quadrature.js +2 -35
- package/dist/sensor/spectral-quadrature.js.map +1 -1
- package/dist/sensor/spectral-response.js +1 -47
- package/dist/sensor/spectral-response.js.map +1 -1
- package/dist/stabilization/camera-shake.js +1 -6
- package/dist/stabilization/camera-shake.js.map +1 -1
- package/dist/stabilization/system.js +2 -80
- package/dist/stabilization/system.js.map +1 -1
- package/docs/AGENT_GUIDE.md +4 -0
- package/docs/API_REFERENCE.md +69 -2
- package/docs/QUICK_START.md +136 -6
- package/docs/USAGE.md +89 -3
- package/docs/api/camera-sensor-equivalence.md +209 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-ideal-lens.md +26 -0
- package/docs/api/exports.json +118 -3
- package/docs/api/exposure-filtered-exposure-time.md +105 -0
- package/docs/api/facade-develop-joined-parts.md +32 -0
- package/docs/api/facade-simulate-capture.md +62 -8
- package/docs/api/optics-generic-nd-filter-presets.md +34 -0
- package/docs/api/optics-lens-psf-profile.md +3 -1
- package/docs/api/output-developed-lens-corrections.md +12 -0
- package/docs/api/output-generic-lens-corrections.md +0 -9
- package/llms.txt +3 -0
- package/package.json +1 -1
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
import { approximationResult } from "../core/calculation-result.js";
|
|
21
21
|
import { parseEvidenceList } from "../core/evidence-provenance.js";
|
|
22
22
|
import { InvalidScientificInputError, requirePositiveInteger } from "../core/validation.js";
|
|
23
|
-
import { requireInputObject, requireInputKeys,
|
|
23
|
+
import { requireInputObject, requireInputKeys, requireConfigRecord as requireRecord } from "../core/record-validation.js";
|
|
24
24
|
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
25
|
+
import { fullRect } from "../core/shared-helpers.js";
|
|
25
26
|
const SHUTTER_MECHANISMS = new Set([
|
|
26
27
|
"mechanical",
|
|
27
28
|
"electronic-first-curtain",
|
|
@@ -37,20 +38,12 @@ const BOUNDARY_KINDS = new Set([
|
|
|
37
38
|
"simultaneous",
|
|
38
39
|
"uniform-linear-native-scan"
|
|
39
40
|
]);
|
|
40
|
-
function
|
|
41
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
42
|
-
throw new InvalidConfigurationError(path + " must be an object.");
|
|
43
|
-
}
|
|
44
|
-
if (allowedKeys !== null)
|
|
45
|
-
rejectUnknownFields(value, allowedKeys, path);
|
|
46
|
-
return value;
|
|
47
|
-
}
|
|
48
|
-
function requireNonNegativeInteger(name, value) {
|
|
41
|
+
export function requireNonNegativeInteger(name, value) {
|
|
49
42
|
if (!Number.isSafeInteger(value) || value < 0) {
|
|
50
43
|
throw new InvalidScientificInputError(name + " must be a non-negative safe integer.");
|
|
51
44
|
}
|
|
52
45
|
}
|
|
53
|
-
function requirePositiveFinite(name, value) {
|
|
46
|
+
export function requirePositiveFinite(name, value) {
|
|
54
47
|
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
55
48
|
throw new InvalidScientificInputError(name + " must be a finite number greater than zero.");
|
|
56
49
|
}
|
|
@@ -114,19 +107,11 @@ export function parseBoundarySchedule(value, path) {
|
|
|
114
107
|
traversalDurationSeconds: parseSecondsFact(record.traversalDurationSeconds, path + ".traversalDurationSeconds")
|
|
115
108
|
};
|
|
116
109
|
}
|
|
117
|
-
function validateNativeRaster(raster) {
|
|
110
|
+
export function validateNativeRaster(raster) {
|
|
118
111
|
requireInputObject(raster, "raster input");
|
|
119
112
|
requirePositiveInteger("nativeRaster.pixelWidth", raster.pixelWidth);
|
|
120
113
|
requirePositiveInteger("nativeRaster.pixelHeight", raster.pixelHeight);
|
|
121
114
|
}
|
|
122
|
-
function fullRect(raster) {
|
|
123
|
-
return {
|
|
124
|
-
x: 0,
|
|
125
|
-
y: 0,
|
|
126
|
-
width: raster.pixelWidth,
|
|
127
|
-
height: raster.pixelHeight
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
115
|
function validateActiveCaptureRect(rect, raster) {
|
|
131
116
|
requireNonNegativeInteger("activeCaptureRect.x", rect.x);
|
|
132
117
|
requireNonNegativeInteger("activeCaptureRect.y", rect.y);
|
|
@@ -154,7 +139,7 @@ function validatePointWithinActiveCapture(point, rect, path) {
|
|
|
154
139
|
throw new InvalidScientificInputError(path + " must lie within activeCaptureRect edge-coordinate bounds.");
|
|
155
140
|
}
|
|
156
141
|
}
|
|
157
|
-
function scanVector(direction) {
|
|
142
|
+
export function scanVector(direction) {
|
|
158
143
|
switch (direction) {
|
|
159
144
|
case "top-to-bottom":
|
|
160
145
|
return { x: 0, y: 1 };
|
|
@@ -166,7 +151,7 @@ function scanVector(direction) {
|
|
|
166
151
|
return { x: -1, y: 0 };
|
|
167
152
|
}
|
|
168
153
|
}
|
|
169
|
-
function normalizedScanPosition(point, rect, direction) {
|
|
154
|
+
export function normalizedScanPosition(point, rect, direction) {
|
|
170
155
|
switch (direction) {
|
|
171
156
|
case "top-to-bottom":
|
|
172
157
|
return (point.y - rect.y) / rect.height;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exposure-window.js","sourceRoot":"","sources":["../../src/sensor/exposure-window.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,mBAAmB,EAEpB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,iBAAiB,EAElB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,2BAA2B,EAC3B,sBAAsB,EACvB,MAAM,uBAAuB,CAAC;AAY/B,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACzG,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAiG3E,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAA0B;IAC1D,YAAY;IACZ,0BAA0B;IAC1B,YAAY;CACb,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAmC;IAChE,eAAe;IACf,eAAe;IACf,eAAe;IACf,eAAe;CAChB,CAAC,CAAC;AAEH,MAAM,cAAc,GAAG,IAAI,GAAG,CAAmC;IAC/D,cAAc;IACd,4BAA4B;CAC7B,CAAC,CAAC;AAEH,SAAS,aAAa,CACpB,KAAc,EACd,IAAY,EACZ,WAAqC;IAErC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,yBAAyB,CAAC,IAAI,GAAG,qBAAqB,CAAC,CAAC;IACpE,CAAC;IACD,IAAI,WAAW,KAAK,IAAI;QAAE,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,CAAC;IACxE,OAAO,KAAsB,CAAC;AAChC,CAAC;AAED,SAAS,yBAAyB,CAAC,IAAY,EAAE,KAAa;IAC5D,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,2BAA2B,CACnC,IAAI,GAAG,uCAAuC,CAC/C,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,qBAAqB,CAAC,IAAY,EAAE,KAAc;IACzD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;QACvE,MAAM,IAAI,2BAA2B,CACnC,IAAI,GAAG,6CAA6C,CACrD,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,gBAAgB,CAC9B,KAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;IACzE,IAAI,MAAM,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;QACxB,MAAM,IAAI,yBAAyB,CAAC,IAAI,GAAG,oBAAoB,CAAC,CAAC;IACnE,CAAC;IAED,OAAO;QACL,KAAK,EAAE,qBAAqB,CAAC,IAAI,GAAG,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC;QAC3D,IAAI,EAAE,GAAG;QACT,QAAQ,EAAE,iBAAiB,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,GAAG,WAAW,CAAC;KACjE,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CACzB,KAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;IACjE,IACE,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ;QAChC,CAAC,eAAe,CAAC,GAAG,CAClB,MAAM,CAAC,KAAyC,CACjD,EACD,CAAC;QACD,MAAM,IAAI,yBAAyB,CAAC,IAAI,GAAG,oBAAoB,CAAC,CAAC;IACnE,CAAC;IAED,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,KAAyC;QACvD,QAAQ,EAAE,iBAAiB,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,GAAG,WAAW,CAAC;KACjE,CAAC;AACJ,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,qBAAqB,CACnC,KAAc,EACd,IAAY;IAEZ,qGAAqG;IACrG,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,kBAAkB,CAAC,CAAC,CAAC;IACvH,IACE,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ;QAC/B,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,IAAwC,CAAC,EACpE,CAAC;QACD,MAAM,IAAI,yBAAyB,CAAC,IAAI,GAAG,mBAAmB,CAAC,CAAC;IAClE,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACnC,IAAI,MAAM,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;YACzC,MAAM,IAAI,2BAA2B,CACnC,IAAI,GAAG,+DAA+D,CACvE,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,wBAAwB,KAAK,SAAS,EAAE,CAAC;YAClD,MAAM,IAAI,2BAA2B,CACnC,IAAI;gBACF,wEAAwE,CAC3E,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;YAC1C,MAAM,IAAI,2BAA2B,CACnC,IAAI,GAAG,gEAAgE,CACxE,CAAC;QACJ,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;IAClC,CAAC;IAED,MAAM,eAAe,GAAG,kBAAkB,CACxC,MAAM,CAAC,eAAe,EACtB,IAAI,GAAG,kBAAkB,CAC1B,CAAC;IACF,IAAI,MAAM,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,gBAAgB,EAAE,IAAI,GAAG,mBAAmB,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QAChG,MAAM,OAAO,GAAG,UAAU,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;QAClD,IAAI,QAAQ,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC;YACzD,MAAM,IAAI,yBAAyB,CAAC,IAAI,GAAG,+CAA+C,CAAC,CAAC;QAC9F,CAAC;IACH,CAAC;IACD,OAAO;QACL,IAAI,EAAE,4BAA4B;QAClC,eAAe;QACf,wBAAwB,EAAE,gBAAgB,CACxC,MAAM,CAAC,wBAAwB,EAC/B,IAAI,GAAG,2BAA2B,CACnC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,MAAyB;IACrD,kBAAkB,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC3C,sBAAsB,CAAC,yBAAyB,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IACrE,sBAAsB,CAAC,0BAA0B,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;AACzE,CAAC;AAED,SAAS,QAAQ,CAAC,MAAyB;IACzC,OAAO;QACL,CAAC,EAAE,CAAC;QACJ,CAAC,EAAE,CAAC;QACJ,KAAK,EAAE,MAAM,CAAC,UAAU;QACxB,MAAM,EAAE,MAAM,CAAC,WAAW;KAC3B,CAAC;AACJ,CAAC;AAED,SAAS,yBAAyB,CAChC,IAAgB,EAChB,MAAyB;IAEzB,yBAAyB,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACzD,yBAAyB,CAAC,qBAAqB,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACzD,sBAAsB,CAAC,yBAAyB,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9D,sBAAsB,CAAC,0BAA0B,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAEhE,IACE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,UAAU;QACvC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,WAAW,EACzC,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,0DAA0D,CAC3D,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,wBAAwB,CAC/B,KAAc;IAEd,IACE,OAAO,KAAK,KAAK,QAAQ;QACzB,CAAC,kBAAkB,CAAC,GAAG,CAAC,KAAgC,CAAC,EACzD,CAAC;QACD,MAAM,IAAI,yBAAyB,CAAC,8BAA8B,CAAC,CAAC;IACtE,CAAC;AACH,CAAC;AAED,SAAS,gCAAgC,CACvC,KAAkB,EAClB,IAAgB,EAChB,IAAY;IAEZ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,2BAA2B,CACnC,IAAI,GAAG,8BAA8B,CACtC,CAAC;IACJ,CAAC;IAED,IACE,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAChB,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK;QAC7B,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAChB,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAC9B,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,IAAI,GAAG,4DAA4D,CACpE,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CACjB,SAA2C;IAE3C,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,eAAe;YAClB,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;QACxB,KAAK,eAAe;YAClB,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QACzB,KAAK,eAAe;YAClB,OAAO,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;QACxB,KAAK,eAAe;YAClB,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,SAAS,sBAAsB,CAC7B,KAAkB,EAClB,IAAgB,EAChB,SAA2C;IAE3C,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,eAAe;YAClB,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1C,KAAK,eAAe;YAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QAC9C,KAAK,eAAe;YAClB,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;QACzC,KAAK,eAAe;YAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC;IAC/C,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CACpB,KAAkB,EAClB,IAAgB,EAChB,QAAkC;IAKlC,IAAI,QAAQ,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACrC,OAAO;YACL,sBAAsB,EAAE,IAAI;YAC5B,kBAAkB,EAAE,CAAC;SACtB,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,sBAAsB,CACvC,KAAK,EACL,IAAI,EACJ,QAAQ,CAAC,eAAe,CAAC,KAAK,CAC/B,CAAC;IAEF,OAAO;QACL,sBAAsB,EAAE,UAAU;QAClC,kBAAkB,EAChB,UAAU,GAAG,QAAQ,CAAC,wBAAwB,CAAC,KAAK;KACvD,CAAC;AACJ,CAAC;AAED,SAAS,wBAAwB,CAC/B,SAAkC;IAKlC,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,YAAY;YACf,OAAO;gBACL,OAAO,EAAE,YAAY;gBACrB,OAAO,EAAE,YAAY;aACtB,CAAC;QACJ,KAAK,0BAA0B;YAC7B,OAAO;gBACL,OAAO,EAAE,YAAY;gBACrB,OAAO,EAAE,YAAY;aACtB,CAAC;QACJ,KAAK,YAAY;YACf,OAAO;gBACL,OAAO,EAAE,YAAY;gBACrB,OAAO,EAAE,YAAY;aACtB,CAAC;IACN,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CACtB,QAAyC,EACzC,QAAkC;IAElC,IAAI,QAAQ,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACrC,OAAO;YACL,QAAQ;YACR,QAAQ,EAAE;gBACR,IAAI,EAAE,cAAc;aACrB;SACF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ;QACR,QAAQ,EAAE;YACR,IAAI,EAAE,4BAA4B;YAClC,eAAe,EAAE,QAAQ,CAAC,eAAe;YACzC,gBAAgB,EAAE,UAAU,CAAC,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC;YAC5D,wBAAwB,EAAE,QAAQ,CAAC,wBAAwB;SAC5D;KACF,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CACzB,KAAkB,EAClB,IAAgB,EAChB,OAAiC,EACjC,OAAiC,EACjC,8BAAsC;IAEtC,MAAM,YAAY,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,YAAY,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACzD,MAAM,sCAAsC,GAC1C,YAAY,CAAC,kBAAkB,CAAC;IAClC,MAAM,oCAAoC,GACxC,8BAA8B,GAAG,YAAY,CAAC,kBAAkB,CAAC;IACnE,MAAM,4BAA4B,GAChC,oCAAoC;QACpC,sCAAsC,CAAC;IAEzC,OAAO;QACL,WAAW,EAAE,EAAE,GAAG,KAAK,EAAE;QACzB,6BAA6B,EAC3B,YAAY,CAAC,sBAAsB;QACrC,6BAA6B,EAC3B,YAAY,CAAC,sBAAsB;QACrC,sCAAsC;QACtC,oCAAoC;QACpC,4BAA4B;KAC7B,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,IAAgB;IACzC,OAAO;QACL,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE;QACxB,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE;QACrC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE;QACtC;YACE,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK;YACtB,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM;SACxB;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,+BAA+B,CAC7C,KAA2C;IAE3C,gBAAgB,CAAC,KAAK,EAAE,iCAAiC,EAAE,CAAC,cAAc,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,gCAAgC,EAAE,SAAS,EAAE,SAAS,EAAE,oBAAoB,CAAC,CAAC,CAAC;IACpM,oBAAoB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACzC,wBAAwB,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAEjD,MAAM,8BAA8B,GAAG,gBAAgB,CACrD,KAAK,CAAC,8BAA8B,EACpC,gCAAgC,CACjC,CAAC;IACF,MAAM,OAAO,GAAG,qBAAqB,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAChE,MAAM,OAAO,GAAG,qBAAqB,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAEhE,MAAM,iBAAiB,GACrB,KAAK,CAAC,iBAAiB,IAAI,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAC1D,yBAAyB,CAAC,iBAAiB,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;IAEjE,IACE,KAAK,CAAC,kBAAkB,KAAK,SAAS;QACtC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,EACxC,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,oDAAoD,CACrD,CAAC;IACJ,CAAC;IAED,MAAM,aAAa,GAAG,iBAAiB,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAC5D,CAAC,KAAK,EAAE,EAAE,CACR,kBAAkB,CAChB,KAAK,EACL,iBAAiB,EACjB,OAAO,EACP,OAAO,EACP,8BAA8B,CAAC,KAAK,CACrC,CACJ,CAAC;IAEF,MAAM,cAAc,GAAG,aAAa,CAAC,GAAG,CACtC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,4BAA4B,CAChD,CAAC;IACF,MAAM,mCAAmC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,CAAC;IACxE,MAAM,mCAAmC,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,CAAC;IAExE,IACE,CAAC,MAAM,CAAC,QAAQ,CAAC,mCAAmC,CAAC;QACrD,mCAAmC,IAAI,CAAC,EACxC,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,+HAA+H,CAChI,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACpE,gCAAgC,CAC9B,KAAK,EACL,iBAAiB,EACjB,qBAAqB,GAAG,KAAK,GAAG,GAAG,CACpC,CAAC;QAEF,OAAO,kBAAkB,CACvB,KAAK,EACL,iBAAiB,EACjB,OAAO,EACP,OAAO,EACP,8BAA8B,CAAC,KAAK,CACrC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,wBAAwB,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAEnE,OAAO,mBAAmB,CACxB;QACE,gBAAgB,EAAE,KAAK,CAAC,gBAAgB;QACxC,aAAa,EAAE,8BAA8B;QAC7C,YAAY,EAAE,EAAE,GAAG,KAAK,CAAC,YAAY,EAAE;QACvC,iBAAiB,EAAE,EAAE,GAAG,iBAAiB,EAAE;QAC3C,8BAA8B;QAC9B,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC;QACpD,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC;QACpD,iCAAiC,EAAE;YACjC,OAAO,EAAE,mCAAmC;YAC5C,OAAO,EAAE,mCAAmC;SAC7C;QACD,OAAO;KACR,EACD,kCAAkC,EAClC,OAAO,EACP;QACE,qHAAqH;QACrH,0GAA0G;QAC1G,sJAAsJ;QACtJ,sOAAsO;QACtO,mIAAmI;QACnI,0IAA0I;QAC1I,iHAAiH;QACjH,2HAA2H;QAC3H,+IAA+I;QAC/I,iOAAiO;KAClO,CACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mCAAmC,CACjD,KAAuE;IAEvE,MAAM,QAAQ,GAAG,+BAA+B,CAAC,EAAE,GAAG,KAAK,EAAE,kBAAkB,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC;IAC7F,MAAM,IAAI,GAAG,EAAE,GAAG,QAAQ,CAAC,iBAAiB,EAAE,CAAC;IAC/C,MAAM,OAAO,GAAG,qBAAqB,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAChE,MAAM,OAAO,GAAG,qBAAqB,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAChE,MAAM,OAAO,GAAG,QAAQ,CAAC,8BAA8B,CAAC,KAAK,CAAC;IAC9D,OAAO,CAAC,KAAK,EAAE,EAAE;QACf,gCAAgC,CAAC,KAAK,EAAE,IAAI,EAAE,uBAAuB,CAAC,CAAC;QACvE,OAAO,kBAAkB,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACpE,CAAC,CAAC;AACJ,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Exposure windows: when each point on the sensor starts and stops collecting light.\n *\n * The shutter has an opening boundary and a closing boundary, each with its own schedule. With a\n * rolling or curtain shutter the boundaries sweep across the sensor, so each point has its own\n * (local) window. Times are seconds from the first opening boundary. That is deliberately not the\n * same as the camera-rotation API's global \"exposure start\"; a later layer must link the two\n * explicitly.\n *\n * The mechanism records whether each boundary is mechanical or electronic, but supplies no sweep\n * duration or direction. Every timing value and direction for a non-simultaneous boundary is declared\n * by the caller, with evidence. Only simultaneous boundaries and a uniform linear sweep along one\n * native sensor axis are supported. Not modelled: curtain acceleration, segmented or non-linear\n * electronic schedules, flash or flicker, rolling-shutter image distortion, shutter shock, and\n * EFCS (electronic first-curtain shutter) effects on the pupil or bokeh.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport {\n parseEvidenceList,\n type EvidenceBackedFact\n} from \"../core/evidence-provenance.js\";\nimport {\n InvalidScientificInputError,\n requirePositiveInteger\n} from \"../core/validation.js\";\nimport type {\n RasterPoint,\n RasterRect,\n RasterVector\n} from \"../output/capture-geometry.js\";\nimport type { NativeImageRaster } from \"./sensor-geometry.js\";\nimport type {\n CaptureShutterMechanism,\n NativeSensorReadoutScanDirection,\n SourcedSensorTimingSeconds\n} from \"./readout-timing.js\";\nimport { requireInputObject, requireInputKeys, rejectUnknownFields } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\nexport type CaptureExposureBoundaryActuator = \"mechanical\" | \"electronic\";\n\nexport type SourcedCaptureTimingSeconds = SourcedSensorTimingSeconds;\n\nexport type SourcedCaptureBoundaryDirection =\n EvidenceBackedFact<NativeSensorReadoutScanDirection>;\n\nexport interface SimultaneousExposureBoundarySchedule {\n kind: \"simultaneous\";\n directionNative?: never;\n traversalDurationSeconds?: never;\n}\n\nexport interface UniformLinearExposureBoundarySchedule {\n kind: \"uniform-linear-native-scan\";\n directionNative: SourcedCaptureBoundaryDirection;\n traversalDurationSeconds: SourcedCaptureTimingSeconds;\n}\n\nexport type ExposureBoundarySchedule =\n | SimultaneousExposureBoundarySchedule\n | UniformLinearExposureBoundarySchedule;\n\nexport interface CalculateCaptureExposureWindowsInput {\n nativeRaster: NativeImageRaster;\n /**\n * Active capture rectangle, in fixed native pixel coordinates.\n *\n * Defaults to the whole native image. Sweep timing is never worked out by\n * scaling a full-frame timing value to this rectangle.\n */\n activeCaptureRect?: RasterRect;\n shutterMechanism: CaptureShutterMechanism;\n /**\n * Nominal time between the opening and closing reference points (phase zero).\n *\n * A point's local exposure can differ from this when the opening and closing\n * boundaries sweep differently across the sensor.\n */\n nominalExposureDurationSeconds: SourcedCaptureTimingSeconds;\n opening: ExposureBoundarySchedule;\n closing: ExposureBoundarySchedule;\n /**\n * Optional points, in continuous native pixel coordinates measured from pixel\n * edges, to report windows for. A pixel centre is at +0.5.\n */\n samplePointsNative?: readonly RasterPoint[];\n}\n\nexport interface ResolvedCaptureExposureBoundary {\n actuator: CaptureExposureBoundaryActuator;\n schedule:\n | {\n kind: \"simultaneous\";\n }\n | {\n kind: \"uniform-linear-native-scan\";\n directionNative: SourcedCaptureBoundaryDirection;\n unitVectorNative: RasterVector;\n traversalDurationSeconds: SourcedCaptureTimingSeconds;\n };\n}\n\nexport interface CaptureExposureWindowSample {\n pointNative: RasterPoint;\n openingNormalizedScanPosition: number | null;\n closingNormalizedScanPosition: number | null;\n /**\n * Seconds from the first opening boundary.\n */\n startOffsetSecondsFromOpeningReference: number;\n /**\n * Seconds from the first opening boundary.\n */\n endOffsetSecondsFromOpeningReference: number;\n localExposureDurationSeconds: number;\n}\n\nexport interface CaptureExposureWindows {\n shutterMechanism: CaptureShutterMechanism;\n timeReference: \"first-opening-boundary-phase\";\n nativeRaster: NativeImageRaster;\n activeCaptureRect: RasterRect;\n nominalExposureDurationSeconds: SourcedCaptureTimingSeconds;\n opening: ResolvedCaptureExposureBoundary;\n closing: ResolvedCaptureExposureBoundary;\n localExposureDurationRangeSeconds: {\n minimum: number;\n maximum: number;\n };\n samples: readonly CaptureExposureWindowSample[];\n}\n\ntype UnknownRecord = Record<string, unknown>;\n\nconst SHUTTER_MECHANISMS = new Set<CaptureShutterMechanism>([\n \"mechanical\",\n \"electronic-first-curtain\",\n \"electronic\"\n]);\n\nconst SCAN_DIRECTIONS = new Set<NativeSensorReadoutScanDirection>([\n \"top-to-bottom\",\n \"bottom-to-top\",\n \"left-to-right\",\n \"right-to-left\"\n]);\n\nconst BOUNDARY_KINDS = new Set<ExposureBoundarySchedule[\"kind\"]>([\n \"simultaneous\",\n \"uniform-linear-native-scan\"\n]);\n\nfunction requireRecord(\n value: unknown,\n path: string,\n allowedKeys: readonly string[] | null\n): UnknownRecord {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new InvalidConfigurationError(path + \" must be an object.\");\n }\n if (allowedKeys !== null) rejectUnknownFields(value, allowedKeys, path);\n return value as UnknownRecord;\n}\n\nfunction requireNonNegativeInteger(name: string, value: number): void {\n if (!Number.isSafeInteger(value) || value < 0) {\n throw new InvalidScientificInputError(\n name + \" must be a non-negative safe integer.\"\n );\n }\n}\n\nfunction requirePositiveFinite(name: string, value: unknown): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new InvalidScientificInputError(\n name + \" must be a finite number greater than zero.\"\n );\n }\n return value;\n}\n\n/** Parses a sourced duration in seconds (`{ unit: \"s\", value, evidence }`). Not a root export. */\nexport function parseSecondsFact(\n value: unknown,\n path: string\n): SourcedCaptureTimingSeconds {\n const record = requireRecord(value, path, [\"unit\", \"value\", \"evidence\"]);\n if (record.unit !== \"s\") {\n throw new InvalidConfigurationError(path + '.unit must be \"s\".');\n }\n\n return {\n value: requirePositiveFinite(path + \".value\", record.value),\n unit: \"s\",\n evidence: parseEvidenceList(record.evidence, path + \".evidence\")\n };\n}\n\nfunction parseDirectionFact(\n value: unknown,\n path: string\n): SourcedCaptureBoundaryDirection {\n const record = requireRecord(value, path, [\"value\", \"evidence\"]);\n if (\n typeof record.value !== \"string\" ||\n !SCAN_DIRECTIONS.has(\n record.value as NativeSensorReadoutScanDirection\n )\n ) {\n throw new InvalidConfigurationError(path + \".value is invalid.\");\n }\n\n return {\n value: record.value as NativeSensorReadoutScanDirection,\n evidence: parseEvidenceList(record.evidence, path + \".evidence\")\n };\n}\n\n/** Parses one exposure boundary schedule (opening or closing). Not a root export. */\nexport function parseBoundarySchedule(\n value: unknown,\n path: string\n): ExposureBoundarySchedule {\n // unitVectorNative is the derived field of a resolved schedule; it may be passed back if it matches.\n const record = requireRecord(value, path, [\"kind\", \"directionNative\", \"traversalDurationSeconds\", \"unitVectorNative\"]);\n if (\n typeof record.kind !== \"string\" ||\n !BOUNDARY_KINDS.has(record.kind as ExposureBoundarySchedule[\"kind\"])\n ) {\n throw new InvalidConfigurationError(path + \".kind is invalid.\");\n }\n\n if (record.kind === \"simultaneous\") {\n if (record.directionNative !== undefined) {\n throw new InvalidScientificInputError(\n path + \".directionNative must be omitted for a simultaneous boundary.\"\n );\n }\n if (record.traversalDurationSeconds !== undefined) {\n throw new InvalidScientificInputError(\n path +\n \".traversalDurationSeconds must be omitted for a simultaneous boundary.\"\n );\n }\n if (record.unitVectorNative !== undefined) {\n throw new InvalidScientificInputError(\n path + \".unitVectorNative must be omitted for a simultaneous boundary.\"\n );\n }\n\n return { kind: \"simultaneous\" };\n }\n\n const directionNative = parseDirectionFact(\n record.directionNative,\n path + \".directionNative\"\n );\n if (record.unitVectorNative !== undefined) {\n const supplied = requireRecord(record.unitVectorNative, path + \".unitVectorNative\", [\"x\", \"y\"]);\n const derived = scanVector(directionNative.value);\n if (supplied.x !== derived.x || supplied.y !== derived.y) {\n throw new InvalidConfigurationError(path + \".unitVectorNative must match directionNative.\");\n }\n }\n return {\n kind: \"uniform-linear-native-scan\",\n directionNative,\n traversalDurationSeconds: parseSecondsFact(\n record.traversalDurationSeconds,\n path + \".traversalDurationSeconds\"\n )\n };\n}\n\nfunction validateNativeRaster(raster: NativeImageRaster): void {\n requireInputObject(raster, \"raster input\");\n requirePositiveInteger(\"nativeRaster.pixelWidth\", raster.pixelWidth);\n requirePositiveInteger(\"nativeRaster.pixelHeight\", raster.pixelHeight);\n}\n\nfunction fullRect(raster: NativeImageRaster): RasterRect {\n return {\n x: 0,\n y: 0,\n width: raster.pixelWidth,\n height: raster.pixelHeight\n };\n}\n\nfunction validateActiveCaptureRect(\n rect: RasterRect,\n raster: NativeImageRaster\n): void {\n requireNonNegativeInteger(\"activeCaptureRect.x\", rect.x);\n requireNonNegativeInteger(\"activeCaptureRect.y\", rect.y);\n requirePositiveInteger(\"activeCaptureRect.width\", rect.width);\n requirePositiveInteger(\"activeCaptureRect.height\", rect.height);\n\n if (\n rect.x + rect.width > raster.pixelWidth ||\n rect.y + rect.height > raster.pixelHeight\n ) {\n throw new InvalidScientificInputError(\n \"activeCaptureRect must fit entirely within nativeRaster.\"\n );\n }\n}\n\nfunction validateShutterMechanism(\n value: unknown\n): asserts value is CaptureShutterMechanism {\n if (\n typeof value !== \"string\" ||\n !SHUTTER_MECHANISMS.has(value as CaptureShutterMechanism)\n ) {\n throw new InvalidConfigurationError(\"shutterMechanism is invalid.\");\n }\n}\n\nfunction validatePointWithinActiveCapture(\n point: RasterPoint,\n rect: RasterRect,\n path: string\n): void {\n if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) {\n throw new InvalidScientificInputError(\n path + \" coordinates must be finite.\"\n );\n }\n\n if (\n point.x < rect.x ||\n point.x > rect.x + rect.width ||\n point.y < rect.y ||\n point.y > rect.y + rect.height\n ) {\n throw new InvalidScientificInputError(\n path + \" must lie within activeCaptureRect edge-coordinate bounds.\"\n );\n }\n}\n\nfunction scanVector(\n direction: NativeSensorReadoutScanDirection\n): RasterVector {\n switch (direction) {\n case \"top-to-bottom\":\n return { x: 0, y: 1 };\n case \"bottom-to-top\":\n return { x: 0, y: -1 };\n case \"left-to-right\":\n return { x: 1, y: 0 };\n case \"right-to-left\":\n return { x: -1, y: 0 };\n }\n}\n\nfunction normalizedScanPosition(\n point: RasterPoint,\n rect: RasterRect,\n direction: NativeSensorReadoutScanDirection\n): number {\n switch (direction) {\n case \"top-to-bottom\":\n return (point.y - rect.y) / rect.height;\n case \"bottom-to-top\":\n return 1 - (point.y - rect.y) / rect.height;\n case \"left-to-right\":\n return (point.x - rect.x) / rect.width;\n case \"right-to-left\":\n return 1 - (point.x - rect.x) / rect.width;\n }\n}\n\nfunction boundaryPhase(\n point: RasterPoint,\n rect: RasterRect,\n schedule: ExposureBoundarySchedule\n): {\n normalizedScanPosition: number | null;\n phaseOffsetSeconds: number;\n} {\n if (schedule.kind === \"simultaneous\") {\n return {\n normalizedScanPosition: null,\n phaseOffsetSeconds: 0\n };\n }\n\n const normalized = normalizedScanPosition(\n point,\n rect,\n schedule.directionNative.value\n );\n\n return {\n normalizedScanPosition: normalized,\n phaseOffsetSeconds:\n normalized * schedule.traversalDurationSeconds.value\n };\n}\n\nfunction resolveBoundaryActuators(\n mechanism: CaptureShutterMechanism\n): {\n opening: CaptureExposureBoundaryActuator;\n closing: CaptureExposureBoundaryActuator;\n} {\n switch (mechanism) {\n case \"mechanical\":\n return {\n opening: \"mechanical\",\n closing: \"mechanical\"\n };\n case \"electronic-first-curtain\":\n return {\n opening: \"electronic\",\n closing: \"mechanical\"\n };\n case \"electronic\":\n return {\n opening: \"electronic\",\n closing: \"electronic\"\n };\n }\n}\n\nfunction resolveBoundary(\n actuator: CaptureExposureBoundaryActuator,\n schedule: ExposureBoundarySchedule\n): ResolvedCaptureExposureBoundary {\n if (schedule.kind === \"simultaneous\") {\n return {\n actuator,\n schedule: {\n kind: \"simultaneous\"\n }\n };\n }\n\n return {\n actuator,\n schedule: {\n kind: \"uniform-linear-native-scan\",\n directionNative: schedule.directionNative,\n unitVectorNative: scanVector(schedule.directionNative.value),\n traversalDurationSeconds: schedule.traversalDurationSeconds\n }\n };\n}\n\nfunction createWindowSample(\n point: RasterPoint,\n rect: RasterRect,\n opening: ExposureBoundarySchedule,\n closing: ExposureBoundarySchedule,\n nominalExposureDurationSeconds: number\n): CaptureExposureWindowSample {\n const openingPhase = boundaryPhase(point, rect, opening);\n const closingPhase = boundaryPhase(point, rect, closing);\n const startOffsetSecondsFromOpeningReference =\n openingPhase.phaseOffsetSeconds;\n const endOffsetSecondsFromOpeningReference =\n nominalExposureDurationSeconds + closingPhase.phaseOffsetSeconds;\n const localExposureDurationSeconds =\n endOffsetSecondsFromOpeningReference -\n startOffsetSecondsFromOpeningReference;\n\n return {\n pointNative: { ...point },\n openingNormalizedScanPosition:\n openingPhase.normalizedScanPosition,\n closingNormalizedScanPosition:\n closingPhase.normalizedScanPosition,\n startOffsetSecondsFromOpeningReference,\n endOffsetSecondsFromOpeningReference,\n localExposureDurationSeconds\n };\n}\n\nfunction activeRectCorners(rect: RasterRect): readonly RasterPoint[] {\n return [\n { x: rect.x, y: rect.y },\n { x: rect.x + rect.width, y: rect.y },\n { x: rect.x, y: rect.y + rect.height },\n {\n x: rect.x + rect.width,\n y: rect.y + rect.height\n }\n ];\n}\n\n/**\n * Works out each point's local exposure window from separate opening and\n * closing boundary schedules.\n *\n * Times are seconds from the first opening boundary. This is deliberately not\n * the camera-rotation API's global \"exposure start\"; a later layer must link the\n * two explicitly.\n *\n * The mechanism records whether each boundary is mechanical or electronic, but\n * supplies no sweep duration or direction. Every timing value and direction for\n * a non-simultaneous boundary is declared by the caller, with evidence.\n *\n * Only simultaneous boundaries and a uniform linear sweep along one native\n * sensor axis are supported. Not modelled: curtain acceleration, segmented or\n * non-linear electronic schedules, flash or flicker, rolling-shutter image\n * distortion, shutter shock, and EFCS (electronic first-curtain shutter)\n * effects on the pupil or bokeh.\n */\nexport function calculateCaptureExposureWindows(\n input: CalculateCaptureExposureWindowsInput\n): CalculationResult<CaptureExposureWindows> {\n requireInputKeys(input, \"calculateCaptureExposureWindows\", [\"nativeRaster\", \"activeCaptureRect\", \"shutterMechanism\", \"nominalExposureDurationSeconds\", \"opening\", \"closing\", \"samplePointsNative\"]);\n validateNativeRaster(input.nativeRaster);\n validateShutterMechanism(input.shutterMechanism);\n\n const nominalExposureDurationSeconds = parseSecondsFact(\n input.nominalExposureDurationSeconds,\n \"nominalExposureDurationSeconds\"\n );\n const opening = parseBoundarySchedule(input.opening, \"opening\");\n const closing = parseBoundarySchedule(input.closing, \"closing\");\n\n const activeCaptureRect =\n input.activeCaptureRect ?? fullRect(input.nativeRaster);\n validateActiveCaptureRect(activeCaptureRect, input.nativeRaster);\n\n if (\n input.samplePointsNative !== undefined &&\n !Array.isArray(input.samplePointsNative)\n ) {\n throw new InvalidConfigurationError(\n \"samplePointsNative must be an array when supplied.\"\n );\n }\n\n const cornerSamples = activeRectCorners(activeCaptureRect).map(\n (point) =>\n createWindowSample(\n point,\n activeCaptureRect,\n opening,\n closing,\n nominalExposureDurationSeconds.value\n )\n );\n\n const localDurations = cornerSamples.map(\n (sample) => sample.localExposureDurationSeconds\n );\n const minimumLocalExposureDurationSeconds = Math.min(...localDurations);\n const maximumLocalExposureDurationSeconds = Math.max(...localDurations);\n\n if (\n !Number.isFinite(minimumLocalExposureDurationSeconds) ||\n minimumLocalExposureDurationSeconds <= 0\n ) {\n throw new InvalidScientificInputError(\n \"The declared opening/closing schedules produce a zero or negative local exposure duration somewhere within activeCaptureRect.\"\n );\n }\n\n const samples = (input.samplePointsNative ?? []).map((point, index) => {\n validatePointWithinActiveCapture(\n point,\n activeCaptureRect,\n \"samplePointsNative[\" + index + \"]\"\n );\n\n return createWindowSample(\n point,\n activeCaptureRect,\n opening,\n closing,\n nominalExposureDurationSeconds.value\n );\n });\n\n const actuators = resolveBoundaryActuators(input.shutterMechanism);\n\n return approximationResult(\n {\n shutterMechanism: input.shutterMechanism,\n timeReference: \"first-opening-boundary-phase\",\n nativeRaster: { ...input.nativeRaster },\n activeCaptureRect: { ...activeCaptureRect },\n nominalExposureDurationSeconds,\n opening: resolveBoundary(actuators.opening, opening),\n closing: resolveBoundary(actuators.closing, closing),\n localExposureDurationRangeSeconds: {\n minimum: minimumLocalExposureDurationSeconds,\n maximum: maximumLocalExposureDurationSeconds\n },\n samples\n },\n \"capture-exposure-window-schedule\",\n \"1.0.0\",\n [\n \"Time is reported in seconds from the first opening-boundary phase, not from a universal local exposure-start event.\",\n \"Nominal exposure duration, opening traversal, and closing traversal are independent declared quantities.\",\n \"Mechanical, electronic-first-curtain, and electronic mechanisms determine boundary actuator categories only; they do not supply timing or direction.\",\n \"Uniform-linear scan boundaries are an explicit approximation over continuous effective native-raster coordinates and do not assert one raster row/column equals one physical curtain line, photodiode row, or hardware readout line.\",\n \"Opening and closing directions and traversal durations are independent and may produce spatially varying local exposure duration.\",\n \"The complete active rectangle is validated using the extrema of the affine boundary schedules, so unsampled invalid corners cannot pass.\",\n \"Active-capture timing is caller-declared and is not inferred by scaling full-frame timing from crop dimensions.\",\n \"Physical camera orientation and digital output crop/resolution remain downstream of the invariant native timing schedule.\",\n \"Sensor readout timing remains a separate contract; this model does not automatically equate a sensor readout phase with an exposure boundary.\",\n \"This model does not include curtain acceleration, segmented/nonlinear electronic timing, rolling-shutter image distortion, motion integration, flash/flicker interaction, shutter shock, or EFCS-specific pupil/bokeh behavior.\"\n ]\n );\n}\n\n/**\n * Internal, for the faster (\"lean\") native environment executor (a 2.0 performance change). It checks a\n * schedule once with every check of calculateCaptureExposureWindows, then returns a per-point sampler that\n * uses the same point validation and the same createWindowSample arithmetic. So each sampled window is\n * identical to what calculateCaptureExposureWindows returns for that point. Not exported from the package.\n */\nexport function prepareCaptureExposureWindowSampler(\n input: Omit<CalculateCaptureExposureWindowsInput, \"samplePointsNative\">\n): (point: RasterPoint) => CaptureExposureWindowSample {\n const admitted = calculateCaptureExposureWindows({ ...input, samplePointsNative: [] }).value;\n const rect = { ...admitted.activeCaptureRect };\n const opening = parseBoundarySchedule(input.opening, \"opening\");\n const closing = parseBoundarySchedule(input.closing, \"closing\");\n const nominal = admitted.nominalExposureDurationSeconds.value;\n return (point) => {\n validatePointWithinActiveCapture(point, rect, \"samplePointsNative[0]\");\n return createWindowSample(point, rect, opening, closing, nominal);\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"exposure-window.js","sourceRoot":"","sources":["../../src/sensor/exposure-window.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Exposure windows: when each point on the sensor starts and stops collecting light.\n *\n * The shutter has an opening boundary and a closing boundary, each with its own schedule. With a\n * rolling or curtain shutter the boundaries sweep across the sensor, so each point has its own\n * (local) window. Times are seconds from the first opening boundary. That is deliberately not the\n * same as the camera-rotation API's global \"exposure start\"; a later layer must link the two\n * explicitly.\n *\n * The mechanism records whether each boundary is mechanical or electronic, but supplies no sweep\n * duration or direction. Every timing value and direction for a non-simultaneous boundary is declared\n * by the caller, with evidence. Only simultaneous boundaries and a uniform linear sweep along one\n * native sensor axis are supported. Not modelled: curtain acceleration, segmented or non-linear\n * electronic schedules, flash or flicker, rolling-shutter image distortion, shutter shock, and\n * EFCS (electronic first-curtain shutter) effects on the pupil or bokeh.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport {\n parseEvidenceList,\n type EvidenceBackedFact\n} from \"../core/evidence-provenance.js\";\nimport {\n InvalidScientificInputError,\n requirePositiveInteger\n} from \"../core/validation.js\";\nimport type {\n RasterPoint,\n RasterRect,\n RasterVector\n} from \"../output/capture-geometry.js\";\nimport type { NativeImageRaster } from \"./sensor-geometry.js\";\nimport type {\n CaptureShutterMechanism,\n NativeSensorReadoutScanDirection,\n SourcedSensorTimingSeconds\n} from \"./readout-timing.js\";\nimport { requireInputObject, requireInputKeys, requireConfigRecord as requireRecord } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { fullRect } from \"../core/shared-helpers.js\";\n\nexport type CaptureExposureBoundaryActuator = \"mechanical\" | \"electronic\";\n\nexport type SourcedCaptureTimingSeconds = SourcedSensorTimingSeconds;\n\nexport type SourcedCaptureBoundaryDirection =\n EvidenceBackedFact<NativeSensorReadoutScanDirection>;\n\nexport interface SimultaneousExposureBoundarySchedule {\n kind: \"simultaneous\";\n directionNative?: never;\n traversalDurationSeconds?: never;\n}\n\nexport interface UniformLinearExposureBoundarySchedule {\n kind: \"uniform-linear-native-scan\";\n directionNative: SourcedCaptureBoundaryDirection;\n traversalDurationSeconds: SourcedCaptureTimingSeconds;\n}\n\nexport type ExposureBoundarySchedule =\n | SimultaneousExposureBoundarySchedule\n | UniformLinearExposureBoundarySchedule;\n\nexport interface CalculateCaptureExposureWindowsInput {\n nativeRaster: NativeImageRaster;\n /**\n * Active capture rectangle, in fixed native pixel coordinates.\n *\n * Defaults to the whole native image. Sweep timing is never worked out by\n * scaling a full-frame timing value to this rectangle.\n */\n activeCaptureRect?: RasterRect;\n shutterMechanism: CaptureShutterMechanism;\n /**\n * Nominal time between the opening and closing reference points (phase zero).\n *\n * A point's local exposure can differ from this when the opening and closing\n * boundaries sweep differently across the sensor.\n */\n nominalExposureDurationSeconds: SourcedCaptureTimingSeconds;\n opening: ExposureBoundarySchedule;\n closing: ExposureBoundarySchedule;\n /**\n * Optional points, in continuous native pixel coordinates measured from pixel\n * edges, to report windows for. A pixel centre is at +0.5.\n */\n samplePointsNative?: readonly RasterPoint[];\n}\n\nexport interface ResolvedCaptureExposureBoundary {\n actuator: CaptureExposureBoundaryActuator;\n schedule:\n | {\n kind: \"simultaneous\";\n }\n | {\n kind: \"uniform-linear-native-scan\";\n directionNative: SourcedCaptureBoundaryDirection;\n unitVectorNative: RasterVector;\n traversalDurationSeconds: SourcedCaptureTimingSeconds;\n };\n}\n\nexport interface CaptureExposureWindowSample {\n pointNative: RasterPoint;\n openingNormalizedScanPosition: number | null;\n closingNormalizedScanPosition: number | null;\n /**\n * Seconds from the first opening boundary.\n */\n startOffsetSecondsFromOpeningReference: number;\n /**\n * Seconds from the first opening boundary.\n */\n endOffsetSecondsFromOpeningReference: number;\n localExposureDurationSeconds: number;\n}\n\nexport interface CaptureExposureWindows {\n shutterMechanism: CaptureShutterMechanism;\n timeReference: \"first-opening-boundary-phase\";\n nativeRaster: NativeImageRaster;\n activeCaptureRect: RasterRect;\n nominalExposureDurationSeconds: SourcedCaptureTimingSeconds;\n opening: ResolvedCaptureExposureBoundary;\n closing: ResolvedCaptureExposureBoundary;\n localExposureDurationRangeSeconds: {\n minimum: number;\n maximum: number;\n };\n samples: readonly CaptureExposureWindowSample[];\n}\n\nconst SHUTTER_MECHANISMS = new Set<CaptureShutterMechanism>([\n \"mechanical\",\n \"electronic-first-curtain\",\n \"electronic\"\n]);\n\nconst SCAN_DIRECTIONS = new Set<NativeSensorReadoutScanDirection>([\n \"top-to-bottom\",\n \"bottom-to-top\",\n \"left-to-right\",\n \"right-to-left\"\n]);\n\nconst BOUNDARY_KINDS = new Set<ExposureBoundarySchedule[\"kind\"]>([\n \"simultaneous\",\n \"uniform-linear-native-scan\"\n]);\n\nexport function requireNonNegativeInteger(name: string, value: number): void {\n if (!Number.isSafeInteger(value) || value < 0) {\n throw new InvalidScientificInputError(\n name + \" must be a non-negative safe integer.\"\n );\n }\n}\n\nexport function requirePositiveFinite(name: string, value: unknown): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new InvalidScientificInputError(\n name + \" must be a finite number greater than zero.\"\n );\n }\n return value;\n}\n\n/** Parses a sourced duration in seconds (`{ unit: \"s\", value, evidence }`). Not a root export. */\nexport function parseSecondsFact(\n value: unknown,\n path: string\n): SourcedCaptureTimingSeconds {\n const record = requireRecord(value, path, [\"unit\", \"value\", \"evidence\"]);\n if (record.unit !== \"s\") {\n throw new InvalidConfigurationError(path + '.unit must be \"s\".');\n }\n\n return {\n value: requirePositiveFinite(path + \".value\", record.value),\n unit: \"s\",\n evidence: parseEvidenceList(record.evidence, path + \".evidence\")\n };\n}\n\nfunction parseDirectionFact(\n value: unknown,\n path: string\n): SourcedCaptureBoundaryDirection {\n const record = requireRecord(value, path, [\"value\", \"evidence\"]);\n if (\n typeof record.value !== \"string\" ||\n !SCAN_DIRECTIONS.has(\n record.value as NativeSensorReadoutScanDirection\n )\n ) {\n throw new InvalidConfigurationError(path + \".value is invalid.\");\n }\n\n return {\n value: record.value as NativeSensorReadoutScanDirection,\n evidence: parseEvidenceList(record.evidence, path + \".evidence\")\n };\n}\n\n/** Parses one exposure boundary schedule (opening or closing). Not a root export. */\nexport function parseBoundarySchedule(\n value: unknown,\n path: string\n): ExposureBoundarySchedule {\n // unitVectorNative is the derived field of a resolved schedule; it may be passed back if it matches.\n const record = requireRecord(value, path, [\"kind\", \"directionNative\", \"traversalDurationSeconds\", \"unitVectorNative\"]);\n if (\n typeof record.kind !== \"string\" ||\n !BOUNDARY_KINDS.has(record.kind as ExposureBoundarySchedule[\"kind\"])\n ) {\n throw new InvalidConfigurationError(path + \".kind is invalid.\");\n }\n\n if (record.kind === \"simultaneous\") {\n if (record.directionNative !== undefined) {\n throw new InvalidScientificInputError(\n path + \".directionNative must be omitted for a simultaneous boundary.\"\n );\n }\n if (record.traversalDurationSeconds !== undefined) {\n throw new InvalidScientificInputError(\n path +\n \".traversalDurationSeconds must be omitted for a simultaneous boundary.\"\n );\n }\n if (record.unitVectorNative !== undefined) {\n throw new InvalidScientificInputError(\n path + \".unitVectorNative must be omitted for a simultaneous boundary.\"\n );\n }\n\n return { kind: \"simultaneous\" };\n }\n\n const directionNative = parseDirectionFact(\n record.directionNative,\n path + \".directionNative\"\n );\n if (record.unitVectorNative !== undefined) {\n const supplied = requireRecord(record.unitVectorNative, path + \".unitVectorNative\", [\"x\", \"y\"]);\n const derived = scanVector(directionNative.value);\n if (supplied.x !== derived.x || supplied.y !== derived.y) {\n throw new InvalidConfigurationError(path + \".unitVectorNative must match directionNative.\");\n }\n }\n return {\n kind: \"uniform-linear-native-scan\",\n directionNative,\n traversalDurationSeconds: parseSecondsFact(\n record.traversalDurationSeconds,\n path + \".traversalDurationSeconds\"\n )\n };\n}\n\nexport function validateNativeRaster(raster: NativeImageRaster): void {\n requireInputObject(raster, \"raster input\");\n requirePositiveInteger(\"nativeRaster.pixelWidth\", raster.pixelWidth);\n requirePositiveInteger(\"nativeRaster.pixelHeight\", raster.pixelHeight);\n}\n\nfunction validateActiveCaptureRect(\n rect: RasterRect,\n raster: NativeImageRaster\n): void {\n requireNonNegativeInteger(\"activeCaptureRect.x\", rect.x);\n requireNonNegativeInteger(\"activeCaptureRect.y\", rect.y);\n requirePositiveInteger(\"activeCaptureRect.width\", rect.width);\n requirePositiveInteger(\"activeCaptureRect.height\", rect.height);\n\n if (\n rect.x + rect.width > raster.pixelWidth ||\n rect.y + rect.height > raster.pixelHeight\n ) {\n throw new InvalidScientificInputError(\n \"activeCaptureRect must fit entirely within nativeRaster.\"\n );\n }\n}\n\nfunction validateShutterMechanism(\n value: unknown\n): asserts value is CaptureShutterMechanism {\n if (\n typeof value !== \"string\" ||\n !SHUTTER_MECHANISMS.has(value as CaptureShutterMechanism)\n ) {\n throw new InvalidConfigurationError(\"shutterMechanism is invalid.\");\n }\n}\n\nfunction validatePointWithinActiveCapture(\n point: RasterPoint,\n rect: RasterRect,\n path: string\n): void {\n if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) {\n throw new InvalidScientificInputError(\n path + \" coordinates must be finite.\"\n );\n }\n\n if (\n point.x < rect.x ||\n point.x > rect.x + rect.width ||\n point.y < rect.y ||\n point.y > rect.y + rect.height\n ) {\n throw new InvalidScientificInputError(\n path + \" must lie within activeCaptureRect edge-coordinate bounds.\"\n );\n }\n}\n\nexport function scanVector(\n direction: NativeSensorReadoutScanDirection\n): RasterVector {\n switch (direction) {\n case \"top-to-bottom\":\n return { x: 0, y: 1 };\n case \"bottom-to-top\":\n return { x: 0, y: -1 };\n case \"left-to-right\":\n return { x: 1, y: 0 };\n case \"right-to-left\":\n return { x: -1, y: 0 };\n }\n}\n\nexport function normalizedScanPosition(\n point: RasterPoint,\n rect: RasterRect,\n direction: NativeSensorReadoutScanDirection\n): number {\n switch (direction) {\n case \"top-to-bottom\":\n return (point.y - rect.y) / rect.height;\n case \"bottom-to-top\":\n return 1 - (point.y - rect.y) / rect.height;\n case \"left-to-right\":\n return (point.x - rect.x) / rect.width;\n case \"right-to-left\":\n return 1 - (point.x - rect.x) / rect.width;\n }\n}\n\nfunction boundaryPhase(\n point: RasterPoint,\n rect: RasterRect,\n schedule: ExposureBoundarySchedule\n): {\n normalizedScanPosition: number | null;\n phaseOffsetSeconds: number;\n} {\n if (schedule.kind === \"simultaneous\") {\n return {\n normalizedScanPosition: null,\n phaseOffsetSeconds: 0\n };\n }\n\n const normalized = normalizedScanPosition(\n point,\n rect,\n schedule.directionNative.value\n );\n\n return {\n normalizedScanPosition: normalized,\n phaseOffsetSeconds:\n normalized * schedule.traversalDurationSeconds.value\n };\n}\n\nfunction resolveBoundaryActuators(\n mechanism: CaptureShutterMechanism\n): {\n opening: CaptureExposureBoundaryActuator;\n closing: CaptureExposureBoundaryActuator;\n} {\n switch (mechanism) {\n case \"mechanical\":\n return {\n opening: \"mechanical\",\n closing: \"mechanical\"\n };\n case \"electronic-first-curtain\":\n return {\n opening: \"electronic\",\n closing: \"mechanical\"\n };\n case \"electronic\":\n return {\n opening: \"electronic\",\n closing: \"electronic\"\n };\n }\n}\n\nfunction resolveBoundary(\n actuator: CaptureExposureBoundaryActuator,\n schedule: ExposureBoundarySchedule\n): ResolvedCaptureExposureBoundary {\n if (schedule.kind === \"simultaneous\") {\n return {\n actuator,\n schedule: {\n kind: \"simultaneous\"\n }\n };\n }\n\n return {\n actuator,\n schedule: {\n kind: \"uniform-linear-native-scan\",\n directionNative: schedule.directionNative,\n unitVectorNative: scanVector(schedule.directionNative.value),\n traversalDurationSeconds: schedule.traversalDurationSeconds\n }\n };\n}\n\nfunction createWindowSample(\n point: RasterPoint,\n rect: RasterRect,\n opening: ExposureBoundarySchedule,\n closing: ExposureBoundarySchedule,\n nominalExposureDurationSeconds: number\n): CaptureExposureWindowSample {\n const openingPhase = boundaryPhase(point, rect, opening);\n const closingPhase = boundaryPhase(point, rect, closing);\n const startOffsetSecondsFromOpeningReference =\n openingPhase.phaseOffsetSeconds;\n const endOffsetSecondsFromOpeningReference =\n nominalExposureDurationSeconds + closingPhase.phaseOffsetSeconds;\n const localExposureDurationSeconds =\n endOffsetSecondsFromOpeningReference -\n startOffsetSecondsFromOpeningReference;\n\n return {\n pointNative: { ...point },\n openingNormalizedScanPosition:\n openingPhase.normalizedScanPosition,\n closingNormalizedScanPosition:\n closingPhase.normalizedScanPosition,\n startOffsetSecondsFromOpeningReference,\n endOffsetSecondsFromOpeningReference,\n localExposureDurationSeconds\n };\n}\n\nfunction activeRectCorners(rect: RasterRect): readonly RasterPoint[] {\n return [\n { x: rect.x, y: rect.y },\n { x: rect.x + rect.width, y: rect.y },\n { x: rect.x, y: rect.y + rect.height },\n {\n x: rect.x + rect.width,\n y: rect.y + rect.height\n }\n ];\n}\n\n/**\n * Works out each point's local exposure window from separate opening and\n * closing boundary schedules.\n *\n * Times are seconds from the first opening boundary. This is deliberately not\n * the camera-rotation API's global \"exposure start\"; a later layer must link the\n * two explicitly.\n *\n * The mechanism records whether each boundary is mechanical or electronic, but\n * supplies no sweep duration or direction. Every timing value and direction for\n * a non-simultaneous boundary is declared by the caller, with evidence.\n *\n * Only simultaneous boundaries and a uniform linear sweep along one native\n * sensor axis are supported. Not modelled: curtain acceleration, segmented or\n * non-linear electronic schedules, flash or flicker, rolling-shutter image\n * distortion, shutter shock, and EFCS (electronic first-curtain shutter)\n * effects on the pupil or bokeh.\n */\nexport function calculateCaptureExposureWindows(\n input: CalculateCaptureExposureWindowsInput\n): CalculationResult<CaptureExposureWindows> {\n requireInputKeys(input, \"calculateCaptureExposureWindows\", [\"nativeRaster\", \"activeCaptureRect\", \"shutterMechanism\", \"nominalExposureDurationSeconds\", \"opening\", \"closing\", \"samplePointsNative\"]);\n validateNativeRaster(input.nativeRaster);\n validateShutterMechanism(input.shutterMechanism);\n\n const nominalExposureDurationSeconds = parseSecondsFact(\n input.nominalExposureDurationSeconds,\n \"nominalExposureDurationSeconds\"\n );\n const opening = parseBoundarySchedule(input.opening, \"opening\");\n const closing = parseBoundarySchedule(input.closing, \"closing\");\n\n const activeCaptureRect =\n input.activeCaptureRect ?? fullRect(input.nativeRaster);\n validateActiveCaptureRect(activeCaptureRect, input.nativeRaster);\n\n if (\n input.samplePointsNative !== undefined &&\n !Array.isArray(input.samplePointsNative)\n ) {\n throw new InvalidConfigurationError(\n \"samplePointsNative must be an array when supplied.\"\n );\n }\n\n const cornerSamples = activeRectCorners(activeCaptureRect).map(\n (point) =>\n createWindowSample(\n point,\n activeCaptureRect,\n opening,\n closing,\n nominalExposureDurationSeconds.value\n )\n );\n\n const localDurations = cornerSamples.map(\n (sample) => sample.localExposureDurationSeconds\n );\n const minimumLocalExposureDurationSeconds = Math.min(...localDurations);\n const maximumLocalExposureDurationSeconds = Math.max(...localDurations);\n\n if (\n !Number.isFinite(minimumLocalExposureDurationSeconds) ||\n minimumLocalExposureDurationSeconds <= 0\n ) {\n throw new InvalidScientificInputError(\n \"The declared opening/closing schedules produce a zero or negative local exposure duration somewhere within activeCaptureRect.\"\n );\n }\n\n const samples = (input.samplePointsNative ?? []).map((point, index) => {\n validatePointWithinActiveCapture(\n point,\n activeCaptureRect,\n \"samplePointsNative[\" + index + \"]\"\n );\n\n return createWindowSample(\n point,\n activeCaptureRect,\n opening,\n closing,\n nominalExposureDurationSeconds.value\n );\n });\n\n const actuators = resolveBoundaryActuators(input.shutterMechanism);\n\n return approximationResult(\n {\n shutterMechanism: input.shutterMechanism,\n timeReference: \"first-opening-boundary-phase\",\n nativeRaster: { ...input.nativeRaster },\n activeCaptureRect: { ...activeCaptureRect },\n nominalExposureDurationSeconds,\n opening: resolveBoundary(actuators.opening, opening),\n closing: resolveBoundary(actuators.closing, closing),\n localExposureDurationRangeSeconds: {\n minimum: minimumLocalExposureDurationSeconds,\n maximum: maximumLocalExposureDurationSeconds\n },\n samples\n },\n \"capture-exposure-window-schedule\",\n \"1.0.0\",\n [\n \"Time is reported in seconds from the first opening-boundary phase, not from a universal local exposure-start event.\",\n \"Nominal exposure duration, opening traversal, and closing traversal are independent declared quantities.\",\n \"Mechanical, electronic-first-curtain, and electronic mechanisms determine boundary actuator categories only; they do not supply timing or direction.\",\n \"Uniform-linear scan boundaries are an explicit approximation over continuous effective native-raster coordinates and do not assert one raster row/column equals one physical curtain line, photodiode row, or hardware readout line.\",\n \"Opening and closing directions and traversal durations are independent and may produce spatially varying local exposure duration.\",\n \"The complete active rectangle is validated using the extrema of the affine boundary schedules, so unsampled invalid corners cannot pass.\",\n \"Active-capture timing is caller-declared and is not inferred by scaling full-frame timing from crop dimensions.\",\n \"Physical camera orientation and digital output crop/resolution remain downstream of the invariant native timing schedule.\",\n \"Sensor readout timing remains a separate contract; this model does not automatically equate a sensor readout phase with an exposure boundary.\",\n \"This model does not include curtain acceleration, segmented/nonlinear electronic timing, rolling-shutter image distortion, motion integration, flash/flicker interaction, shutter shock, or EFCS-specific pupil/bokeh behavior.\"\n ]\n );\n}\n\n/**\n * Internal, for the faster (\"lean\") native environment executor (a 2.0 performance change). It checks a\n * schedule once with every check of calculateCaptureExposureWindows, then returns a per-point sampler that\n * uses the same point validation and the same createWindowSample arithmetic. So each sampled window is\n * identical to what calculateCaptureExposureWindows returns for that point. Not exported from the package.\n */\nexport function prepareCaptureExposureWindowSampler(\n input: Omit<CalculateCaptureExposureWindowsInput, \"samplePointsNative\">\n): (point: RasterPoint) => CaptureExposureWindowSample {\n const admitted = calculateCaptureExposureWindows({ ...input, samplePointsNative: [] }).value;\n const rect = { ...admitted.activeCaptureRect };\n const opening = parseBoundarySchedule(input.opening, \"opening\");\n const closing = parseBoundarySchedule(input.closing, \"closing\");\n const nominal = admitted.nominalExposureDurationSeconds.value;\n return (point) => {\n validatePointWithinActiveCapture(point, rect, \"samplePointsNative[0]\");\n return createWindowSample(point, rect, opening, closing, nominal);\n };\n}\n"]}
|
package/dist/sensor/iso-gain.js
CHANGED
|
@@ -26,14 +26,8 @@
|
|
|
26
26
|
*/
|
|
27
27
|
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
28
28
|
import { parseEvidenceList } from "../core/evidence-provenance.js";
|
|
29
|
-
import { requireAllowlistedRecord, requirePublicOpaqueId } from "../core/record-validation.js";
|
|
29
|
+
import { requireAllowlistedRecord, requirePublicOpaqueId, requireConfigPositiveFinite as positive } from "../core/record-validation.js";
|
|
30
30
|
import { parseSensorReadoutConversionProfile } from "./raw-readout.js";
|
|
31
|
-
function positive(value, path) {
|
|
32
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
33
|
-
throw new InvalidConfigurationError(path + " must be finite and greater than zero.");
|
|
34
|
-
}
|
|
35
|
-
return value;
|
|
36
|
-
}
|
|
37
31
|
function positiveFact(value, path) {
|
|
38
32
|
const r = requireAllowlistedRecord(value, ["value", "evidence"], path + " must be an evidence-backed fact.");
|
|
39
33
|
return { value: positive(r.value, path + ".value"), evidence: parseEvidenceList(r.evidence, path + ".evidence") };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iso-gain.js","sourceRoot":"","sources":["../../src/sensor/iso-gain.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * ISO gain: builds one explicit readout regime for each ISO (exposure index) setting from declared\n * sensor parameters, so that raising ISO brightens the RAW the way a camera's programmable gain does.\n *\n * Model (exposure index S = 10 / H of CIPA DC-004-2020, free at https://www.cipa.jp/e/std/std-sec.html,\n * also specified in ISO 12232:2019; EMVA Standard 1288 Release 4.0 linear camera model,\n * where the system gain K in DN/e- multiplies the electron signal):\n * R = S / S0 exposure-index ratio to the base exposure index S0\n * a = min(R, A_max) analog (pre-ADC) gain relative to the base regime\n * d = R / a post-ADC digital gain beyond the analog range\n * g(S) = Q0 / (span * a) electrons per code (1/K), span = digitalSaturation - black\n * Q_preADC(S) = Q0 / a electrons that drive the ADC to digital saturation\n * So the electrons needed to reach digital saturation are Q0 * S0 / S.\n *\n * The exposure indices of CIPA DC-004 put the reference exposure H_ref in proportion to 1/S: standard\n * output sensitivity (SOS) and REI (recommended exposure index) both use S = 10 / H (§3.2, §3.3). So a\n * correct exposure for S gives the same black-subtracted fraction of digital saturation at every S.\n *\n * Read noise in electrons is declared per conversion-gain mode, never derived from S. (With dual\n * conversion gain, the high-conversion-gain mode used at high S has lower input-referred read noise.)\n *\n * Inputs are explicit declarations; Photivra presets use synthetic teaching values. No camera\n * calibration is implied.\n * @see docs/MOTION_AND_SIGNAL.md for equations, sources and limits.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { parseEvidenceList, type EvidenceBackedFact, type EvidenceProvenance } from \"../core/evidence-provenance.js\";\nimport { requireAllowlistedRecord, requirePublicOpaqueId } from \"../core/record-validation.js\";\nimport { parseSensorReadoutConversionProfile, type SensorReadoutConversionRegime,\n type SensorReadoutExposureIndexGain } from \"./raw-readout.js\";\nimport type { GenericIsoStandardRegimeBand } from \"./iso-signal-chain.js\";\nimport type { SensorSpectralResponseScientificStatus } from \"./spectral-response.js\";\n\n/** One conversion-gain mode, active from its minimum exposure index up to the next mode's. */\nexport interface IsoGainConversionGainMode {\n mode: SensorReadoutExposureIndexGain[\"conversionGainMode\"];\n minimumExposureIndex: number;\n readNoiseRmsElectrons: EvidenceBackedFact<number>;\n /** Optional charge storage limit of this mode. Every regime's pre-ADC threshold must be at or below it. */\n maximumChargeElectrons?: EvidenceBackedFact<number>;\n}\n\nexport interface CreateIsoGainReadoutRegimesInput {\n regimeIdPrefix: string;\n scientificStatus: SensorSpectralResponseScientificStatus;\n baseExposureIndex: number;\n /** Strictly increasing settings, each at least the base exposure index. */\n exposureIndices: readonly number[];\n /** Q0: electron-equivalent signal that reaches digital saturation at the base exposure index. */\n baseSaturationElectronEquivalent: EvidenceBackedFact<number>;\n /** A_max: largest analog gain relative to the base regime (>= 1). */\n maximumAnalogGain: EvidenceBackedFact<number>;\n adc: { bitDepth: number; blackLevelCode: number; digitalSaturationCode: number };\n conversionGainModes: readonly IsoGainConversionGainMode[];\n evidence: readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\nexport interface IsoGainReadoutRegimes {\n regimes: readonly SensorReadoutConversionRegime[];\n /** One single-value band per setting, ready for a GenericIsoSignalChainProfile. */\n standardRegimeBands: readonly GenericIsoStandardRegimeBand[];\n}\n\nfunction positive(value: unknown, path: string): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new InvalidConfigurationError(path + \" must be finite and greater than zero.\");\n }\n return value;\n}\n\nfunction positiveFact(value: unknown, path: string): EvidenceBackedFact<number> {\n const r = requireAllowlistedRecord(value, [\"value\", \"evidence\"], path + \" must be an evidence-backed fact.\");\n return { value: positive(r.value, path + \".value\"), evidence: parseEvidenceList(r.evidence, path + \".evidence\") };\n}\n\n/**\n * Builds a readout regime and signal-chain band for each exposure index (see the module comment\n * for the equations). The result is checked with the same rules as parseSensorReadoutConversionProfile.\n * Each regime carries an `exposureIndexGain` record of how it was derived.\n * @param input - The sensor parameters and ISO settings; untrusted values are validated.\n * @returns The readout regimes and bands.\n */\nexport function createIsoGainReadoutRegimes(input: CreateIsoGainReadoutRegimesInput): IsoGainReadoutRegimes {\n const r = requireAllowlistedRecord(input, [\"regimeIdPrefix\", \"scientificStatus\", \"baseExposureIndex\", \"exposureIndices\",\n \"baseSaturationElectronEquivalent\", \"maximumAnalogGain\", \"adc\", \"conversionGainModes\", \"evidence\", \"limitations\"],\n \"Invalid ISO gain readout fields.\");\n const prefix = r.regimeIdPrefix === \"\" ? \"\" : requirePublicOpaqueId(r.regimeIdPrefix, \"regimeIdPrefix must be empty or a public opaque identifier.\");\n const base = positive(r.baseExposureIndex, \"baseExposureIndex\");\n const q0 = positiveFact(r.baseSaturationElectronEquivalent, \"baseSaturationElectronEquivalent\");\n const aMax = positiveFact(r.maximumAnalogGain, \"maximumAnalogGain\");\n if (aMax.value < 1) throw new InvalidConfigurationError(\"maximumAnalogGain must be at least 1.\");\n const evidence = parseEvidenceList(r.evidence, \"evidence\");\n const adc = requireAllowlistedRecord(r.adc, [\"bitDepth\", \"blackLevelCode\", \"digitalSaturationCode\"], \"Invalid ISO gain ADC fields.\");\n if (!Array.isArray(r.exposureIndices) || r.exposureIndices.length === 0 || r.exposureIndices.length > 256) {\n throw new InvalidConfigurationError(\"exposureIndices must contain 1 through 256 settings.\");\n }\n const indices = r.exposureIndices.map((value: unknown, i: number) => positive(value, \"exposureIndices[\" + i + \"]\"));\n if (indices[0]! < base || indices.some((value, i) => i > 0 && value <= indices[i - 1]!)) {\n throw new InvalidConfigurationError(\"exposureIndices must strictly increase from at least the base exposure index.\");\n }\n if (!Array.isArray(r.conversionGainModes) || r.conversionGainModes.length === 0 || r.conversionGainModes.length > 8) {\n throw new InvalidConfigurationError(\"conversionGainModes must contain 1 through 8 modes.\");\n }\n const modes = r.conversionGainModes.map((value: unknown, i: number) => {\n const m = requireAllowlistedRecord(value, [\"mode\", \"minimumExposureIndex\", \"readNoiseRmsElectrons\", \"maximumChargeElectrons\"],\n \"Invalid conversion-gain mode fields.\");\n if (m.mode !== \"single-conversion-gain\" && m.mode !== \"low-conversion-gain\" && m.mode !== \"high-conversion-gain\") {\n throw new InvalidConfigurationError(\"conversionGainModes[\" + i + \"].mode is invalid.\");\n }\n const noise = requireAllowlistedRecord(m.readNoiseRmsElectrons, [\"value\", \"evidence\"], \"Invalid read-noise fact.\");\n if (typeof noise.value !== \"number\" || !Number.isFinite(noise.value) || noise.value < 0) {\n throw new InvalidConfigurationError(\"conversionGainModes[\" + i + \"].readNoiseRmsElectrons.value must be finite and nonnegative.\");\n }\n return { mode: m.mode as IsoGainConversionGainMode[\"mode\"],\n minimumExposureIndex: positive(m.minimumExposureIndex, \"conversionGainModes[\" + i + \"].minimumExposureIndex\"),\n readNoiseRmsElectrons: { value: noise.value, evidence: parseEvidenceList(noise.evidence, \"readNoiseRmsElectrons.evidence\") },\n ...(m.maximumChargeElectrons === undefined ? {} :\n { maximumChargeElectrons: positiveFact(m.maximumChargeElectrons, \"conversionGainModes[\" + i + \"].maximumChargeElectrons\") }) };\n });\n if (modes[0]!.minimumExposureIndex !== base || modes.some((m, i) => i > 0 && m.minimumExposureIndex <= modes[i - 1]!.minimumExposureIndex)) {\n throw new InvalidConfigurationError(\"conversionGainModes must start at the base exposure index and strictly increase.\");\n }\n const gainEvidence = [...q0.evidence, ...aMax.evidence].filter((entry, i, all) =>\n all.findIndex((other) => JSON.stringify(other) === JSON.stringify(entry)) === i);\n const black = adc.blackLevelCode as number, saturation = adc.digitalSaturationCode as number;\n const span = saturation - black;\n const regimes = indices.map((exposureIndex) => {\n const ratio = exposureIndex / base;\n const analogGain = Math.min(ratio, aMax.value);\n const digitalGain = ratio / analogGain;\n const mode = modes.filter((m) => m.minimumExposureIndex <= exposureIndex).pop()!;\n const preAdc = q0.value / analogGain;\n if (mode.maximumChargeElectrons !== undefined && preAdc > mode.maximumChargeElectrons.value) {\n throw new InvalidConfigurationError(\"A \" + mode.mode + \" regime would need more charge than its declared storage limit.\");\n }\n const regimeEvidence = [...evidence];\n return {\n regimeId: prefix + \"iso-\" + exposureIndex,\n scientificStatus: r.scientificStatus as SensorSpectralResponseScientificStatus,\n systemConversionGainElectronsPerCode: { value: preAdc / span, evidence: gainEvidence },\n preAdcSaturationElectronEquivalent: { value: preAdc, evidence: gainEvidence },\n readNoiseComponents: [{ componentId: \"electronic\", rmsElectrons: mode.readNoiseRmsElectrons, evidence: mode.readNoiseRmsElectrons.evidence }],\n adc: { bitDepth: adc.bitDepth as number, blackLevelCode: black, digitalSaturationCode: saturation, transfer: \"uniform-round-half-up\" as const,\n ...(digitalGain === 1 ? {} : { digitalGain }) },\n exposureIndexGain: { exposureIndex, baseExposureIndex: base, analogGain, digitalGain, conversionGainMode: mode.mode, evidence: regimeEvidence },\n evidence: regimeEvidence,\n limitations: r.limitations as readonly string[]\n };\n });\n // Reuse the regime parser to check the ADC, limitations and consistency.\n const parsed = parseSensorReadoutConversionProfile({ schemaVersion: \"0.1.0\", profileId: prefix + \"validation\", colorSamplingProfileId: \"validation\",\n channelId: \"validation\", regimeSelectionOwnedBy: \"explicit-upstream-camera-state-not-inferred-from-iso\", regimes, evidence }).regimes;\n return { regimes: parsed, standardRegimeBands: indices.map((exposureIndex, i) => ({ minimumExposureIndex: exposureIndex,\n maximumExposureIndex: exposureIndex, readoutRegimeId: parsed[i]!.regimeId })) };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"iso-gain.js","sourceRoot":"","sources":["../../src/sensor/iso-gain.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * ISO gain: builds one explicit readout regime for each ISO (exposure index) setting from declared\n * sensor parameters, so that raising ISO brightens the RAW the way a camera's programmable gain does.\n *\n * Model (exposure index S = 10 / H of CIPA DC-004-2020, free at https://www.cipa.jp/e/std/std-sec.html,\n * also specified in ISO 12232:2019; EMVA Standard 1288 Release 4.0 linear camera model,\n * where the system gain K in DN/e- multiplies the electron signal):\n * R = S / S0 exposure-index ratio to the base exposure index S0\n * a = min(R, A_max) analog (pre-ADC) gain relative to the base regime\n * d = R / a post-ADC digital gain beyond the analog range\n * g(S) = Q0 / (span * a) electrons per code (1/K), span = digitalSaturation - black\n * Q_preADC(S) = Q0 / a electrons that drive the ADC to digital saturation\n * So the electrons needed to reach digital saturation are Q0 * S0 / S.\n *\n * The exposure indices of CIPA DC-004 put the reference exposure H_ref in proportion to 1/S: standard\n * output sensitivity (SOS) and REI (recommended exposure index) both use S = 10 / H (§3.2, §3.3). So a\n * correct exposure for S gives the same black-subtracted fraction of digital saturation at every S.\n *\n * Read noise in electrons is declared per conversion-gain mode, never derived from S. (With dual\n * conversion gain, the high-conversion-gain mode used at high S has lower input-referred read noise.)\n *\n * Inputs are explicit declarations; Photivra presets use synthetic teaching values. No camera\n * calibration is implied.\n * @see docs/MOTION_AND_SIGNAL.md for equations, sources and limits.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { parseEvidenceList, type EvidenceBackedFact, type EvidenceProvenance } from \"../core/evidence-provenance.js\";\nimport { requireAllowlistedRecord, requirePublicOpaqueId, requireConfigPositiveFinite as positive } from \"../core/record-validation.js\";\nimport { parseSensorReadoutConversionProfile, type SensorReadoutConversionRegime,\n type SensorReadoutExposureIndexGain } from \"./raw-readout.js\";\nimport type { GenericIsoStandardRegimeBand } from \"./iso-signal-chain.js\";\nimport type { SensorSpectralResponseScientificStatus } from \"./spectral-response.js\";\n\n/** One conversion-gain mode, active from its minimum exposure index up to the next mode's. */\nexport interface IsoGainConversionGainMode {\n mode: SensorReadoutExposureIndexGain[\"conversionGainMode\"];\n minimumExposureIndex: number;\n readNoiseRmsElectrons: EvidenceBackedFact<number>;\n /** Optional charge storage limit of this mode. Every regime's pre-ADC threshold must be at or below it. */\n maximumChargeElectrons?: EvidenceBackedFact<number>;\n}\n\nexport interface CreateIsoGainReadoutRegimesInput {\n regimeIdPrefix: string;\n scientificStatus: SensorSpectralResponseScientificStatus;\n baseExposureIndex: number;\n /** Strictly increasing settings, each at least the base exposure index. */\n exposureIndices: readonly number[];\n /** Q0: electron-equivalent signal that reaches digital saturation at the base exposure index. */\n baseSaturationElectronEquivalent: EvidenceBackedFact<number>;\n /** A_max: largest analog gain relative to the base regime (>= 1). */\n maximumAnalogGain: EvidenceBackedFact<number>;\n adc: { bitDepth: number; blackLevelCode: number; digitalSaturationCode: number };\n conversionGainModes: readonly IsoGainConversionGainMode[];\n evidence: readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\nexport interface IsoGainReadoutRegimes {\n regimes: readonly SensorReadoutConversionRegime[];\n /** One single-value band per setting, ready for a GenericIsoSignalChainProfile. */\n standardRegimeBands: readonly GenericIsoStandardRegimeBand[];\n}\n\nfunction positiveFact(value: unknown, path: string): EvidenceBackedFact<number> {\n const r = requireAllowlistedRecord(value, [\"value\", \"evidence\"], path + \" must be an evidence-backed fact.\");\n return { value: positive(r.value, path + \".value\"), evidence: parseEvidenceList(r.evidence, path + \".evidence\") };\n}\n\n/**\n * Builds a readout regime and signal-chain band for each exposure index (see the module comment\n * for the equations). The result is checked with the same rules as parseSensorReadoutConversionProfile.\n * Each regime carries an `exposureIndexGain` record of how it was derived.\n * @param input - The sensor parameters and ISO settings; untrusted values are validated.\n * @returns The readout regimes and bands.\n */\nexport function createIsoGainReadoutRegimes(input: CreateIsoGainReadoutRegimesInput): IsoGainReadoutRegimes {\n const r = requireAllowlistedRecord(input, [\"regimeIdPrefix\", \"scientificStatus\", \"baseExposureIndex\", \"exposureIndices\",\n \"baseSaturationElectronEquivalent\", \"maximumAnalogGain\", \"adc\", \"conversionGainModes\", \"evidence\", \"limitations\"],\n \"Invalid ISO gain readout fields.\");\n const prefix = r.regimeIdPrefix === \"\" ? \"\" : requirePublicOpaqueId(r.regimeIdPrefix, \"regimeIdPrefix must be empty or a public opaque identifier.\");\n const base = positive(r.baseExposureIndex, \"baseExposureIndex\");\n const q0 = positiveFact(r.baseSaturationElectronEquivalent, \"baseSaturationElectronEquivalent\");\n const aMax = positiveFact(r.maximumAnalogGain, \"maximumAnalogGain\");\n if (aMax.value < 1) throw new InvalidConfigurationError(\"maximumAnalogGain must be at least 1.\");\n const evidence = parseEvidenceList(r.evidence, \"evidence\");\n const adc = requireAllowlistedRecord(r.adc, [\"bitDepth\", \"blackLevelCode\", \"digitalSaturationCode\"], \"Invalid ISO gain ADC fields.\");\n if (!Array.isArray(r.exposureIndices) || r.exposureIndices.length === 0 || r.exposureIndices.length > 256) {\n throw new InvalidConfigurationError(\"exposureIndices must contain 1 through 256 settings.\");\n }\n const indices = r.exposureIndices.map((value: unknown, i: number) => positive(value, \"exposureIndices[\" + i + \"]\"));\n if (indices[0]! < base || indices.some((value, i) => i > 0 && value <= indices[i - 1]!)) {\n throw new InvalidConfigurationError(\"exposureIndices must strictly increase from at least the base exposure index.\");\n }\n if (!Array.isArray(r.conversionGainModes) || r.conversionGainModes.length === 0 || r.conversionGainModes.length > 8) {\n throw new InvalidConfigurationError(\"conversionGainModes must contain 1 through 8 modes.\");\n }\n const modes = r.conversionGainModes.map((value: unknown, i: number) => {\n const m = requireAllowlistedRecord(value, [\"mode\", \"minimumExposureIndex\", \"readNoiseRmsElectrons\", \"maximumChargeElectrons\"],\n \"Invalid conversion-gain mode fields.\");\n if (m.mode !== \"single-conversion-gain\" && m.mode !== \"low-conversion-gain\" && m.mode !== \"high-conversion-gain\") {\n throw new InvalidConfigurationError(\"conversionGainModes[\" + i + \"].mode is invalid.\");\n }\n const noise = requireAllowlistedRecord(m.readNoiseRmsElectrons, [\"value\", \"evidence\"], \"Invalid read-noise fact.\");\n if (typeof noise.value !== \"number\" || !Number.isFinite(noise.value) || noise.value < 0) {\n throw new InvalidConfigurationError(\"conversionGainModes[\" + i + \"].readNoiseRmsElectrons.value must be finite and nonnegative.\");\n }\n return { mode: m.mode as IsoGainConversionGainMode[\"mode\"],\n minimumExposureIndex: positive(m.minimumExposureIndex, \"conversionGainModes[\" + i + \"].minimumExposureIndex\"),\n readNoiseRmsElectrons: { value: noise.value, evidence: parseEvidenceList(noise.evidence, \"readNoiseRmsElectrons.evidence\") },\n ...(m.maximumChargeElectrons === undefined ? {} :\n { maximumChargeElectrons: positiveFact(m.maximumChargeElectrons, \"conversionGainModes[\" + i + \"].maximumChargeElectrons\") }) };\n });\n if (modes[0]!.minimumExposureIndex !== base || modes.some((m, i) => i > 0 && m.minimumExposureIndex <= modes[i - 1]!.minimumExposureIndex)) {\n throw new InvalidConfigurationError(\"conversionGainModes must start at the base exposure index and strictly increase.\");\n }\n const gainEvidence = [...q0.evidence, ...aMax.evidence].filter((entry, i, all) =>\n all.findIndex((other) => JSON.stringify(other) === JSON.stringify(entry)) === i);\n const black = adc.blackLevelCode as number, saturation = adc.digitalSaturationCode as number;\n const span = saturation - black;\n const regimes = indices.map((exposureIndex) => {\n const ratio = exposureIndex / base;\n const analogGain = Math.min(ratio, aMax.value);\n const digitalGain = ratio / analogGain;\n const mode = modes.filter((m) => m.minimumExposureIndex <= exposureIndex).pop()!;\n const preAdc = q0.value / analogGain;\n if (mode.maximumChargeElectrons !== undefined && preAdc > mode.maximumChargeElectrons.value) {\n throw new InvalidConfigurationError(\"A \" + mode.mode + \" regime would need more charge than its declared storage limit.\");\n }\n const regimeEvidence = [...evidence];\n return {\n regimeId: prefix + \"iso-\" + exposureIndex,\n scientificStatus: r.scientificStatus as SensorSpectralResponseScientificStatus,\n systemConversionGainElectronsPerCode: { value: preAdc / span, evidence: gainEvidence },\n preAdcSaturationElectronEquivalent: { value: preAdc, evidence: gainEvidence },\n readNoiseComponents: [{ componentId: \"electronic\", rmsElectrons: mode.readNoiseRmsElectrons, evidence: mode.readNoiseRmsElectrons.evidence }],\n adc: { bitDepth: adc.bitDepth as number, blackLevelCode: black, digitalSaturationCode: saturation, transfer: \"uniform-round-half-up\" as const,\n ...(digitalGain === 1 ? {} : { digitalGain }) },\n exposureIndexGain: { exposureIndex, baseExposureIndex: base, analogGain, digitalGain, conversionGainMode: mode.mode, evidence: regimeEvidence },\n evidence: regimeEvidence,\n limitations: r.limitations as readonly string[]\n };\n });\n // Reuse the regime parser to check the ADC, limitations and consistency.\n const parsed = parseSensorReadoutConversionProfile({ schemaVersion: \"0.1.0\", profileId: prefix + \"validation\", colorSamplingProfileId: \"validation\",\n channelId: \"validation\", regimeSelectionOwnedBy: \"explicit-upstream-camera-state-not-inferred-from-iso\", regimes, evidence }).regimes;\n return { regimes: parsed, standardRegimeBands: indices.map((exposureIndex, i) => ({ minimumExposureIndex: exposureIndex,\n maximumExposureIndex: exposureIndex, readoutRegimeId: parsed[i]!.regimeId })) };\n}\n"]}
|
|
@@ -14,51 +14,9 @@ import { parseEvidenceList } from "../core/evidence-provenance.js";
|
|
|
14
14
|
import { InvalidScientificInputError } from "../core/validation.js";
|
|
15
15
|
import { parseIsoCapabilityProfile, resolveIsoCapability } from "../equipment/iso-capabilities.js";
|
|
16
16
|
import { parseSensorReadoutConversionProfile, resolveSensorReadoutRegime } from "./raw-readout.js";
|
|
17
|
-
import { requireInputKeys,
|
|
17
|
+
import { requireInputKeys, requireConfigRecord as requireRecord, requireTrimmedConfigString as requireNonEmptyString, requireConfigPositiveFinite as requirePositiveFinite, parseConfigLimitations as parseLimitations } from "../core/record-validation.js";
|
|
18
18
|
export const GENERIC_ISO_SIGNAL_CHAIN_PROFILE_SCHEMA_VERSION = "0.1.0";
|
|
19
19
|
export const GENERIC_ISO_SIGNAL_CHAIN_PRESET_CATALOG_SCHEMA_VERSION = "0.1.0";
|
|
20
|
-
function requireRecord(value, path, allowedKeys) {
|
|
21
|
-
if (typeof value !== "object" ||
|
|
22
|
-
value === null ||
|
|
23
|
-
Array.isArray(value)) {
|
|
24
|
-
throw new InvalidConfigurationError(path + " must be an object.");
|
|
25
|
-
}
|
|
26
|
-
if (allowedKeys !== null)
|
|
27
|
-
rejectUnknownFields(value, allowedKeys, path);
|
|
28
|
-
return value;
|
|
29
|
-
}
|
|
30
|
-
function requireNonEmptyString(value, path) {
|
|
31
|
-
if (typeof value !== "string" ||
|
|
32
|
-
value.trim().length === 0) {
|
|
33
|
-
throw new InvalidConfigurationError(path +
|
|
34
|
-
" must be a non-empty string.");
|
|
35
|
-
}
|
|
36
|
-
return value.trim();
|
|
37
|
-
}
|
|
38
|
-
function requirePositiveFinite(value, path) {
|
|
39
|
-
if (typeof value !== "number" ||
|
|
40
|
-
!Number.isFinite(value) ||
|
|
41
|
-
value <= 0) {
|
|
42
|
-
throw new InvalidConfigurationError(path +
|
|
43
|
-
" must be finite and greater than zero.");
|
|
44
|
-
}
|
|
45
|
-
return value;
|
|
46
|
-
}
|
|
47
|
-
function parseLimitations(value, path) {
|
|
48
|
-
if (!Array.isArray(value)) {
|
|
49
|
-
throw new InvalidConfigurationError(path + " must be an array.");
|
|
50
|
-
}
|
|
51
|
-
const parsed = value.map((entry, index) => requireNonEmptyString(entry, path +
|
|
52
|
-
"[" +
|
|
53
|
-
index +
|
|
54
|
-
"]"));
|
|
55
|
-
if (new Set(parsed).size !==
|
|
56
|
-
parsed.length) {
|
|
57
|
-
throw new InvalidConfigurationError(path +
|
|
58
|
-
" must not contain duplicates.");
|
|
59
|
-
}
|
|
60
|
-
return parsed;
|
|
61
|
-
}
|
|
62
20
|
function parseStandardBand(value, path) {
|
|
63
21
|
const record = requireRecord(value, path, ["minimumExposureIndex", "maximumExposureIndex", "readoutRegimeId"]);
|
|
64
22
|
const minimumExposureIndex = requirePositiveFinite(record.minimumExposureIndex, path +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iso-signal-chain.js","sourceRoot":"","sources":["../../src/sensor/iso-signal-chain.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * ISO signal chain: picks the explicit readout regime (gain, read noise, clipping) that belongs to the\n * selected ISO and capture mode.\n *\n * Gain and noise are never derived from the ISO number itself; each regime is declared. Photons and\n * photo or dark shot noise are computed earlier in the chain. Generic tier labels do not calibrate or\n * rank real cameras.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n parseIsoCapabilityProfile,\n resolveIsoCapability,\n type IsoCapabilityProfile,\n type RequestedIsoSetting,\n type ResolvedIsoCapability\n} from \"../equipment/iso-capabilities.js\";\nimport {\n parseSensorReadoutConversionProfile,\n resolveSensorReadoutRegime,\n type ResolvedSensorReadoutRegime,\n type SensorReadoutConversionProfile\n} from \"./raw-readout.js\";\nimport { requireInputKeys, rejectUnknownFields } from \"../core/record-validation.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport const GENERIC_ISO_SIGNAL_CHAIN_PROFILE_SCHEMA_VERSION =\n \"0.1.0\" as const;\nexport const GENERIC_ISO_SIGNAL_CHAIN_PRESET_CATALOG_SCHEMA_VERSION =\n \"0.1.0\" as const;\n\nexport interface GenericIsoStandardRegimeBand {\n minimumExposureIndex: number;\n maximumExposureIndex: number;\n readoutRegimeId: string;\n}\n\nexport interface GenericIsoExpandedRegimeBinding {\n expandedSettingId: string;\n readoutRegimeId: string;\n}\n\nexport interface GenericIsoCaptureModeSignalChainBinding {\n captureModeId: string;\n standardRegimeBands:\n readonly GenericIsoStandardRegimeBand[];\n expandedRegimeBindings:\n readonly GenericIsoExpandedRegimeBinding[];\n evidence:\n readonly EvidenceProvenance[];\n}\n\nexport interface GenericIsoSignalChainProfile {\n schemaVersion:\n typeof GENERIC_ISO_SIGNAL_CHAIN_PROFILE_SCHEMA_VERSION;\n profileId: string;\n profileVersion: string;\n scientificStatus: \"approximation\";\n isoCapabilityProfileId: string;\n readoutProfileId: string;\n behaviorMeaning:\n \"iso-state-selects-explicit-readout-regime-not-noise-equation\";\n captureModeBindings:\n readonly GenericIsoCaptureModeSignalChainBinding[];\n photonShotNoiseOwnedUpstream:\n true;\n processedImageBehaviorIncluded:\n false;\n fixedPatternNoiseModeled:\n false;\n lowSignalColorDegradationModeled:\n false;\n evidence:\n readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\nexport interface ResolveGenericIsoSignalChainInput {\n profile:\n GenericIsoSignalChainProfile;\n isoCapability:\n IsoCapabilityProfile;\n requestedIsoSetting:\n RequestedIsoSetting;\n captureModeId: string;\n readoutProfile:\n SensorReadoutConversionProfile;\n}\n\nexport interface ResolvedGenericIsoSignalChain {\n profileId: string;\n profileVersion: string;\n scientificStatus:\n \"approximation\";\n captureModeId: string;\n iso:\n ResolvedIsoCapability;\n readout:\n ResolvedSensorReadoutRegime;\n regimeSelectionReason:\n | \"standard-exposure-index-band\"\n | \"expanded-setting-binding\";\n capturedPhotonExpectationModified:\n false;\n photonShotNoiseStatisticsModified:\n false;\n sensorGeometryModified: false;\n processedImageBehaviorApplied:\n false;\n fixedPatternNoiseApplied:\n false;\n lowSignalColorDegradationApplied:\n false;\n isoUsedAsDirectNoiseEquation:\n false;\n exactCommercialCameraBehaviorClaimed:\n false;\n}\n\nexport type GenericIsoSignalChainPreset =\n | \"good\"\n | \"better\"\n | \"best\";\n\nexport interface GenericIsoSignalChainPresetCatalog {\n schemaVersion:\n typeof GENERIC_ISO_SIGNAL_CHAIN_PRESET_CATALOG_SCHEMA_VERSION;\n catalogId: string;\n catalogVersion: string;\n entries: readonly {\n preset:\n GenericIsoSignalChainPreset;\n signalChainProfileId:\n string;\n evidence:\n readonly EvidenceProvenance[];\n }[];\n performanceOrderingClaimed:\n false;\n realCameraRankingClaimed:\n false;\n}\n\nfunction requireRecord(\n value: unknown,\n path: string,\n allowedKeys: readonly string[] | null\n): UnknownRecord {\n if (\n typeof value !== \"object\" ||\n value === null ||\n Array.isArray(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be an object.\"\n );\n }\n if (allowedKeys !== null) rejectUnknownFields(value, allowedKeys, path);\n return value as UnknownRecord;\n}\n\nfunction requireNonEmptyString(\n value: unknown,\n path: string\n): string {\n if (\n typeof value !== \"string\" ||\n value.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must be a non-empty string.\"\n );\n }\n return value.trim();\n}\n\nfunction requirePositiveFinite(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n value <= 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must be finite and greater than zero.\"\n );\n }\n return value;\n}\n\nfunction parseLimitations(\n value: unknown,\n path: string\n): readonly string[] {\n if (!Array.isArray(value)) {\n throw new InvalidConfigurationError(\n path + \" must be an array.\"\n );\n }\n const parsed =\n value.map((entry, index) =>\n requireNonEmptyString(\n entry,\n path +\n \"[\" +\n index +\n \"]\"\n )\n );\n if (\n new Set(parsed).size !==\n parsed.length\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must not contain duplicates.\"\n );\n }\n return parsed;\n}\n\nfunction parseStandardBand(\n value: unknown,\n path: string\n): GenericIsoStandardRegimeBand {\n const record =\n requireRecord(value, path, [\"minimumExposureIndex\", \"maximumExposureIndex\", \"readoutRegimeId\"]);\n const minimumExposureIndex =\n requirePositiveFinite(\n record.minimumExposureIndex,\n path +\n \".minimumExposureIndex\"\n );\n const maximumExposureIndex =\n requirePositiveFinite(\n record.maximumExposureIndex,\n path +\n \".maximumExposureIndex\"\n );\n if (\n minimumExposureIndex >\n maximumExposureIndex\n ) {\n throw new InvalidConfigurationError(\n path +\n \".minimumExposureIndex must be less than or equal to maximumExposureIndex.\"\n );\n }\n return {\n minimumExposureIndex,\n maximumExposureIndex,\n readoutRegimeId:\n requireNonEmptyString(\n record.readoutRegimeId,\n path +\n \".readoutRegimeId\"\n )\n };\n}\n\nfunction parseCaptureModeBinding(\n value: unknown,\n path: string\n): GenericIsoCaptureModeSignalChainBinding {\n const record =\n requireRecord(value, path, [\"standardRegimeBands\", \"expandedRegimeBindings\", \"captureModeId\", \"evidence\"]);\n if (\n !Array.isArray(\n record.standardRegimeBands\n ) ||\n record.standardRegimeBands\n .length === 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \".standardRegimeBands must be a non-empty array.\"\n );\n }\n const bands =\n record.standardRegimeBands.map(\n (entry, index) =>\n parseStandardBand(\n entry,\n path +\n \".standardRegimeBands[\" +\n index +\n \"]\"\n )\n ).sort(\n (a, b) =>\n a.minimumExposureIndex -\n b.minimumExposureIndex\n );\n for (\n let index = 1;\n index < bands.length;\n index += 1\n ) {\n const prior =\n bands[index - 1]!;\n const current =\n bands[index]!;\n if (\n current.minimumExposureIndex <=\n prior.maximumExposureIndex\n ) {\n throw new InvalidConfigurationError(\n path +\n \".standardRegimeBands must not overlap.\"\n );\n }\n }\n\n if (\n !Array.isArray(\n record.expandedRegimeBindings\n )\n ) {\n throw new InvalidConfigurationError(\n path +\n \".expandedRegimeBindings must be an array.\"\n );\n }\n const expandedRegimeBindings =\n record.expandedRegimeBindings.map(\n (entry, index) => {\n const entryPath =\n path +\n \".expandedRegimeBindings[\" +\n index +\n \"]\";\n const binding =\n requireRecord(\n entry,\n entryPath, [\"expandedSettingId\", \"readoutRegimeId\"]\n );\n return {\n expandedSettingId:\n requireNonEmptyString(\n binding.expandedSettingId,\n entryPath +\n \".expandedSettingId\"\n ),\n readoutRegimeId:\n requireNonEmptyString(\n binding.readoutRegimeId,\n entryPath +\n \".readoutRegimeId\"\n )\n };\n }\n );\n const expandedIds =\n expandedRegimeBindings.map(\n (entry) =>\n entry.expandedSettingId\n );\n if (\n new Set(expandedIds).size !==\n expandedIds.length\n ) {\n throw new InvalidConfigurationError(\n path +\n \".expandedRegimeBindings must not contain duplicate expandedSettingId values.\"\n );\n }\n\n return {\n captureModeId:\n requireNonEmptyString(\n record.captureModeId,\n path + \".captureModeId\"\n ),\n standardRegimeBands:\n bands,\n expandedRegimeBindings,\n evidence:\n parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n )\n };\n}\n\n/**\n * Validates untrusted input and returns it as a normalized, typed value. Unknown enum values,\n * missing required fields and inconsistent scientific data are rejected.\n *\n * The profile binds each ISO and capture-mode state to an explicit readout regime, whose gain,\n * read noise and clipping are declared separately.\n * @param value - Untrusted data. TypeScript types alone are not validation.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function parseGenericIsoSignalChainProfile(\n value: unknown\n): GenericIsoSignalChainProfile {\n const record =\n requireRecord(\n value,\n \"genericIsoSignalChainProfile\", [\"schemaVersion\", \"scientificStatus\", \"behaviorMeaning\", \"photonShotNoiseOwnedUpstream\", \"processedImageBehaviorIncluded\", \"fixedPatternNoiseModeled\", \"lowSignalColorDegradationModeled\", \"captureModeBindings\", \"profileId\", \"profileVersion\", \"isoCapabilityProfileId\", \"readoutProfileId\", \"evidence\", \"limitations\"]\n );\n if (\n record.schemaVersion !==\n GENERIC_ISO_SIGNAL_CHAIN_PROFILE_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'genericIsoSignalChainProfile.schemaVersion must be \"' +\n GENERIC_ISO_SIGNAL_CHAIN_PROFILE_SCHEMA_VERSION +\n '\".'\n );\n }\n if (\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n 'genericIsoSignalChainProfile.scientificStatus must be \"approximation\".'\n );\n }\n if (\n record.behaviorMeaning !==\n \"iso-state-selects-explicit-readout-regime-not-noise-equation\"\n ) {\n throw new InvalidConfigurationError(\n \"genericIsoSignalChainProfile.behaviorMeaning is invalid.\"\n );\n }\n if (\n record.photonShotNoiseOwnedUpstream !==\n true ||\n record.processedImageBehaviorIncluded !==\n false ||\n record.fixedPatternNoiseModeled !==\n false ||\n record.lowSignalColorDegradationModeled !==\n false\n ) {\n throw new InvalidConfigurationError(\n \"genericIsoSignalChainProfile domain-ownership flags are invalid.\"\n );\n }\n if (\n !Array.isArray(\n record.captureModeBindings\n ) ||\n record.captureModeBindings\n .length === 0\n ) {\n throw new InvalidConfigurationError(\n \"genericIsoSignalChainProfile.captureModeBindings must be a non-empty array.\"\n );\n }\n const captureModeBindings =\n record.captureModeBindings.map(\n (entry, index) =>\n parseCaptureModeBinding(\n entry,\n \"genericIsoSignalChainProfile.captureModeBindings[\" +\n index +\n \"]\"\n )\n );\n const modeIds =\n captureModeBindings.map(\n (entry) =>\n entry.captureModeId\n );\n if (\n new Set(modeIds).size !==\n modeIds.length\n ) {\n throw new InvalidConfigurationError(\n \"genericIsoSignalChainProfile.captureModeBindings must not contain duplicate captureModeId values.\"\n );\n }\n\n return {\n schemaVersion:\n GENERIC_ISO_SIGNAL_CHAIN_PROFILE_SCHEMA_VERSION,\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"genericIsoSignalChainProfile.profileId\"\n ),\n profileVersion:\n requireNonEmptyString(\n record.profileVersion,\n \"genericIsoSignalChainProfile.profileVersion\"\n ),\n scientificStatus:\n \"approximation\",\n isoCapabilityProfileId:\n requireNonEmptyString(\n record.isoCapabilityProfileId,\n \"genericIsoSignalChainProfile.isoCapabilityProfileId\"\n ),\n readoutProfileId:\n requireNonEmptyString(\n record.readoutProfileId,\n \"genericIsoSignalChainProfile.readoutProfileId\"\n ),\n behaviorMeaning:\n \"iso-state-selects-explicit-readout-regime-not-noise-equation\",\n captureModeBindings,\n photonShotNoiseOwnedUpstream:\n true,\n processedImageBehaviorIncluded:\n false,\n fixedPatternNoiseModeled:\n false,\n lowSignalColorDegradationModeled:\n false,\n evidence:\n parseEvidenceList(\n record.evidence,\n \"genericIsoSignalChainProfile.evidence\"\n ),\n limitations:\n parseLimitations(\n record.limitations,\n \"genericIsoSignalChainProfile.limitations\"\n )\n };\n}\n\nfunction regimeIdForIso(\n binding:\n GenericIsoCaptureModeSignalChainBinding,\n iso:\n ResolvedIsoCapability\n): {\n regimeId: string;\n reason:\n ResolvedGenericIsoSignalChain[\"regimeSelectionReason\"];\n} {\n if (\n iso.setting.kind ===\n \"expanded\"\n ) {\n const expandedSettingId =\n iso.setting.settingId;\n const expanded =\n binding\n .expandedRegimeBindings\n .find(\n (entry) =>\n entry\n .expandedSettingId ===\n expandedSettingId\n );\n if (expanded === undefined) {\n throw new InvalidScientificInputError(\n \"Expanded ISO setting has no signal-chain regime binding for the selected capture mode.\"\n );\n }\n return {\n regimeId:\n expanded\n .readoutRegimeId,\n reason:\n \"expanded-setting-binding\"\n };\n }\n\n const matches =\n binding\n .standardRegimeBands\n .filter(\n (band) =>\n iso\n .reportedExposureIndex >=\n band\n .minimumExposureIndex &&\n iso\n .reportedExposureIndex <=\n band\n .maximumExposureIndex\n );\n if (matches.length !== 1) {\n throw new InvalidScientificInputError(\n \"Standard ISO setting must match exactly one signal-chain regime band.\"\n );\n }\n return {\n regimeId:\n matches[0]!\n .readoutRegimeId,\n reason:\n \"standard-exposure-index-band\"\n };\n}\n\n/**\n * Picks the explicit readout regime bound to the resolved ISO and capture-mode state. Gain and noise\n * are never derived from the ISO number.\n * @param input - The input; see `ResolveGenericIsoSignalChainInput` for its fields, units and coordinates.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function resolveGenericIsoSignalChain(\n input:\n ResolveGenericIsoSignalChainInput\n): ResolvedGenericIsoSignalChain {\n requireInputKeys(input, \"resolveGenericIsoSignalChain\", [\"profile\", \"isoCapability\", \"requestedIsoSetting\", \"captureModeId\", \"readoutProfile\"]);\n const profile =\n parseGenericIsoSignalChainProfile(\n input.profile\n );\n const isoCapability =\n parseIsoCapabilityProfile(\n input.isoCapability\n );\n const readoutProfile =\n parseSensorReadoutConversionProfile(\n input.readoutProfile\n );\n\n if (\n profile.isoCapabilityProfileId !==\n isoCapability.profileId\n ) {\n throw new InvalidScientificInputError(\n \"Signal-chain profile isoCapabilityProfileId must match the supplied ISO capability profile.\"\n );\n }\n if (\n profile.readoutProfileId !==\n readoutProfile.profileId\n ) {\n throw new InvalidScientificInputError(\n \"Signal-chain profile readoutProfileId must match the supplied sensor readout profile.\"\n );\n }\n if (\n typeof input.captureModeId !==\n \"string\" ||\n input.captureModeId.trim()\n .length === 0\n ) {\n throw new InvalidConfigurationError(\n \"captureModeId must be a non-empty string.\"\n );\n }\n\n const binding =\n profile.captureModeBindings.find(\n (entry) =>\n entry.captureModeId ===\n input.captureModeId\n );\n if (binding === undefined) {\n throw new InvalidScientificInputError(\n \"Signal-chain profile has no binding for the selected capture mode.\"\n );\n }\n\n const iso =\n resolveIsoCapability({\n profile:\n isoCapability,\n setting:\n input\n .requestedIsoSetting,\n captureModeId:\n input.captureModeId\n });\n const regime =\n regimeIdForIso(\n binding,\n iso\n );\n const readout =\n resolveSensorReadoutRegime({\n profile:\n readoutProfile,\n regimeId:\n regime.regimeId\n });\n const gain =\n readout.regime\n .exposureIndexGain;\n if (\n gain !== undefined &&\n gain.exposureIndex !==\n iso.reportedExposureIndex\n ) {\n throw new InvalidScientificInputError(\n \"The bound readout regime declares an ISO-gain derivation for a different exposure index.\"\n );\n }\n\n return {\n profileId:\n profile.profileId,\n profileVersion:\n profile.profileVersion,\n scientificStatus:\n \"approximation\",\n captureModeId:\n input.captureModeId,\n iso,\n readout,\n regimeSelectionReason:\n regime.reason,\n capturedPhotonExpectationModified:\n false,\n photonShotNoiseStatisticsModified:\n false,\n sensorGeometryModified:\n false,\n processedImageBehaviorApplied:\n false,\n fixedPatternNoiseApplied:\n false,\n lowSignalColorDegradationApplied:\n false,\n isoUsedAsDirectNoiseEquation:\n false,\n exactCommercialCameraBehaviorClaimed:\n false\n };\n}\n\nconst PRESETS =\n new Set<GenericIsoSignalChainPreset>([\n \"good\",\n \"better\",\n \"best\"\n ]);\n\n/**\n * Validates untrusted input and returns it as a normalized, typed value. Unknown enum values,\n * missing required fields and inconsistent scientific data are rejected.\n *\n * Generic tier labels in the catalogue do not calibrate or rank real cameras.\n * @param value - Untrusted data. TypeScript types alone are not validation.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function parseGenericIsoSignalChainPresetCatalog(\n value: unknown\n): GenericIsoSignalChainPresetCatalog {\n const record =\n requireRecord(\n value,\n \"genericIsoSignalChainPresetCatalog\", [\"schemaVersion\", \"performanceOrderingClaimed\", \"realCameraRankingClaimed\", \"entries\", \"catalogId\", \"catalogVersion\"]\n );\n if (\n record.schemaVersion !==\n GENERIC_ISO_SIGNAL_CHAIN_PRESET_CATALOG_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'genericIsoSignalChainPresetCatalog.schemaVersion must be \"' +\n GENERIC_ISO_SIGNAL_CHAIN_PRESET_CATALOG_SCHEMA_VERSION +\n '\".'\n );\n }\n if (\n record.performanceOrderingClaimed !==\n false ||\n record.realCameraRankingClaimed !==\n false\n ) {\n throw new InvalidConfigurationError(\n \"Generic ISO preset catalog must not claim physical performance ordering or real-camera ranking.\"\n );\n }\n if (\n !Array.isArray(\n record.entries\n ) ||\n record.entries.length !== 3\n ) {\n throw new InvalidConfigurationError(\n \"Generic ISO preset catalog must contain exactly Good, Better, and Best entries.\"\n );\n }\n\n const entries =\n record.entries.map(\n (entry, index) => {\n const path =\n \"genericIsoSignalChainPresetCatalog.entries[\" +\n index +\n \"]\";\n const parsed =\n requireRecord(\n entry,\n path, [\"preset\", \"signalChainProfileId\", \"evidence\"]\n );\n if (\n typeof parsed.preset !==\n \"string\" ||\n !PRESETS.has(\n parsed.preset as\n GenericIsoSignalChainPreset\n )\n ) {\n throw new InvalidConfigurationError(\n path +\n \".preset is invalid.\"\n );\n }\n return {\n preset:\n parsed.preset as\n GenericIsoSignalChainPreset,\n signalChainProfileId:\n requireNonEmptyString(\n parsed\n .signalChainProfileId,\n path +\n \".signalChainProfileId\"\n ),\n evidence:\n parseEvidenceList(\n parsed.evidence,\n path + \".evidence\"\n )\n };\n }\n );\n\n const presets =\n entries.map(\n (entry) =>\n entry.preset\n );\n if (\n new Set(presets).size !==\n 3\n ) {\n throw new InvalidConfigurationError(\n \"Generic ISO preset catalog must contain each Good, Better, and Best preset exactly once.\"\n );\n }\n\n return {\n schemaVersion:\n GENERIC_ISO_SIGNAL_CHAIN_PRESET_CATALOG_SCHEMA_VERSION,\n catalogId:\n requireNonEmptyString(\n record.catalogId,\n \"genericIsoSignalChainPresetCatalog.catalogId\"\n ),\n catalogVersion:\n requireNonEmptyString(\n record.catalogVersion,\n \"genericIsoSignalChainPresetCatalog.catalogVersion\"\n ),\n entries,\n performanceOrderingClaimed:\n false,\n realCameraRankingClaimed:\n false\n };\n}\n\n/**\n * Picks a generic preset from the catalogue and returns the signal-chain profile it uses. It makes\n * no claim of manufacturer calibration.\n *\n * Generic tier labels do not calibrate or rank real cameras.\n * @param input - The preset catalogue and the chosen preset.\n * @returns The preset and its signal-chain profile ID. `performanceOrderingClaimed` and\n * `realCameraRankingClaimed` are always false. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function resolveGenericIsoSignalChainPreset(\n input: {\n catalog:\n GenericIsoSignalChainPresetCatalog;\n preset:\n GenericIsoSignalChainPreset;\n }\n): {\n preset:\n GenericIsoSignalChainPreset;\n signalChainProfileId:\n string;\n performanceOrderingClaimed:\n false;\n realCameraRankingClaimed:\n false;\n} {\n requireInputKeys(input, \"resolveGenericIsoSignalChainPreset\", [\"catalog\", \"preset\"]);\n const catalog =\n parseGenericIsoSignalChainPresetCatalog(\n input.catalog\n );\n if (\n !PRESETS.has(\n input.preset\n )\n ) {\n throw new InvalidConfigurationError(\n \"preset must be good, better, or best.\"\n );\n }\n const entry =\n catalog.entries.find(\n (candidate) =>\n candidate.preset ===\n input.preset\n )!;\n\n return {\n preset:\n input.preset,\n signalChainProfileId:\n entry\n .signalChainProfileId,\n performanceOrderingClaimed:\n false,\n realCameraRankingClaimed:\n false\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"iso-signal-chain.js","sourceRoot":"","sources":["../../src/sensor/iso-signal-chain.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * ISO signal chain: picks the explicit readout regime (gain, read noise, clipping) that belongs to the\n * selected ISO and capture mode.\n *\n * Gain and noise are never derived from the ISO number itself; each regime is declared. Photons and\n * photo or dark shot noise are computed earlier in the chain. Generic tier labels do not calibrate or\n * rank real cameras.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n parseIsoCapabilityProfile,\n resolveIsoCapability,\n type IsoCapabilityProfile,\n type RequestedIsoSetting,\n type ResolvedIsoCapability\n} from \"../equipment/iso-capabilities.js\";\nimport {\n parseSensorReadoutConversionProfile,\n resolveSensorReadoutRegime,\n type ResolvedSensorReadoutRegime,\n type SensorReadoutConversionProfile\n} from \"./raw-readout.js\";\nimport { requireInputKeys, requireConfigRecord as requireRecord, requireTrimmedConfigString as requireNonEmptyString, requireConfigPositiveFinite as requirePositiveFinite, parseConfigLimitations as parseLimitations } from \"../core/record-validation.js\";\n\nexport const GENERIC_ISO_SIGNAL_CHAIN_PROFILE_SCHEMA_VERSION =\n \"0.1.0\" as const;\nexport const GENERIC_ISO_SIGNAL_CHAIN_PRESET_CATALOG_SCHEMA_VERSION =\n \"0.1.0\" as const;\n\nexport interface GenericIsoStandardRegimeBand {\n minimumExposureIndex: number;\n maximumExposureIndex: number;\n readoutRegimeId: string;\n}\n\nexport interface GenericIsoExpandedRegimeBinding {\n expandedSettingId: string;\n readoutRegimeId: string;\n}\n\nexport interface GenericIsoCaptureModeSignalChainBinding {\n captureModeId: string;\n standardRegimeBands:\n readonly GenericIsoStandardRegimeBand[];\n expandedRegimeBindings:\n readonly GenericIsoExpandedRegimeBinding[];\n evidence:\n readonly EvidenceProvenance[];\n}\n\nexport interface GenericIsoSignalChainProfile {\n schemaVersion:\n typeof GENERIC_ISO_SIGNAL_CHAIN_PROFILE_SCHEMA_VERSION;\n profileId: string;\n profileVersion: string;\n scientificStatus: \"approximation\";\n isoCapabilityProfileId: string;\n readoutProfileId: string;\n behaviorMeaning:\n \"iso-state-selects-explicit-readout-regime-not-noise-equation\";\n captureModeBindings:\n readonly GenericIsoCaptureModeSignalChainBinding[];\n photonShotNoiseOwnedUpstream:\n true;\n processedImageBehaviorIncluded:\n false;\n fixedPatternNoiseModeled:\n false;\n lowSignalColorDegradationModeled:\n false;\n evidence:\n readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\nexport interface ResolveGenericIsoSignalChainInput {\n profile:\n GenericIsoSignalChainProfile;\n isoCapability:\n IsoCapabilityProfile;\n requestedIsoSetting:\n RequestedIsoSetting;\n captureModeId: string;\n readoutProfile:\n SensorReadoutConversionProfile;\n}\n\nexport interface ResolvedGenericIsoSignalChain {\n profileId: string;\n profileVersion: string;\n scientificStatus:\n \"approximation\";\n captureModeId: string;\n iso:\n ResolvedIsoCapability;\n readout:\n ResolvedSensorReadoutRegime;\n regimeSelectionReason:\n | \"standard-exposure-index-band\"\n | \"expanded-setting-binding\";\n capturedPhotonExpectationModified:\n false;\n photonShotNoiseStatisticsModified:\n false;\n sensorGeometryModified: false;\n processedImageBehaviorApplied:\n false;\n fixedPatternNoiseApplied:\n false;\n lowSignalColorDegradationApplied:\n false;\n isoUsedAsDirectNoiseEquation:\n false;\n exactCommercialCameraBehaviorClaimed:\n false;\n}\n\nexport type GenericIsoSignalChainPreset =\n | \"good\"\n | \"better\"\n | \"best\";\n\nexport interface GenericIsoSignalChainPresetCatalog {\n schemaVersion:\n typeof GENERIC_ISO_SIGNAL_CHAIN_PRESET_CATALOG_SCHEMA_VERSION;\n catalogId: string;\n catalogVersion: string;\n entries: readonly {\n preset:\n GenericIsoSignalChainPreset;\n signalChainProfileId:\n string;\n evidence:\n readonly EvidenceProvenance[];\n }[];\n performanceOrderingClaimed:\n false;\n realCameraRankingClaimed:\n false;\n}\n\nfunction parseStandardBand(\n value: unknown,\n path: string\n): GenericIsoStandardRegimeBand {\n const record =\n requireRecord(value, path, [\"minimumExposureIndex\", \"maximumExposureIndex\", \"readoutRegimeId\"]);\n const minimumExposureIndex =\n requirePositiveFinite(\n record.minimumExposureIndex,\n path +\n \".minimumExposureIndex\"\n );\n const maximumExposureIndex =\n requirePositiveFinite(\n record.maximumExposureIndex,\n path +\n \".maximumExposureIndex\"\n );\n if (\n minimumExposureIndex >\n maximumExposureIndex\n ) {\n throw new InvalidConfigurationError(\n path +\n \".minimumExposureIndex must be less than or equal to maximumExposureIndex.\"\n );\n }\n return {\n minimumExposureIndex,\n maximumExposureIndex,\n readoutRegimeId:\n requireNonEmptyString(\n record.readoutRegimeId,\n path +\n \".readoutRegimeId\"\n )\n };\n}\n\nfunction parseCaptureModeBinding(\n value: unknown,\n path: string\n): GenericIsoCaptureModeSignalChainBinding {\n const record =\n requireRecord(value, path, [\"standardRegimeBands\", \"expandedRegimeBindings\", \"captureModeId\", \"evidence\"]);\n if (\n !Array.isArray(\n record.standardRegimeBands\n ) ||\n record.standardRegimeBands\n .length === 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \".standardRegimeBands must be a non-empty array.\"\n );\n }\n const bands =\n record.standardRegimeBands.map(\n (entry, index) =>\n parseStandardBand(\n entry,\n path +\n \".standardRegimeBands[\" +\n index +\n \"]\"\n )\n ).sort(\n (a, b) =>\n a.minimumExposureIndex -\n b.minimumExposureIndex\n );\n for (\n let index = 1;\n index < bands.length;\n index += 1\n ) {\n const prior =\n bands[index - 1]!;\n const current =\n bands[index]!;\n if (\n current.minimumExposureIndex <=\n prior.maximumExposureIndex\n ) {\n throw new InvalidConfigurationError(\n path +\n \".standardRegimeBands must not overlap.\"\n );\n }\n }\n\n if (\n !Array.isArray(\n record.expandedRegimeBindings\n )\n ) {\n throw new InvalidConfigurationError(\n path +\n \".expandedRegimeBindings must be an array.\"\n );\n }\n const expandedRegimeBindings =\n record.expandedRegimeBindings.map(\n (entry, index) => {\n const entryPath =\n path +\n \".expandedRegimeBindings[\" +\n index +\n \"]\";\n const binding =\n requireRecord(\n entry,\n entryPath, [\"expandedSettingId\", \"readoutRegimeId\"]\n );\n return {\n expandedSettingId:\n requireNonEmptyString(\n binding.expandedSettingId,\n entryPath +\n \".expandedSettingId\"\n ),\n readoutRegimeId:\n requireNonEmptyString(\n binding.readoutRegimeId,\n entryPath +\n \".readoutRegimeId\"\n )\n };\n }\n );\n const expandedIds =\n expandedRegimeBindings.map(\n (entry) =>\n entry.expandedSettingId\n );\n if (\n new Set(expandedIds).size !==\n expandedIds.length\n ) {\n throw new InvalidConfigurationError(\n path +\n \".expandedRegimeBindings must not contain duplicate expandedSettingId values.\"\n );\n }\n\n return {\n captureModeId:\n requireNonEmptyString(\n record.captureModeId,\n path + \".captureModeId\"\n ),\n standardRegimeBands:\n bands,\n expandedRegimeBindings,\n evidence:\n parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n )\n };\n}\n\n/**\n * Validates untrusted input and returns it as a normalized, typed value. Unknown enum values,\n * missing required fields and inconsistent scientific data are rejected.\n *\n * The profile binds each ISO and capture-mode state to an explicit readout regime, whose gain,\n * read noise and clipping are declared separately.\n * @param value - Untrusted data. TypeScript types alone are not validation.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function parseGenericIsoSignalChainProfile(\n value: unknown\n): GenericIsoSignalChainProfile {\n const record =\n requireRecord(\n value,\n \"genericIsoSignalChainProfile\", [\"schemaVersion\", \"scientificStatus\", \"behaviorMeaning\", \"photonShotNoiseOwnedUpstream\", \"processedImageBehaviorIncluded\", \"fixedPatternNoiseModeled\", \"lowSignalColorDegradationModeled\", \"captureModeBindings\", \"profileId\", \"profileVersion\", \"isoCapabilityProfileId\", \"readoutProfileId\", \"evidence\", \"limitations\"]\n );\n if (\n record.schemaVersion !==\n GENERIC_ISO_SIGNAL_CHAIN_PROFILE_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'genericIsoSignalChainProfile.schemaVersion must be \"' +\n GENERIC_ISO_SIGNAL_CHAIN_PROFILE_SCHEMA_VERSION +\n '\".'\n );\n }\n if (\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n 'genericIsoSignalChainProfile.scientificStatus must be \"approximation\".'\n );\n }\n if (\n record.behaviorMeaning !==\n \"iso-state-selects-explicit-readout-regime-not-noise-equation\"\n ) {\n throw new InvalidConfigurationError(\n \"genericIsoSignalChainProfile.behaviorMeaning is invalid.\"\n );\n }\n if (\n record.photonShotNoiseOwnedUpstream !==\n true ||\n record.processedImageBehaviorIncluded !==\n false ||\n record.fixedPatternNoiseModeled !==\n false ||\n record.lowSignalColorDegradationModeled !==\n false\n ) {\n throw new InvalidConfigurationError(\n \"genericIsoSignalChainProfile domain-ownership flags are invalid.\"\n );\n }\n if (\n !Array.isArray(\n record.captureModeBindings\n ) ||\n record.captureModeBindings\n .length === 0\n ) {\n throw new InvalidConfigurationError(\n \"genericIsoSignalChainProfile.captureModeBindings must be a non-empty array.\"\n );\n }\n const captureModeBindings =\n record.captureModeBindings.map(\n (entry, index) =>\n parseCaptureModeBinding(\n entry,\n \"genericIsoSignalChainProfile.captureModeBindings[\" +\n index +\n \"]\"\n )\n );\n const modeIds =\n captureModeBindings.map(\n (entry) =>\n entry.captureModeId\n );\n if (\n new Set(modeIds).size !==\n modeIds.length\n ) {\n throw new InvalidConfigurationError(\n \"genericIsoSignalChainProfile.captureModeBindings must not contain duplicate captureModeId values.\"\n );\n }\n\n return {\n schemaVersion:\n GENERIC_ISO_SIGNAL_CHAIN_PROFILE_SCHEMA_VERSION,\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"genericIsoSignalChainProfile.profileId\"\n ),\n profileVersion:\n requireNonEmptyString(\n record.profileVersion,\n \"genericIsoSignalChainProfile.profileVersion\"\n ),\n scientificStatus:\n \"approximation\",\n isoCapabilityProfileId:\n requireNonEmptyString(\n record.isoCapabilityProfileId,\n \"genericIsoSignalChainProfile.isoCapabilityProfileId\"\n ),\n readoutProfileId:\n requireNonEmptyString(\n record.readoutProfileId,\n \"genericIsoSignalChainProfile.readoutProfileId\"\n ),\n behaviorMeaning:\n \"iso-state-selects-explicit-readout-regime-not-noise-equation\",\n captureModeBindings,\n photonShotNoiseOwnedUpstream:\n true,\n processedImageBehaviorIncluded:\n false,\n fixedPatternNoiseModeled:\n false,\n lowSignalColorDegradationModeled:\n false,\n evidence:\n parseEvidenceList(\n record.evidence,\n \"genericIsoSignalChainProfile.evidence\"\n ),\n limitations:\n parseLimitations(\n record.limitations,\n \"genericIsoSignalChainProfile.limitations\"\n )\n };\n}\n\nfunction regimeIdForIso(\n binding:\n GenericIsoCaptureModeSignalChainBinding,\n iso:\n ResolvedIsoCapability\n): {\n regimeId: string;\n reason:\n ResolvedGenericIsoSignalChain[\"regimeSelectionReason\"];\n} {\n if (\n iso.setting.kind ===\n \"expanded\"\n ) {\n const expandedSettingId =\n iso.setting.settingId;\n const expanded =\n binding\n .expandedRegimeBindings\n .find(\n (entry) =>\n entry\n .expandedSettingId ===\n expandedSettingId\n );\n if (expanded === undefined) {\n throw new InvalidScientificInputError(\n \"Expanded ISO setting has no signal-chain regime binding for the selected capture mode.\"\n );\n }\n return {\n regimeId:\n expanded\n .readoutRegimeId,\n reason:\n \"expanded-setting-binding\"\n };\n }\n\n const matches =\n binding\n .standardRegimeBands\n .filter(\n (band) =>\n iso\n .reportedExposureIndex >=\n band\n .minimumExposureIndex &&\n iso\n .reportedExposureIndex <=\n band\n .maximumExposureIndex\n );\n if (matches.length !== 1) {\n throw new InvalidScientificInputError(\n \"Standard ISO setting must match exactly one signal-chain regime band.\"\n );\n }\n return {\n regimeId:\n matches[0]!\n .readoutRegimeId,\n reason:\n \"standard-exposure-index-band\"\n };\n}\n\n/**\n * Picks the explicit readout regime bound to the resolved ISO and capture-mode state. Gain and noise\n * are never derived from the ISO number.\n * @param input - The input; see `ResolveGenericIsoSignalChainInput` for its fields, units and coordinates.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function resolveGenericIsoSignalChain(\n input:\n ResolveGenericIsoSignalChainInput\n): ResolvedGenericIsoSignalChain {\n requireInputKeys(input, \"resolveGenericIsoSignalChain\", [\"profile\", \"isoCapability\", \"requestedIsoSetting\", \"captureModeId\", \"readoutProfile\"]);\n const profile =\n parseGenericIsoSignalChainProfile(\n input.profile\n );\n const isoCapability =\n parseIsoCapabilityProfile(\n input.isoCapability\n );\n const readoutProfile =\n parseSensorReadoutConversionProfile(\n input.readoutProfile\n );\n\n if (\n profile.isoCapabilityProfileId !==\n isoCapability.profileId\n ) {\n throw new InvalidScientificInputError(\n \"Signal-chain profile isoCapabilityProfileId must match the supplied ISO capability profile.\"\n );\n }\n if (\n profile.readoutProfileId !==\n readoutProfile.profileId\n ) {\n throw new InvalidScientificInputError(\n \"Signal-chain profile readoutProfileId must match the supplied sensor readout profile.\"\n );\n }\n if (\n typeof input.captureModeId !==\n \"string\" ||\n input.captureModeId.trim()\n .length === 0\n ) {\n throw new InvalidConfigurationError(\n \"captureModeId must be a non-empty string.\"\n );\n }\n\n const binding =\n profile.captureModeBindings.find(\n (entry) =>\n entry.captureModeId ===\n input.captureModeId\n );\n if (binding === undefined) {\n throw new InvalidScientificInputError(\n \"Signal-chain profile has no binding for the selected capture mode.\"\n );\n }\n\n const iso =\n resolveIsoCapability({\n profile:\n isoCapability,\n setting:\n input\n .requestedIsoSetting,\n captureModeId:\n input.captureModeId\n });\n const regime =\n regimeIdForIso(\n binding,\n iso\n );\n const readout =\n resolveSensorReadoutRegime({\n profile:\n readoutProfile,\n regimeId:\n regime.regimeId\n });\n const gain =\n readout.regime\n .exposureIndexGain;\n if (\n gain !== undefined &&\n gain.exposureIndex !==\n iso.reportedExposureIndex\n ) {\n throw new InvalidScientificInputError(\n \"The bound readout regime declares an ISO-gain derivation for a different exposure index.\"\n );\n }\n\n return {\n profileId:\n profile.profileId,\n profileVersion:\n profile.profileVersion,\n scientificStatus:\n \"approximation\",\n captureModeId:\n input.captureModeId,\n iso,\n readout,\n regimeSelectionReason:\n regime.reason,\n capturedPhotonExpectationModified:\n false,\n photonShotNoiseStatisticsModified:\n false,\n sensorGeometryModified:\n false,\n processedImageBehaviorApplied:\n false,\n fixedPatternNoiseApplied:\n false,\n lowSignalColorDegradationApplied:\n false,\n isoUsedAsDirectNoiseEquation:\n false,\n exactCommercialCameraBehaviorClaimed:\n false\n };\n}\n\nconst PRESETS =\n new Set<GenericIsoSignalChainPreset>([\n \"good\",\n \"better\",\n \"best\"\n ]);\n\n/**\n * Validates untrusted input and returns it as a normalized, typed value. Unknown enum values,\n * missing required fields and inconsistent scientific data are rejected.\n *\n * Generic tier labels in the catalogue do not calibrate or rank real cameras.\n * @param value - Untrusted data. TypeScript types alone are not validation.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function parseGenericIsoSignalChainPresetCatalog(\n value: unknown\n): GenericIsoSignalChainPresetCatalog {\n const record =\n requireRecord(\n value,\n \"genericIsoSignalChainPresetCatalog\", [\"schemaVersion\", \"performanceOrderingClaimed\", \"realCameraRankingClaimed\", \"entries\", \"catalogId\", \"catalogVersion\"]\n );\n if (\n record.schemaVersion !==\n GENERIC_ISO_SIGNAL_CHAIN_PRESET_CATALOG_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'genericIsoSignalChainPresetCatalog.schemaVersion must be \"' +\n GENERIC_ISO_SIGNAL_CHAIN_PRESET_CATALOG_SCHEMA_VERSION +\n '\".'\n );\n }\n if (\n record.performanceOrderingClaimed !==\n false ||\n record.realCameraRankingClaimed !==\n false\n ) {\n throw new InvalidConfigurationError(\n \"Generic ISO preset catalog must not claim physical performance ordering or real-camera ranking.\"\n );\n }\n if (\n !Array.isArray(\n record.entries\n ) ||\n record.entries.length !== 3\n ) {\n throw new InvalidConfigurationError(\n \"Generic ISO preset catalog must contain exactly Good, Better, and Best entries.\"\n );\n }\n\n const entries =\n record.entries.map(\n (entry, index) => {\n const path =\n \"genericIsoSignalChainPresetCatalog.entries[\" +\n index +\n \"]\";\n const parsed =\n requireRecord(\n entry,\n path, [\"preset\", \"signalChainProfileId\", \"evidence\"]\n );\n if (\n typeof parsed.preset !==\n \"string\" ||\n !PRESETS.has(\n parsed.preset as\n GenericIsoSignalChainPreset\n )\n ) {\n throw new InvalidConfigurationError(\n path +\n \".preset is invalid.\"\n );\n }\n return {\n preset:\n parsed.preset as\n GenericIsoSignalChainPreset,\n signalChainProfileId:\n requireNonEmptyString(\n parsed\n .signalChainProfileId,\n path +\n \".signalChainProfileId\"\n ),\n evidence:\n parseEvidenceList(\n parsed.evidence,\n path + \".evidence\"\n )\n };\n }\n );\n\n const presets =\n entries.map(\n (entry) =>\n entry.preset\n );\n if (\n new Set(presets).size !==\n 3\n ) {\n throw new InvalidConfigurationError(\n \"Generic ISO preset catalog must contain each Good, Better, and Best preset exactly once.\"\n );\n }\n\n return {\n schemaVersion:\n GENERIC_ISO_SIGNAL_CHAIN_PRESET_CATALOG_SCHEMA_VERSION,\n catalogId:\n requireNonEmptyString(\n record.catalogId,\n \"genericIsoSignalChainPresetCatalog.catalogId\"\n ),\n catalogVersion:\n requireNonEmptyString(\n record.catalogVersion,\n \"genericIsoSignalChainPresetCatalog.catalogVersion\"\n ),\n entries,\n performanceOrderingClaimed:\n false,\n realCameraRankingClaimed:\n false\n };\n}\n\n/**\n * Picks a generic preset from the catalogue and returns the signal-chain profile it uses. It makes\n * no claim of manufacturer calibration.\n *\n * Generic tier labels do not calibrate or rank real cameras.\n * @param input - The preset catalogue and the chosen preset.\n * @returns The preset and its signal-chain profile ID. `performanceOrderingClaimed` and\n * `realCameraRankingClaimed` are always false. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function resolveGenericIsoSignalChainPreset(\n input: {\n catalog:\n GenericIsoSignalChainPresetCatalog;\n preset:\n GenericIsoSignalChainPreset;\n }\n): {\n preset:\n GenericIsoSignalChainPreset;\n signalChainProfileId:\n string;\n performanceOrderingClaimed:\n false;\n realCameraRankingClaimed:\n false;\n} {\n requireInputKeys(input, \"resolveGenericIsoSignalChainPreset\", [\"catalog\", \"preset\"]);\n const catalog =\n parseGenericIsoSignalChainPresetCatalog(\n input.catalog\n );\n if (\n !PRESETS.has(\n input.preset\n )\n ) {\n throw new InvalidConfigurationError(\n \"preset must be good, better, or best.\"\n );\n }\n const entry =\n catalog.entries.find(\n (candidate) =>\n candidate.preset ===\n input.preset\n )!;\n\n return {\n preset:\n input.preset,\n signalChainProfileId:\n entry\n .signalChainProfileId,\n performanceOrderingClaimed:\n false,\n realCameraRankingClaimed:\n false\n };\n}\n"]}
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
* @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support
|
|
12
12
|
* limits.
|
|
13
13
|
*/
|
|
14
|
-
import { rejectUnknownFields } from "../core/record-validation.js";
|
|
15
14
|
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
16
15
|
import { parseEvidenceList } from "../core/evidence-provenance.js";
|
|
17
16
|
import { InvalidScientificInputError } from "../core/validation.js";
|
|
17
|
+
import { requireConfigRecord as requireRecord, requireConfigString as requireNonEmptyString, requireConfigFiniteNumber as requireFiniteNumber } from "../core/record-validation.js";
|
|
18
18
|
const COMPONENT_ROLES = new Set([
|
|
19
19
|
"cover-glass",
|
|
20
20
|
"infrared-cut",
|
|
@@ -24,23 +24,6 @@ const COMPONENT_ROLES = new Set([
|
|
|
24
24
|
"wave-plate",
|
|
25
25
|
"other-optical-filter"
|
|
26
26
|
]);
|
|
27
|
-
function requireRecord(value, path, allowedKeys) {
|
|
28
|
-
if (typeof value !== "object" ||
|
|
29
|
-
value === null ||
|
|
30
|
-
Array.isArray(value)) {
|
|
31
|
-
throw new InvalidConfigurationError(path + " must be an object.");
|
|
32
|
-
}
|
|
33
|
-
if (allowedKeys !== null)
|
|
34
|
-
rejectUnknownFields(value, allowedKeys, path);
|
|
35
|
-
return value;
|
|
36
|
-
}
|
|
37
|
-
function requireNonEmptyString(value, path) {
|
|
38
|
-
if (typeof value !== "string" ||
|
|
39
|
-
value.trim().length === 0) {
|
|
40
|
-
throw new InvalidConfigurationError(path + " must be a non-empty string.");
|
|
41
|
-
}
|
|
42
|
-
return value;
|
|
43
|
-
}
|
|
44
27
|
function parseComponentRoles(value, path) {
|
|
45
28
|
if (!Array.isArray(value) || value.length === 0) {
|
|
46
29
|
throw new InvalidConfigurationError(path + " must be a non-empty array.");
|
|
@@ -75,13 +58,6 @@ function parseOrderedComponents(value, path) {
|
|
|
75
58
|
}
|
|
76
59
|
return components;
|
|
77
60
|
}
|
|
78
|
-
function requireFiniteNumber(value, path) {
|
|
79
|
-
if (typeof value !== "number" ||
|
|
80
|
-
!Number.isFinite(value)) {
|
|
81
|
-
throw new InvalidConfigurationError(path + " must be a finite number.");
|
|
82
|
-
}
|
|
83
|
-
return value;
|
|
84
|
-
}
|
|
85
61
|
function parsePointSplitComponents(value, path) {
|
|
86
62
|
if (!Array.isArray(value) || value.length === 0) {
|
|
87
63
|
throw new InvalidConfigurationError(path + " must be a non-empty array.");
|