@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capture-mode.js","sourceRoot":"","sources":["../../src/sensor/capture-mode.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Capture modes: how a camera mode samples the sensor (for example full resolution, binning, or\n * multi-shot sensor shift) and what image size it produces.\n *\n * Sensor physics is never guessed from output resolution or a marketing mode name. A mode's parts\n * are independent: the sequence of frames, sampling within each frame, optional sensor shift between\n * frames, reconstruction stages, the processed image size and downstream dependencies. Changing\n * resolution never changes the physical sensor geometry.\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 EvidenceBackedFact,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport {\n InvalidScientificInputError,\n requirePositiveInteger\n} from \"../core/validation.js\";\nimport type {\n NativeImageRaster,\n RasterDimensions\n} from \"./sensor-geometry.js\";\nimport { requireInputObject, requireInputKeys, rejectUnknownFields } from \"../core/record-validation.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport type CaptureModeSampleCombinationDomain =\n | \"charge-domain\"\n | \"pre-conversion-analog\"\n | \"post-conversion-digital\";\n\nexport type CaptureModeReconstructionStage =\n | \"remosaic\"\n | \"pixel-shift-combination\"\n | \"multi-frame-computational-combination\";\n\nexport type CaptureModeDependency =\n | \"color-sampling-model\"\n | \"mode-specific-readout-timing\"\n | \"radiometric-calibration\"\n | \"inter-frame-registration\";\n\nexport interface CaptureModeSensorOffsetNativeSamples {\n /**\n * Horizontal sensor shift, in units of the native sample pitch. This says\n * nothing about the physical photodiode pitch.\n */\n x: number;\n /**\n * Vertical sensor shift, in units of the native sample pitch. This says\n * nothing about the physical photodiode pitch.\n */\n y: number;\n}\n\nexport type SourcedCaptureModeFact<T> = EvidenceBackedFact<T>;\n\nexport interface SingleFrameCaptureAcquisition {\n kind: \"single-frame\";\n}\n\nexport interface FixedMultiFrameCaptureAcquisition {\n kind: \"fixed-multi-frame\";\n frameCount: SourcedCaptureModeFact<number>;\n}\n\nexport interface VariableMultiFrameCaptureAcquisition {\n kind: \"variable-multi-frame\";\n /**\n * Evidence that the mode uses a variable number of frames, even when the\n * exact frame-count limits are not published.\n */\n evidence: readonly EvidenceProvenance[];\n minimumFrameCount?: SourcedCaptureModeFact<number>;\n maximumFrameCount?: SourcedCaptureModeFact<number>;\n}\n\nexport type CaptureModeAcquisition =\n | SingleFrameCaptureAcquisition\n | FixedMultiFrameCaptureAcquisition\n | VariableMultiFrameCaptureAcquisition;\n\nexport interface NativeEffectiveCaptureSampling {\n kind: \"native-effective-raster\";\n}\n\nexport interface GroupedNativeCaptureSampling {\n kind: \"grouped-native-samples\";\n groupWidthSamples: SourcedCaptureModeFact<number>;\n groupHeightSamples: SourcedCaptureModeFact<number>;\n /**\n * Omitted means \"unknown, not stated\". Do not guess where samples are\n * combined from the word \"binning\" or from the ratio of image sizes.\n */\n combinationDomain?: SourcedCaptureModeFact<CaptureModeSampleCombinationDomain>;\n}\n\nexport interface DeclaredEffectiveCaptureSampling {\n kind: \"declared-effective-raster\";\n /**\n * Sample grid per frame, for a mode whose relationship to the native grid\n * cannot safely be written as simple integer grouping.\n */\n raster: SourcedCaptureModeFact<RasterDimensions>;\n}\n\nexport type CaptureModePerFrameSampling =\n | NativeEffectiveCaptureSampling\n | GroupedNativeCaptureSampling\n | DeclaredEffectiveCaptureSampling;\n\nexport interface CaptureModeDefinition {\n modeId: string;\n /**\n * Evidence that this mode exists and for how it captures overall. Facts in\n * the fields below keep their own evidence where needed.\n */\n evidence: readonly EvidenceProvenance[];\n acquisition: CaptureModeAcquisition;\n perFrameSampling: CaptureModePerFrameSampling;\n /**\n * Optional ordered sequence of sensor shifts, for modes with a fixed number\n * of frames. The whole sequence is one evidence-backed fact, because\n * published documentation usually describes it as a unit.\n */\n interFrameSensorOffsetsNativeSamples?: SourcedCaptureModeFact<\n readonly CaptureModeSensorOffsetNativeSamples[]\n >;\n /**\n * Ordered reconstruction or combination stages. These labels only name the\n * stages for later reconstruction models; they implement no algorithm.\n */\n reconstructionStages?: readonly SourcedCaptureModeFact<CaptureModeReconstructionStage>[];\n /**\n * Image size produced by the capture mode and reconstruction, before the\n * final output crop or resampling.\n *\n * It does not change the physical sensor geometry or field of view.\n */\n processedImageRaster: SourcedCaptureModeFact<RasterDimensions>;\n /**\n * Explicit downstream scientific/model dependencies for this mode.\n */\n dependencies?: readonly CaptureModeDependency[];\n}\n\nexport interface CaptureModeProfile {\n schemaVersion: \"0.1.0\";\n modes: readonly CaptureModeDefinition[];\n}\n\nexport interface ResolveCaptureModeInput {\n nativeRaster: NativeImageRaster;\n profile: CaptureModeProfile;\n modeId: string;\n}\n\nexport interface ResolvedCaptureMode {\n modeId: string;\n modeEvidence: readonly EvidenceProvenance[];\n nativeRaster: NativeImageRaster;\n nativeRasterSemantic:\n \"effective-image-sampling-grid-not-photosite-count\";\n acquisition:\n | {\n kind: \"single-frame\";\n frameCount: 1;\n }\n | {\n kind: \"fixed-multi-frame\";\n frameCount: SourcedCaptureModeFact<number>;\n }\n | {\n kind: \"variable-multi-frame\";\n evidence: readonly EvidenceProvenance[];\n minimumFrameCount?: SourcedCaptureModeFact<number>;\n maximumFrameCount?: SourcedCaptureModeFact<number>;\n };\n perFrameSampling:\n | {\n kind: \"native-effective-raster\";\n raster: NativeImageRaster;\n }\n | {\n kind: \"grouped-native-samples\";\n raster: RasterDimensions;\n groupWidthSamples: SourcedCaptureModeFact<number>;\n groupHeightSamples: SourcedCaptureModeFact<number>;\n combinationDomain?: SourcedCaptureModeFact<CaptureModeSampleCombinationDomain>;\n }\n | {\n kind: \"declared-effective-raster\";\n raster: SourcedCaptureModeFact<RasterDimensions>;\n };\n interFrameSensorOffsetsNativeSamples?: SourcedCaptureModeFact<\n readonly CaptureModeSensorOffsetNativeSamples[]\n >;\n reconstructionStages: readonly SourcedCaptureModeFact<CaptureModeReconstructionStage>[];\n processedImageRaster: SourcedCaptureModeFact<RasterDimensions>;\n processedImageMegapixels: number;\n dependencies: readonly CaptureModeDependency[];\n physicalSensorGeometryMutation: false;\n photositeCountInference:\n \"not-permitted-from-native-or-processed-raster\";\n finalOutputRasterOwnership:\n \"downstream-capture-output-geometry\";\n}\n\nconst COMBINATION_DOMAINS =\n new Set<CaptureModeSampleCombinationDomain>([\n \"charge-domain\",\n \"pre-conversion-analog\",\n \"post-conversion-digital\"\n ]);\n\nconst RECONSTRUCTION_STAGES =\n new Set<CaptureModeReconstructionStage>([\n \"remosaic\",\n \"pixel-shift-combination\",\n \"multi-frame-computational-combination\"\n ]);\n\nconst DEPENDENCIES = new Set<CaptureModeDependency>([\n \"color-sampling-model\",\n \"mode-specific-readout-timing\",\n \"radiometric-calibration\",\n \"inter-frame-registration\"\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 requireNonEmptyString(value: unknown, path: string): string {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new InvalidConfigurationError(\n path + \" must be a non-empty string.\"\n );\n }\n return value;\n}\n\nfunction requirePositiveSafeIntegerValue(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isSafeInteger(value) ||\n value <= 0\n ) {\n throw new InvalidConfigurationError(\n path + \" must be a positive safe integer.\"\n );\n }\n return value;\n}\n\nfunction parseIntegerFact(\n value: unknown,\n path: string\n): SourcedCaptureModeFact<number> {\n const record = requireRecord(value, path, [\"value\", \"evidence\"]);\n return {\n value: requirePositiveSafeIntegerValue(\n record.value,\n path + \".value\"\n ),\n evidence: parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n )\n };\n}\n\nfunction parseRasterDimensions(\n value: unknown,\n path: string\n): RasterDimensions {\n const record = requireRecord(value, path, [\"pixelWidth\", \"pixelHeight\"]);\n const pixelWidth = requirePositiveSafeIntegerValue(\n record.pixelWidth,\n path + \".pixelWidth\"\n );\n const pixelHeight = requirePositiveSafeIntegerValue(\n record.pixelHeight,\n path + \".pixelHeight\"\n );\n const total = pixelWidth * pixelHeight;\n if (!Number.isSafeInteger(total)) {\n throw new InvalidConfigurationError(\n path + \" total sample count must be a safe integer.\"\n );\n }\n return { pixelWidth, pixelHeight };\n}\n\nfunction parseRasterFact(\n value: unknown,\n path: string\n): SourcedCaptureModeFact<RasterDimensions> {\n const record = requireRecord(value, path, [\"value\", \"evidence\"]);\n return {\n value: parseRasterDimensions(\n record.value,\n path + \".value\"\n ),\n evidence: parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n )\n };\n}\n\nfunction parseCombinationDomainFact(\n value: unknown,\n path: string\n): SourcedCaptureModeFact<CaptureModeSampleCombinationDomain> {\n const record = requireRecord(value, path, [\"value\", \"evidence\"]);\n if (\n typeof record.value !== \"string\" ||\n !COMBINATION_DOMAINS.has(\n record.value as CaptureModeSampleCombinationDomain\n )\n ) {\n throw new InvalidConfigurationError(\n path + \".value is invalid.\"\n );\n }\n return {\n value: record.value as CaptureModeSampleCombinationDomain,\n evidence: parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n )\n };\n}\n\nfunction parseAcquisition(\n value: unknown,\n path: string\n): CaptureModeAcquisition {\n const record = requireRecord(value, path, [\"kind\", \"frameCount\", \"minimumFrameCount\", \"maximumFrameCount\", \"evidence\"]);\n\n if (record.kind === \"single-frame\") {\n if (\n record.frameCount !== undefined ||\n record.minimumFrameCount !== undefined ||\n record.maximumFrameCount !== undefined ||\n record.evidence !== undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" single-frame acquisition must omit frame-count and evidence fields.\"\n );\n }\n return { kind: \"single-frame\" };\n }\n\n if (record.kind === \"fixed-multi-frame\") {\n if (\n record.minimumFrameCount !== undefined ||\n record.maximumFrameCount !== undefined ||\n record.evidence !== undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" fixed-multi-frame acquisition must use frameCount only.\"\n );\n }\n const frameCount = parseIntegerFact(\n record.frameCount,\n path + \".frameCount\"\n );\n if (frameCount.value < 2) {\n throw new InvalidConfigurationError(\n path +\n \".frameCount.value must be at least 2 for fixed-multi-frame acquisition.\"\n );\n }\n return {\n kind: \"fixed-multi-frame\",\n frameCount\n };\n }\n\n if (record.kind === \"variable-multi-frame\") {\n if (record.frameCount !== undefined) {\n throw new InvalidConfigurationError(\n path +\n \".frameCount must be omitted for variable-multi-frame acquisition.\"\n );\n }\n\n const evidence = parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n );\n const minimumFrameCount =\n record.minimumFrameCount === undefined\n ? undefined\n : parseIntegerFact(\n record.minimumFrameCount,\n path + \".minimumFrameCount\"\n );\n const maximumFrameCount =\n record.maximumFrameCount === undefined\n ? undefined\n : parseIntegerFact(\n record.maximumFrameCount,\n path + \".maximumFrameCount\"\n );\n\n if (\n minimumFrameCount !== undefined &&\n minimumFrameCount.value < 2\n ) {\n throw new InvalidConfigurationError(\n path +\n \".minimumFrameCount.value must be at least 2 for variable-multi-frame acquisition.\"\n );\n }\n if (\n maximumFrameCount !== undefined &&\n maximumFrameCount.value < 2\n ) {\n throw new InvalidConfigurationError(\n path +\n \".maximumFrameCount.value must be at least 2 for variable-multi-frame acquisition.\"\n );\n }\n if (\n minimumFrameCount !== undefined &&\n maximumFrameCount !== undefined &&\n minimumFrameCount.value > maximumFrameCount.value\n ) {\n throw new InvalidConfigurationError(\n path +\n \".minimumFrameCount.value must not exceed maximumFrameCount.value.\"\n );\n }\n\n return {\n kind: \"variable-multi-frame\",\n evidence,\n ...(minimumFrameCount === undefined\n ? {}\n : { minimumFrameCount }),\n ...(maximumFrameCount === undefined\n ? {}\n : { maximumFrameCount })\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parsePerFrameSampling(\n value: unknown,\n path: string\n): CaptureModePerFrameSampling {\n const record = requireRecord(value, path, [\"kind\", \"groupWidthSamples\", \"groupHeightSamples\", \"combinationDomain\", \"raster\"]);\n\n if (record.kind === \"native-effective-raster\") {\n if (\n record.groupWidthSamples !== undefined ||\n record.groupHeightSamples !== undefined ||\n record.combinationDomain !== undefined ||\n record.raster !== undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" native-effective-raster sampling must omit grouping/raster fields.\"\n );\n }\n return { kind: \"native-effective-raster\" };\n }\n\n if (record.kind === \"grouped-native-samples\") {\n if (record.raster !== undefined) {\n throw new InvalidConfigurationError(\n path +\n \".raster must be omitted for grouped-native-samples sampling.\"\n );\n }\n\n return {\n kind: \"grouped-native-samples\",\n groupWidthSamples: parseIntegerFact(\n record.groupWidthSamples,\n path + \".groupWidthSamples\"\n ),\n groupHeightSamples: parseIntegerFact(\n record.groupHeightSamples,\n path + \".groupHeightSamples\"\n ),\n ...(record.combinationDomain === undefined\n ? {}\n : {\n combinationDomain:\n parseCombinationDomainFact(\n record.combinationDomain,\n path + \".combinationDomain\"\n )\n })\n };\n }\n\n if (record.kind === \"declared-effective-raster\") {\n if (\n record.groupWidthSamples !== undefined ||\n record.groupHeightSamples !== undefined ||\n record.combinationDomain !== undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" declared-effective-raster sampling must omit grouping fields.\"\n );\n }\n\n return {\n kind: \"declared-effective-raster\",\n raster: parseRasterFact(\n record.raster,\n path + \".raster\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parseOffsetsFact(\n value: unknown,\n path: string\n): SourcedCaptureModeFact<\n readonly CaptureModeSensorOffsetNativeSamples[]\n> {\n const record = requireRecord(value, path, [\"value\", \"evidence\"]);\n if (\n !Array.isArray(record.value) ||\n record.value.length === 0\n ) {\n throw new InvalidConfigurationError(\n path + \".value must be a non-empty array.\"\n );\n }\n\n const offsets = record.value.map((entry, index) => {\n const item = requireRecord(\n entry,\n path + \".value[\" + index + \"]\", [\"x\", \"y\"]\n );\n if (\n typeof item.x !== \"number\" ||\n !Number.isFinite(item.x) ||\n typeof item.y !== \"number\" ||\n !Number.isFinite(item.y)\n ) {\n throw new InvalidConfigurationError(\n path +\n \".value[\" +\n index +\n \"] x/y must be finite numbers.\"\n );\n }\n return {\n x: item.x,\n y: item.y\n };\n });\n\n return {\n value: offsets,\n evidence: parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n )\n };\n}\n\nfunction parseReconstructionStages(\n value: unknown,\n path: string\n): readonly SourcedCaptureModeFact<CaptureModeReconstructionStage>[] {\n if (!Array.isArray(value)) {\n throw new InvalidConfigurationError(\n path + \" must be an array.\"\n );\n }\n\n const stages = value.map((entry, index) => {\n const record = requireRecord(\n entry,\n path + \"[\" + index + \"]\", [\"value\", \"evidence\"]\n );\n if (\n typeof record.value !== \"string\" ||\n !RECONSTRUCTION_STAGES.has(\n record.value as CaptureModeReconstructionStage\n )\n ) {\n throw new InvalidConfigurationError(\n path + \"[\" + index + \"].value is invalid.\"\n );\n }\n return {\n value: record.value as CaptureModeReconstructionStage,\n evidence: parseEvidenceList(\n record.evidence,\n path + \"[\" + index + \"].evidence\"\n )\n };\n });\n\n const values = stages.map((stage) => stage.value);\n if (new Set(values).size !== values.length) {\n throw new InvalidConfigurationError(\n path + \" must not contain duplicate stages.\"\n );\n }\n\n return stages;\n}\n\nfunction parseDependencies(\n value: unknown,\n path: string\n): readonly CaptureModeDependency[] {\n if (!Array.isArray(value)) {\n throw new InvalidConfigurationError(\n path + \" must be an array.\"\n );\n }\n\n const dependencies = value.map((entry, index) => {\n if (\n typeof entry !== \"string\" ||\n !DEPENDENCIES.has(entry as CaptureModeDependency)\n ) {\n throw new InvalidConfigurationError(\n path + \"[\" + index + \"] is invalid.\"\n );\n }\n return entry as CaptureModeDependency;\n });\n\n if (new Set(dependencies).size !== dependencies.length) {\n throw new InvalidConfigurationError(\n path + \" must not contain duplicates.\"\n );\n }\n\n return dependencies;\n}\n\nfunction parseModeDefinition(\n value: unknown,\n path: string\n): CaptureModeDefinition {\n const record = requireRecord(value, path, [\"modeId\", \"acquisition\", \"interFrameSensorOffsetsNativeSamples\", \"evidence\", \"perFrameSampling\", \"reconstructionStages\", \"processedImageRaster\", \"dependencies\"]);\n const modeId = requireNonEmptyString(\n record.modeId,\n path + \".modeId\"\n );\n const acquisition = parseAcquisition(\n record.acquisition,\n path + \".acquisition\"\n );\n const interFrameSensorOffsetsNativeSamples =\n record.interFrameSensorOffsetsNativeSamples === undefined\n ? undefined\n : parseOffsetsFact(\n record.interFrameSensorOffsetsNativeSamples,\n path + \".interFrameSensorOffsetsNativeSamples\"\n );\n\n if (\n interFrameSensorOffsetsNativeSamples !== undefined &&\n acquisition.kind !== \"fixed-multi-frame\"\n ) {\n throw new InvalidConfigurationError(\n path +\n \".interFrameSensorOffsetsNativeSamples is supported only for fixed-multi-frame acquisition.\"\n );\n }\n\n if (\n interFrameSensorOffsetsNativeSamples !== undefined &&\n acquisition.kind === \"fixed-multi-frame\" &&\n interFrameSensorOffsetsNativeSamples.value.length !==\n acquisition.frameCount.value\n ) {\n throw new InvalidConfigurationError(\n path +\n \".interFrameSensorOffsetsNativeSamples.value length must equal acquisition.frameCount.value.\"\n );\n }\n\n return {\n modeId,\n evidence: parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n ),\n acquisition,\n perFrameSampling: parsePerFrameSampling(\n record.perFrameSampling,\n path + \".perFrameSampling\"\n ),\n ...(interFrameSensorOffsetsNativeSamples === undefined\n ? {}\n : { interFrameSensorOffsetsNativeSamples }),\n reconstructionStages:\n record.reconstructionStages === undefined\n ? []\n : parseReconstructionStages(\n record.reconstructionStages,\n path + \".reconstructionStages\"\n ),\n processedImageRaster: parseRasterFact(\n record.processedImageRaster,\n path + \".processedImageRaster\"\n ),\n dependencies:\n record.dependencies === undefined\n ? []\n : parseDependencies(\n record.dependencies,\n path + \".dependencies\"\n )\n };\n}\n\n/**\n * Parses a capture-mode profile. Sensor physics is never guessed from output\n * resolution or marketing mode names.\n *\n * The profile's parts are independent of each other: the frame sequence,\n * sampling within each frame, optional sensor shift between frames,\n * reconstruction stages, the processed image size and downstream dependencies.\n */\nexport function parseCaptureModeProfile(\n value: unknown\n): CaptureModeProfile {\n const profile = requireRecord(value, \"captureModes\", [\"schemaVersion\", \"modes\"]);\n\n if (profile.schemaVersion !== \"0.1.0\") {\n throw new InvalidConfigurationError(\n 'captureModes.schemaVersion must be \"0.1.0\".'\n );\n }\n\n if (\n !Array.isArray(profile.modes) ||\n profile.modes.length === 0\n ) {\n throw new InvalidConfigurationError(\n \"captureModes.modes must be a non-empty array.\"\n );\n }\n\n const modes = profile.modes.map((mode, index) =>\n parseModeDefinition(\n mode,\n \"captureModes.modes[\" + index + \"]\"\n )\n );\n const ids = modes.map((mode) => mode.modeId);\n if (new Set(ids).size !== ids.length) {\n throw new InvalidConfigurationError(\n \"captureModes.modes must not contain duplicate modeId values.\"\n );\n }\n\n return {\n schemaVersion: \"0.1.0\",\n modes\n };\n}\n\nfunction validateNativeRaster(\n raster: NativeImageRaster\n): void {\n requireInputObject(raster, \"raster input\");\n requirePositiveInteger(\n \"nativeRaster.pixelWidth\",\n raster.pixelWidth\n );\n requirePositiveInteger(\n \"nativeRaster.pixelHeight\",\n raster.pixelHeight\n );\n const total =\n raster.pixelWidth * raster.pixelHeight;\n if (!Number.isSafeInteger(total)) {\n throw new InvalidScientificInputError(\n \"nativeRaster total image-sample count must be a safe integer.\"\n );\n }\n}\n\nfunction resolvePerFrameSampling(\n sampling: CaptureModePerFrameSampling,\n nativeRaster: NativeImageRaster\n): ResolvedCaptureMode[\"perFrameSampling\"] {\n if (sampling.kind === \"native-effective-raster\") {\n return {\n kind: \"native-effective-raster\",\n raster: { ...nativeRaster }\n };\n }\n\n if (sampling.kind === \"declared-effective-raster\") {\n return {\n kind: \"declared-effective-raster\",\n raster: {\n value: { ...sampling.raster.value },\n evidence: sampling.raster.evidence\n }\n };\n }\n\n const groupWidth = sampling.groupWidthSamples.value;\n const groupHeight = sampling.groupHeightSamples.value;\n\n if (\n nativeRaster.pixelWidth % groupWidth !== 0 ||\n nativeRaster.pixelHeight % groupHeight !== 0\n ) {\n throw new InvalidScientificInputError(\n \"Grouped native sampling requires nativeRaster dimensions to be exactly divisible by the declared grouping factors.\"\n );\n }\n\n return {\n kind: \"grouped-native-samples\",\n raster: {\n pixelWidth:\n nativeRaster.pixelWidth / groupWidth,\n pixelHeight:\n nativeRaster.pixelHeight / groupHeight\n },\n groupWidthSamples: sampling.groupWidthSamples,\n groupHeightSamples: sampling.groupHeightSamples,\n ...(sampling.combinationDomain === undefined\n ? {}\n : {\n combinationDomain:\n sampling.combinationDomain\n })\n };\n}\n\n/**\n * Resolves one capture mode against the selected sensor's native pixel grid.\n *\n * Resolution changes do not change the physical sensor geometry. The returned\n * processed image size is before output, and must not be used to work out crop\n * factor, field of view or the physical photosite count.\n */\nexport function resolveCaptureMode(\n input: ResolveCaptureModeInput\n): ResolvedCaptureMode {\n requireInputKeys(input, \"resolveCaptureMode\", [\"nativeRaster\", \"profile\", \"modeId\"]);\n validateNativeRaster(input.nativeRaster);\n if (\n typeof input.modeId !== \"string\" ||\n input.modeId.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n \"modeId must be a non-empty string.\"\n );\n }\n const profile = parseCaptureModeProfile(input.profile);\n\n const mode = profile.modes.find(\n (candidate) => candidate.modeId === input.modeId\n );\n if (mode === undefined) {\n throw new InvalidScientificInputError(\n \"modeId does not exist in the supplied capture-mode profile.\"\n );\n }\n\n const processedTotal =\n mode.processedImageRaster.value.pixelWidth *\n mode.processedImageRaster.value.pixelHeight;\n if (!Number.isSafeInteger(processedTotal)) {\n throw new InvalidScientificInputError(\n \"processedImageRaster total image-sample count must be a safe integer.\"\n );\n }\n\n const acquisition: ResolvedCaptureMode[\"acquisition\"] =\n mode.acquisition.kind === \"single-frame\"\n ? {\n kind: \"single-frame\",\n frameCount: 1\n }\n : mode.acquisition.kind === \"fixed-multi-frame\"\n ? {\n kind: \"fixed-multi-frame\",\n frameCount: mode.acquisition.frameCount\n }\n : {\n kind: \"variable-multi-frame\",\n evidence: mode.acquisition.evidence,\n ...(mode.acquisition.minimumFrameCount === undefined\n ? {}\n : {\n minimumFrameCount:\n mode.acquisition.minimumFrameCount\n }),\n ...(mode.acquisition.maximumFrameCount === undefined\n ? {}\n : {\n maximumFrameCount:\n mode.acquisition.maximumFrameCount\n })\n };\n\n return {\n modeId: mode.modeId,\n modeEvidence: mode.evidence,\n nativeRaster: { ...input.nativeRaster },\n nativeRasterSemantic:\n \"effective-image-sampling-grid-not-photosite-count\",\n acquisition,\n perFrameSampling: resolvePerFrameSampling(\n mode.perFrameSampling,\n input.nativeRaster\n ),\n ...(mode.interFrameSensorOffsetsNativeSamples === undefined\n ? {}\n : {\n interFrameSensorOffsetsNativeSamples:\n mode.interFrameSensorOffsetsNativeSamples\n }),\n reconstructionStages:\n mode.reconstructionStages ?? [],\n processedImageRaster: {\n value: {\n ...mode.processedImageRaster.value\n },\n evidence: mode.processedImageRaster.evidence\n },\n processedImageMegapixels:\n processedTotal / 1_000_000,\n dependencies: mode.dependencies ?? [],\n physicalSensorGeometryMutation: false,\n photositeCountInference:\n \"not-permitted-from-native-or-processed-raster\",\n finalOutputRasterOwnership:\n \"downstream-capture-output-geometry\"\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"capture-mode.js","sourceRoot":"","sources":["../../src/sensor/capture-mode.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EACL,iBAAiB,EAGlB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,2BAA2B,EAC3B,sBAAsB,EACvB,MAAM,uBAAuB,CAAC;AAK/B,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,mBAAmB,IAAI,aAAa,EAAE,mBAAmB,IAAI,qBAAqB,EAAE,gCAAgC,IAAI,+BAA+B,EAAE,MAAM,8BAA8B,CAAC;AAyL7O,MAAM,mBAAmB,GACvB,IAAI,GAAG,CAAqC;IAC1C,eAAe;IACf,uBAAuB;IACvB,yBAAyB;CAC1B,CAAC,CAAC;AAEL,MAAM,qBAAqB,GACzB,IAAI,GAAG,CAAiC;IACtC,UAAU;IACV,yBAAyB;IACzB,uCAAuC;CACxC,CAAC,CAAC;AAEL,MAAM,YAAY,GAAG,IAAI,GAAG,CAAwB;IAClD,sBAAsB;IACtB,8BAA8B;IAC9B,yBAAyB;IACzB,0BAA0B;CAC3B,CAAC,CAAC;AAEH,SAAS,gBAAgB,CACvB,KAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;IACjE,OAAO;QACL,KAAK,EAAE,+BAA+B,CACpC,MAAM,CAAC,KAAK,EACZ,IAAI,GAAG,QAAQ,CAChB;QACD,QAAQ,EAAE,iBAAiB,CACzB,MAAM,CAAC,QAAQ,EACf,IAAI,GAAG,WAAW,CACnB;KACF,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAC5B,KAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC;IACzE,MAAM,UAAU,GAAG,+BAA+B,CAChD,MAAM,CAAC,UAAU,EACjB,IAAI,GAAG,aAAa,CACrB,CAAC;IACF,MAAM,WAAW,GAAG,+BAA+B,CACjD,MAAM,CAAC,WAAW,EAClB,IAAI,GAAG,cAAc,CACtB,CAAC;IACF,MAAM,KAAK,GAAG,UAAU,GAAG,WAAW,CAAC;IACvC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,6CAA6C,CACrD,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;AACrC,CAAC;AAED,SAAS,eAAe,CACtB,KAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;IACjE,OAAO;QACL,KAAK,EAAE,qBAAqB,CAC1B,MAAM,CAAC,KAAK,EACZ,IAAI,GAAG,QAAQ,CAChB;QACD,QAAQ,EAAE,iBAAiB,CACzB,MAAM,CAAC,QAAQ,EACf,IAAI,GAAG,WAAW,CACnB;KACF,CAAC;AACJ,CAAC;AAED,SAAS,0BAA0B,CACjC,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,mBAAmB,CAAC,GAAG,CACtB,MAAM,CAAC,KAA2C,CACnD,EACD,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,oBAAoB,CAC5B,CAAC;IACJ,CAAC;IACD,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,KAA2C;QACzD,QAAQ,EAAE,iBAAiB,CACzB,MAAM,CAAC,QAAQ,EACf,IAAI,GAAG,WAAW,CACnB;KACF,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CACvB,KAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,UAAU,CAAC,CAAC,CAAC;IAExH,IAAI,MAAM,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACnC,IACE,MAAM,CAAC,UAAU,KAAK,SAAS;YAC/B,MAAM,CAAC,iBAAiB,KAAK,SAAS;YACtC,MAAM,CAAC,iBAAiB,KAAK,SAAS;YACtC,MAAM,CAAC,QAAQ,KAAK,SAAS,EAC7B,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,sEAAsE,CACzE,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;IAClC,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;QACxC,IACE,MAAM,CAAC,iBAAiB,KAAK,SAAS;YACtC,MAAM,CAAC,iBAAiB,KAAK,SAAS;YACtC,MAAM,CAAC,QAAQ,KAAK,SAAS,EAC7B,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,0DAA0D,CAC7D,CAAC;QACJ,CAAC;QACD,MAAM,UAAU,GAAG,gBAAgB,CACjC,MAAM,CAAC,UAAU,EACjB,IAAI,GAAG,aAAa,CACrB,CAAC;QACF,IAAI,UAAU,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,yEAAyE,CAC5E,CAAC;QACJ,CAAC;QACD,OAAO;YACL,IAAI,EAAE,mBAAmB;YACzB,UAAU;SACX,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,KAAK,sBAAsB,EAAE,CAAC;QAC3C,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACpC,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,mEAAmE,CACtE,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,iBAAiB,CAChC,MAAM,CAAC,QAAQ,EACf,IAAI,GAAG,WAAW,CACnB,CAAC;QACF,MAAM,iBAAiB,GACrB,MAAM,CAAC,iBAAiB,KAAK,SAAS;YACpC,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,gBAAgB,CACd,MAAM,CAAC,iBAAiB,EACxB,IAAI,GAAG,oBAAoB,CAC5B,CAAC;QACR,MAAM,iBAAiB,GACrB,MAAM,CAAC,iBAAiB,KAAK,SAAS;YACpC,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,gBAAgB,CACd,MAAM,CAAC,iBAAiB,EACxB,IAAI,GAAG,oBAAoB,CAC5B,CAAC;QAER,IACE,iBAAiB,KAAK,SAAS;YAC/B,iBAAiB,CAAC,KAAK,GAAG,CAAC,EAC3B,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,mFAAmF,CACtF,CAAC;QACJ,CAAC;QACD,IACE,iBAAiB,KAAK,SAAS;YAC/B,iBAAiB,CAAC,KAAK,GAAG,CAAC,EAC3B,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,mFAAmF,CACtF,CAAC;QACJ,CAAC;QACD,IACE,iBAAiB,KAAK,SAAS;YAC/B,iBAAiB,KAAK,SAAS;YAC/B,iBAAiB,CAAC,KAAK,GAAG,iBAAiB,CAAC,KAAK,EACjD,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,mEAAmE,CACtE,CAAC;QACJ,CAAC;QAED,OAAO;YACL,IAAI,EAAE,sBAAsB;YAC5B,QAAQ;YACR,GAAG,CAAC,iBAAiB,KAAK,SAAS;gBACjC,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,EAAE,iBAAiB,EAAE,CAAC;YAC1B,GAAG,CAAC,iBAAiB,KAAK,SAAS;gBACjC,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC,EAAE,iBAAiB,EAAE,CAAC;SAC3B,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,mBAAmB,CAC3B,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAC5B,KAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,QAAQ,CAAC,CAAC,CAAC;IAE9H,IAAI,MAAM,CAAC,IAAI,KAAK,yBAAyB,EAAE,CAAC;QAC9C,IACE,MAAM,CAAC,iBAAiB,KAAK,SAAS;YACtC,MAAM,CAAC,kBAAkB,KAAK,SAAS;YACvC,MAAM,CAAC,iBAAiB,KAAK,SAAS;YACtC,MAAM,CAAC,MAAM,KAAK,SAAS,EAC3B,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,qEAAqE,CACxE,CAAC;QACJ,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,yBAAyB,EAAE,CAAC;IAC7C,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,KAAK,wBAAwB,EAAE,CAAC;QAC7C,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,8DAA8D,CACjE,CAAC;QACJ,CAAC;QAED,OAAO;YACL,IAAI,EAAE,wBAAwB;YAC9B,iBAAiB,EAAE,gBAAgB,CACjC,MAAM,CAAC,iBAAiB,EACxB,IAAI,GAAG,oBAAoB,CAC5B;YACD,kBAAkB,EAAE,gBAAgB,CAClC,MAAM,CAAC,kBAAkB,EACzB,IAAI,GAAG,qBAAqB,CAC7B;YACD,GAAG,CAAC,MAAM,CAAC,iBAAiB,KAAK,SAAS;gBACxC,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC;oBACE,iBAAiB,EACf,0BAA0B,CACxB,MAAM,CAAC,iBAAiB,EACxB,IAAI,GAAG,oBAAoB,CAC5B;iBACJ,CAAC;SACP,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,KAAK,2BAA2B,EAAE,CAAC;QAChD,IACE,MAAM,CAAC,iBAAiB,KAAK,SAAS;YACtC,MAAM,CAAC,kBAAkB,KAAK,SAAS;YACvC,MAAM,CAAC,iBAAiB,KAAK,SAAS,EACtC,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,gEAAgE,CACnE,CAAC;QACJ,CAAC;QAED,OAAO;YACL,IAAI,EAAE,2BAA2B;YACjC,MAAM,EAAE,eAAe,CACrB,MAAM,CAAC,MAAM,EACb,IAAI,GAAG,SAAS,CACjB;SACF,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,mBAAmB,CAC3B,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CACvB,KAAc,EACd,IAAY;IAIZ,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;IACjE,IACE,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;QAC5B,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EACzB,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,mCAAmC,CAC3C,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAChD,MAAM,IAAI,GAAG,aAAa,CACxB,KAAK,EACL,IAAI,GAAG,SAAS,GAAG,KAAK,GAAG,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAC3C,CAAC;QACF,IACE,OAAO,IAAI,CAAC,CAAC,KAAK,QAAQ;YAC1B,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;YACxB,OAAO,IAAI,CAAC,CAAC,KAAK,QAAQ;YAC1B,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EACxB,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;gBACF,SAAS;gBACT,KAAK;gBACL,+BAA+B,CAClC,CAAC;QACJ,CAAC;QACD,OAAO;YACL,CAAC,EAAE,IAAI,CAAC,CAAC;YACT,CAAC,EAAE,IAAI,CAAC,CAAC;SACV,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,EAAE,OAAO;QACd,QAAQ,EAAE,iBAAiB,CACzB,MAAM,CAAC,QAAQ,EACf,IAAI,GAAG,WAAW,CACnB;KACF,CAAC;AACJ,CAAC;AAED,SAAS,yBAAyB,CAChC,KAAc,EACd,IAAY;IAEZ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,oBAAoB,CAC5B,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACxC,MAAM,MAAM,GAAG,aAAa,CAC1B,KAAK,EACL,IAAI,GAAG,GAAG,GAAG,KAAK,GAAG,GAAG,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,CAChD,CAAC;QACF,IACE,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ;YAChC,CAAC,qBAAqB,CAAC,GAAG,CACxB,MAAM,CAAC,KAAuC,CAC/C,EACD,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,GAAG,GAAG,KAAK,GAAG,qBAAqB,CAC3C,CAAC;QACJ,CAAC;QACD,OAAO;YACL,KAAK,EAAE,MAAM,CAAC,KAAuC;YACrD,QAAQ,EAAE,iBAAiB,CACzB,MAAM,CAAC,QAAQ,EACf,IAAI,GAAG,GAAG,GAAG,KAAK,GAAG,YAAY,CAClC;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAClD,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC;QAC3C,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,qCAAqC,CAC7C,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,iBAAiB,CACxB,KAAc,EACd,IAAY;IAEZ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,oBAAoB,CAC5B,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QAC9C,IACE,OAAO,KAAK,KAAK,QAAQ;YACzB,CAAC,YAAY,CAAC,GAAG,CAAC,KAA8B,CAAC,EACjD,CAAC;YACD,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,GAAG,GAAG,KAAK,GAAG,eAAe,CACrC,CAAC;QACJ,CAAC;QACD,OAAO,KAA8B,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,IAAI,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC,MAAM,EAAE,CAAC;QACvD,MAAM,IAAI,yBAAyB,CACjC,IAAI,GAAG,+BAA+B,CACvC,CAAC;IACJ,CAAC;IAED,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,SAAS,mBAAmB,CAC1B,KAAc,EACd,IAAY;IAEZ,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,sCAAsC,EAAE,UAAU,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,cAAc,CAAC,CAAC,CAAC;IAC7M,MAAM,MAAM,GAAG,qBAAqB,CAClC,MAAM,CAAC,MAAM,EACb,IAAI,GAAG,SAAS,CACjB,CAAC;IACF,MAAM,WAAW,GAAG,gBAAgB,CAClC,MAAM,CAAC,WAAW,EAClB,IAAI,GAAG,cAAc,CACtB,CAAC;IACF,MAAM,oCAAoC,GACxC,MAAM,CAAC,oCAAoC,KAAK,SAAS;QACvD,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,gBAAgB,CACd,MAAM,CAAC,oCAAoC,EAC3C,IAAI,GAAG,uCAAuC,CAC/C,CAAC;IAER,IACE,oCAAoC,KAAK,SAAS;QAClD,WAAW,CAAC,IAAI,KAAK,mBAAmB,EACxC,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;YACF,4FAA4F,CAC/F,CAAC;IACJ,CAAC;IAED,IACE,oCAAoC,KAAK,SAAS;QAClD,WAAW,CAAC,IAAI,KAAK,mBAAmB;QACxC,oCAAoC,CAAC,KAAK,CAAC,MAAM;YAC/C,WAAW,CAAC,UAAU,CAAC,KAAK,EAC9B,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,IAAI;YACF,6FAA6F,CAChG,CAAC;IACJ,CAAC;IAED,OAAO;QACL,MAAM;QACN,QAAQ,EAAE,iBAAiB,CACzB,MAAM,CAAC,QAAQ,EACf,IAAI,GAAG,WAAW,CACnB;QACD,WAAW;QACX,gBAAgB,EAAE,qBAAqB,CACrC,MAAM,CAAC,gBAAgB,EACvB,IAAI,GAAG,mBAAmB,CAC3B;QACD,GAAG,CAAC,oCAAoC,KAAK,SAAS;YACpD,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,oCAAoC,EAAE,CAAC;QAC7C,oBAAoB,EAClB,MAAM,CAAC,oBAAoB,KAAK,SAAS;YACvC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,yBAAyB,CACvB,MAAM,CAAC,oBAAoB,EAC3B,IAAI,GAAG,uBAAuB,CAC/B;QACP,oBAAoB,EAAE,eAAe,CACnC,MAAM,CAAC,oBAAoB,EAC3B,IAAI,GAAG,uBAAuB,CAC/B;QACD,YAAY,EACV,MAAM,CAAC,YAAY,KAAK,SAAS;YAC/B,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,iBAAiB,CACf,MAAM,CAAC,YAAY,EACnB,IAAI,GAAG,eAAe,CACvB;KACR,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAAc;IAEd,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,EAAE,cAAc,EAAE,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;IAEjF,IAAI,OAAO,CAAC,aAAa,KAAK,OAAO,EAAE,CAAC;QACtC,MAAM,IAAI,yBAAyB,CACjC,6CAA6C,CAC9C,CAAC;IACJ,CAAC;IAED,IACE,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC;QAC7B,OAAO,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAC1B,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,+CAA+C,CAChD,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAC9C,mBAAmB,CACjB,IAAI,EACJ,qBAAqB,GAAG,KAAK,GAAG,GAAG,CACpC,CACF,CAAC;IACF,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,MAAM,EAAE,CAAC;QACrC,MAAM,IAAI,yBAAyB,CACjC,8DAA8D,CAC/D,CAAC;IACJ,CAAC;IAED,OAAO;QACL,aAAa,EAAE,OAAO;QACtB,KAAK;KACN,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAC3B,MAAyB;IAEzB,kBAAkB,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC3C,sBAAsB,CACpB,yBAAyB,EACzB,MAAM,CAAC,UAAU,CAClB,CAAC;IACF,sBAAsB,CACpB,0BAA0B,EAC1B,MAAM,CAAC,WAAW,CACnB,CAAC;IACF,MAAM,KAAK,GACT,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC;IACzC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,2BAA2B,CACnC,+DAA+D,CAChE,CAAC;IACJ,CAAC;AACH,CAAC;AAED,SAAS,uBAAuB,CAC9B,QAAqC,EACrC,YAA+B;IAE/B,IAAI,QAAQ,CAAC,IAAI,KAAK,yBAAyB,EAAE,CAAC;QAChD,OAAO;YACL,IAAI,EAAE,yBAAyB;YAC/B,MAAM,EAAE,EAAE,GAAG,YAAY,EAAE;SAC5B,CAAC;IACJ,CAAC;IAED,IAAI,QAAQ,CAAC,IAAI,KAAK,2BAA2B,EAAE,CAAC;QAClD,OAAO;YACL,IAAI,EAAE,2BAA2B;YACjC,MAAM,EAAE;gBACN,KAAK,EAAE,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE;gBACnC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ;aACnC;SACF,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,QAAQ,CAAC,iBAAiB,CAAC,KAAK,CAAC;IACpD,MAAM,WAAW,GAAG,QAAQ,CAAC,kBAAkB,CAAC,KAAK,CAAC;IAEtD,IACE,YAAY,CAAC,UAAU,GAAG,UAAU,KAAK,CAAC;QAC1C,YAAY,CAAC,WAAW,GAAG,WAAW,KAAK,CAAC,EAC5C,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,oHAAoH,CACrH,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,wBAAwB;QAC9B,MAAM,EAAE;YACN,UAAU,EACR,YAAY,CAAC,UAAU,GAAG,UAAU;YACtC,WAAW,EACT,YAAY,CAAC,WAAW,GAAG,WAAW;SACzC;QACD,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB;QAC7C,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;QAC/C,GAAG,CAAC,QAAQ,CAAC,iBAAiB,KAAK,SAAS;YAC1C,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,iBAAiB,EACf,QAAQ,CAAC,iBAAiB;aAC7B,CAAC;KACP,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAA8B;IAE9B,gBAAgB,CAAC,KAAK,EAAE,oBAAoB,EAAE,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;IACrF,oBAAoB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACzC,IACE,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;QAChC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAChC,CAAC;QACD,MAAM,IAAI,yBAAyB,CACjC,oCAAoC,CACrC,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,uBAAuB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEvD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAC7B,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CACjD,CAAC;IACF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,2BAA2B,CACnC,6DAA6D,CAC9D,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAClB,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,UAAU;QAC1C,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,WAAW,CAAC;IAC9C,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,cAAc,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,2BAA2B,CACnC,uEAAuE,CACxE,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GACf,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,cAAc;QACtC,CAAC,CAAC;YACE,IAAI,EAAE,cAAc;YACpB,UAAU,EAAE,CAAC;SACd;QACH,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,mBAAmB;YAC7C,CAAC,CAAC;gBACE,IAAI,EAAE,mBAAmB;gBACzB,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,UAAU;aACxC;YACH,CAAC,CAAC;gBACE,IAAI,EAAE,sBAAsB;gBAC5B,QAAQ,EAAE,IAAI,CAAC,WAAW,CAAC,QAAQ;gBACnC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,iBAAiB,KAAK,SAAS;oBAClD,CAAC,CAAC,EAAE;oBACJ,CAAC,CAAC;wBACE,iBAAiB,EACf,IAAI,CAAC,WAAW,CAAC,iBAAiB;qBACrC,CAAC;gBACN,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,iBAAiB,KAAK,SAAS;oBAClD,CAAC,CAAC,EAAE;oBACJ,CAAC,CAAC;wBACE,iBAAiB,EACf,IAAI,CAAC,WAAW,CAAC,iBAAiB;qBACrC,CAAC;aACP,CAAC;IAEV,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,YAAY,EAAE,IAAI,CAAC,QAAQ;QAC3B,YAAY,EAAE,EAAE,GAAG,KAAK,CAAC,YAAY,EAAE;QACvC,oBAAoB,EAClB,mDAAmD;QACrD,WAAW;QACX,gBAAgB,EAAE,uBAAuB,CACvC,IAAI,CAAC,gBAAgB,EACrB,KAAK,CAAC,YAAY,CACnB;QACD,GAAG,CAAC,IAAI,CAAC,oCAAoC,KAAK,SAAS;YACzD,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC;gBACE,oCAAoC,EAClC,IAAI,CAAC,oCAAoC;aAC5C,CAAC;QACN,oBAAoB,EAClB,IAAI,CAAC,oBAAoB,IAAI,EAAE;QACjC,oBAAoB,EAAE;YACpB,KAAK,EAAE;gBACL,GAAG,IAAI,CAAC,oBAAoB,CAAC,KAAK;aACnC;YACD,QAAQ,EAAE,IAAI,CAAC,oBAAoB,CAAC,QAAQ;SAC7C;QACD,wBAAwB,EACtB,cAAc,GAAG,SAAS;QAC5B,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,EAAE;QACrC,8BAA8B,EAAE,KAAK;QACrC,uBAAuB,EACrB,+CAA+C;QACjD,0BAA0B,EACxB,oCAAoC;KACvC,CAAC;AACJ,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Capture modes: how a camera mode samples the sensor (for example full resolution, binning, or\n * multi-shot sensor shift) and what image size it produces.\n *\n * Sensor physics is never guessed from output resolution or a marketing mode name. A mode's parts\n * are independent: the sequence of frames, sampling within each frame, optional sensor shift between\n * frames, reconstruction stages, the processed image size and downstream dependencies. Changing\n * resolution never changes the physical sensor geometry.\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 EvidenceBackedFact,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport {\n InvalidScientificInputError,\n requirePositiveInteger\n} from \"../core/validation.js\";\nimport type {\n NativeImageRaster,\n RasterDimensions\n} from \"./sensor-geometry.js\";\nimport { requireInputObject, requireInputKeys, requireConfigRecord as requireRecord, requireConfigString as requireNonEmptyString, requireConfigPositiveSafeInteger as requirePositiveSafeIntegerValue } from \"../core/record-validation.js\";\n\n\nexport type CaptureModeSampleCombinationDomain =\n | \"charge-domain\"\n | \"pre-conversion-analog\"\n | \"post-conversion-digital\";\n\nexport type CaptureModeReconstructionStage =\n | \"remosaic\"\n | \"pixel-shift-combination\"\n | \"multi-frame-computational-combination\";\n\nexport type CaptureModeDependency =\n | \"color-sampling-model\"\n | \"mode-specific-readout-timing\"\n | \"radiometric-calibration\"\n | \"inter-frame-registration\";\n\nexport interface CaptureModeSensorOffsetNativeSamples {\n /**\n * Horizontal sensor shift, in units of the native sample pitch. This says\n * nothing about the physical photodiode pitch.\n */\n x: number;\n /**\n * Vertical sensor shift, in units of the native sample pitch. This says\n * nothing about the physical photodiode pitch.\n */\n y: number;\n}\n\nexport type SourcedCaptureModeFact<T> = EvidenceBackedFact<T>;\n\nexport interface SingleFrameCaptureAcquisition {\n kind: \"single-frame\";\n}\n\nexport interface FixedMultiFrameCaptureAcquisition {\n kind: \"fixed-multi-frame\";\n frameCount: SourcedCaptureModeFact<number>;\n}\n\nexport interface VariableMultiFrameCaptureAcquisition {\n kind: \"variable-multi-frame\";\n /**\n * Evidence that the mode uses a variable number of frames, even when the\n * exact frame-count limits are not published.\n */\n evidence: readonly EvidenceProvenance[];\n minimumFrameCount?: SourcedCaptureModeFact<number>;\n maximumFrameCount?: SourcedCaptureModeFact<number>;\n}\n\nexport type CaptureModeAcquisition =\n | SingleFrameCaptureAcquisition\n | FixedMultiFrameCaptureAcquisition\n | VariableMultiFrameCaptureAcquisition;\n\nexport interface NativeEffectiveCaptureSampling {\n kind: \"native-effective-raster\";\n}\n\nexport interface GroupedNativeCaptureSampling {\n kind: \"grouped-native-samples\";\n groupWidthSamples: SourcedCaptureModeFact<number>;\n groupHeightSamples: SourcedCaptureModeFact<number>;\n /**\n * Omitted means \"unknown, not stated\". Do not guess where samples are\n * combined from the word \"binning\" or from the ratio of image sizes.\n */\n combinationDomain?: SourcedCaptureModeFact<CaptureModeSampleCombinationDomain>;\n}\n\nexport interface DeclaredEffectiveCaptureSampling {\n kind: \"declared-effective-raster\";\n /**\n * Sample grid per frame, for a mode whose relationship to the native grid\n * cannot safely be written as simple integer grouping.\n */\n raster: SourcedCaptureModeFact<RasterDimensions>;\n}\n\nexport type CaptureModePerFrameSampling =\n | NativeEffectiveCaptureSampling\n | GroupedNativeCaptureSampling\n | DeclaredEffectiveCaptureSampling;\n\nexport interface CaptureModeDefinition {\n modeId: string;\n /**\n * Evidence that this mode exists and for how it captures overall. Facts in\n * the fields below keep their own evidence where needed.\n */\n evidence: readonly EvidenceProvenance[];\n acquisition: CaptureModeAcquisition;\n perFrameSampling: CaptureModePerFrameSampling;\n /**\n * Optional ordered sequence of sensor shifts, for modes with a fixed number\n * of frames. The whole sequence is one evidence-backed fact, because\n * published documentation usually describes it as a unit.\n */\n interFrameSensorOffsetsNativeSamples?: SourcedCaptureModeFact<\n readonly CaptureModeSensorOffsetNativeSamples[]\n >;\n /**\n * Ordered reconstruction or combination stages. These labels only name the\n * stages for later reconstruction models; they implement no algorithm.\n */\n reconstructionStages?: readonly SourcedCaptureModeFact<CaptureModeReconstructionStage>[];\n /**\n * Image size produced by the capture mode and reconstruction, before the\n * final output crop or resampling.\n *\n * It does not change the physical sensor geometry or field of view.\n */\n processedImageRaster: SourcedCaptureModeFact<RasterDimensions>;\n /**\n * Explicit downstream scientific/model dependencies for this mode.\n */\n dependencies?: readonly CaptureModeDependency[];\n}\n\nexport interface CaptureModeProfile {\n schemaVersion: \"0.1.0\";\n modes: readonly CaptureModeDefinition[];\n}\n\nexport interface ResolveCaptureModeInput {\n nativeRaster: NativeImageRaster;\n profile: CaptureModeProfile;\n modeId: string;\n}\n\nexport interface ResolvedCaptureMode {\n modeId: string;\n modeEvidence: readonly EvidenceProvenance[];\n nativeRaster: NativeImageRaster;\n nativeRasterSemantic:\n \"effective-image-sampling-grid-not-photosite-count\";\n acquisition:\n | {\n kind: \"single-frame\";\n frameCount: 1;\n }\n | {\n kind: \"fixed-multi-frame\";\n frameCount: SourcedCaptureModeFact<number>;\n }\n | {\n kind: \"variable-multi-frame\";\n evidence: readonly EvidenceProvenance[];\n minimumFrameCount?: SourcedCaptureModeFact<number>;\n maximumFrameCount?: SourcedCaptureModeFact<number>;\n };\n perFrameSampling:\n | {\n kind: \"native-effective-raster\";\n raster: NativeImageRaster;\n }\n | {\n kind: \"grouped-native-samples\";\n raster: RasterDimensions;\n groupWidthSamples: SourcedCaptureModeFact<number>;\n groupHeightSamples: SourcedCaptureModeFact<number>;\n combinationDomain?: SourcedCaptureModeFact<CaptureModeSampleCombinationDomain>;\n }\n | {\n kind: \"declared-effective-raster\";\n raster: SourcedCaptureModeFact<RasterDimensions>;\n };\n interFrameSensorOffsetsNativeSamples?: SourcedCaptureModeFact<\n readonly CaptureModeSensorOffsetNativeSamples[]\n >;\n reconstructionStages: readonly SourcedCaptureModeFact<CaptureModeReconstructionStage>[];\n processedImageRaster: SourcedCaptureModeFact<RasterDimensions>;\n processedImageMegapixels: number;\n dependencies: readonly CaptureModeDependency[];\n physicalSensorGeometryMutation: false;\n photositeCountInference:\n \"not-permitted-from-native-or-processed-raster\";\n finalOutputRasterOwnership:\n \"downstream-capture-output-geometry\";\n}\n\nconst COMBINATION_DOMAINS =\n new Set<CaptureModeSampleCombinationDomain>([\n \"charge-domain\",\n \"pre-conversion-analog\",\n \"post-conversion-digital\"\n ]);\n\nconst RECONSTRUCTION_STAGES =\n new Set<CaptureModeReconstructionStage>([\n \"remosaic\",\n \"pixel-shift-combination\",\n \"multi-frame-computational-combination\"\n ]);\n\nconst DEPENDENCIES = new Set<CaptureModeDependency>([\n \"color-sampling-model\",\n \"mode-specific-readout-timing\",\n \"radiometric-calibration\",\n \"inter-frame-registration\"\n]);\n\nfunction parseIntegerFact(\n value: unknown,\n path: string\n): SourcedCaptureModeFact<number> {\n const record = requireRecord(value, path, [\"value\", \"evidence\"]);\n return {\n value: requirePositiveSafeIntegerValue(\n record.value,\n path + \".value\"\n ),\n evidence: parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n )\n };\n}\n\nfunction parseRasterDimensions(\n value: unknown,\n path: string\n): RasterDimensions {\n const record = requireRecord(value, path, [\"pixelWidth\", \"pixelHeight\"]);\n const pixelWidth = requirePositiveSafeIntegerValue(\n record.pixelWidth,\n path + \".pixelWidth\"\n );\n const pixelHeight = requirePositiveSafeIntegerValue(\n record.pixelHeight,\n path + \".pixelHeight\"\n );\n const total = pixelWidth * pixelHeight;\n if (!Number.isSafeInteger(total)) {\n throw new InvalidConfigurationError(\n path + \" total sample count must be a safe integer.\"\n );\n }\n return { pixelWidth, pixelHeight };\n}\n\nfunction parseRasterFact(\n value: unknown,\n path: string\n): SourcedCaptureModeFact<RasterDimensions> {\n const record = requireRecord(value, path, [\"value\", \"evidence\"]);\n return {\n value: parseRasterDimensions(\n record.value,\n path + \".value\"\n ),\n evidence: parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n )\n };\n}\n\nfunction parseCombinationDomainFact(\n value: unknown,\n path: string\n): SourcedCaptureModeFact<CaptureModeSampleCombinationDomain> {\n const record = requireRecord(value, path, [\"value\", \"evidence\"]);\n if (\n typeof record.value !== \"string\" ||\n !COMBINATION_DOMAINS.has(\n record.value as CaptureModeSampleCombinationDomain\n )\n ) {\n throw new InvalidConfigurationError(\n path + \".value is invalid.\"\n );\n }\n return {\n value: record.value as CaptureModeSampleCombinationDomain,\n evidence: parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n )\n };\n}\n\nfunction parseAcquisition(\n value: unknown,\n path: string\n): CaptureModeAcquisition {\n const record = requireRecord(value, path, [\"kind\", \"frameCount\", \"minimumFrameCount\", \"maximumFrameCount\", \"evidence\"]);\n\n if (record.kind === \"single-frame\") {\n if (\n record.frameCount !== undefined ||\n record.minimumFrameCount !== undefined ||\n record.maximumFrameCount !== undefined ||\n record.evidence !== undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" single-frame acquisition must omit frame-count and evidence fields.\"\n );\n }\n return { kind: \"single-frame\" };\n }\n\n if (record.kind === \"fixed-multi-frame\") {\n if (\n record.minimumFrameCount !== undefined ||\n record.maximumFrameCount !== undefined ||\n record.evidence !== undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" fixed-multi-frame acquisition must use frameCount only.\"\n );\n }\n const frameCount = parseIntegerFact(\n record.frameCount,\n path + \".frameCount\"\n );\n if (frameCount.value < 2) {\n throw new InvalidConfigurationError(\n path +\n \".frameCount.value must be at least 2 for fixed-multi-frame acquisition.\"\n );\n }\n return {\n kind: \"fixed-multi-frame\",\n frameCount\n };\n }\n\n if (record.kind === \"variable-multi-frame\") {\n if (record.frameCount !== undefined) {\n throw new InvalidConfigurationError(\n path +\n \".frameCount must be omitted for variable-multi-frame acquisition.\"\n );\n }\n\n const evidence = parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n );\n const minimumFrameCount =\n record.minimumFrameCount === undefined\n ? undefined\n : parseIntegerFact(\n record.minimumFrameCount,\n path + \".minimumFrameCount\"\n );\n const maximumFrameCount =\n record.maximumFrameCount === undefined\n ? undefined\n : parseIntegerFact(\n record.maximumFrameCount,\n path + \".maximumFrameCount\"\n );\n\n if (\n minimumFrameCount !== undefined &&\n minimumFrameCount.value < 2\n ) {\n throw new InvalidConfigurationError(\n path +\n \".minimumFrameCount.value must be at least 2 for variable-multi-frame acquisition.\"\n );\n }\n if (\n maximumFrameCount !== undefined &&\n maximumFrameCount.value < 2\n ) {\n throw new InvalidConfigurationError(\n path +\n \".maximumFrameCount.value must be at least 2 for variable-multi-frame acquisition.\"\n );\n }\n if (\n minimumFrameCount !== undefined &&\n maximumFrameCount !== undefined &&\n minimumFrameCount.value > maximumFrameCount.value\n ) {\n throw new InvalidConfigurationError(\n path +\n \".minimumFrameCount.value must not exceed maximumFrameCount.value.\"\n );\n }\n\n return {\n kind: \"variable-multi-frame\",\n evidence,\n ...(minimumFrameCount === undefined\n ? {}\n : { minimumFrameCount }),\n ...(maximumFrameCount === undefined\n ? {}\n : { maximumFrameCount })\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parsePerFrameSampling(\n value: unknown,\n path: string\n): CaptureModePerFrameSampling {\n const record = requireRecord(value, path, [\"kind\", \"groupWidthSamples\", \"groupHeightSamples\", \"combinationDomain\", \"raster\"]);\n\n if (record.kind === \"native-effective-raster\") {\n if (\n record.groupWidthSamples !== undefined ||\n record.groupHeightSamples !== undefined ||\n record.combinationDomain !== undefined ||\n record.raster !== undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" native-effective-raster sampling must omit grouping/raster fields.\"\n );\n }\n return { kind: \"native-effective-raster\" };\n }\n\n if (record.kind === \"grouped-native-samples\") {\n if (record.raster !== undefined) {\n throw new InvalidConfigurationError(\n path +\n \".raster must be omitted for grouped-native-samples sampling.\"\n );\n }\n\n return {\n kind: \"grouped-native-samples\",\n groupWidthSamples: parseIntegerFact(\n record.groupWidthSamples,\n path + \".groupWidthSamples\"\n ),\n groupHeightSamples: parseIntegerFact(\n record.groupHeightSamples,\n path + \".groupHeightSamples\"\n ),\n ...(record.combinationDomain === undefined\n ? {}\n : {\n combinationDomain:\n parseCombinationDomainFact(\n record.combinationDomain,\n path + \".combinationDomain\"\n )\n })\n };\n }\n\n if (record.kind === \"declared-effective-raster\") {\n if (\n record.groupWidthSamples !== undefined ||\n record.groupHeightSamples !== undefined ||\n record.combinationDomain !== undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" declared-effective-raster sampling must omit grouping fields.\"\n );\n }\n\n return {\n kind: \"declared-effective-raster\",\n raster: parseRasterFact(\n record.raster,\n path + \".raster\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parseOffsetsFact(\n value: unknown,\n path: string\n): SourcedCaptureModeFact<\n readonly CaptureModeSensorOffsetNativeSamples[]\n> {\n const record = requireRecord(value, path, [\"value\", \"evidence\"]);\n if (\n !Array.isArray(record.value) ||\n record.value.length === 0\n ) {\n throw new InvalidConfigurationError(\n path + \".value must be a non-empty array.\"\n );\n }\n\n const offsets = record.value.map((entry, index) => {\n const item = requireRecord(\n entry,\n path + \".value[\" + index + \"]\", [\"x\", \"y\"]\n );\n if (\n typeof item.x !== \"number\" ||\n !Number.isFinite(item.x) ||\n typeof item.y !== \"number\" ||\n !Number.isFinite(item.y)\n ) {\n throw new InvalidConfigurationError(\n path +\n \".value[\" +\n index +\n \"] x/y must be finite numbers.\"\n );\n }\n return {\n x: item.x,\n y: item.y\n };\n });\n\n return {\n value: offsets,\n evidence: parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n )\n };\n}\n\nfunction parseReconstructionStages(\n value: unknown,\n path: string\n): readonly SourcedCaptureModeFact<CaptureModeReconstructionStage>[] {\n if (!Array.isArray(value)) {\n throw new InvalidConfigurationError(\n path + \" must be an array.\"\n );\n }\n\n const stages = value.map((entry, index) => {\n const record = requireRecord(\n entry,\n path + \"[\" + index + \"]\", [\"value\", \"evidence\"]\n );\n if (\n typeof record.value !== \"string\" ||\n !RECONSTRUCTION_STAGES.has(\n record.value as CaptureModeReconstructionStage\n )\n ) {\n throw new InvalidConfigurationError(\n path + \"[\" + index + \"].value is invalid.\"\n );\n }\n return {\n value: record.value as CaptureModeReconstructionStage,\n evidence: parseEvidenceList(\n record.evidence,\n path + \"[\" + index + \"].evidence\"\n )\n };\n });\n\n const values = stages.map((stage) => stage.value);\n if (new Set(values).size !== values.length) {\n throw new InvalidConfigurationError(\n path + \" must not contain duplicate stages.\"\n );\n }\n\n return stages;\n}\n\nfunction parseDependencies(\n value: unknown,\n path: string\n): readonly CaptureModeDependency[] {\n if (!Array.isArray(value)) {\n throw new InvalidConfigurationError(\n path + \" must be an array.\"\n );\n }\n\n const dependencies = value.map((entry, index) => {\n if (\n typeof entry !== \"string\" ||\n !DEPENDENCIES.has(entry as CaptureModeDependency)\n ) {\n throw new InvalidConfigurationError(\n path + \"[\" + index + \"] is invalid.\"\n );\n }\n return entry as CaptureModeDependency;\n });\n\n if (new Set(dependencies).size !== dependencies.length) {\n throw new InvalidConfigurationError(\n path + \" must not contain duplicates.\"\n );\n }\n\n return dependencies;\n}\n\nfunction parseModeDefinition(\n value: unknown,\n path: string\n): CaptureModeDefinition {\n const record = requireRecord(value, path, [\"modeId\", \"acquisition\", \"interFrameSensorOffsetsNativeSamples\", \"evidence\", \"perFrameSampling\", \"reconstructionStages\", \"processedImageRaster\", \"dependencies\"]);\n const modeId = requireNonEmptyString(\n record.modeId,\n path + \".modeId\"\n );\n const acquisition = parseAcquisition(\n record.acquisition,\n path + \".acquisition\"\n );\n const interFrameSensorOffsetsNativeSamples =\n record.interFrameSensorOffsetsNativeSamples === undefined\n ? undefined\n : parseOffsetsFact(\n record.interFrameSensorOffsetsNativeSamples,\n path + \".interFrameSensorOffsetsNativeSamples\"\n );\n\n if (\n interFrameSensorOffsetsNativeSamples !== undefined &&\n acquisition.kind !== \"fixed-multi-frame\"\n ) {\n throw new InvalidConfigurationError(\n path +\n \".interFrameSensorOffsetsNativeSamples is supported only for fixed-multi-frame acquisition.\"\n );\n }\n\n if (\n interFrameSensorOffsetsNativeSamples !== undefined &&\n acquisition.kind === \"fixed-multi-frame\" &&\n interFrameSensorOffsetsNativeSamples.value.length !==\n acquisition.frameCount.value\n ) {\n throw new InvalidConfigurationError(\n path +\n \".interFrameSensorOffsetsNativeSamples.value length must equal acquisition.frameCount.value.\"\n );\n }\n\n return {\n modeId,\n evidence: parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n ),\n acquisition,\n perFrameSampling: parsePerFrameSampling(\n record.perFrameSampling,\n path + \".perFrameSampling\"\n ),\n ...(interFrameSensorOffsetsNativeSamples === undefined\n ? {}\n : { interFrameSensorOffsetsNativeSamples }),\n reconstructionStages:\n record.reconstructionStages === undefined\n ? []\n : parseReconstructionStages(\n record.reconstructionStages,\n path + \".reconstructionStages\"\n ),\n processedImageRaster: parseRasterFact(\n record.processedImageRaster,\n path + \".processedImageRaster\"\n ),\n dependencies:\n record.dependencies === undefined\n ? []\n : parseDependencies(\n record.dependencies,\n path + \".dependencies\"\n )\n };\n}\n\n/**\n * Parses a capture-mode profile. Sensor physics is never guessed from output\n * resolution or marketing mode names.\n *\n * The profile's parts are independent of each other: the frame sequence,\n * sampling within each frame, optional sensor shift between frames,\n * reconstruction stages, the processed image size and downstream dependencies.\n */\nexport function parseCaptureModeProfile(\n value: unknown\n): CaptureModeProfile {\n const profile = requireRecord(value, \"captureModes\", [\"schemaVersion\", \"modes\"]);\n\n if (profile.schemaVersion !== \"0.1.0\") {\n throw new InvalidConfigurationError(\n 'captureModes.schemaVersion must be \"0.1.0\".'\n );\n }\n\n if (\n !Array.isArray(profile.modes) ||\n profile.modes.length === 0\n ) {\n throw new InvalidConfigurationError(\n \"captureModes.modes must be a non-empty array.\"\n );\n }\n\n const modes = profile.modes.map((mode, index) =>\n parseModeDefinition(\n mode,\n \"captureModes.modes[\" + index + \"]\"\n )\n );\n const ids = modes.map((mode) => mode.modeId);\n if (new Set(ids).size !== ids.length) {\n throw new InvalidConfigurationError(\n \"captureModes.modes must not contain duplicate modeId values.\"\n );\n }\n\n return {\n schemaVersion: \"0.1.0\",\n modes\n };\n}\n\nfunction validateNativeRaster(\n raster: NativeImageRaster\n): void {\n requireInputObject(raster, \"raster input\");\n requirePositiveInteger(\n \"nativeRaster.pixelWidth\",\n raster.pixelWidth\n );\n requirePositiveInteger(\n \"nativeRaster.pixelHeight\",\n raster.pixelHeight\n );\n const total =\n raster.pixelWidth * raster.pixelHeight;\n if (!Number.isSafeInteger(total)) {\n throw new InvalidScientificInputError(\n \"nativeRaster total image-sample count must be a safe integer.\"\n );\n }\n}\n\nfunction resolvePerFrameSampling(\n sampling: CaptureModePerFrameSampling,\n nativeRaster: NativeImageRaster\n): ResolvedCaptureMode[\"perFrameSampling\"] {\n if (sampling.kind === \"native-effective-raster\") {\n return {\n kind: \"native-effective-raster\",\n raster: { ...nativeRaster }\n };\n }\n\n if (sampling.kind === \"declared-effective-raster\") {\n return {\n kind: \"declared-effective-raster\",\n raster: {\n value: { ...sampling.raster.value },\n evidence: sampling.raster.evidence\n }\n };\n }\n\n const groupWidth = sampling.groupWidthSamples.value;\n const groupHeight = sampling.groupHeightSamples.value;\n\n if (\n nativeRaster.pixelWidth % groupWidth !== 0 ||\n nativeRaster.pixelHeight % groupHeight !== 0\n ) {\n throw new InvalidScientificInputError(\n \"Grouped native sampling requires nativeRaster dimensions to be exactly divisible by the declared grouping factors.\"\n );\n }\n\n return {\n kind: \"grouped-native-samples\",\n raster: {\n pixelWidth:\n nativeRaster.pixelWidth / groupWidth,\n pixelHeight:\n nativeRaster.pixelHeight / groupHeight\n },\n groupWidthSamples: sampling.groupWidthSamples,\n groupHeightSamples: sampling.groupHeightSamples,\n ...(sampling.combinationDomain === undefined\n ? {}\n : {\n combinationDomain:\n sampling.combinationDomain\n })\n };\n}\n\n/**\n * Resolves one capture mode against the selected sensor's native pixel grid.\n *\n * Resolution changes do not change the physical sensor geometry. The returned\n * processed image size is before output, and must not be used to work out crop\n * factor, field of view or the physical photosite count.\n */\nexport function resolveCaptureMode(\n input: ResolveCaptureModeInput\n): ResolvedCaptureMode {\n requireInputKeys(input, \"resolveCaptureMode\", [\"nativeRaster\", \"profile\", \"modeId\"]);\n validateNativeRaster(input.nativeRaster);\n if (\n typeof input.modeId !== \"string\" ||\n input.modeId.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n \"modeId must be a non-empty string.\"\n );\n }\n const profile = parseCaptureModeProfile(input.profile);\n\n const mode = profile.modes.find(\n (candidate) => candidate.modeId === input.modeId\n );\n if (mode === undefined) {\n throw new InvalidScientificInputError(\n \"modeId does not exist in the supplied capture-mode profile.\"\n );\n }\n\n const processedTotal =\n mode.processedImageRaster.value.pixelWidth *\n mode.processedImageRaster.value.pixelHeight;\n if (!Number.isSafeInteger(processedTotal)) {\n throw new InvalidScientificInputError(\n \"processedImageRaster total image-sample count must be a safe integer.\"\n );\n }\n\n const acquisition: ResolvedCaptureMode[\"acquisition\"] =\n mode.acquisition.kind === \"single-frame\"\n ? {\n kind: \"single-frame\",\n frameCount: 1\n }\n : mode.acquisition.kind === \"fixed-multi-frame\"\n ? {\n kind: \"fixed-multi-frame\",\n frameCount: mode.acquisition.frameCount\n }\n : {\n kind: \"variable-multi-frame\",\n evidence: mode.acquisition.evidence,\n ...(mode.acquisition.minimumFrameCount === undefined\n ? {}\n : {\n minimumFrameCount:\n mode.acquisition.minimumFrameCount\n }),\n ...(mode.acquisition.maximumFrameCount === undefined\n ? {}\n : {\n maximumFrameCount:\n mode.acquisition.maximumFrameCount\n })\n };\n\n return {\n modeId: mode.modeId,\n modeEvidence: mode.evidence,\n nativeRaster: { ...input.nativeRaster },\n nativeRasterSemantic:\n \"effective-image-sampling-grid-not-photosite-count\",\n acquisition,\n perFrameSampling: resolvePerFrameSampling(\n mode.perFrameSampling,\n input.nativeRaster\n ),\n ...(mode.interFrameSensorOffsetsNativeSamples === undefined\n ? {}\n : {\n interFrameSensorOffsetsNativeSamples:\n mode.interFrameSensorOffsetsNativeSamples\n }),\n reconstructionStages:\n mode.reconstructionStages ?? [],\n processedImageRaster: {\n value: {\n ...mode.processedImageRaster.value\n },\n evidence: mode.processedImageRaster.evidence\n },\n processedImageMegapixels:\n processedTotal / 1_000_000,\n dependencies: mode.dependencies ?? [],\n physicalSensorGeometryMutation: false,\n photositeCountInference:\n \"not-permitted-from-native-or-processed-raster\",\n finalOutputRasterOwnership:\n \"downstream-capture-output-geometry\"\n };\n}\n"]}
|
|
@@ -14,32 +14,7 @@
|
|
|
14
14
|
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
15
15
|
import { parseEvidenceList } from "../core/evidence-provenance.js";
|
|
16
16
|
import { InvalidScientificInputError } from "../core/validation.js";
|
|
17
|
-
import { requireInputKeys,
|
|
18
|
-
function requireRecord(value, path, allowedKeys) {
|
|
19
|
-
if (typeof value !== "object" ||
|
|
20
|
-
value === null ||
|
|
21
|
-
Array.isArray(value)) {
|
|
22
|
-
throw new InvalidConfigurationError(path + " must be an object.");
|
|
23
|
-
}
|
|
24
|
-
if (allowedKeys !== null)
|
|
25
|
-
rejectUnknownFields(value, allowedKeys, path);
|
|
26
|
-
return value;
|
|
27
|
-
}
|
|
28
|
-
function requireNonEmptyString(value, path) {
|
|
29
|
-
if (typeof value !== "string" ||
|
|
30
|
-
value.trim().length === 0) {
|
|
31
|
-
throw new InvalidConfigurationError(path + " must be a non-empty string.");
|
|
32
|
-
}
|
|
33
|
-
return value;
|
|
34
|
-
}
|
|
35
|
-
function requirePositiveSafeInteger(value, path) {
|
|
36
|
-
if (typeof value !== "number" ||
|
|
37
|
-
!Number.isSafeInteger(value) ||
|
|
38
|
-
value <= 0) {
|
|
39
|
-
throw new InvalidConfigurationError(path + " must be a positive safe integer.");
|
|
40
|
-
}
|
|
41
|
-
return value;
|
|
42
|
-
}
|
|
17
|
+
import { requireInputKeys, requireConfigRecord as requireRecord, requireConfigString as requireNonEmptyString, requireConfigPositiveSafeInteger as requirePositiveSafeInteger } from "../core/record-validation.js";
|
|
43
18
|
function parseChannelIds(value, path, options) {
|
|
44
19
|
if (!Array.isArray(value) || value.length === 0) {
|
|
45
20
|
throw new InvalidConfigurationError(path + " must be a non-empty array.");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"color-sampling.js","sourceRoot":"","sources":["../../src/sensor/color-sampling.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Colour-sampling layout: which colour channel each sensor site measures. Examples are monochrome,\n * a repeating mosaic such as a Bayer CFA (colour filter array), or a layered (stacked) sensor.\n *\n * Sites form an abstract grid with zero-based integer indices, origin at the top left, +X right and\n * +Y down. A site is not necessarily one physical photodiode. The mosaic's repeat pattern is fixed to\n * this grid, so later crops or orientation changes must keep the original native site indices.\n * Layered layouts are described structurally only, because real layered sensors may sample each layer\n * at different densities or offsets.\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 { requireInputKeys, rejectUnknownFields } from \"../core/record-validation.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport type NativeColorSamplingSiteCoordinateSystem =\n \"native-sensor-color-sampling-site-index\";\n\nexport type SensorColorSamplingProfileCoordinateSystem =\n | NativeColorSamplingSiteCoordinateSystem\n | \"native-sensor-layered-spatial-relationship-not-resolved\";\n\nexport interface NativeColorSamplingSiteIndex {\n /**\n * Zero-based integer site index from the native sensor's left edge.\n *\n * It indexes the abstract grid of colour-sampling sites. It is not a\n * NativeImageRaster coordinate and does not claim one site is one physical\n * photodiode.\n */\n x: number;\n /**\n * Zero-based integer site index from the native sensor's top edge.\n *\n * +Y points downward in native sensor-facing coordinates.\n */\n y: number;\n}\n\nexport interface MonochromeColorSamplingLayout {\n kind: \"monochrome\";\n /**\n * A name for the measured channel only. It is not a spectral response\n * curve, wavelength, colour primary or calibration.\n */\n channelId: string;\n}\n\nexport interface PeriodicMosaicColorSamplingLayout {\n kind: \"periodic-mosaic\";\n /**\n * Number of color-sampling sites in one horizontal repeat period.\n */\n repeatWidthSites: number;\n /**\n * Number of color-sampling sites in one vertical repeat period.\n */\n repeatHeightSites: number;\n /**\n * Channel IDs for one repeating tile, in row-major order.\n *\n * Length must equal repeatWidthSites × repeatHeightSites.\n */\n siteChannelIds: readonly string[];\n /**\n * The repeat tile starts at native sensor site (0, 0).\n *\n * Active-area crops and camera orientation must not silently reset this phase.\n */\n anchor: \"native-sensor-top-left-site\";\n}\n\nexport interface LayeredColorSamplingLayout {\n kind: \"layered\";\n /**\n * Names for the layers or channels only. They do not define spectral\n * response, depth order, sampling density or alignment between layers.\n */\n layerChannelIds: readonly string[];\n /**\n * Layered layouts are described structurally only for now, because real\n * layered sensors may sample each layer differently.\n */\n spatialSamplingRelationship: \"not-resolved\";\n}\n\nexport type SensorColorSamplingLayout =\n | MonochromeColorSamplingLayout\n | PeriodicMosaicColorSamplingLayout\n | LayeredColorSamplingLayout;\n\nexport interface SensorColorSamplingProfile {\n schemaVersion: \"0.1.0\";\n profileId: string;\n /**\n * Evidence for this exact layout.\n *\n * Descriptive sensor-architecture metadata is a separate type.\n */\n evidence: readonly EvidenceProvenance[];\n coordinateSystem: SensorColorSamplingProfileCoordinateSystem;\n layout: SensorColorSamplingLayout;\n}\n\nexport interface ResolveColorSamplingSiteInput {\n profile: SensorColorSamplingProfile;\n site: NativeColorSamplingSiteIndex;\n}\n\nexport interface ResolvedColorSamplingSite {\n profileId: string;\n coordinateSystem: NativeColorSamplingSiteCoordinateSystem;\n site: NativeColorSamplingSiteIndex;\n mapping:\n | {\n kind: \"monochrome\";\n channelId: string;\n }\n | {\n kind: \"periodic-mosaic\";\n channelId: string;\n repeatCoordinate: {\n x: number;\n y: number;\n };\n repeatWidthSites: number;\n repeatHeightSites: number;\n };\n /**\n * States what this profile does not establish, for downstream code.\n */\n spectralResponseEstablished: false;\n nativeImageRasterBindingEstablished: false;\n physicalPhotodiodeBindingEstablished: 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 + \" must be a non-empty string.\"\n );\n }\n return value;\n}\n\nfunction requirePositiveSafeInteger(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isSafeInteger(value) ||\n value <= 0\n ) {\n throw new InvalidConfigurationError(\n path + \" must be a positive safe integer.\"\n );\n }\n return value;\n}\n\nfunction parseChannelIds(\n value: unknown,\n path: string,\n options: {\n allowDuplicates: boolean;\n exactLength?: number;\n }\n): readonly string[] {\n if (!Array.isArray(value) || value.length === 0) {\n throw new InvalidConfigurationError(\n path + \" must be a non-empty array.\"\n );\n }\n\n if (\n options.exactLength !== undefined &&\n value.length !== options.exactLength\n ) {\n throw new InvalidConfigurationError(\n path +\n \" length must equal the declared repeating-tile site count.\"\n );\n }\n\n const ids = value.map((entry, index) =>\n requireNonEmptyString(\n entry,\n path + \"[\" + index + \"]\"\n )\n );\n\n if (\n !options.allowDuplicates &&\n new Set(ids).size !== ids.length\n ) {\n throw new InvalidConfigurationError(\n path + \" must not contain duplicate channel IDs.\"\n );\n }\n\n return ids;\n}\n\nfunction parseLayout(\n value: unknown,\n path: string\n): SensorColorSamplingLayout {\n const record = requireRecord(value, path, [\"kind\", \"repeatWidthSites\", \"repeatHeightSites\", \"siteChannelIds\", \"anchor\", \"layerChannelIds\", \"spatialSamplingRelationship\", \"channelId\"]);\n\n if (record.kind === \"monochrome\") {\n if (\n record.repeatWidthSites !== undefined ||\n record.repeatHeightSites !== undefined ||\n record.siteChannelIds !== undefined ||\n record.anchor !== undefined ||\n record.layerChannelIds !== undefined ||\n record.spatialSamplingRelationship !== undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" monochrome layout must omit mosaic/layered fields.\"\n );\n }\n\n return {\n kind: \"monochrome\",\n channelId: requireNonEmptyString(\n record.channelId,\n path + \".channelId\"\n )\n };\n }\n\n if (record.kind === \"periodic-mosaic\") {\n if (\n record.channelId !== undefined ||\n record.layerChannelIds !== undefined ||\n record.spatialSamplingRelationship !== undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" periodic-mosaic layout must omit monochrome/layered fields.\"\n );\n }\n\n const repeatWidthSites =\n requirePositiveSafeInteger(\n record.repeatWidthSites,\n path + \".repeatWidthSites\"\n );\n const repeatHeightSites =\n requirePositiveSafeInteger(\n record.repeatHeightSites,\n path + \".repeatHeightSites\"\n );\n const repeatSiteCount =\n repeatWidthSites * repeatHeightSites;\n\n if (!Number.isSafeInteger(repeatSiteCount)) {\n throw new InvalidConfigurationError(\n path +\n \" repeating-tile site count must be a safe integer.\"\n );\n }\n\n if (record.anchor !== \"native-sensor-top-left-site\") {\n throw new InvalidConfigurationError(\n path +\n '.anchor must be \"native-sensor-top-left-site\".'\n );\n }\n\n return {\n kind: \"periodic-mosaic\",\n repeatWidthSites,\n repeatHeightSites,\n siteChannelIds: parseChannelIds(\n record.siteChannelIds,\n path + \".siteChannelIds\",\n {\n allowDuplicates: true,\n exactLength: repeatSiteCount\n }\n ),\n anchor: \"native-sensor-top-left-site\"\n };\n }\n\n if (record.kind === \"layered\") {\n if (\n record.channelId !== undefined ||\n record.repeatWidthSites !== undefined ||\n record.repeatHeightSites !== undefined ||\n record.siteChannelIds !== undefined ||\n record.anchor !== undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" layered layout must omit monochrome/mosaic fields.\"\n );\n }\n\n if (\n record.spatialSamplingRelationship !==\n \"not-resolved\"\n ) {\n throw new InvalidConfigurationError(\n path +\n '.spatialSamplingRelationship must be \"not-resolved\" for the first layered contract.'\n );\n }\n\n return {\n kind: \"layered\",\n layerChannelIds: parseChannelIds(\n record.layerChannelIds,\n path + \".layerChannelIds\",\n {\n allowDuplicates: false\n }\n ),\n spatialSamplingRelationship: \"not-resolved\"\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\n/**\n * Parses an exact colour-sampling layout. It is not tied to NativeImageRaster,\n * physical photodiodes, spectral response or a manufacturer's layout name.\n *\n * Repeating mosaics use an abstract grid of native sensor sites with zero-based\n * integer indices, origin at the top left, +X right and +Y down. The repeat\n * pattern is fixed to this grid, so later crops or orientation changes must keep\n * the original native site indices.\n *\n * Layered layouts are described structurally only, because real layered sensors\n * may sample each layer at different densities or offsets. There is no per-site\n * lookup for layered layouts until that is modelled explicitly.\n */\nexport function parseSensorColorSamplingProfile(\n value: unknown\n): SensorColorSamplingProfile {\n const profile = requireRecord(\n value,\n \"sensorColorSampling\", [\"schemaVersion\", \"layout\", \"coordinateSystem\", \"profileId\", \"evidence\"]\n );\n\n if (profile.schemaVersion !== \"0.1.0\") {\n throw new InvalidConfigurationError(\n 'sensorColorSampling.schemaVersion must be \"0.1.0\".'\n );\n }\n\n const layout = parseLayout(\n profile.layout,\n \"sensorColorSampling.layout\"\n );\n\n const expectedCoordinateSystem =\n layout.kind === \"layered\"\n ? \"native-sensor-layered-spatial-relationship-not-resolved\"\n : \"native-sensor-color-sampling-site-index\";\n\n if (\n profile.coordinateSystem !==\n expectedCoordinateSystem\n ) {\n throw new InvalidConfigurationError(\n \"sensorColorSampling.coordinateSystem does not match the declared layout kind.\"\n );\n }\n\n return {\n schemaVersion: \"0.1.0\",\n profileId: requireNonEmptyString(\n profile.profileId,\n \"sensorColorSampling.profileId\"\n ),\n evidence: parseEvidenceList(\n profile.evidence,\n \"sensorColorSampling.evidence\"\n ),\n coordinateSystem: expectedCoordinateSystem,\n layout\n };\n}\n\nfunction requireNativeSiteIndex(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isSafeInteger(value) ||\n value < 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be a non-negative safe integer native color-sampling-site index.\"\n );\n }\n return value;\n}\n\n/**\n * Returns which channel one native site measures, for monochrome or repeating\n * mosaic layouts.\n *\n * It deliberately takes no NativeImageRaster. A separate binding\n * (capture-color-sampling-binding) must say how a capture mode's sample grid\n * maps onto this site grid before RAW or CFA sampling can be simulated.\n *\n * Layered layouts are rejected, because how their layers line up spatially is\n * not defined yet.\n */\nexport function resolveColorSamplingSite(\n input: ResolveColorSamplingSiteInput\n): ResolvedColorSamplingSite {\n requireInputKeys(input, \"resolveColorSamplingSite\", [\"profile\", \"site\"]);\n const profile =\n parseSensorColorSamplingProfile(input.profile);\n\n if (\n typeof input.site !== \"object\" ||\n input.site === null\n ) {\n throw new InvalidConfigurationError(\n \"site must be an object.\"\n );\n }\n\n const x = requireNativeSiteIndex(\n input.site.x,\n \"site.x\"\n );\n const y = requireNativeSiteIndex(\n input.site.y,\n \"site.y\"\n );\n\n if (profile.layout.kind === \"layered\") {\n throw new InvalidScientificInputError(\n \"Layered color sampling is structural-only in schema 0.1.0; per-site resolution requires an explicit per-layer spatial sampling relationship.\"\n );\n }\n\n if (\n profile.coordinateSystem !==\n \"native-sensor-color-sampling-site-index\"\n ) {\n throw new InvalidScientificInputError(\n \"Resolvable color sampling requires the native sensor color-sampling-site coordinate system.\"\n );\n }\n\n if (profile.layout.kind === \"monochrome\") {\n return {\n profileId: profile.profileId,\n coordinateSystem: profile.coordinateSystem,\n site: { x, y },\n mapping: {\n kind: \"monochrome\",\n channelId: profile.layout.channelId\n },\n spectralResponseEstablished: false,\n nativeImageRasterBindingEstablished: false,\n physicalPhotodiodeBindingEstablished: false\n };\n }\n\n const repeatX =\n x % profile.layout.repeatWidthSites;\n const repeatY =\n y % profile.layout.repeatHeightSites;\n const index =\n repeatY * profile.layout.repeatWidthSites +\n repeatX;\n const channelId =\n profile.layout.siteChannelIds[index];\n\n if (channelId === undefined) {\n throw new InvalidScientificInputError(\n \"Periodic color-sampling tile resolution failed.\"\n );\n }\n\n return {\n profileId: profile.profileId,\n coordinateSystem: profile.coordinateSystem,\n site: { x, y },\n mapping: {\n kind: \"periodic-mosaic\",\n channelId,\n repeatCoordinate: {\n x: repeatX,\n y: repeatY\n },\n repeatWidthSites:\n profile.layout.repeatWidthSites,\n repeatHeightSites:\n profile.layout.repeatHeightSites\n },\n spectralResponseEstablished: false,\n nativeImageRasterBindingEstablished: false,\n physicalPhotodiodeBindingEstablished: false\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"color-sampling.js","sourceRoot":"","sources":["../../src/sensor/color-sampling.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Colour-sampling layout: which colour channel each sensor site measures. Examples are monochrome,\n * a repeating mosaic such as a Bayer CFA (colour filter array), or a layered (stacked) sensor.\n *\n * Sites form an abstract grid with zero-based integer indices, origin at the top left, +X right and\n * +Y down. A site is not necessarily one physical photodiode. The mosaic's repeat pattern is fixed to\n * this grid, so later crops or orientation changes must keep the original native site indices.\n * Layered layouts are described structurally only, because real layered sensors may sample each layer\n * at different densities or offsets.\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 { requireInputKeys, requireConfigRecord as requireRecord, requireConfigString as requireNonEmptyString, requireConfigPositiveSafeInteger as requirePositiveSafeInteger } from \"../core/record-validation.js\";\n\n\nexport type NativeColorSamplingSiteCoordinateSystem =\n \"native-sensor-color-sampling-site-index\";\n\nexport type SensorColorSamplingProfileCoordinateSystem =\n | NativeColorSamplingSiteCoordinateSystem\n | \"native-sensor-layered-spatial-relationship-not-resolved\";\n\nexport interface NativeColorSamplingSiteIndex {\n /**\n * Zero-based integer site index from the native sensor's left edge.\n *\n * It indexes the abstract grid of colour-sampling sites. It is not a\n * NativeImageRaster coordinate and does not claim one site is one physical\n * photodiode.\n */\n x: number;\n /**\n * Zero-based integer site index from the native sensor's top edge.\n *\n * +Y points downward in native sensor-facing coordinates.\n */\n y: number;\n}\n\nexport interface MonochromeColorSamplingLayout {\n kind: \"monochrome\";\n /**\n * A name for the measured channel only. It is not a spectral response\n * curve, wavelength, colour primary or calibration.\n */\n channelId: string;\n}\n\nexport interface PeriodicMosaicColorSamplingLayout {\n kind: \"periodic-mosaic\";\n /**\n * Number of color-sampling sites in one horizontal repeat period.\n */\n repeatWidthSites: number;\n /**\n * Number of color-sampling sites in one vertical repeat period.\n */\n repeatHeightSites: number;\n /**\n * Channel IDs for one repeating tile, in row-major order.\n *\n * Length must equal repeatWidthSites × repeatHeightSites.\n */\n siteChannelIds: readonly string[];\n /**\n * The repeat tile starts at native sensor site (0, 0).\n *\n * Active-area crops and camera orientation must not silently reset this phase.\n */\n anchor: \"native-sensor-top-left-site\";\n}\n\nexport interface LayeredColorSamplingLayout {\n kind: \"layered\";\n /**\n * Names for the layers or channels only. They do not define spectral\n * response, depth order, sampling density or alignment between layers.\n */\n layerChannelIds: readonly string[];\n /**\n * Layered layouts are described structurally only for now, because real\n * layered sensors may sample each layer differently.\n */\n spatialSamplingRelationship: \"not-resolved\";\n}\n\nexport type SensorColorSamplingLayout =\n | MonochromeColorSamplingLayout\n | PeriodicMosaicColorSamplingLayout\n | LayeredColorSamplingLayout;\n\nexport interface SensorColorSamplingProfile {\n schemaVersion: \"0.1.0\";\n profileId: string;\n /**\n * Evidence for this exact layout.\n *\n * Descriptive sensor-architecture metadata is a separate type.\n */\n evidence: readonly EvidenceProvenance[];\n coordinateSystem: SensorColorSamplingProfileCoordinateSystem;\n layout: SensorColorSamplingLayout;\n}\n\nexport interface ResolveColorSamplingSiteInput {\n profile: SensorColorSamplingProfile;\n site: NativeColorSamplingSiteIndex;\n}\n\nexport interface ResolvedColorSamplingSite {\n profileId: string;\n coordinateSystem: NativeColorSamplingSiteCoordinateSystem;\n site: NativeColorSamplingSiteIndex;\n mapping:\n | {\n kind: \"monochrome\";\n channelId: string;\n }\n | {\n kind: \"periodic-mosaic\";\n channelId: string;\n repeatCoordinate: {\n x: number;\n y: number;\n };\n repeatWidthSites: number;\n repeatHeightSites: number;\n };\n /**\n * States what this profile does not establish, for downstream code.\n */\n spectralResponseEstablished: false;\n nativeImageRasterBindingEstablished: false;\n physicalPhotodiodeBindingEstablished: false;\n}\n\nfunction parseChannelIds(\n value: unknown,\n path: string,\n options: {\n allowDuplicates: boolean;\n exactLength?: number;\n }\n): readonly string[] {\n if (!Array.isArray(value) || value.length === 0) {\n throw new InvalidConfigurationError(\n path + \" must be a non-empty array.\"\n );\n }\n\n if (\n options.exactLength !== undefined &&\n value.length !== options.exactLength\n ) {\n throw new InvalidConfigurationError(\n path +\n \" length must equal the declared repeating-tile site count.\"\n );\n }\n\n const ids = value.map((entry, index) =>\n requireNonEmptyString(\n entry,\n path + \"[\" + index + \"]\"\n )\n );\n\n if (\n !options.allowDuplicates &&\n new Set(ids).size !== ids.length\n ) {\n throw new InvalidConfigurationError(\n path + \" must not contain duplicate channel IDs.\"\n );\n }\n\n return ids;\n}\n\nfunction parseLayout(\n value: unknown,\n path: string\n): SensorColorSamplingLayout {\n const record = requireRecord(value, path, [\"kind\", \"repeatWidthSites\", \"repeatHeightSites\", \"siteChannelIds\", \"anchor\", \"layerChannelIds\", \"spatialSamplingRelationship\", \"channelId\"]);\n\n if (record.kind === \"monochrome\") {\n if (\n record.repeatWidthSites !== undefined ||\n record.repeatHeightSites !== undefined ||\n record.siteChannelIds !== undefined ||\n record.anchor !== undefined ||\n record.layerChannelIds !== undefined ||\n record.spatialSamplingRelationship !== undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" monochrome layout must omit mosaic/layered fields.\"\n );\n }\n\n return {\n kind: \"monochrome\",\n channelId: requireNonEmptyString(\n record.channelId,\n path + \".channelId\"\n )\n };\n }\n\n if (record.kind === \"periodic-mosaic\") {\n if (\n record.channelId !== undefined ||\n record.layerChannelIds !== undefined ||\n record.spatialSamplingRelationship !== undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" periodic-mosaic layout must omit monochrome/layered fields.\"\n );\n }\n\n const repeatWidthSites =\n requirePositiveSafeInteger(\n record.repeatWidthSites,\n path + \".repeatWidthSites\"\n );\n const repeatHeightSites =\n requirePositiveSafeInteger(\n record.repeatHeightSites,\n path + \".repeatHeightSites\"\n );\n const repeatSiteCount =\n repeatWidthSites * repeatHeightSites;\n\n if (!Number.isSafeInteger(repeatSiteCount)) {\n throw new InvalidConfigurationError(\n path +\n \" repeating-tile site count must be a safe integer.\"\n );\n }\n\n if (record.anchor !== \"native-sensor-top-left-site\") {\n throw new InvalidConfigurationError(\n path +\n '.anchor must be \"native-sensor-top-left-site\".'\n );\n }\n\n return {\n kind: \"periodic-mosaic\",\n repeatWidthSites,\n repeatHeightSites,\n siteChannelIds: parseChannelIds(\n record.siteChannelIds,\n path + \".siteChannelIds\",\n {\n allowDuplicates: true,\n exactLength: repeatSiteCount\n }\n ),\n anchor: \"native-sensor-top-left-site\"\n };\n }\n\n if (record.kind === \"layered\") {\n if (\n record.channelId !== undefined ||\n record.repeatWidthSites !== undefined ||\n record.repeatHeightSites !== undefined ||\n record.siteChannelIds !== undefined ||\n record.anchor !== undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" layered layout must omit monochrome/mosaic fields.\"\n );\n }\n\n if (\n record.spatialSamplingRelationship !==\n \"not-resolved\"\n ) {\n throw new InvalidConfigurationError(\n path +\n '.spatialSamplingRelationship must be \"not-resolved\" for the first layered contract.'\n );\n }\n\n return {\n kind: \"layered\",\n layerChannelIds: parseChannelIds(\n record.layerChannelIds,\n path + \".layerChannelIds\",\n {\n allowDuplicates: false\n }\n ),\n spatialSamplingRelationship: \"not-resolved\"\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\n/**\n * Parses an exact colour-sampling layout. It is not tied to NativeImageRaster,\n * physical photodiodes, spectral response or a manufacturer's layout name.\n *\n * Repeating mosaics use an abstract grid of native sensor sites with zero-based\n * integer indices, origin at the top left, +X right and +Y down. The repeat\n * pattern is fixed to this grid, so later crops or orientation changes must keep\n * the original native site indices.\n *\n * Layered layouts are described structurally only, because real layered sensors\n * may sample each layer at different densities or offsets. There is no per-site\n * lookup for layered layouts until that is modelled explicitly.\n */\nexport function parseSensorColorSamplingProfile(\n value: unknown\n): SensorColorSamplingProfile {\n const profile = requireRecord(\n value,\n \"sensorColorSampling\", [\"schemaVersion\", \"layout\", \"coordinateSystem\", \"profileId\", \"evidence\"]\n );\n\n if (profile.schemaVersion !== \"0.1.0\") {\n throw new InvalidConfigurationError(\n 'sensorColorSampling.schemaVersion must be \"0.1.0\".'\n );\n }\n\n const layout = parseLayout(\n profile.layout,\n \"sensorColorSampling.layout\"\n );\n\n const expectedCoordinateSystem =\n layout.kind === \"layered\"\n ? \"native-sensor-layered-spatial-relationship-not-resolved\"\n : \"native-sensor-color-sampling-site-index\";\n\n if (\n profile.coordinateSystem !==\n expectedCoordinateSystem\n ) {\n throw new InvalidConfigurationError(\n \"sensorColorSampling.coordinateSystem does not match the declared layout kind.\"\n );\n }\n\n return {\n schemaVersion: \"0.1.0\",\n profileId: requireNonEmptyString(\n profile.profileId,\n \"sensorColorSampling.profileId\"\n ),\n evidence: parseEvidenceList(\n profile.evidence,\n \"sensorColorSampling.evidence\"\n ),\n coordinateSystem: expectedCoordinateSystem,\n layout\n };\n}\n\nfunction requireNativeSiteIndex(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isSafeInteger(value) ||\n value < 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be a non-negative safe integer native color-sampling-site index.\"\n );\n }\n return value;\n}\n\n/**\n * Returns which channel one native site measures, for monochrome or repeating\n * mosaic layouts.\n *\n * It deliberately takes no NativeImageRaster. A separate binding\n * (capture-color-sampling-binding) must say how a capture mode's sample grid\n * maps onto this site grid before RAW or CFA sampling can be simulated.\n *\n * Layered layouts are rejected, because how their layers line up spatially is\n * not defined yet.\n */\nexport function resolveColorSamplingSite(\n input: ResolveColorSamplingSiteInput\n): ResolvedColorSamplingSite {\n requireInputKeys(input, \"resolveColorSamplingSite\", [\"profile\", \"site\"]);\n const profile =\n parseSensorColorSamplingProfile(input.profile);\n\n if (\n typeof input.site !== \"object\" ||\n input.site === null\n ) {\n throw new InvalidConfigurationError(\n \"site must be an object.\"\n );\n }\n\n const x = requireNativeSiteIndex(\n input.site.x,\n \"site.x\"\n );\n const y = requireNativeSiteIndex(\n input.site.y,\n \"site.y\"\n );\n\n if (profile.layout.kind === \"layered\") {\n throw new InvalidScientificInputError(\n \"Layered color sampling is structural-only in schema 0.1.0; per-site resolution requires an explicit per-layer spatial sampling relationship.\"\n );\n }\n\n if (\n profile.coordinateSystem !==\n \"native-sensor-color-sampling-site-index\"\n ) {\n throw new InvalidScientificInputError(\n \"Resolvable color sampling requires the native sensor color-sampling-site coordinate system.\"\n );\n }\n\n if (profile.layout.kind === \"monochrome\") {\n return {\n profileId: profile.profileId,\n coordinateSystem: profile.coordinateSystem,\n site: { x, y },\n mapping: {\n kind: \"monochrome\",\n channelId: profile.layout.channelId\n },\n spectralResponseEstablished: false,\n nativeImageRasterBindingEstablished: false,\n physicalPhotodiodeBindingEstablished: false\n };\n }\n\n const repeatX =\n x % profile.layout.repeatWidthSites;\n const repeatY =\n y % profile.layout.repeatHeightSites;\n const index =\n repeatY * profile.layout.repeatWidthSites +\n repeatX;\n const channelId =\n profile.layout.siteChannelIds[index];\n\n if (channelId === undefined) {\n throw new InvalidScientificInputError(\n \"Periodic color-sampling tile resolution failed.\"\n );\n }\n\n return {\n profileId: profile.profileId,\n coordinateSystem: profile.coordinateSystem,\n site: { x, y },\n mapping: {\n kind: \"periodic-mosaic\",\n channelId,\n repeatCoordinate: {\n x: repeatX,\n y: repeatY\n },\n repeatWidthSites:\n profile.layout.repeatWidthSites,\n repeatHeightSites:\n profile.layout.repeatHeightSites\n },\n spectralResponseEstablished: false,\n nativeImageRasterBindingEstablished: false,\n physicalPhotodiodeBindingEstablished: false\n };\n}\n"]}
|
|
@@ -14,40 +14,8 @@ import { approximationResult, calculatedResult } from "../core/calculation-resul
|
|
|
14
14
|
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
15
15
|
import { parseEvidenceList } from "../core/evidence-provenance.js";
|
|
16
16
|
import { InvalidScientificInputError } from "../core/validation.js";
|
|
17
|
-
import { requireInputObject, requireInputKeys,
|
|
18
|
-
|
|
19
|
-
if (typeof value !== "object" ||
|
|
20
|
-
value === null ||
|
|
21
|
-
Array.isArray(value)) {
|
|
22
|
-
throw new InvalidConfigurationError(path + " must be an object.");
|
|
23
|
-
}
|
|
24
|
-
if (allowedKeys !== null)
|
|
25
|
-
rejectUnknownFields(value, allowedKeys, path);
|
|
26
|
-
return value;
|
|
27
|
-
}
|
|
28
|
-
function requireNonEmptyString(value, path) {
|
|
29
|
-
if (typeof value !== "string" ||
|
|
30
|
-
value.trim().length === 0) {
|
|
31
|
-
throw new InvalidConfigurationError(path + " must be a non-empty string.");
|
|
32
|
-
}
|
|
33
|
-
return value;
|
|
34
|
-
}
|
|
35
|
-
function requireNonNegativeSafeInteger(value, path) {
|
|
36
|
-
if (typeof value !== "number" ||
|
|
37
|
-
!Number.isSafeInteger(value) ||
|
|
38
|
-
value < 0) {
|
|
39
|
-
throw new InvalidConfigurationError(path +
|
|
40
|
-
" must be a non-negative safe integer.");
|
|
41
|
-
}
|
|
42
|
-
return value;
|
|
43
|
-
}
|
|
44
|
-
function requireFinite(value, path) {
|
|
45
|
-
if (typeof value !== "number" ||
|
|
46
|
-
!Number.isFinite(value)) {
|
|
47
|
-
throw new InvalidConfigurationError(path + " must be finite.");
|
|
48
|
-
}
|
|
49
|
-
return value;
|
|
50
|
-
}
|
|
17
|
+
import { requireInputObject, requireInputKeys, requireConfigRecord as requireRecord, requireConfigFinite as requireFinite, requireConfigString as requireNonEmptyString, requireInputNonNegativeFinite as requireNonNegativeFiniteRate, requireConfigNonNegativeSafeInteger as requireNonNegativeSafeInteger } from "../core/record-validation.js";
|
|
18
|
+
import { sameSite } from "../core/shared-helpers.js";
|
|
51
19
|
/**
|
|
52
20
|
* Validates untrusted input and returns it as a normalized, typed value. Unknown enum values,
|
|
53
21
|
* missing required fields and inconsistent scientific data are rejected.
|
|
@@ -122,9 +90,6 @@ export function parseSensorRateTemporalStationarityProfile(value) {
|
|
|
122
90
|
: { limitation })
|
|
123
91
|
};
|
|
124
92
|
}
|
|
125
|
-
function sameSite(a, b) {
|
|
126
|
-
return a.x === b.x && a.y === b.y;
|
|
127
|
-
}
|
|
128
93
|
function requireRateSite(rate) {
|
|
129
94
|
requireInputObject(rate, "rate input");
|
|
130
95
|
if (rate.site === undefined ||
|
|
@@ -243,15 +208,6 @@ function validateStationarity(stationarity, binding, rateDomain, site) {
|
|
|
243
208
|
throw new InvalidScientificInputError("stationarityProfile must apply to the exact bound rate domain, site, binding, and local exposure window.");
|
|
244
209
|
}
|
|
245
210
|
}
|
|
246
|
-
function requireNonNegativeFiniteRate(value, path) {
|
|
247
|
-
if (typeof value !== "number" ||
|
|
248
|
-
!Number.isFinite(value) ||
|
|
249
|
-
value < 0) {
|
|
250
|
-
throw new InvalidScientificInputError(path +
|
|
251
|
-
" must be finite and greater than or equal to zero.");
|
|
252
|
-
}
|
|
253
|
-
return value;
|
|
254
|
-
}
|
|
255
211
|
/**
|
|
256
212
|
* Integrates one response rate, declared constant, over its exact local
|
|
257
213
|
* exposure window.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constant-rate-temporal-integration.js","sourceRoot":"","sources":["../../src/sensor/constant-rate-temporal-integration.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Constant-rate exposure: multiplies a sensor response rate by the exposure time.\n *\n * This shortcut (rate × duration) is allowed only when the caller declares the rate constant\n * (stationary) for this exact site and local shutter interval. With EQE (external quantum\n * efficiency) the result is expected photons and electrons. With A/W responsivity it is charge in\n * coulombs, with no assumed conversion to electrons. Counts stay fractional expected values, before\n * any random sampling. Time-varying signals are not handled here.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport {\n approximationResult,\n calculatedResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\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 type {\n SensorEqeElectronRate\n} from \"./eqe-electron-rate.js\";\nimport type {\n SensorInstantaneousRateResult,\n SensorRateLocalExposureBinding\n} from \"./local-exposure-binding.js\";\nimport type {\n SensorResponsivityPhotocurrent\n} from \"./responsivity-photocurrent.js\";\nimport { requireInputObject, requireInputKeys, rejectUnknownFields } from \"../core/record-validation.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport type SensorRateStationarityStatus =\n | \"established\"\n | \"approximation\";\n\nexport interface SensorRateTemporalStationarityProfile {\n schemaVersion: \"0.1.0\";\n profileId: string;\n rateDomain:\n SensorRateLocalExposureBinding[\"rateDomain\"];\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n localExposureWindow: {\n timeReference:\n \"first-opening-boundary-phase\";\n startOffsetSecondsFromOpeningReference:\n number;\n endOffsetSecondsFromOpeningReference:\n number;\n };\n stationarityMeaning:\n \"reported-rate-constant-through-bound-local-exposure\";\n status:\n SensorRateStationarityStatus;\n evidence: readonly EvidenceProvenance[];\n limitation?: string;\n}\n\nexport interface IntegrateStationarySensorRateInput {\n rate:\n SensorInstantaneousRateResult;\n exposureBinding:\n SensorRateLocalExposureBinding;\n stationarityProfile:\n SensorRateTemporalStationarityProfile;\n}\n\ninterface SensorTemporalIntegrationCommon {\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n stationarityProfileId: string;\n stationarityStatus:\n SensorRateStationarityStatus;\n timeReference:\n \"first-opening-boundary-phase\";\n startOffsetSecondsFromOpeningReference:\n number;\n endOffsetSecondsFromOpeningReference:\n number;\n localExposureDurationSeconds:\n number;\n integrationMethod:\n \"constant-rate-times-local-exposure-duration\";\n timeStationarityEstablished: true;\n temporalIntegrationApplied: true;\n exposureDurationApplied: true;\n timeVaryingSignalIntegrated: false;\n multiFrameSequenceIntegrated: false;\n darkChargeIncluded: false;\n otherChargeIncluded: false;\n accumulatedSignalCompleteness:\n \"photo-signal-only\";\n physicalFullWellAssessmentAuthorized:\n false;\n cameraSaturationAssessmentAuthorized:\n false;\n saturationAssessed: false;\n shotNoiseApplied: false;\n readNoiseApplied: false;\n adcQuantizationApplied: false;\n rawCodeValueProduced: false;\n componentEvidence: {\n stationarity:\n readonly EvidenceProvenance[];\n exposureBinding:\n SensorRateLocalExposureBinding[\"componentEvidence\"];\n };\n}\n\nexport interface SensorEqeExposureIntegration\n extends SensorTemporalIntegrationCommon {\n kind: \"eqe-expected-counts\";\n incidentPhotonRatePerSecond: number;\n expectedGeneratedElectronRatePerSecond:\n number;\n expectedIncidentPhotonCount: number;\n expectedGeneratedElectronCount:\n number;\n countsAreExpectationValues: true;\n integerPhotonCountSampled: false;\n integerElectronCountSampled: false;\n chargeCalculated: false;\n currentCalculated: false;\n}\n\nexport interface SensorResponsivityExposureIntegration\n extends SensorTemporalIntegrationCommon {\n kind:\n \"responsivity-photocurrent-charge\";\n photocurrentMagnitudeAmperes: number;\n photochargeMagnitudeCoulombs:\n number;\n currentSignConvention:\n \"magnitude-only-no-circuit-polarity\";\n chargeSignConvention:\n \"magnitude-only-no-carrier-or-circuit-polarity\";\n carrierCountInferred: false;\n electronCountCalculated: false;\n photonCountCalculated: false;\n detectorBandwidthModeled: false;\n}\n\nexport type SensorStationaryRateExposureIntegration =\n | SensorEqeExposureIntegration\n | SensorResponsivityExposureIntegration;\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 + \" must be a non-empty string.\"\n );\n }\n return value;\n}\n\nfunction requireNonNegativeSafeInteger(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isSafeInteger(value) ||\n value < 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must be a non-negative safe integer.\"\n );\n }\n return value;\n}\n\nfunction requireFinite(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be finite.\"\n );\n }\n return value;\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 declares that a rate is constant (stationary) for an exact site and local shutter\n * interval, which is what allows rate × duration.\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 parseSensorRateTemporalStationarityProfile(\n value: unknown\n): SensorRateTemporalStationarityProfile {\n const record = requireRecord(\n value,\n \"sensorRateTemporalStationarity\", [\"schemaVersion\", \"rateDomain\", \"site\", \"localExposureWindow\", \"stationarityMeaning\", \"status\", \"limitation\", \"profileId\", \"colorSamplingProfileId\", \"channelId\", \"bindingId\", \"evidence\"]\n );\n\n if (\n record.schemaVersion !== \"0.1.0\"\n ) {\n throw new InvalidConfigurationError(\n 'sensorRateTemporalStationarity.schemaVersion must be \"0.1.0\".'\n );\n }\n\n if (\n record.rateDomain !==\n \"eqe-electron-rate\" &&\n record.rateDomain !==\n \"responsivity-photocurrent\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorRateTemporalStationarity.rateDomain is invalid.\"\n );\n }\n\n const site = requireRecord(\n record.site,\n \"sensorRateTemporalStationarity.site\", [\"x\", \"y\"]\n );\n const localExposureWindow =\n requireRecord(\n record.localExposureWindow,\n \"sensorRateTemporalStationarity.localExposureWindow\", [\"timeReference\", \"startOffsetSecondsFromOpeningReference\", \"endOffsetSecondsFromOpeningReference\"]\n );\n\n if (\n localExposureWindow.timeReference !==\n \"first-opening-boundary-phase\"\n ) {\n throw new InvalidConfigurationError(\n 'sensorRateTemporalStationarity.localExposureWindow.timeReference must be \"first-opening-boundary-phase\".'\n );\n }\n\n if (\n record.stationarityMeaning !==\n \"reported-rate-constant-through-bound-local-exposure\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorRateTemporalStationarity.stationarityMeaning is invalid.\"\n );\n }\n\n if (\n record.status !== \"established\" &&\n record.status !== \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorRateTemporalStationarity.status is invalid.\"\n );\n }\n\n const limitation =\n record.limitation === undefined\n ? undefined\n : requireNonEmptyString(\n record.limitation,\n \"sensorRateTemporalStationarity.limitation\"\n );\n\n if (\n record.status === \"approximation\" &&\n limitation === undefined\n ) {\n throw new InvalidConfigurationError(\n \"sensorRateTemporalStationarity.limitation is required for an approximation.\"\n );\n }\n\n const start =\n requireFinite(\n localExposureWindow\n .startOffsetSecondsFromOpeningReference,\n \"sensorRateTemporalStationarity.localExposureWindow.startOffsetSecondsFromOpeningReference\"\n );\n const end =\n requireFinite(\n localExposureWindow\n .endOffsetSecondsFromOpeningReference,\n \"sensorRateTemporalStationarity.localExposureWindow.endOffsetSecondsFromOpeningReference\"\n );\n\n if (!(end > start)) {\n throw new InvalidConfigurationError(\n \"sensorRateTemporalStationarity.localExposureWindow end must be greater than start.\"\n );\n }\n\n return {\n schemaVersion: \"0.1.0\",\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"sensorRateTemporalStationarity.profileId\"\n ),\n rateDomain:\n record.rateDomain,\n colorSamplingProfileId:\n requireNonEmptyString(\n record.colorSamplingProfileId,\n \"sensorRateTemporalStationarity.colorSamplingProfileId\"\n ),\n channelId:\n requireNonEmptyString(\n record.channelId,\n \"sensorRateTemporalStationarity.channelId\"\n ),\n site: {\n x: requireNonNegativeSafeInteger(\n site.x,\n \"sensorRateTemporalStationarity.site.x\"\n ),\n y: requireNonNegativeSafeInteger(\n site.y,\n \"sensorRateTemporalStationarity.site.y\"\n )\n },\n bindingId:\n requireNonEmptyString(\n record.bindingId,\n \"sensorRateTemporalStationarity.bindingId\"\n ),\n localExposureWindow: {\n timeReference:\n \"first-opening-boundary-phase\",\n startOffsetSecondsFromOpeningReference:\n start,\n endOffsetSecondsFromOpeningReference:\n end\n },\n stationarityMeaning:\n \"reported-rate-constant-through-bound-local-exposure\",\n status:\n record.status,\n evidence:\n parseEvidenceList(\n record.evidence,\n \"sensorRateTemporalStationarity.evidence\"\n ),\n ...(limitation === undefined\n ? {}\n : { limitation })\n };\n}\n\nfunction sameSite(\n a: { x: number; y: number },\n b: { x: number; y: number }\n): boolean {\n return a.x === b.x && a.y === b.y;\n}\n\nfunction requireRateSite(\n rate:\n SensorInstantaneousRateResult\n): {\n x: number;\n y: number;\n} {\n requireInputObject(rate, \"rate input\");\n if (\n rate.site === undefined ||\n !Number.isSafeInteger(rate.site.x) ||\n rate.site.x < 0 ||\n !Number.isSafeInteger(rate.site.y) ||\n rate.site.y < 0\n ) {\n throw new InvalidScientificInputError(\n \"rate.site is required for temporal integration.\"\n );\n }\n return {\n x: rate.site.x,\n y: rate.site.y\n };\n}\n\nfunction validateBinding(\n rate:\n SensorInstantaneousRateResult,\n binding:\n SensorRateLocalExposureBinding\n): {\n rateDomain:\n SensorRateLocalExposureBinding[\"rateDomain\"];\n site: {\n x: number;\n y: number;\n };\n duration: number;\n} {\n const site = requireRateSite(rate);\n\n if (\n !sameSite(site, binding.site) ||\n binding.colorSamplingProfileId !==\n rate.colorSamplingProfileId ||\n binding.channelId !==\n rate.channelId\n ) {\n throw new InvalidScientificInputError(\n \"rate and exposureBinding must reference the same color site/channel.\"\n );\n }\n\n const rateDomain =\n rate.sourceResponseKind ===\n \"effective-spectral-responsivity\"\n ? \"responsivity-photocurrent\"\n : \"eqe-electron-rate\";\n\n if (\n binding.rateDomain !==\n rateDomain ||\n binding\n .nativeImageRasterBindingEstablished !==\n true ||\n binding.channelAtSiteValidated !==\n true ||\n binding\n .constantRateTemporalIntegrationAuthorized !==\n false ||\n binding.timeStationarityEstablished !==\n false ||\n binding.temporalIntegrationApplied !==\n false ||\n binding\n .multiFrameSequenceBindingEstablished !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"exposureBinding must remain an unintegrated local timing binding for the same rate domain.\"\n );\n }\n\n if (\n binding.rateIdentity\n .responseProfileId !==\n rate.responseProfileId ||\n binding.rateIdentity\n .responseApplicationProfileId !==\n rate.responseApplicationProfileId ||\n binding.rateIdentity\n .operatingRangeProfileId !==\n rate.operatingRangeProfileId\n ) {\n throw new InvalidScientificInputError(\n \"exposureBinding response-rate identity must match the rate.\"\n );\n }\n\n if (\n rateDomain ===\n \"responsivity-photocurrent\"\n ) {\n const current =\n rate as SensorResponsivityPhotocurrent;\n if (\n binding.rateIdentity\n .electricalApplicabilityProfileId !==\n current\n .electricalApplicabilityProfileId\n ) {\n throw new InvalidScientificInputError(\n \"exposureBinding electrical applicability identity must match the A/W rate.\"\n );\n }\n } else if (\n binding.rateIdentity\n .electricalApplicabilityProfileId !==\n undefined\n ) {\n throw new InvalidScientificInputError(\n \"EQE exposureBinding must not carry an A/W electrical applicability identity.\"\n );\n }\n\n const window =\n binding.localExposureWindow;\n const duration =\n window\n .endOffsetSecondsFromOpeningReference -\n window\n .startOffsetSecondsFromOpeningReference;\n\n if (\n !Number.isFinite(duration) ||\n duration <= 0 ||\n !Number.isFinite(\n binding.localExposureDurationSeconds\n ) ||\n binding.localExposureDurationSeconds !==\n duration\n ) {\n throw new InvalidScientificInputError(\n \"exposureBinding local duration must exactly equal end minus start and remain positive.\"\n );\n }\n\n return {\n rateDomain,\n site,\n duration\n };\n}\n\nfunction validateStationarity(\n stationarity:\n SensorRateTemporalStationarityProfile,\n binding:\n SensorRateLocalExposureBinding,\n rateDomain:\n SensorRateLocalExposureBinding[\"rateDomain\"],\n site: {\n x: number;\n y: number;\n }\n): void {\n if (\n stationarity.rateDomain !==\n rateDomain ||\n stationarity\n .colorSamplingProfileId !==\n binding.colorSamplingProfileId ||\n stationarity.channelId !==\n binding.channelId ||\n !sameSite(\n stationarity.site,\n site\n ) ||\n stationarity.bindingId !==\n binding.bindingId ||\n stationarity\n .localExposureWindow\n .timeReference !==\n binding.timeReference ||\n stationarity\n .localExposureWindow\n .startOffsetSecondsFromOpeningReference !==\n binding.localExposureWindow\n .startOffsetSecondsFromOpeningReference ||\n stationarity\n .localExposureWindow\n .endOffsetSecondsFromOpeningReference !==\n binding.localExposureWindow\n .endOffsetSecondsFromOpeningReference\n ) {\n throw new InvalidScientificInputError(\n \"stationarityProfile must apply to the exact bound rate domain, site, binding, and local exposure window.\"\n );\n }\n}\n\nfunction requireNonNegativeFiniteRate(\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 InvalidScientificInputError(\n path +\n \" must be finite and greater than or equal to zero.\"\n );\n }\n return value;\n}\n\n/**\n * Integrates one response rate, declared constant, over its exact local\n * exposure window.\n *\n * This is the constant-rate approximation only. It does not sample or\n * integrate a time-varying signal. EQE rates give expected photons and\n * electrons; A/W rates give coulombs. Counts stay fractional.\n */\nexport function integrateStationarySensorRateOverLocalExposure(\n input:\n IntegrateStationarySensorRateInput\n): CalculationResult<SensorStationaryRateExposureIntegration> {\n requireInputKeys(input, \"integrateStationarySensorRateOverLocalExposure\", [\"rate\", \"exposureBinding\", \"stationarityProfile\"]);\n const binding =\n validateBinding(\n input.rate,\n input.exposureBinding\n );\n const stationarity =\n parseSensorRateTemporalStationarityProfile(\n input.stationarityProfile\n );\n validateStationarity(\n stationarity,\n input.exposureBinding,\n binding.rateDomain,\n binding.site\n );\n\n const common = {\n colorSamplingProfileId:\n input.rate\n .colorSamplingProfileId,\n channelId:\n input.rate.channelId,\n site: binding.site,\n bindingId:\n input.exposureBinding.bindingId,\n stationarityProfileId:\n stationarity.profileId,\n stationarityStatus:\n stationarity.status,\n timeReference:\n input.exposureBinding\n .timeReference,\n startOffsetSecondsFromOpeningReference:\n input.exposureBinding\n .localExposureWindow\n .startOffsetSecondsFromOpeningReference,\n endOffsetSecondsFromOpeningReference:\n input.exposureBinding\n .localExposureWindow\n .endOffsetSecondsFromOpeningReference,\n localExposureDurationSeconds:\n binding.duration,\n integrationMethod:\n \"constant-rate-times-local-exposure-duration\" as const,\n timeStationarityEstablished:\n true as const,\n temporalIntegrationApplied:\n true as const,\n exposureDurationApplied:\n true as const,\n timeVaryingSignalIntegrated:\n false as const,\n multiFrameSequenceIntegrated:\n false as const,\n darkChargeIncluded:\n false as const,\n otherChargeIncluded:\n false as const,\n accumulatedSignalCompleteness:\n \"photo-signal-only\" as const,\n physicalFullWellAssessmentAuthorized:\n false as const,\n cameraSaturationAssessmentAuthorized:\n false as const,\n saturationAssessed:\n false as const,\n shotNoiseApplied:\n false as const,\n readNoiseApplied:\n false as const,\n adcQuantizationApplied:\n false as const,\n rawCodeValueProduced:\n false as const,\n componentEvidence: {\n stationarity:\n stationarity.evidence,\n exposureBinding:\n input.exposureBinding\n .componentEvidence\n }\n };\n\n const assumptions = [\n \"The reported instantaneous rate is explicitly declared constant through this exact bound local exposure window.\",\n \"The integration is therefore rate × local duration; no temporal quadrature or time-varying signal samples are generated.\",\n \"Motion, flicker, flash, time-varying illumination/vignetting, shutter modulation and other temporal scene/optical effects are excluded unless already proven irrelevant by the stationarity evidence.\",\n \"A/W stationarity additionally treats the quasi-static detector-terminal photocurrent result as valid throughout the window; detector bandwidth/transient response remains unmodeled.\",\n \"This result contains photo-signal accumulation only. Dark current, defects, leakage, charge injection and other charge contributors are not included.\",\n \"Physical full-well and camera/digital saturation must not be assessed from this incomplete photo-signal accumulation alone.\",\n \"Shot/read noise, conversion gain, ADC/RAW conversion and reconstruction remain downstream.\"\n ];\n\n if (\n binding.rateDomain ===\n \"eqe-electron-rate\"\n ) {\n const rate =\n input.rate as\n SensorEqeElectronRate;\n const photonRate =\n requireNonNegativeFiniteRate(\n rate.incidentPhotonRatePerSecond,\n \"rate.incidentPhotonRatePerSecond\"\n );\n const electronRate =\n requireNonNegativeFiniteRate(\n rate\n .expectedGeneratedElectronRatePerSecond,\n \"rate.expectedGeneratedElectronRatePerSecond\"\n );\n const photonCount =\n photonRate *\n binding.duration;\n const electronCount =\n electronRate *\n binding.duration;\n\n if (\n !Number.isFinite(photonCount) ||\n !Number.isFinite(electronCount)\n ) {\n throw new InvalidScientificInputError(\n \"EQE constant-rate temporal integration must remain finite.\"\n );\n }\n\n const result:\n SensorEqeExposureIntegration = {\n ...common,\n kind:\n \"eqe-expected-counts\",\n incidentPhotonRatePerSecond:\n photonRate,\n expectedGeneratedElectronRatePerSecond:\n electronRate,\n expectedIncidentPhotonCount:\n photonCount,\n expectedGeneratedElectronCount:\n electronCount,\n countsAreExpectationValues:\n true,\n integerPhotonCountSampled:\n false,\n integerElectronCountSampled:\n false,\n chargeCalculated: false,\n currentCalculated: false\n };\n\n return stationarity.status ===\n \"established\"\n ? calculatedResult(\n result,\n \"sensor-stationary-eqe-exposure-integration\",\n \"1.0.0\",\n assumptions\n )\n : approximationResult(\n result,\n \"sensor-stationary-eqe-exposure-integration\",\n \"1.0.0\",\n [\n ...assumptions,\n stationarity.limitation!\n ]\n );\n }\n\n const rate =\n input.rate as\n SensorResponsivityPhotocurrent;\n const current =\n requireNonNegativeFiniteRate(\n rate.photocurrentMagnitudeAmperes,\n \"rate.photocurrentMagnitudeAmperes\"\n );\n\n if (\n rate.temporalResponseModel !==\n \"quasi-static-steady-state-only\" ||\n rate.detectorBandwidthModeled !==\n false ||\n rate.chargeCalculated !== false\n ) {\n throw new InvalidScientificInputError(\n \"A/W input must remain a quasi-static current result with no prior charge integration.\"\n );\n }\n\n const charge =\n current * binding.duration;\n if (!Number.isFinite(charge)) {\n throw new InvalidScientificInputError(\n \"A/W constant-current temporal integration must remain finite.\"\n );\n }\n\n const result:\n SensorResponsivityExposureIntegration =\n {\n ...common,\n kind:\n \"responsivity-photocurrent-charge\",\n photocurrentMagnitudeAmperes:\n current,\n photochargeMagnitudeCoulombs:\n charge,\n currentSignConvention:\n rate.currentSignConvention,\n chargeSignConvention:\n \"magnitude-only-no-carrier-or-circuit-polarity\",\n carrierCountInferred: false,\n electronCountCalculated:\n false,\n photonCountCalculated:\n false,\n detectorBandwidthModeled:\n false\n };\n\n return stationarity.status ===\n \"established\"\n ? calculatedResult(\n result,\n \"sensor-stationary-responsivity-exposure-integration\",\n \"1.0.0\",\n assumptions\n )\n : approximationResult(\n result,\n \"sensor-stationary-responsivity-exposure-integration\",\n \"1.0.0\",\n [\n ...assumptions,\n stationarity.limitation!\n ]\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"constant-rate-temporal-integration.js","sourceRoot":"","sources":["../../src/sensor/constant-rate-temporal-integration.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Constant-rate exposure: multiplies a sensor response rate by the exposure time.\n *\n * This shortcut (rate × duration) is allowed only when the caller declares the rate constant\n * (stationary) for this exact site and local shutter interval. With EQE (external quantum\n * efficiency) the result is expected photons and electrons. With A/W responsivity it is charge in\n * coulombs, with no assumed conversion to electrons. Counts stay fractional expected values, before\n * any random sampling. Time-varying signals are not handled here.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport {\n approximationResult,\n calculatedResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\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 type {\n SensorEqeElectronRate\n} from \"./eqe-electron-rate.js\";\nimport type {\n SensorInstantaneousRateResult,\n SensorRateLocalExposureBinding\n} from \"./local-exposure-binding.js\";\nimport type {\n SensorResponsivityPhotocurrent\n} from \"./responsivity-photocurrent.js\";\nimport { requireInputObject, requireInputKeys, requireConfigRecord as requireRecord, requireConfigFinite as requireFinite, requireConfigString as requireNonEmptyString, requireInputNonNegativeFinite as requireNonNegativeFiniteRate, requireConfigNonNegativeSafeInteger as requireNonNegativeSafeInteger } from \"../core/record-validation.js\";\nimport { sameSite } from \"../core/shared-helpers.js\";\n\n\nexport type SensorRateStationarityStatus =\n | \"established\"\n | \"approximation\";\n\nexport interface SensorRateTemporalStationarityProfile {\n schemaVersion: \"0.1.0\";\n profileId: string;\n rateDomain:\n SensorRateLocalExposureBinding[\"rateDomain\"];\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n localExposureWindow: {\n timeReference:\n \"first-opening-boundary-phase\";\n startOffsetSecondsFromOpeningReference:\n number;\n endOffsetSecondsFromOpeningReference:\n number;\n };\n stationarityMeaning:\n \"reported-rate-constant-through-bound-local-exposure\";\n status:\n SensorRateStationarityStatus;\n evidence: readonly EvidenceProvenance[];\n limitation?: string;\n}\n\nexport interface IntegrateStationarySensorRateInput {\n rate:\n SensorInstantaneousRateResult;\n exposureBinding:\n SensorRateLocalExposureBinding;\n stationarityProfile:\n SensorRateTemporalStationarityProfile;\n}\n\ninterface SensorTemporalIntegrationCommon {\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n stationarityProfileId: string;\n stationarityStatus:\n SensorRateStationarityStatus;\n timeReference:\n \"first-opening-boundary-phase\";\n startOffsetSecondsFromOpeningReference:\n number;\n endOffsetSecondsFromOpeningReference:\n number;\n localExposureDurationSeconds:\n number;\n integrationMethod:\n \"constant-rate-times-local-exposure-duration\";\n timeStationarityEstablished: true;\n temporalIntegrationApplied: true;\n exposureDurationApplied: true;\n timeVaryingSignalIntegrated: false;\n multiFrameSequenceIntegrated: false;\n darkChargeIncluded: false;\n otherChargeIncluded: false;\n accumulatedSignalCompleteness:\n \"photo-signal-only\";\n physicalFullWellAssessmentAuthorized:\n false;\n cameraSaturationAssessmentAuthorized:\n false;\n saturationAssessed: false;\n shotNoiseApplied: false;\n readNoiseApplied: false;\n adcQuantizationApplied: false;\n rawCodeValueProduced: false;\n componentEvidence: {\n stationarity:\n readonly EvidenceProvenance[];\n exposureBinding:\n SensorRateLocalExposureBinding[\"componentEvidence\"];\n };\n}\n\nexport interface SensorEqeExposureIntegration\n extends SensorTemporalIntegrationCommon {\n kind: \"eqe-expected-counts\";\n incidentPhotonRatePerSecond: number;\n expectedGeneratedElectronRatePerSecond:\n number;\n expectedIncidentPhotonCount: number;\n expectedGeneratedElectronCount:\n number;\n countsAreExpectationValues: true;\n integerPhotonCountSampled: false;\n integerElectronCountSampled: false;\n chargeCalculated: false;\n currentCalculated: false;\n}\n\nexport interface SensorResponsivityExposureIntegration\n extends SensorTemporalIntegrationCommon {\n kind:\n \"responsivity-photocurrent-charge\";\n photocurrentMagnitudeAmperes: number;\n photochargeMagnitudeCoulombs:\n number;\n currentSignConvention:\n \"magnitude-only-no-circuit-polarity\";\n chargeSignConvention:\n \"magnitude-only-no-carrier-or-circuit-polarity\";\n carrierCountInferred: false;\n electronCountCalculated: false;\n photonCountCalculated: false;\n detectorBandwidthModeled: false;\n}\n\nexport type SensorStationaryRateExposureIntegration =\n | SensorEqeExposureIntegration\n | SensorResponsivityExposureIntegration;\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 declares that a rate is constant (stationary) for an exact site and local shutter\n * interval, which is what allows rate × duration.\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 parseSensorRateTemporalStationarityProfile(\n value: unknown\n): SensorRateTemporalStationarityProfile {\n const record = requireRecord(\n value,\n \"sensorRateTemporalStationarity\", [\"schemaVersion\", \"rateDomain\", \"site\", \"localExposureWindow\", \"stationarityMeaning\", \"status\", \"limitation\", \"profileId\", \"colorSamplingProfileId\", \"channelId\", \"bindingId\", \"evidence\"]\n );\n\n if (\n record.schemaVersion !== \"0.1.0\"\n ) {\n throw new InvalidConfigurationError(\n 'sensorRateTemporalStationarity.schemaVersion must be \"0.1.0\".'\n );\n }\n\n if (\n record.rateDomain !==\n \"eqe-electron-rate\" &&\n record.rateDomain !==\n \"responsivity-photocurrent\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorRateTemporalStationarity.rateDomain is invalid.\"\n );\n }\n\n const site = requireRecord(\n record.site,\n \"sensorRateTemporalStationarity.site\", [\"x\", \"y\"]\n );\n const localExposureWindow =\n requireRecord(\n record.localExposureWindow,\n \"sensorRateTemporalStationarity.localExposureWindow\", [\"timeReference\", \"startOffsetSecondsFromOpeningReference\", \"endOffsetSecondsFromOpeningReference\"]\n );\n\n if (\n localExposureWindow.timeReference !==\n \"first-opening-boundary-phase\"\n ) {\n throw new InvalidConfigurationError(\n 'sensorRateTemporalStationarity.localExposureWindow.timeReference must be \"first-opening-boundary-phase\".'\n );\n }\n\n if (\n record.stationarityMeaning !==\n \"reported-rate-constant-through-bound-local-exposure\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorRateTemporalStationarity.stationarityMeaning is invalid.\"\n );\n }\n\n if (\n record.status !== \"established\" &&\n record.status !== \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorRateTemporalStationarity.status is invalid.\"\n );\n }\n\n const limitation =\n record.limitation === undefined\n ? undefined\n : requireNonEmptyString(\n record.limitation,\n \"sensorRateTemporalStationarity.limitation\"\n );\n\n if (\n record.status === \"approximation\" &&\n limitation === undefined\n ) {\n throw new InvalidConfigurationError(\n \"sensorRateTemporalStationarity.limitation is required for an approximation.\"\n );\n }\n\n const start =\n requireFinite(\n localExposureWindow\n .startOffsetSecondsFromOpeningReference,\n \"sensorRateTemporalStationarity.localExposureWindow.startOffsetSecondsFromOpeningReference\"\n );\n const end =\n requireFinite(\n localExposureWindow\n .endOffsetSecondsFromOpeningReference,\n \"sensorRateTemporalStationarity.localExposureWindow.endOffsetSecondsFromOpeningReference\"\n );\n\n if (!(end > start)) {\n throw new InvalidConfigurationError(\n \"sensorRateTemporalStationarity.localExposureWindow end must be greater than start.\"\n );\n }\n\n return {\n schemaVersion: \"0.1.0\",\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"sensorRateTemporalStationarity.profileId\"\n ),\n rateDomain:\n record.rateDomain,\n colorSamplingProfileId:\n requireNonEmptyString(\n record.colorSamplingProfileId,\n \"sensorRateTemporalStationarity.colorSamplingProfileId\"\n ),\n channelId:\n requireNonEmptyString(\n record.channelId,\n \"sensorRateTemporalStationarity.channelId\"\n ),\n site: {\n x: requireNonNegativeSafeInteger(\n site.x,\n \"sensorRateTemporalStationarity.site.x\"\n ),\n y: requireNonNegativeSafeInteger(\n site.y,\n \"sensorRateTemporalStationarity.site.y\"\n )\n },\n bindingId:\n requireNonEmptyString(\n record.bindingId,\n \"sensorRateTemporalStationarity.bindingId\"\n ),\n localExposureWindow: {\n timeReference:\n \"first-opening-boundary-phase\",\n startOffsetSecondsFromOpeningReference:\n start,\n endOffsetSecondsFromOpeningReference:\n end\n },\n stationarityMeaning:\n \"reported-rate-constant-through-bound-local-exposure\",\n status:\n record.status,\n evidence:\n parseEvidenceList(\n record.evidence,\n \"sensorRateTemporalStationarity.evidence\"\n ),\n ...(limitation === undefined\n ? {}\n : { limitation })\n };\n}\n\nfunction requireRateSite(\n rate:\n SensorInstantaneousRateResult\n): {\n x: number;\n y: number;\n} {\n requireInputObject(rate, \"rate input\");\n if (\n rate.site === undefined ||\n !Number.isSafeInteger(rate.site.x) ||\n rate.site.x < 0 ||\n !Number.isSafeInteger(rate.site.y) ||\n rate.site.y < 0\n ) {\n throw new InvalidScientificInputError(\n \"rate.site is required for temporal integration.\"\n );\n }\n return {\n x: rate.site.x,\n y: rate.site.y\n };\n}\n\nfunction validateBinding(\n rate:\n SensorInstantaneousRateResult,\n binding:\n SensorRateLocalExposureBinding\n): {\n rateDomain:\n SensorRateLocalExposureBinding[\"rateDomain\"];\n site: {\n x: number;\n y: number;\n };\n duration: number;\n} {\n const site = requireRateSite(rate);\n\n if (\n !sameSite(site, binding.site) ||\n binding.colorSamplingProfileId !==\n rate.colorSamplingProfileId ||\n binding.channelId !==\n rate.channelId\n ) {\n throw new InvalidScientificInputError(\n \"rate and exposureBinding must reference the same color site/channel.\"\n );\n }\n\n const rateDomain =\n rate.sourceResponseKind ===\n \"effective-spectral-responsivity\"\n ? \"responsivity-photocurrent\"\n : \"eqe-electron-rate\";\n\n if (\n binding.rateDomain !==\n rateDomain ||\n binding\n .nativeImageRasterBindingEstablished !==\n true ||\n binding.channelAtSiteValidated !==\n true ||\n binding\n .constantRateTemporalIntegrationAuthorized !==\n false ||\n binding.timeStationarityEstablished !==\n false ||\n binding.temporalIntegrationApplied !==\n false ||\n binding\n .multiFrameSequenceBindingEstablished !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"exposureBinding must remain an unintegrated local timing binding for the same rate domain.\"\n );\n }\n\n if (\n binding.rateIdentity\n .responseProfileId !==\n rate.responseProfileId ||\n binding.rateIdentity\n .responseApplicationProfileId !==\n rate.responseApplicationProfileId ||\n binding.rateIdentity\n .operatingRangeProfileId !==\n rate.operatingRangeProfileId\n ) {\n throw new InvalidScientificInputError(\n \"exposureBinding response-rate identity must match the rate.\"\n );\n }\n\n if (\n rateDomain ===\n \"responsivity-photocurrent\"\n ) {\n const current =\n rate as SensorResponsivityPhotocurrent;\n if (\n binding.rateIdentity\n .electricalApplicabilityProfileId !==\n current\n .electricalApplicabilityProfileId\n ) {\n throw new InvalidScientificInputError(\n \"exposureBinding electrical applicability identity must match the A/W rate.\"\n );\n }\n } else if (\n binding.rateIdentity\n .electricalApplicabilityProfileId !==\n undefined\n ) {\n throw new InvalidScientificInputError(\n \"EQE exposureBinding must not carry an A/W electrical applicability identity.\"\n );\n }\n\n const window =\n binding.localExposureWindow;\n const duration =\n window\n .endOffsetSecondsFromOpeningReference -\n window\n .startOffsetSecondsFromOpeningReference;\n\n if (\n !Number.isFinite(duration) ||\n duration <= 0 ||\n !Number.isFinite(\n binding.localExposureDurationSeconds\n ) ||\n binding.localExposureDurationSeconds !==\n duration\n ) {\n throw new InvalidScientificInputError(\n \"exposureBinding local duration must exactly equal end minus start and remain positive.\"\n );\n }\n\n return {\n rateDomain,\n site,\n duration\n };\n}\n\nfunction validateStationarity(\n stationarity:\n SensorRateTemporalStationarityProfile,\n binding:\n SensorRateLocalExposureBinding,\n rateDomain:\n SensorRateLocalExposureBinding[\"rateDomain\"],\n site: {\n x: number;\n y: number;\n }\n): void {\n if (\n stationarity.rateDomain !==\n rateDomain ||\n stationarity\n .colorSamplingProfileId !==\n binding.colorSamplingProfileId ||\n stationarity.channelId !==\n binding.channelId ||\n !sameSite(\n stationarity.site,\n site\n ) ||\n stationarity.bindingId !==\n binding.bindingId ||\n stationarity\n .localExposureWindow\n .timeReference !==\n binding.timeReference ||\n stationarity\n .localExposureWindow\n .startOffsetSecondsFromOpeningReference !==\n binding.localExposureWindow\n .startOffsetSecondsFromOpeningReference ||\n stationarity\n .localExposureWindow\n .endOffsetSecondsFromOpeningReference !==\n binding.localExposureWindow\n .endOffsetSecondsFromOpeningReference\n ) {\n throw new InvalidScientificInputError(\n \"stationarityProfile must apply to the exact bound rate domain, site, binding, and local exposure window.\"\n );\n }\n}\n\n/**\n * Integrates one response rate, declared constant, over its exact local\n * exposure window.\n *\n * This is the constant-rate approximation only. It does not sample or\n * integrate a time-varying signal. EQE rates give expected photons and\n * electrons; A/W rates give coulombs. Counts stay fractional.\n */\nexport function integrateStationarySensorRateOverLocalExposure(\n input:\n IntegrateStationarySensorRateInput\n): CalculationResult<SensorStationaryRateExposureIntegration> {\n requireInputKeys(input, \"integrateStationarySensorRateOverLocalExposure\", [\"rate\", \"exposureBinding\", \"stationarityProfile\"]);\n const binding =\n validateBinding(\n input.rate,\n input.exposureBinding\n );\n const stationarity =\n parseSensorRateTemporalStationarityProfile(\n input.stationarityProfile\n );\n validateStationarity(\n stationarity,\n input.exposureBinding,\n binding.rateDomain,\n binding.site\n );\n\n const common = {\n colorSamplingProfileId:\n input.rate\n .colorSamplingProfileId,\n channelId:\n input.rate.channelId,\n site: binding.site,\n bindingId:\n input.exposureBinding.bindingId,\n stationarityProfileId:\n stationarity.profileId,\n stationarityStatus:\n stationarity.status,\n timeReference:\n input.exposureBinding\n .timeReference,\n startOffsetSecondsFromOpeningReference:\n input.exposureBinding\n .localExposureWindow\n .startOffsetSecondsFromOpeningReference,\n endOffsetSecondsFromOpeningReference:\n input.exposureBinding\n .localExposureWindow\n .endOffsetSecondsFromOpeningReference,\n localExposureDurationSeconds:\n binding.duration,\n integrationMethod:\n \"constant-rate-times-local-exposure-duration\" as const,\n timeStationarityEstablished:\n true as const,\n temporalIntegrationApplied:\n true as const,\n exposureDurationApplied:\n true as const,\n timeVaryingSignalIntegrated:\n false as const,\n multiFrameSequenceIntegrated:\n false as const,\n darkChargeIncluded:\n false as const,\n otherChargeIncluded:\n false as const,\n accumulatedSignalCompleteness:\n \"photo-signal-only\" as const,\n physicalFullWellAssessmentAuthorized:\n false as const,\n cameraSaturationAssessmentAuthorized:\n false as const,\n saturationAssessed:\n false as const,\n shotNoiseApplied:\n false as const,\n readNoiseApplied:\n false as const,\n adcQuantizationApplied:\n false as const,\n rawCodeValueProduced:\n false as const,\n componentEvidence: {\n stationarity:\n stationarity.evidence,\n exposureBinding:\n input.exposureBinding\n .componentEvidence\n }\n };\n\n const assumptions = [\n \"The reported instantaneous rate is explicitly declared constant through this exact bound local exposure window.\",\n \"The integration is therefore rate × local duration; no temporal quadrature or time-varying signal samples are generated.\",\n \"Motion, flicker, flash, time-varying illumination/vignetting, shutter modulation and other temporal scene/optical effects are excluded unless already proven irrelevant by the stationarity evidence.\",\n \"A/W stationarity additionally treats the quasi-static detector-terminal photocurrent result as valid throughout the window; detector bandwidth/transient response remains unmodeled.\",\n \"This result contains photo-signal accumulation only. Dark current, defects, leakage, charge injection and other charge contributors are not included.\",\n \"Physical full-well and camera/digital saturation must not be assessed from this incomplete photo-signal accumulation alone.\",\n \"Shot/read noise, conversion gain, ADC/RAW conversion and reconstruction remain downstream.\"\n ];\n\n if (\n binding.rateDomain ===\n \"eqe-electron-rate\"\n ) {\n const rate =\n input.rate as\n SensorEqeElectronRate;\n const photonRate =\n requireNonNegativeFiniteRate(\n rate.incidentPhotonRatePerSecond,\n \"rate.incidentPhotonRatePerSecond\"\n );\n const electronRate =\n requireNonNegativeFiniteRate(\n rate\n .expectedGeneratedElectronRatePerSecond,\n \"rate.expectedGeneratedElectronRatePerSecond\"\n );\n const photonCount =\n photonRate *\n binding.duration;\n const electronCount =\n electronRate *\n binding.duration;\n\n if (\n !Number.isFinite(photonCount) ||\n !Number.isFinite(electronCount)\n ) {\n throw new InvalidScientificInputError(\n \"EQE constant-rate temporal integration must remain finite.\"\n );\n }\n\n const result:\n SensorEqeExposureIntegration = {\n ...common,\n kind:\n \"eqe-expected-counts\",\n incidentPhotonRatePerSecond:\n photonRate,\n expectedGeneratedElectronRatePerSecond:\n electronRate,\n expectedIncidentPhotonCount:\n photonCount,\n expectedGeneratedElectronCount:\n electronCount,\n countsAreExpectationValues:\n true,\n integerPhotonCountSampled:\n false,\n integerElectronCountSampled:\n false,\n chargeCalculated: false,\n currentCalculated: false\n };\n\n return stationarity.status ===\n \"established\"\n ? calculatedResult(\n result,\n \"sensor-stationary-eqe-exposure-integration\",\n \"1.0.0\",\n assumptions\n )\n : approximationResult(\n result,\n \"sensor-stationary-eqe-exposure-integration\",\n \"1.0.0\",\n [\n ...assumptions,\n stationarity.limitation!\n ]\n );\n }\n\n const rate =\n input.rate as\n SensorResponsivityPhotocurrent;\n const current =\n requireNonNegativeFiniteRate(\n rate.photocurrentMagnitudeAmperes,\n \"rate.photocurrentMagnitudeAmperes\"\n );\n\n if (\n rate.temporalResponseModel !==\n \"quasi-static-steady-state-only\" ||\n rate.detectorBandwidthModeled !==\n false ||\n rate.chargeCalculated !== false\n ) {\n throw new InvalidScientificInputError(\n \"A/W input must remain a quasi-static current result with no prior charge integration.\"\n );\n }\n\n const charge =\n current * binding.duration;\n if (!Number.isFinite(charge)) {\n throw new InvalidScientificInputError(\n \"A/W constant-current temporal integration must remain finite.\"\n );\n }\n\n const result:\n SensorResponsivityExposureIntegration =\n {\n ...common,\n kind:\n \"responsivity-photocurrent-charge\",\n photocurrentMagnitudeAmperes:\n current,\n photochargeMagnitudeCoulombs:\n charge,\n currentSignConvention:\n rate.currentSignConvention,\n chargeSignConvention:\n \"magnitude-only-no-carrier-or-circuit-polarity\",\n carrierCountInferred: false,\n electronCountCalculated:\n false,\n photonCountCalculated:\n false,\n detectorBandwidthModeled:\n false\n };\n\n return stationarity.status ===\n \"established\"\n ? calculatedResult(\n result,\n \"sensor-stationary-responsivity-exposure-integration\",\n \"1.0.0\",\n assumptions\n )\n : approximationResult(\n result,\n \"sensor-stationary-responsivity-exposure-integration\",\n \"1.0.0\",\n [\n ...assumptions,\n stationarity.limitation!\n ]\n );\n}\n"]}
|