@photivra/engine 2.18.0 → 2.20.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 +179 -1
- package/CITATION.cff +1 -1
- package/NODE_SUPPORT.md +7 -5
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +14 -10
- package/dist/capture/dng-lens-correction-opcodes.d.ts +32 -0
- package/dist/capture/dng-lens-correction-opcodes.js +119 -0
- package/dist/capture/dng-lens-correction-opcodes.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 +2 -6
- 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-raw-dng.d.ts +6 -0
- package/dist/capture/native-raw-dng.js +16 -2
- package/dist/capture/native-raw-dng.js.map +1 -1
- package/dist/capture/release-sequence.js +2 -47
- package/dist/capture/release-sequence.js.map +1 -1
- package/dist/capture/sensor-raw-producer.js +3 -5
- package/dist/capture/sensor-raw-producer.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 +35 -0
- package/dist/core/shared-helpers.js +38 -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/generic-lens-catalogue.d.ts +8 -2
- package/dist/equipment/generic-lens-catalogue.js +31 -2
- package/dist/equipment/generic-lens-catalogue.js.map +1 -1
- package/dist/equipment/generic-tier-assets.js +2 -3
- package/dist/equipment/generic-tier-assets.js.map +1 -1
- package/dist/equipment/generic-tier-presets.js +2 -3
- package/dist/equipment/generic-tier-presets.js.map +1 -1
- 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/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-gpu.d.ts +7 -4
- package/dist/facade/display-view-gpu.js +4 -4
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view-lens-signal.d.ts +1 -1
- package/dist/facade/display-view-lens-signal.js +5 -3
- package/dist/facade/display-view-lens-signal.js.map +1 -1
- package/dist/facade/display-view.d.ts +3 -1
- package/dist/facade/display-view.js +1 -1
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/fast-methods.d.ts +50 -0
- package/dist/facade/fast-methods.js +50 -0
- package/dist/facade/fast-methods.js.map +1 -0
- package/dist/facade/fast-resolution.d.ts +12 -0
- package/dist/facade/fast-resolution.js +18 -0
- package/dist/facade/fast-resolution.js.map +1 -0
- package/dist/facade/generic-lens-capture.d.ts +6 -3
- package/dist/facade/generic-lens-capture.js +6 -3
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +88 -16
- package/dist/facade/simulate-capture.js +279 -153
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +3 -2
- package/dist/facade/simulate-display-view.js +22 -12
- 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 +11 -1
- package/dist/index.js +8 -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/aperture-diameter.d.ts +47 -0
- package/dist/optics/aperture-diameter.js +50 -0
- package/dist/optics/aperture-diameter.js.map +1 -0
- 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/detail-limit.d.ts +182 -0
- package/dist/optics/detail-limit.js +336 -0
- package/dist/optics/detail-limit.js.map +1 -0
- 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-lens-psf.d.ts +18 -2
- package/dist/optics/generic-lens-psf.js +25 -2
- package/dist/optics/generic-lens-psf.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.d.ts +13 -0
- package/dist/optics/scene-to-sensor-irradiance.js +11 -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/crop-to-match-focal-length.d.ts +62 -0
- package/dist/output/crop-to-match-focal-length.js +63 -0
- package/dist/output/crop-to-match-focal-length.js.map +1 -0
- 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 +5 -7
- package/dist/output/generic-lens-corrections.js +11 -10
- 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/lens-corrections.d.ts +12 -2
- package/dist/output/lens-corrections.js +22 -7
- package/dist/output/lens-corrections.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/output/print-contrast-reference.js +1 -1
- package/dist/output/print-contrast-reference.js.map +1 -1
- package/dist/output/print-size-blur-noise.d.ts +110 -0
- package/dist/output/print-size-blur-noise.js +138 -0
- package/dist/output/print-size-blur-noise.js.map +1 -0
- 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 -41
- package/dist/sensor/eqe-electron-rate.js.map +1 -1
- package/dist/sensor/exposure-window.d.ts +7 -0
- package/dist/sensor/exposure-window.js +9 -24
- 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 +4 -79
- 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 +4 -35
- 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 -114
- 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/sensor-characteristics.d.ts +137 -0
- package/dist/sensor/sensor-characteristics.js +115 -0
- package/dist/sensor/sensor-characteristics.js.map +1 -0
- package/dist/sensor/spatial-sample-reduction.js +3 -24
- 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 +3 -41
- 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/sensor/temporal-photo-signal.d.ts +3 -0
- package/dist/sensor/temporal-photo-signal.js +5 -2
- package/dist/sensor/temporal-photo-signal.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.d.ts +9 -0
- package/dist/stabilization/system.js +7 -86
- package/dist/stabilization/system.js.map +1 -1
- package/docs/AGENT_GUIDE.md +5 -0
- package/docs/API_REFERENCE.md +116 -3
- package/docs/QUICK_START.md +178 -14
- package/docs/USAGE.md +111 -3
- package/docs/api/capture-native-raw-dng.md +6 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-generic-lens-catalogue.md +7 -2
- package/docs/api/exports.json +203 -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-display-view-gpu.md +5 -2
- package/docs/api/facade-fast-methods.md +91 -0
- package/docs/api/facade-simulate-capture.md +72 -16
- package/docs/api/facade-simulate-display-view.md +3 -2
- package/docs/api/optics-aperture-diameter.md +51 -0
- package/docs/api/optics-detail-limit.md +203 -0
- 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-crop-to-match-focal-length.md +67 -0
- package/docs/api/output-developed-lens-corrections.md +12 -0
- package/docs/api/output-generic-lens-corrections.md +3 -10
- package/docs/api/output-lens-corrections.md +11 -1
- package/docs/api/output-print-size-blur-noise.md +115 -0
- package/docs/api/sensor-sensor-characteristics.md +105 -0
- package/llms.txt +5 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scene-to-sensor-irradiance.js","sourceRoot":"","sources":["../../src/optics/scene-to-sensor-irradiance.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Scene-to-sensor optical bridge: from spectral scene radiance to the irradiance that reaches the\n * sensor.\n *\n * The profile parser validates untrusted input and returns the normalized typed contract. Unknown\n * enum values, missing required fields and incompatible scientific data are rejected here.\n *\n * The calculation applies the declared pupil acceptance and exactly one transmission or\n * field-throughput path. It returns the irradiance before the sensor stack, with conservative\n * evidence.\n *\n * The optical bridge turns outgoing scene radiance (W/m²/sr/nm) into irradiance (W/m²/nm) arriving\n * at the front of the sensor stack.\n * - Circular paraxial acceptance is pi/(4*Nworking²). At finite focus with unit pupil magnification,\n * the working f-number is N*(1+m).\n * - Spectral transmission and an effective working T-stop are alternative paths; use one.\n * - Field throughput is applied exactly once.\n * - Sensor response and PSF (point-spread function) blur happen later in the pipeline.\n * @see docs/SCENE_SENSOR_QUADRATURE.md for the equations, units, coordinate conventions, known\n * blockers and support limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceBackedFact,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport {\n parseSpectralWavelengthBasis,\n type SpectralWavelengthBasis\n} from \"../core/spectral.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n composeFrontOfLensFilterTransmission,\n type FrontOfLensFilterProfile\n} from \"./front-of-lens-filter.js\";\nimport type { IlluminationVignetting } from \"./illumination-vignetting.js\";\nimport { calculateThinLensImageDistance } from \"./thin-lens.js\";\nimport {\n parseSceneRadianceEvaluationRequest,\n parseSceneRadianceEvaluationResult,\n type SceneRadianceEvaluationRequest,\n type SceneRadianceEvaluationResult\n} from \"../schema/scene-radiance.js\";\nimport { requireInputKeys, rejectUnknownFields, rejectUnknownVariantFields, UNCERTAINTY_VARIANT_FIELDS } from \"../core/record-validation.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport const SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION =\n \"0.1.0\" as const;\n\nexport type OpticalBridgeScientificStatus =\n | \"calibrated\"\n | \"approximation\";\n\nexport type OpticalBridgeUncertainty =\n | {\n kind: \"relative\";\n fraction: number;\n basis: string;\n }\n | {\n kind: \"not-quantified\";\n limitation: string;\n };\n\nexport interface NumericRange {\n minimum: number;\n maximum: number;\n}\n\nexport type OpticalBridgeFocusApplicability =\n | {\n kind: \"any\";\n }\n | {\n kind: \"finite-distance-range\";\n objectDistanceM: NumericRange;\n };\n\nexport interface SpectralTransmissionSample {\n wavelengthNanometers: number;\n linearTransmissionFactor: number;\n}\n\nexport type OpticalTransmissionModel =\n | {\n kind: \"spectral-transmission\";\n scientificStatus:\n OpticalBridgeScientificStatus;\n wavelengthBasis:\n Exclude<\n SpectralWavelengthBasis,\n \"unspecified\"\n >;\n samples:\n EvidenceBackedFact<\n readonly SpectralTransmissionSample[]\n >;\n uncertainty:\n OpticalBridgeUncertainty;\n }\n | {\n kind:\n \"effective-working-t-stop-approximation\";\n scientificStatus: \"approximation\";\n workingTStop:\n EvidenceBackedFact<number>;\n wavelengthBasis:\n Exclude<\n SpectralWavelengthBasis,\n \"unspecified\"\n >;\n wavelengthRangeNanometers:\n NumericRange;\n uncertainty:\n OpticalBridgeUncertainty;\n limitation: string;\n };\n\nexport interface SceneToSensorIrradianceProfile {\n schemaVersion:\n typeof SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION;\n profileId: string;\n profileVersion: string;\n lensProfileId: string;\n scientificStatus:\n OpticalBridgeScientificStatus;\n applicability: {\n focalLengthMm: NumericRange;\n nominalFNumber: NumericRange;\n focus:\n OpticalBridgeFocusApplicability;\n };\n transmission:\n OpticalTransmissionModel;\n distortionAreaMappingOwnership:\n \"not-applied-by-bridge\";\n psfRedistributionOwnership:\n \"downstream-normalized-energy-redistribution\";\n sensorOpticalStackIncluded: false;\n strayLightIncluded: false;\n polarizationModeled: false;\n wavelengthChangingBehaviorModeled: false;\n volumetricScatteringModeled: false;\n evidence: readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\nexport type OpticalBridgeFocusContext =\n | {\n kind: \"infinity-focus\";\n }\n | {\n kind:\n \"ideal-symmetric-thin-lens\";\n objectDistanceM: number;\n pupilMagnificationAssumption:\n \"unity\";\n }\n | {\n kind:\n \"supplied-working-f-number\";\n focus:\n | {\n kind: \"infinity\";\n }\n | {\n kind: \"finite\";\n objectDistanceM: number;\n };\n workingFNumber:\n EvidenceBackedFact<number>;\n basis: string;\n };\n\nexport type OpticalBridgeFieldThroughput =\n | {\n kind: \"unity\";\n }\n | {\n kind:\n \"illumination-vignetting-result\";\n result:\n IlluminationVignetting;\n };\n\nexport interface CalculateSceneRadianceToSensorIrradianceInput {\n sceneRadianceRequest:\n SceneRadianceEvaluationRequest;\n sceneRadianceResult:\n SceneRadianceEvaluationResult;\n profile:\n SceneToSensorIrradianceProfile;\n focalLengthMm: number;\n nominalFNumber: number;\n focus:\n OpticalBridgeFocusContext;\n imagePointMm: {\n x: number;\n y: number;\n };\n fieldThroughput:\n OpticalBridgeFieldThroughput;\n frontOfLensFilters?:\n readonly FrontOfLensFilterProfile[];\n}\n\nexport interface SceneToSensorIrradianceResult {\n schemaVersion:\n typeof SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION;\n profileId: string;\n profileVersion: string;\n lensProfileId: string;\n sampleId: string;\n providerProfileId: string;\n sceneId: string;\n timeSecondsFromExposureStart: number;\n wavelengthNanometers: number;\n wavelengthBasis:\n Exclude<\n SpectralWavelengthBasis,\n \"unspecified\"\n >;\n inputQuantity:\n \"outgoing-spectral-radiance\";\n inputUnit:\n \"W/m^2/sr/nm\";\n outputQuantity:\n \"sensor-plane-spectral-irradiance\";\n outputUnit:\n \"W/m^2/nm\";\n sceneSpectralRadianceWattsPerSquareMeterSteradianNanometer:\n number;\n focalLengthMm: number;\n nominalFNumber: number;\n workingFNumber: number;\n focusModel:\n OpticalBridgeFocusContext[\"kind\"];\n paraxialGeometricAcceptanceSolidAngleSteradians:\n number;\n transmissionPath:\n OpticalTransmissionModel[\"kind\"];\n spectralTransmissionFactor:\n number | null;\n effectiveWorkingTStop:\n number | null;\n tStopEquivalentTransmissionFactor:\n number | null;\n frontOfLensFilterCount: number;\n frontOfLensFilterTransmissionFactor:\n number;\n frontOfLensFilterAttenuationStops:\n number;\n frontOfLensFilterTransmissionAppliedExactlyOnce:\n true;\n /**\n * True when a front-of-lens linear polarizer was applied. Scene radiance is unpolarized in this\n * bridge, so a polarizer transmits (k1 + k2) / 2.\n */\n frontOfLensFilterPolarizationModeled:\n boolean;\n fieldThroughputFactor: number;\n effectiveAcceptanceAfterTransmissionAndFieldSteradians:\n number;\n sensorPlaneSpectralIrradianceWattsPerSquareMeterNanometer:\n number;\n scientificStatus: \"approximation\";\n sceneRadianceScientificStatus:\n SceneRadianceEvaluationResult[\"scientificStatus\"];\n opticalProfileScientificStatus:\n OpticalBridgeScientificStatus;\n sceneRadianceUncertainty:\n SceneRadianceEvaluationResult[\"uncertainty\"];\n opticalUncertainty:\n OpticalBridgeUncertainty;\n radianceAmplificationApplied: false;\n fieldThroughputAppliedExactlyOnce:\n true;\n universalCos4FalloffApplied: false;\n distortionAreaCorrectionApplied: false;\n psfRedistributionApplied: false;\n sensorOpticalStackApplied: false;\n cfaApplied: false;\n quantumEfficiencyApplied: false;\n spectralResponsivityApplied: false;\n strayLightApplied: false;\n polarizationModeled: false;\n wavelengthChangingBehaviorModeled:\n false;\n volumetricScatteringModeled: false;\n calibratedSensorPlaneIrradianceClaimAuthorized:\n false;\n componentEvidence: {\n sceneRadiance:\n readonly EvidenceProvenance[];\n opticalProfile:\n readonly EvidenceProvenance[];\n transmission:\n readonly EvidenceProvenance[];\n workingFNumber:\n readonly EvidenceProvenance[];\n frontOfLensFilters:\n readonly EvidenceProvenance[];\n };\n limitations:\n readonly string[];\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.trim();\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\nfunction requirePositiveFinite(\n value: unknown,\n path: string\n): number {\n const parsed =\n requireFinite(value, path);\n if (parsed <= 0) {\n throw new InvalidConfigurationError(\n path +\n \" must be greater than zero.\"\n );\n }\n return parsed;\n}\n\nfunction requireFraction(\n value: unknown,\n path: string\n): number {\n const parsed =\n requireFinite(value, path);\n if (\n parsed < 0 ||\n parsed > 1\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must be from zero through one.\"\n );\n }\n return parsed;\n}\n\nfunction parseRange(\n value: unknown,\n path: string\n): NumericRange {\n const record =\n requireRecord(value, path, [\"minimum\", \"maximum\"]);\n const minimum =\n requirePositiveFinite(\n record.minimum,\n path + \".minimum\"\n );\n const maximum =\n requirePositiveFinite(\n record.maximum,\n path + \".maximum\"\n );\n if (minimum > maximum) {\n throw new InvalidConfigurationError(\n path +\n \".minimum must be less than or equal to maximum.\"\n );\n }\n return {\n minimum,\n maximum\n };\n}\n\nfunction parseScientificStatus(\n value: unknown,\n path: string\n): OpticalBridgeScientificStatus {\n if (\n value !== \"calibrated\" &&\n value !== \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n path + \" is invalid.\"\n );\n }\n return value;\n}\n\nfunction parseUncertainty(\n value: unknown,\n path: string\n): OpticalBridgeUncertainty {\n const record =\n requireRecord(value, path, null);\n rejectUnknownVariantFields(record, \"kind\", UNCERTAINTY_VARIANT_FIELDS, path);\n\n if (record.kind === \"relative\") {\n return {\n kind: \"relative\",\n fraction:\n requireFraction(\n record.fraction,\n path + \".fraction\"\n ),\n basis:\n requireNonEmptyString(\n record.basis,\n path + \".basis\"\n )\n };\n }\n\n if (\n record.kind ===\n \"not-quantified\"\n ) {\n return {\n kind: \"not-quantified\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n path + \".limitation\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parseLimitations(\n value: unknown,\n path: string\n): readonly string[] {\n if (!Array.isArray(value)) {\n throw new InvalidConfigurationError(\n path + \" must be an array.\"\n );\n }\n const parsed = value.map(\n (entry, index) =>\n requireNonEmptyString(\n entry,\n path +\n \"[\" +\n index +\n \"]\"\n )\n );\n if (\n new Set(parsed).size !==\n parsed.length\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must not contain duplicates.\"\n );\n }\n return parsed;\n}\n\nfunction parseReusableNumericEvidence(\n value: unknown,\n path: string\n): readonly EvidenceProvenance[] {\n const evidence =\n parseEvidenceList(value, path);\n\n if (\n evidence.some(\n (entry) =>\n entry.reuseStatus ===\n \"factual-reference-only\"\n )\n ) {\n throw new InvalidConfigurationError(\n path +\n \" for numeric optical curve data must be reusable-data or photivra-owned.\"\n );\n }\n return evidence;\n}\n\nfunction parseSpectralSamples(\n value: unknown,\n path: string\n): readonly SpectralTransmissionSample[] {\n if (\n !Array.isArray(value) ||\n value.length < 2\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must contain at least two samples.\"\n );\n }\n\n let previous =\n Number.NEGATIVE_INFINITY;\n return value.map(\n (entry, index) => {\n const samplePath =\n path +\n \"[\" +\n index +\n \"]\";\n const record =\n requireRecord(\n entry,\n samplePath, [\"wavelengthNanometers\", \"linearTransmissionFactor\"]\n );\n const wavelengthNanometers =\n requirePositiveFinite(\n record.wavelengthNanometers,\n samplePath +\n \".wavelengthNanometers\"\n );\n if (\n wavelengthNanometers <=\n previous\n ) {\n throw new InvalidConfigurationError(\n path +\n \" wavelengths must be strictly increasing.\"\n );\n }\n previous =\n wavelengthNanometers;\n return {\n wavelengthNanometers,\n linearTransmissionFactor:\n requireFraction(\n record.linearTransmissionFactor,\n samplePath +\n \".linearTransmissionFactor\"\n )\n };\n }\n );\n}\n\nfunction parseTransmission(\n value: unknown,\n path: string\n): OpticalTransmissionModel {\n const record =\n requireRecord(value, path, null);\n rejectUnknownVariantFields(record, \"kind\", {\n // workingTStop and wavelengthRangeNanometers (spectral) and samples (working T-stop) are rejected below with their own messages.\n \"spectral-transmission\": [\"kind\", \"scientificStatus\", \"wavelengthBasis\", \"samples\", \"uncertainty\", \"workingTStop\", \"wavelengthRangeNanometers\"],\n \"effective-working-t-stop-approximation\": [\"kind\", \"scientificStatus\", \"workingTStop\", \"wavelengthBasis\", \"wavelengthRangeNanometers\",\n \"uncertainty\", \"limitation\", \"samples\"]\n }, path);\n\n if (\n record.kind ===\n \"spectral-transmission\"\n ) {\n if (\n record.workingTStop !==\n undefined ||\n record.wavelengthRangeNanometers !==\n undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" spectral-transmission must not include T-stop-only fields.\"\n );\n }\n\n const status =\n parseScientificStatus(\n record.scientificStatus,\n path +\n \".scientificStatus\"\n );\n const samplesFact =\n requireRecord(\n record.samples,\n path + \".samples\", [\"value\", \"evidence\"]\n );\n const uncertainty =\n parseUncertainty(\n record.uncertainty,\n path + \".uncertainty\"\n );\n\n if (\n status === \"calibrated\" &&\n uncertainty.kind !==\n \"relative\"\n ) {\n throw new InvalidConfigurationError(\n path +\n \" calibrated spectral transmission requires quantified relative uncertainty.\"\n );\n }\n\n return {\n kind:\n \"spectral-transmission\",\n scientificStatus: status,\n wavelengthBasis:\n parseResolvedBasis(\n record.wavelengthBasis,\n path +\n \".wavelengthBasis\"\n ),\n samples: {\n value:\n parseSpectralSamples(\n samplesFact.value,\n path +\n \".samples.value\"\n ),\n evidence:\n parseReusableNumericEvidence(\n samplesFact.evidence,\n path +\n \".samples.evidence\"\n )\n },\n uncertainty\n };\n }\n\n if (\n record.kind ===\n \"effective-working-t-stop-approximation\"\n ) {\n if (\n record.samples !==\n undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" T-stop approximation must not include spectral transmission samples.\"\n );\n }\n if (\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n path +\n ' T-stop scientificStatus must be \"approximation\".'\n );\n }\n const workingTStopFact =\n requireRecord(\n record.workingTStop,\n path +\n \".workingTStop\", [\"value\", \"evidence\"]\n );\n return {\n kind:\n \"effective-working-t-stop-approximation\",\n scientificStatus:\n \"approximation\",\n workingTStop: {\n value:\n requirePositiveFinite(\n workingTStopFact.value,\n path +\n \".workingTStop.value\"\n ),\n evidence:\n parseEvidenceList(\n workingTStopFact.evidence,\n path +\n \".workingTStop.evidence\"\n )\n },\n wavelengthBasis:\n parseResolvedBasis(\n record.wavelengthBasis,\n path +\n \".wavelengthBasis\"\n ),\n wavelengthRangeNanometers:\n parseRange(\n record.wavelengthRangeNanometers,\n path +\n \".wavelengthRangeNanometers\"\n ),\n uncertainty:\n parseUncertainty(\n record.uncertainty,\n path + \".uncertainty\"\n ),\n limitation:\n requireNonEmptyString(\n record.limitation,\n path +\n \".limitation\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parseResolvedBasis(\n value: unknown,\n path: string\n): Exclude<\n SpectralWavelengthBasis,\n \"unspecified\"\n> {\n const parsed =\n parseSpectralWavelengthBasis(\n value,\n path\n );\n if (parsed === \"unspecified\") {\n throw new InvalidConfigurationError(\n path +\n ' must resolve to \"air\" or \"vacuum\".'\n );\n }\n return parsed;\n}\n\nfunction parseFocusApplicability(\n value: unknown,\n path: string\n): OpticalBridgeFocusApplicability {\n const record =\n requireRecord(value, path, null);\n rejectUnknownVariantFields(record, \"kind\", { any: [\"kind\"], \"finite-distance-range\": [\"kind\", \"objectDistanceM\"] }, path);\n\n if (record.kind === \"any\") {\n return {\n kind: \"any\"\n };\n }\n if (\n record.kind ===\n \"finite-distance-range\"\n ) {\n return {\n kind:\n \"finite-distance-range\",\n objectDistanceM:\n parseRange(\n record.objectDistanceM,\n path +\n \".objectDistanceM\"\n )\n };\n }\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\n/**\n * Validates an untrusted scene-to-sensor profile and returns the normalized typed contract. Unknown\n * enum values, missing required fields and incompatible scientific data are rejected here.\n *\n * The optical bridge turns outgoing scene radiance (W/m²/sr/nm) into irradiance (W/m²/nm) arriving\n * at the front of the sensor stack.\n * - Circular paraxial acceptance is pi/(4*Nworking²). At finite focus with unit pupil magnification,\n * the working f-number is N*(1+m).\n * - Spectral transmission and an effective working T-stop are alternative paths; use one.\n * - Field throughput is applied exactly once.\n * - Sensor response and PSF (point-spread function) blur happen later in the pipeline.\n * @param value - unknown. Untrusted input; TypeScript types alone are not validation.\n * @returns SceneToSensorIrradianceProfile. The result states its shape and scientific status; a successful call does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the root export and exact type graph.\n */\nexport function parseSceneToSensorIrradianceProfile(\n value: unknown\n): SceneToSensorIrradianceProfile {\n const record =\n requireRecord(\n value,\n \"sceneToSensorIrradianceProfile\", [\"schemaVersion\", \"applicability\", \"scientificStatus\", \"transmission\", \"distortionAreaMappingOwnership\", \"psfRedistributionOwnership\", \"sensorOpticalStackIncluded\", \"strayLightIncluded\", \"polarizationModeled\", \"wavelengthChangingBehaviorModeled\", \"volumetricScatteringModeled\", \"profileId\", \"profileVersion\", \"lensProfileId\", \"evidence\", \"limitations\"]\n );\n if (\n record.schemaVersion !==\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'sceneToSensorIrradianceProfile.schemaVersion must be \"' +\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION +\n '\".'\n );\n }\n\n const applicability =\n requireRecord(\n record.applicability,\n \"sceneToSensorIrradianceProfile.applicability\", [\"focalLengthMm\", \"nominalFNumber\", \"focus\"]\n );\n const status =\n parseScientificStatus(\n record.scientificStatus,\n \"sceneToSensorIrradianceProfile.scientificStatus\"\n );\n const transmission =\n parseTransmission(\n record.transmission,\n \"sceneToSensorIrradianceProfile.transmission\"\n );\n\n if (\n status === \"calibrated\" &&\n transmission.scientificStatus !==\n \"calibrated\"\n ) {\n throw new InvalidConfigurationError(\n \"A calibrated optical bridge profile requires calibrated spectral transmission.\"\n );\n }\n\n if (\n record.distortionAreaMappingOwnership !==\n \"not-applied-by-bridge\" ||\n record.psfRedistributionOwnership !==\n \"downstream-normalized-energy-redistribution\" ||\n record.sensorOpticalStackIncluded !==\n false ||\n record.strayLightIncluded !==\n false ||\n record.polarizationModeled !==\n false ||\n record.wavelengthChangingBehaviorModeled !==\n false ||\n record.volumetricScatteringModeled !==\n false\n ) {\n throw new InvalidConfigurationError(\n \"sceneToSensorIrradianceProfile ownership/completeness flags are invalid for schema 0.1.0.\"\n );\n }\n\n return {\n schemaVersion:\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION,\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"sceneToSensorIrradianceProfile.profileId\"\n ),\n profileVersion:\n requireNonEmptyString(\n record.profileVersion,\n \"sceneToSensorIrradianceProfile.profileVersion\"\n ),\n lensProfileId:\n requireNonEmptyString(\n record.lensProfileId,\n \"sceneToSensorIrradianceProfile.lensProfileId\"\n ),\n scientificStatus: status,\n applicability: {\n focalLengthMm:\n parseRange(\n applicability.focalLengthMm,\n \"sceneToSensorIrradianceProfile.applicability.focalLengthMm\"\n ),\n nominalFNumber:\n parseRange(\n applicability.nominalFNumber,\n \"sceneToSensorIrradianceProfile.applicability.nominalFNumber\"\n ),\n focus:\n parseFocusApplicability(\n applicability.focus,\n \"sceneToSensorIrradianceProfile.applicability.focus\"\n )\n },\n transmission,\n distortionAreaMappingOwnership:\n \"not-applied-by-bridge\",\n psfRedistributionOwnership:\n \"downstream-normalized-energy-redistribution\",\n sensorOpticalStackIncluded:\n false,\n strayLightIncluded: false,\n polarizationModeled: false,\n wavelengthChangingBehaviorModeled:\n false,\n volumetricScatteringModeled:\n false,\n evidence:\n parseEvidenceList(\n record.evidence,\n \"sceneToSensorIrradianceProfile.evidence\"\n ),\n limitations:\n parseLimitations(\n record.limitations,\n \"sceneToSensorIrradianceProfile.limitations\"\n )\n };\n}\n\nfunction ensureWithin(\n value: number,\n range: NumericRange,\n path: string\n): void {\n if (\n value < range.minimum ||\n value > range.maximum\n ) {\n throw new InvalidScientificInputError(\n path +\n \" lies outside the optical profile applicability range.\"\n );\n }\n}\n\n/**\n * Internal (2.6; not exported from the package): N_w / N = 1 + m for an ideal symmetric thin lens with unity pupil\n * magnification focused at `objectDistanceM`, m = v/s from `calculateThinLensImageDistance`. The irradiance bridge's\n * `ideal-symmetric-thin-lens` focus uses exactly this factor, and the framing reports derive their close-up light\n * factor 1/(1 + m)² from it, so the two cannot drift apart.\n */\nexport function thinLensWorkingFNumberFactor(focalLengthMm: number, objectDistanceM: number): number {\n return 1 + calculateThinLensImageDistance({ focalLengthMm, objectDistanceM }).value.magnification;\n}\n\nfunction workingFNumber(\n focus:\n OpticalBridgeFocusContext,\n focalLengthMm: number,\n nominalFNumber: number\n): {\n value: number;\n evidence:\n readonly EvidenceProvenance[];\n} {\n if (\n focus.kind === \"infinity-focus\"\n ) {\n return {\n value: nominalFNumber,\n evidence: []\n };\n }\n\n if (\n focus.kind ===\n \"ideal-symmetric-thin-lens\"\n ) {\n if (\n focus\n .pupilMagnificationAssumption !==\n \"unity\"\n ) {\n throw new InvalidScientificInputError(\n 'Ideal symmetric thin-lens focus requires pupilMagnificationAssumption \"unity\".'\n );\n }\n return {\n value:\n nominalFNumber *\n thinLensWorkingFNumberFactor(focalLengthMm, focus.objectDistanceM),\n evidence: []\n };\n }\n\n if (\n focus.kind ===\n \"supplied-working-f-number\"\n ) {\n const value =\n requirePositiveFiniteScientific(\n focus.workingFNumber.value,\n \"focus.workingFNumber.value\"\n );\n return {\n value,\n evidence:\n parseEvidenceList(\n focus.workingFNumber.evidence,\n \"focus.workingFNumber.evidence\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n \"focus.kind is invalid.\"\n );\n}\n\nfunction requirePositiveFiniteScientific(\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 zero.\"\n );\n }\n return value;\n}\n\nfunction finiteFocusDistance(\n focus:\n OpticalBridgeFocusContext\n): number | null {\n if (\n focus.kind ===\n \"ideal-symmetric-thin-lens\"\n ) {\n return requirePositiveFiniteScientific(\n focus.objectDistanceM,\n \"focus.objectDistanceM\"\n );\n }\n if (\n focus.kind ===\n \"supplied-working-f-number\" &&\n focus.focus.kind === \"finite\"\n ) {\n return requirePositiveFiniteScientific(\n focus.focus.objectDistanceM,\n \"focus.focus.objectDistanceM\"\n );\n }\n return null;\n}\n\nfunction interpolateTransmission(\n model:\n Extract<\n OpticalTransmissionModel,\n { kind: \"spectral-transmission\" }\n >,\n wavelengthNanometers: number\n): number {\n const samples =\n model.samples.value;\n const first =\n samples[0]!;\n const last =\n samples[\n samples.length - 1\n ]!;\n\n if (\n wavelengthNanometers <\n first.wavelengthNanometers ||\n wavelengthNanometers >\n last.wavelengthNanometers\n ) {\n throw new InvalidScientificInputError(\n \"Scene wavelength lies outside the spectral transmission sample range.\"\n );\n }\n\n if (\n wavelengthNanometers ===\n first.wavelengthNanometers\n ) {\n return first\n .linearTransmissionFactor;\n }\n\n for (\n let index = 1;\n index < samples.length;\n index += 1\n ) {\n const right =\n samples[index]!;\n if (\n wavelengthNanometers <=\n right.wavelengthNanometers\n ) {\n const left =\n samples[index - 1]!;\n if (\n wavelengthNanometers ===\n right.wavelengthNanometers\n ) {\n return right\n .linearTransmissionFactor;\n }\n const phase =\n (wavelengthNanometers -\n left.wavelengthNanometers) /\n (right.wavelengthNanometers -\n left.wavelengthNanometers);\n return (\n left\n .linearTransmissionFactor +\n phase *\n (right\n .linearTransmissionFactor -\n left\n .linearTransmissionFactor)\n );\n }\n }\n\n return last\n .linearTransmissionFactor;\n}\n\nfunction fieldFactor(\n field:\n OpticalBridgeFieldThroughput,\n imagePointMm: {\n x: number;\n y: number;\n }\n): number {\n if (field.kind === \"unity\") {\n return 1;\n }\n if (\n field.kind ===\n \"illumination-vignetting-result\"\n ) {\n const result =\n field.result;\n const scale = Math.max(\n 1,\n Math.abs(imagePointMm.x),\n Math.abs(imagePointMm.y),\n Math.abs(result.imagePointMm.x),\n Math.abs(result.imagePointMm.y)\n );\n const tolerance =\n Number.EPSILON *\n 64 *\n scale;\n if (\n Math.abs(\n result.imagePointMm.x -\n imagePointMm.x\n ) > tolerance ||\n Math.abs(\n result.imagePointMm.y -\n imagePointMm.y\n ) > tolerance\n ) {\n throw new InvalidScientificInputError(\n \"Illumination-vignetting result imagePointMm must match the optical bridge imagePointMm.\"\n );\n }\n const factor =\n result.linearThroughputFactor;\n if (\n !Number.isFinite(factor) ||\n factor <= 0 ||\n factor > 1\n ) {\n throw new InvalidScientificInputError(\n \"Field throughput factor must be finite and in (0, 1].\"\n );\n }\n return factor;\n }\n throw new InvalidConfigurationError(\n \"fieldThroughput.kind is invalid.\"\n );\n}\n\nfunction validateSceneIdentity(\n request:\n SceneRadianceEvaluationRequest,\n result:\n SceneRadianceEvaluationResult\n): void {\n if (\n request.sampleId !==\n result.sampleId ||\n request.providerProfileId !==\n result.providerProfileId ||\n request.sceneId !==\n result.sceneId ||\n request.wavelengthNanometers !==\n result.wavelengthNanometers ||\n request.wavelengthBasis !==\n result.wavelengthBasis\n ) {\n throw new InvalidScientificInputError(\n \"Scene-radiance request/result identity must match exactly.\"\n );\n }\n}\n\n/**\n * The request-independent part of {@link calculateSceneRadianceToSensorIrradiance}: the profile, focal length, f-number,\n * applicability, focus and working f-number checks and the paraxial acceptance, in the public function's order.\n */\ninterface PreparedSceneRadianceToSensorIrradiance {\n profile: ReturnType<typeof parseSceneToSensorIrradianceProfile>;\n focalLengthMm: number;\n nominalFNumber: number;\n working: ReturnType<typeof workingFNumber>;\n workingFNumberValue: number;\n geometricAcceptance: number;\n transmission: ReturnType<typeof parseSceneToSensorIrradianceProfile>[\"transmission\"];\n}\n\nfunction prepareSceneRadianceToSensorIrradiance(\n input: Pick<CalculateSceneRadianceToSensorIrradianceInput, \"profile\" | \"focalLengthMm\" | \"nominalFNumber\" | \"focus\">\n): PreparedSceneRadianceToSensorIrradiance {\n const profile =\n parseSceneToSensorIrradianceProfile(\n input.profile\n );\n const focalLengthMm =\n requirePositiveFiniteScientific(\n input.focalLengthMm,\n \"focalLengthMm\"\n );\n const nominalFNumber =\n requirePositiveFiniteScientific(\n input.nominalFNumber,\n \"nominalFNumber\"\n );\n\n ensureWithin(\n focalLengthMm,\n profile.applicability\n .focalLengthMm,\n \"focalLengthMm\"\n );\n ensureWithin(\n nominalFNumber,\n profile.applicability\n .nominalFNumber,\n \"nominalFNumber\"\n );\n\n const focusDistanceM =\n finiteFocusDistance(input.focus);\n if (\n profile.applicability.focus.kind ===\n \"finite-distance-range\"\n ) {\n if (focusDistanceM === null) {\n throw new InvalidScientificInputError(\n \"Optical profile requires a finite focus distance.\"\n );\n }\n ensureWithin(\n focusDistanceM,\n profile.applicability\n .focus.objectDistanceM,\n \"focus object distance\"\n );\n }\n\n const working =\n workingFNumber(\n input.focus,\n focalLengthMm,\n nominalFNumber\n );\n const workingFNumberValue =\n working.value;\n const geometricAcceptance =\n Math.PI /\n (4 *\n workingFNumberValue *\n workingFNumberValue);\n\n if (\n !Number.isFinite(\n geometricAcceptance\n ) ||\n geometricAcceptance <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Paraxial geometric acceptance must remain finite and greater than zero.\"\n );\n }\n\n const transmission =\n profile.transmission;\n return { profile, focalLengthMm, nominalFNumber, working, workingFNumberValue, geometricAcceptance, transmission };\n}\n\ninterface AppliedSceneRadianceToSensorIrradiance {\n spectralTransmissionFactor: number | null;\n effectiveWorkingTStop: number | null;\n tStopEquivalentTransmissionFactor: number | null;\n effectiveAcceptance: number;\n transmissionEvidence: readonly EvidenceProvenance[];\n frontOfLensFilterCount: number;\n frontOfLensFilterPolarizationModeled: boolean;\n frontOfLensFilterTransmissionFactor: number;\n frontOfLensFilterAttenuationStops: number;\n frontOfLensFilterEvidence: readonly EvidenceProvenance[];\n frontOfLensFilterLimitations: readonly string[];\n fieldThroughputFactor: number;\n sceneRadiance: number;\n irradiance: number;\n}\n\n/**\n * The per-query part of {@link calculateSceneRadianceToSensorIrradiance}, after the request and result are parsed and their\n * identities matched: the wavelength-basis check, the transmission or T-stop path, the front-of-lens filters, the field\n * throughput and the irradiance, with the same operations in the same order.\n */\nfunction applySceneRadianceToSensorIrradiance(\n prepared: PreparedSceneRadianceToSensorIrradiance,\n input: Pick<CalculateSceneRadianceToSensorIrradianceInput, \"imagePointMm\" | \"fieldThroughput\"> &\n { frontOfLensFilters?: readonly FrontOfLensFilterProfile[] | undefined },\n request: SceneRadianceEvaluationRequest,\n sceneResult: SceneRadianceEvaluationResult\n): AppliedSceneRadianceToSensorIrradiance {\n const { workingFNumberValue, geometricAcceptance, transmission } = prepared;\n if (\n request.wavelengthBasis !==\n transmission.wavelengthBasis\n ) {\n throw new InvalidScientificInputError(\n \"Scene-radiance and optical-transmission wavelength bases must match; implicit air/vacuum conversion is not performed.\"\n );\n }\n\n let spectralTransmissionFactor:\n number | null = null;\n let effectiveWorkingTStop:\n number | null = null;\n let tStopEquivalentTransmissionFactor:\n number | null = null;\n let effectiveAcceptance =\n geometricAcceptance;\n let transmissionEvidence:\n readonly EvidenceProvenance[];\n\n if (\n transmission.kind ===\n \"spectral-transmission\"\n ) {\n spectralTransmissionFactor =\n interpolateTransmission(\n transmission,\n request.wavelengthNanometers\n );\n effectiveAcceptance *=\n spectralTransmissionFactor;\n transmissionEvidence =\n transmission.samples.evidence;\n } else {\n ensureWithin(\n request.wavelengthNanometers,\n transmission\n .wavelengthRangeNanometers,\n \"scene wavelength\"\n );\n effectiveWorkingTStop =\n transmission.workingTStop.value;\n if (\n effectiveWorkingTStop <\n workingFNumberValue\n ) {\n throw new InvalidScientificInputError(\n \"effective working T-stop must not be smaller than the geometric working f-number for a passive optical approximation.\"\n );\n }\n tStopEquivalentTransmissionFactor =\n (workingFNumberValue /\n effectiveWorkingTStop) **\n 2;\n effectiveAcceptance =\n Math.PI /\n (4 *\n effectiveWorkingTStop *\n effectiveWorkingTStop);\n transmissionEvidence =\n transmission\n .workingTStop.evidence;\n }\n\n let frontOfLensFilterCount = 0;\n let frontOfLensFilterPolarizationModeled = false;\n let frontOfLensFilterTransmissionFactor = 1;\n let frontOfLensFilterAttenuationStops = 0;\n let frontOfLensFilterEvidence:\n readonly EvidenceProvenance[] = [];\n let frontOfLensFilterLimitations:\n readonly string[] = [];\n\n if (\n input.frontOfLensFilters !==\n undefined\n ) {\n const composed =\n composeFrontOfLensFilterTransmission({\n filters:\n input.frontOfLensFilters,\n wavelengthNanometers:\n request.wavelengthNanometers,\n wavelengthBasis:\n request.wavelengthBasis\n });\n frontOfLensFilterCount =\n composed.appliedFilterCount;\n frontOfLensFilterPolarizationModeled =\n composed.polarizationModeled;\n frontOfLensFilterTransmissionFactor =\n composed\n .combinedLinearTransmissionFactor;\n frontOfLensFilterAttenuationStops =\n composed\n .combinedAttenuationStops;\n frontOfLensFilterEvidence =\n composed.components.flatMap(\n (component) =>\n component.evidence\n );\n frontOfLensFilterLimitations =\n composed.components.flatMap(\n (component) =>\n component.limitations\n );\n effectiveAcceptance *=\n frontOfLensFilterTransmissionFactor;\n }\n\n const fieldThroughputFactor =\n fieldFactor(\n input.fieldThroughput,\n input.imagePointMm\n );\n effectiveAcceptance *=\n fieldThroughputFactor;\n\n const sceneRadiance =\n sceneResult\n .spectralRadianceWattsPerSquareMeterSteradianNanometer;\n const irradiance =\n sceneRadiance *\n effectiveAcceptance;\n\n if (\n !Number.isFinite(irradiance) ||\n irradiance < 0\n ) {\n throw new InvalidScientificInputError(\n \"Sensor-plane spectral irradiance must remain finite and non-negative.\"\n );\n }\n\n return { spectralTransmissionFactor, effectiveWorkingTStop, tStopEquivalentTransmissionFactor, effectiveAcceptance, transmissionEvidence,\n frontOfLensFilterCount, frontOfLensFilterPolarizationModeled, frontOfLensFilterTransmissionFactor, frontOfLensFilterAttenuationStops,\n frontOfLensFilterEvidence, frontOfLensFilterLimitations, fieldThroughputFactor, sceneRadiance, irradiance };\n}\n\n/**\n * Internal (#453; not exported from the package): the sensor-plane spectral irradiance that\n * {@link calculateSceneRadianceToSensorIrradiance} returns, for repeated queries of one optical state. This call runs the\n * request-independent checks once (the public function's own code, in its order); each call of the returned function then\n * runs the public function's per-query steps on an engine-built request and a result already parsed by the caller: the identity\n * match, the wavelength-basis check, the transmission or T-stop path, the front-of-lens filters, the field throughput and\n * the finite, non-negative irradiance, with the same operations, order and errors. It skips only the re-validation of the\n * unchanged optical inputs, the re-parse of the request and result, and the result envelope.\n */\nexport function prepareSceneRadianceToSensorIrradianceValue(\n optics: Pick<CalculateSceneRadianceToSensorIrradianceInput, \"profile\" | \"focalLengthMm\" | \"nominalFNumber\" | \"focus\" | \"frontOfLensFilters\">\n): (request: SceneRadianceEvaluationRequest, result: SceneRadianceEvaluationResult, imagePointMm: { x: number; y: number },\n fieldThroughput: OpticalBridgeFieldThroughput) => number {\n const prepared = prepareSceneRadianceToSensorIrradiance(optics);\n const frontOfLensFilters = optics.frontOfLensFilters;\n return (request, result, imagePointMm, fieldThroughput) => {\n validateSceneIdentity(request, result);\n return applySceneRadianceToSensorIrradiance(prepared, { imagePointMm, fieldThroughput, frontOfLensFilters }, request, result).irradiance;\n };\n}\n\n/**\n * Applies the declared pupil acceptance and exactly one transmission or field-throughput path to\n * spectral scene radiance. Returns the irradiance before the sensor stack, with conservative\n * evidence.\n *\n * The optical bridge turns outgoing scene radiance (W/m²/sr/nm) into irradiance (W/m²/nm) arriving\n * at the front of the sensor stack.\n * - Circular paraxial acceptance is pi/(4*Nworking²). At finite focus with unit pupil magnification,\n * the working f-number is N*(1+m).\n * - Spectral transmission and an effective working T-stop are alternative paths; use one.\n * - Field throughput is applied exactly once.\n * - Sensor response and PSF (point-spread function) blur happen later in the pipeline.\n * @param input - CalculateSceneRadianceToSensorIrradianceInput. See that type for coordinates, units and profile binding.\n * @returns CalculationResult<\n SceneToSensorIrradianceResult\n>. The result states its shape and scientific status; a successful call does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the root export and exact type graph.\n */\nexport function calculateSceneRadianceToSensorIrradiance(\n input:\n CalculateSceneRadianceToSensorIrradianceInput\n): CalculationResult<\n SceneToSensorIrradianceResult\n> {\n requireInputKeys(input, \"calculateSceneRadianceToSensorIrradiance\", [\"sceneRadianceRequest\", \"sceneRadianceResult\", \"profile\", \"focalLengthMm\", \"nominalFNumber\", \"focus\", \"imagePointMm\", \"fieldThroughput\", \"frontOfLensFilters\"]);\n const request =\n parseSceneRadianceEvaluationRequest(\n input.sceneRadianceRequest\n );\n const sceneResult =\n parseSceneRadianceEvaluationResult(\n input.sceneRadianceResult\n );\n validateSceneIdentity(\n request,\n sceneResult\n );\n\n const prepared = prepareSceneRadianceToSensorIrradiance(input);\n const { profile, focalLengthMm, nominalFNumber, working, workingFNumberValue, geometricAcceptance, transmission } = prepared;\n const { spectralTransmissionFactor, effectiveWorkingTStop, tStopEquivalentTransmissionFactor, effectiveAcceptance, transmissionEvidence,\n frontOfLensFilterCount, frontOfLensFilterPolarizationModeled, frontOfLensFilterTransmissionFactor, frontOfLensFilterAttenuationStops,\n frontOfLensFilterEvidence, frontOfLensFilterLimitations, fieldThroughputFactor, sceneRadiance, irradiance } =\n applySceneRadianceToSensorIrradiance(prepared, input, request, sceneResult);\n\n const limitations = [\n ...profile.limitations,\n ...frontOfLensFilterLimitations,\n \"Front-of-lens filter transmission is applied outside the lens throughput path exactly once and does not change geometric working f-number.\",\n \"Paraxial circular-pupil acceptance uses pi/(4*N_working^2); no universal cos^4 field falloff is added.\",\n \"Field throughput is supplied separately and applied exactly once.\",\n \"Geometric distortion area-density/Jacobian correction is not applied by this bridge.\",\n \"PSF/diffraction energy redistribution is downstream and must preserve normalized energy apart from separately owned throughput losses.\",\n \"Sensor optical stack, microlens, CFA, QE/responsivity and charge conversion are downstream.\",\n frontOfLensFilterPolarizationModeled\n ? \"Flare, ghosting, veiling glare, scene-radiance polarization (scene light is treated as unpolarized; the front-of-lens linear polarizer transmits (k1 + k2) / 2), volumetric scattering and wavelength-changing behavior are not modeled.\"\n : \"Flare, ghosting, veiling glare, polarization, volumetric scattering and wavelength-changing behavior are not modeled.\"\n ];\n\n return approximationResult(\n {\n schemaVersion:\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION,\n profileId: profile.profileId,\n profileVersion:\n profile.profileVersion,\n lensProfileId:\n profile.lensProfileId,\n sampleId: request.sampleId,\n providerProfileId:\n request.providerProfileId,\n sceneId: request.sceneId,\n timeSecondsFromExposureStart:\n request\n .timeSecondsFromExposureStart,\n wavelengthNanometers:\n request.wavelengthNanometers,\n wavelengthBasis:\n request.wavelengthBasis,\n inputQuantity:\n \"outgoing-spectral-radiance\",\n inputUnit:\n \"W/m^2/sr/nm\",\n outputQuantity:\n \"sensor-plane-spectral-irradiance\",\n outputUnit: \"W/m^2/nm\",\n sceneSpectralRadianceWattsPerSquareMeterSteradianNanometer:\n sceneRadiance,\n focalLengthMm,\n nominalFNumber,\n workingFNumber:\n workingFNumberValue,\n focusModel: input.focus.kind,\n paraxialGeometricAcceptanceSolidAngleSteradians:\n geometricAcceptance,\n transmissionPath:\n transmission.kind,\n spectralTransmissionFactor,\n effectiveWorkingTStop,\n tStopEquivalentTransmissionFactor,\n frontOfLensFilterCount,\n frontOfLensFilterTransmissionFactor,\n frontOfLensFilterAttenuationStops,\n frontOfLensFilterTransmissionAppliedExactlyOnce:\n true,\n frontOfLensFilterPolarizationModeled,\n fieldThroughputFactor,\n effectiveAcceptanceAfterTransmissionAndFieldSteradians:\n effectiveAcceptance,\n sensorPlaneSpectralIrradianceWattsPerSquareMeterNanometer:\n irradiance,\n scientificStatus:\n \"approximation\",\n sceneRadianceScientificStatus:\n sceneResult.scientificStatus,\n opticalProfileScientificStatus:\n profile.scientificStatus,\n sceneRadianceUncertainty:\n sceneResult.uncertainty,\n opticalUncertainty:\n transmission.uncertainty,\n radianceAmplificationApplied:\n false,\n fieldThroughputAppliedExactlyOnce:\n true,\n universalCos4FalloffApplied:\n false,\n distortionAreaCorrectionApplied:\n false,\n psfRedistributionApplied:\n false,\n sensorOpticalStackApplied:\n false,\n cfaApplied: false,\n quantumEfficiencyApplied: false,\n spectralResponsivityApplied:\n false,\n strayLightApplied: false,\n polarizationModeled: false,\n wavelengthChangingBehaviorModeled:\n false,\n volumetricScatteringModeled:\n false,\n calibratedSensorPlaneIrradianceClaimAuthorized:\n false,\n componentEvidence: {\n sceneRadiance:\n sceneResult.evidence,\n opticalProfile:\n profile.evidence,\n transmission:\n transmissionEvidence,\n workingFNumber:\n working.evidence,\n frontOfLensFilters:\n frontOfLensFilterEvidence\n },\n limitations\n },\n \"scene-radiance-to-sensor-irradiance\",\n \"1.1.0\",\n limitations\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"scene-to-sensor-irradiance.js","sourceRoot":"","sources":["../../src/optics/scene-to-sensor-irradiance.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Scene-to-sensor optical bridge: from spectral scene radiance to the irradiance that reaches the\n * sensor.\n *\n * The profile parser validates untrusted input and returns the normalized typed contract. Unknown\n * enum values, missing required fields and incompatible scientific data are rejected here.\n *\n * The calculation applies the declared pupil acceptance and exactly one transmission or\n * field-throughput path. It returns the irradiance before the sensor stack, with conservative\n * evidence.\n *\n * The optical bridge turns outgoing scene radiance (W/m²/sr/nm) into irradiance (W/m²/nm) arriving\n * at the front of the sensor stack.\n * - Circular paraxial acceptance is pi/(4*Nworking²). At finite focus with unit pupil magnification,\n * the working f-number is N*(1+m).\n * - Spectral transmission and an effective working T-stop are alternative paths; use one.\n * - Field throughput is applied exactly once.\n * - Sensor response and PSF (point-spread function) blur happen later in the pipeline.\n * @see docs/SCENE_SENSOR_QUADRATURE.md for the equations, units, coordinate conventions, known\n * blockers and support limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceBackedFact,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport { type SpectralWavelengthBasis } from \"../core/spectral.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport { composeFrontOfLensFilterTransmission, type FrontOfLensFilterProfile, parseRange, parseResolvedBasis } from \"./front-of-lens-filter.js\";\nimport type { IlluminationVignetting } from \"./illumination-vignetting.js\";\nimport { calculateThinLensImageDistance } from \"./thin-lens.js\";\nimport {\n parseSceneRadianceEvaluationRequest,\n parseSceneRadianceEvaluationResult,\n type SceneRadianceEvaluationRequest,\n type SceneRadianceEvaluationResult\n} from \"../schema/scene-radiance.js\";\nimport { requireInputKeys, rejectUnknownVariantFields, UNCERTAINTY_VARIANT_FIELDS, requireConfigRecord as requireRecord, requireTrimmedConfigString as requireNonEmptyString, parseConfigScientificStatus as parseScientificStatus, requireInputPositiveFinite as requirePositiveFiniteScientific, parseConfigLimitations as parseLimitations, requireConfigFiniteAboveZero as requirePositiveFinite } from \"../core/record-validation.js\";\nimport { requireFraction } from \"../color/white-balance.js\";\n\n/**\n * The scope fields every engine-built scene-to-sensor profile declares, in this order (shared by the capture facade and the\n * generic tier presets): the bridge leaves distortion area mapping and PSF redistribution downstream and models none of the rest.\n */\nexport const SCENE_TO_SENSOR_BRIDGE_SCOPE = /* @__PURE__ */ Object.freeze({ distortionAreaMappingOwnership: \"not-applied-by-bridge\" as const,\n psfRedistributionOwnership: \"downstream-normalized-energy-redistribution\" as const, sensorOpticalStackIncluded: false as const, strayLightIncluded: false as const,\n polarizationModeled: false as const, wavelengthChangingBehaviorModeled: false as const, volumetricScatteringModeled: false as const });\n\nexport const SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION =\n \"0.1.0\" as const;\n\nexport type OpticalBridgeScientificStatus =\n | \"calibrated\"\n | \"approximation\";\n\nexport type OpticalBridgeUncertainty =\n | {\n kind: \"relative\";\n fraction: number;\n basis: string;\n }\n | {\n kind: \"not-quantified\";\n limitation: string;\n };\n\nexport interface NumericRange {\n minimum: number;\n maximum: number;\n}\n\nexport type OpticalBridgeFocusApplicability =\n | {\n kind: \"any\";\n }\n | {\n kind: \"finite-distance-range\";\n objectDistanceM: NumericRange;\n };\n\nexport interface SpectralTransmissionSample {\n wavelengthNanometers: number;\n linearTransmissionFactor: number;\n}\n\nexport type OpticalTransmissionModel =\n | {\n kind: \"spectral-transmission\";\n scientificStatus:\n OpticalBridgeScientificStatus;\n wavelengthBasis:\n Exclude<\n SpectralWavelengthBasis,\n \"unspecified\"\n >;\n samples:\n EvidenceBackedFact<\n readonly SpectralTransmissionSample[]\n >;\n uncertainty:\n OpticalBridgeUncertainty;\n }\n | {\n kind:\n \"effective-working-t-stop-approximation\";\n scientificStatus: \"approximation\";\n workingTStop:\n EvidenceBackedFact<number>;\n wavelengthBasis:\n Exclude<\n SpectralWavelengthBasis,\n \"unspecified\"\n >;\n wavelengthRangeNanometers:\n NumericRange;\n uncertainty:\n OpticalBridgeUncertainty;\n limitation: string;\n };\n\nexport interface SceneToSensorIrradianceProfile {\n schemaVersion:\n typeof SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION;\n profileId: string;\n profileVersion: string;\n lensProfileId: string;\n scientificStatus:\n OpticalBridgeScientificStatus;\n applicability: {\n focalLengthMm: NumericRange;\n nominalFNumber: NumericRange;\n focus:\n OpticalBridgeFocusApplicability;\n };\n transmission:\n OpticalTransmissionModel;\n distortionAreaMappingOwnership:\n \"not-applied-by-bridge\";\n psfRedistributionOwnership:\n \"downstream-normalized-energy-redistribution\";\n sensorOpticalStackIncluded: false;\n strayLightIncluded: false;\n polarizationModeled: false;\n wavelengthChangingBehaviorModeled: false;\n volumetricScatteringModeled: false;\n evidence: readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\nexport type OpticalBridgeFocusContext =\n | {\n kind: \"infinity-focus\";\n }\n | {\n kind:\n \"ideal-symmetric-thin-lens\";\n objectDistanceM: number;\n pupilMagnificationAssumption:\n \"unity\";\n }\n | {\n kind:\n \"supplied-working-f-number\";\n focus:\n | {\n kind: \"infinity\";\n }\n | {\n kind: \"finite\";\n objectDistanceM: number;\n };\n workingFNumber:\n EvidenceBackedFact<number>;\n basis: string;\n };\n\nexport type OpticalBridgeFieldThroughput =\n | {\n kind: \"unity\";\n }\n | {\n kind:\n \"illumination-vignetting-result\";\n result:\n IlluminationVignetting;\n };\n\nexport interface CalculateSceneRadianceToSensorIrradianceInput {\n sceneRadianceRequest:\n SceneRadianceEvaluationRequest;\n sceneRadianceResult:\n SceneRadianceEvaluationResult;\n profile:\n SceneToSensorIrradianceProfile;\n focalLengthMm: number;\n nominalFNumber: number;\n focus:\n OpticalBridgeFocusContext;\n imagePointMm: {\n x: number;\n y: number;\n };\n fieldThroughput:\n OpticalBridgeFieldThroughput;\n frontOfLensFilters?:\n readonly FrontOfLensFilterProfile[];\n}\n\nexport interface SceneToSensorIrradianceResult {\n schemaVersion:\n typeof SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION;\n profileId: string;\n profileVersion: string;\n lensProfileId: string;\n sampleId: string;\n providerProfileId: string;\n sceneId: string;\n timeSecondsFromExposureStart: number;\n wavelengthNanometers: number;\n wavelengthBasis:\n Exclude<\n SpectralWavelengthBasis,\n \"unspecified\"\n >;\n inputQuantity:\n \"outgoing-spectral-radiance\";\n inputUnit:\n \"W/m^2/sr/nm\";\n outputQuantity:\n \"sensor-plane-spectral-irradiance\";\n outputUnit:\n \"W/m^2/nm\";\n sceneSpectralRadianceWattsPerSquareMeterSteradianNanometer:\n number;\n focalLengthMm: number;\n nominalFNumber: number;\n workingFNumber: number;\n focusModel:\n OpticalBridgeFocusContext[\"kind\"];\n paraxialGeometricAcceptanceSolidAngleSteradians:\n number;\n transmissionPath:\n OpticalTransmissionModel[\"kind\"];\n spectralTransmissionFactor:\n number | null;\n effectiveWorkingTStop:\n number | null;\n tStopEquivalentTransmissionFactor:\n number | null;\n frontOfLensFilterCount: number;\n frontOfLensFilterTransmissionFactor:\n number;\n frontOfLensFilterAttenuationStops:\n number;\n frontOfLensFilterTransmissionAppliedExactlyOnce:\n true;\n /**\n * True when a front-of-lens linear polarizer was applied. Scene radiance is unpolarized in this\n * bridge, so a polarizer transmits (k1 + k2) / 2.\n */\n frontOfLensFilterPolarizationModeled:\n boolean;\n fieldThroughputFactor: number;\n effectiveAcceptanceAfterTransmissionAndFieldSteradians:\n number;\n sensorPlaneSpectralIrradianceWattsPerSquareMeterNanometer:\n number;\n scientificStatus: \"approximation\";\n sceneRadianceScientificStatus:\n SceneRadianceEvaluationResult[\"scientificStatus\"];\n opticalProfileScientificStatus:\n OpticalBridgeScientificStatus;\n sceneRadianceUncertainty:\n SceneRadianceEvaluationResult[\"uncertainty\"];\n opticalUncertainty:\n OpticalBridgeUncertainty;\n radianceAmplificationApplied: false;\n fieldThroughputAppliedExactlyOnce:\n true;\n universalCos4FalloffApplied: false;\n distortionAreaCorrectionApplied: false;\n psfRedistributionApplied: false;\n sensorOpticalStackApplied: false;\n cfaApplied: false;\n quantumEfficiencyApplied: false;\n spectralResponsivityApplied: false;\n strayLightApplied: false;\n polarizationModeled: false;\n wavelengthChangingBehaviorModeled:\n false;\n volumetricScatteringModeled: false;\n calibratedSensorPlaneIrradianceClaimAuthorized:\n false;\n componentEvidence: {\n sceneRadiance:\n readonly EvidenceProvenance[];\n opticalProfile:\n readonly EvidenceProvenance[];\n transmission:\n readonly EvidenceProvenance[];\n workingFNumber:\n readonly EvidenceProvenance[];\n frontOfLensFilters:\n readonly EvidenceProvenance[];\n };\n limitations:\n readonly string[];\n}\n\nfunction parseUncertainty(\n value: unknown,\n path: string\n): OpticalBridgeUncertainty {\n const record =\n requireRecord(value, path, null);\n rejectUnknownVariantFields(record, \"kind\", UNCERTAINTY_VARIANT_FIELDS, path);\n\n if (record.kind === \"relative\") {\n return {\n kind: \"relative\",\n fraction:\n requireFraction(\n record.fraction,\n path + \".fraction\"\n ),\n basis:\n requireNonEmptyString(\n record.basis,\n path + \".basis\"\n )\n };\n }\n\n if (\n record.kind ===\n \"not-quantified\"\n ) {\n return {\n kind: \"not-quantified\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n path + \".limitation\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parseReusableNumericEvidence(\n value: unknown,\n path: string\n): readonly EvidenceProvenance[] {\n const evidence =\n parseEvidenceList(value, path);\n\n if (\n evidence.some(\n (entry) =>\n entry.reuseStatus ===\n \"factual-reference-only\"\n )\n ) {\n throw new InvalidConfigurationError(\n path +\n \" for numeric optical curve data must be reusable-data or photivra-owned.\"\n );\n }\n return evidence;\n}\n\nfunction parseSpectralSamples(\n value: unknown,\n path: string\n): readonly SpectralTransmissionSample[] {\n if (\n !Array.isArray(value) ||\n value.length < 2\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must contain at least two samples.\"\n );\n }\n\n let previous =\n Number.NEGATIVE_INFINITY;\n return value.map(\n (entry, index) => {\n const samplePath =\n path +\n \"[\" +\n index +\n \"]\";\n const record =\n requireRecord(\n entry,\n samplePath, [\"wavelengthNanometers\", \"linearTransmissionFactor\"]\n );\n const wavelengthNanometers =\n requirePositiveFinite(\n record.wavelengthNanometers,\n samplePath +\n \".wavelengthNanometers\"\n );\n if (\n wavelengthNanometers <=\n previous\n ) {\n throw new InvalidConfigurationError(\n path +\n \" wavelengths must be strictly increasing.\"\n );\n }\n previous =\n wavelengthNanometers;\n return {\n wavelengthNanometers,\n linearTransmissionFactor:\n requireFraction(\n record.linearTransmissionFactor,\n samplePath +\n \".linearTransmissionFactor\"\n )\n };\n }\n );\n}\n\nfunction parseTransmission(\n value: unknown,\n path: string\n): OpticalTransmissionModel {\n const record =\n requireRecord(value, path, null);\n rejectUnknownVariantFields(record, \"kind\", {\n // workingTStop and wavelengthRangeNanometers (spectral) and samples (working T-stop) are rejected below with their own messages.\n \"spectral-transmission\": [\"kind\", \"scientificStatus\", \"wavelengthBasis\", \"samples\", \"uncertainty\", \"workingTStop\", \"wavelengthRangeNanometers\"],\n \"effective-working-t-stop-approximation\": [\"kind\", \"scientificStatus\", \"workingTStop\", \"wavelengthBasis\", \"wavelengthRangeNanometers\",\n \"uncertainty\", \"limitation\", \"samples\"]\n }, path);\n\n if (\n record.kind ===\n \"spectral-transmission\"\n ) {\n if (\n record.workingTStop !==\n undefined ||\n record.wavelengthRangeNanometers !==\n undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" spectral-transmission must not include T-stop-only fields.\"\n );\n }\n\n const status =\n parseScientificStatus(\n record.scientificStatus,\n path +\n \".scientificStatus\"\n );\n const samplesFact =\n requireRecord(\n record.samples,\n path + \".samples\", [\"value\", \"evidence\"]\n );\n const uncertainty =\n parseUncertainty(\n record.uncertainty,\n path + \".uncertainty\"\n );\n\n if (\n status === \"calibrated\" &&\n uncertainty.kind !==\n \"relative\"\n ) {\n throw new InvalidConfigurationError(\n path +\n \" calibrated spectral transmission requires quantified relative uncertainty.\"\n );\n }\n\n return {\n kind:\n \"spectral-transmission\",\n scientificStatus: status,\n wavelengthBasis:\n parseResolvedBasis(\n record.wavelengthBasis,\n path +\n \".wavelengthBasis\"\n ),\n samples: {\n value:\n parseSpectralSamples(\n samplesFact.value,\n path +\n \".samples.value\"\n ),\n evidence:\n parseReusableNumericEvidence(\n samplesFact.evidence,\n path +\n \".samples.evidence\"\n )\n },\n uncertainty\n };\n }\n\n if (\n record.kind ===\n \"effective-working-t-stop-approximation\"\n ) {\n if (\n record.samples !==\n undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" T-stop approximation must not include spectral transmission samples.\"\n );\n }\n if (\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n path +\n ' T-stop scientificStatus must be \"approximation\".'\n );\n }\n const workingTStopFact =\n requireRecord(\n record.workingTStop,\n path +\n \".workingTStop\", [\"value\", \"evidence\"]\n );\n return {\n kind:\n \"effective-working-t-stop-approximation\",\n scientificStatus:\n \"approximation\",\n workingTStop: {\n value:\n requirePositiveFinite(\n workingTStopFact.value,\n path +\n \".workingTStop.value\"\n ),\n evidence:\n parseEvidenceList(\n workingTStopFact.evidence,\n path +\n \".workingTStop.evidence\"\n )\n },\n wavelengthBasis:\n parseResolvedBasis(\n record.wavelengthBasis,\n path +\n \".wavelengthBasis\"\n ),\n wavelengthRangeNanometers:\n parseRange(\n record.wavelengthRangeNanometers,\n path +\n \".wavelengthRangeNanometers\"\n ),\n uncertainty:\n parseUncertainty(\n record.uncertainty,\n path + \".uncertainty\"\n ),\n limitation:\n requireNonEmptyString(\n record.limitation,\n path +\n \".limitation\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parseFocusApplicability(\n value: unknown,\n path: string\n): OpticalBridgeFocusApplicability {\n const record =\n requireRecord(value, path, null);\n rejectUnknownVariantFields(record, \"kind\", { any: [\"kind\"], \"finite-distance-range\": [\"kind\", \"objectDistanceM\"] }, path);\n\n if (record.kind === \"any\") {\n return {\n kind: \"any\"\n };\n }\n if (\n record.kind ===\n \"finite-distance-range\"\n ) {\n return {\n kind:\n \"finite-distance-range\",\n objectDistanceM:\n parseRange(\n record.objectDistanceM,\n path +\n \".objectDistanceM\"\n )\n };\n }\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\n/**\n * Validates an untrusted scene-to-sensor profile and returns the normalized typed contract. Unknown\n * enum values, missing required fields and incompatible scientific data are rejected here.\n *\n * The optical bridge turns outgoing scene radiance (W/m²/sr/nm) into irradiance (W/m²/nm) arriving\n * at the front of the sensor stack.\n * - Circular paraxial acceptance is pi/(4*Nworking²). At finite focus with unit pupil magnification,\n * the working f-number is N*(1+m).\n * - Spectral transmission and an effective working T-stop are alternative paths; use one.\n * - Field throughput is applied exactly once.\n * - Sensor response and PSF (point-spread function) blur happen later in the pipeline.\n * @param value - unknown. Untrusted input; TypeScript types alone are not validation.\n * @returns SceneToSensorIrradianceProfile. The result states its shape and scientific status; a successful call does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the root export and exact type graph.\n */\nexport function parseSceneToSensorIrradianceProfile(\n value: unknown\n): SceneToSensorIrradianceProfile {\n const record =\n requireRecord(\n value,\n \"sceneToSensorIrradianceProfile\", [\"schemaVersion\", \"applicability\", \"scientificStatus\", \"transmission\", \"distortionAreaMappingOwnership\", \"psfRedistributionOwnership\", \"sensorOpticalStackIncluded\", \"strayLightIncluded\", \"polarizationModeled\", \"wavelengthChangingBehaviorModeled\", \"volumetricScatteringModeled\", \"profileId\", \"profileVersion\", \"lensProfileId\", \"evidence\", \"limitations\"]\n );\n if (\n record.schemaVersion !==\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'sceneToSensorIrradianceProfile.schemaVersion must be \"' +\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION +\n '\".'\n );\n }\n\n const applicability =\n requireRecord(\n record.applicability,\n \"sceneToSensorIrradianceProfile.applicability\", [\"focalLengthMm\", \"nominalFNumber\", \"focus\"]\n );\n const status =\n parseScientificStatus(\n record.scientificStatus,\n \"sceneToSensorIrradianceProfile.scientificStatus\"\n );\n const transmission =\n parseTransmission(\n record.transmission,\n \"sceneToSensorIrradianceProfile.transmission\"\n );\n\n if (\n status === \"calibrated\" &&\n transmission.scientificStatus !==\n \"calibrated\"\n ) {\n throw new InvalidConfigurationError(\n \"A calibrated optical bridge profile requires calibrated spectral transmission.\"\n );\n }\n\n if (\n record.distortionAreaMappingOwnership !==\n \"not-applied-by-bridge\" ||\n record.psfRedistributionOwnership !==\n \"downstream-normalized-energy-redistribution\" ||\n record.sensorOpticalStackIncluded !==\n false ||\n record.strayLightIncluded !==\n false ||\n record.polarizationModeled !==\n false ||\n record.wavelengthChangingBehaviorModeled !==\n false ||\n record.volumetricScatteringModeled !==\n false\n ) {\n throw new InvalidConfigurationError(\n \"sceneToSensorIrradianceProfile ownership/completeness flags are invalid for schema 0.1.0.\"\n );\n }\n\n return {\n schemaVersion:\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION,\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"sceneToSensorIrradianceProfile.profileId\"\n ),\n profileVersion:\n requireNonEmptyString(\n record.profileVersion,\n \"sceneToSensorIrradianceProfile.profileVersion\"\n ),\n lensProfileId:\n requireNonEmptyString(\n record.lensProfileId,\n \"sceneToSensorIrradianceProfile.lensProfileId\"\n ),\n scientificStatus: status,\n applicability: {\n focalLengthMm:\n parseRange(\n applicability.focalLengthMm,\n \"sceneToSensorIrradianceProfile.applicability.focalLengthMm\"\n ),\n nominalFNumber:\n parseRange(\n applicability.nominalFNumber,\n \"sceneToSensorIrradianceProfile.applicability.nominalFNumber\"\n ),\n focus:\n parseFocusApplicability(\n applicability.focus,\n \"sceneToSensorIrradianceProfile.applicability.focus\"\n )\n },\n transmission,\n distortionAreaMappingOwnership:\n \"not-applied-by-bridge\",\n psfRedistributionOwnership:\n \"downstream-normalized-energy-redistribution\",\n sensorOpticalStackIncluded:\n false,\n strayLightIncluded: false,\n polarizationModeled: false,\n wavelengthChangingBehaviorModeled:\n false,\n volumetricScatteringModeled:\n false,\n evidence:\n parseEvidenceList(\n record.evidence,\n \"sceneToSensorIrradianceProfile.evidence\"\n ),\n limitations:\n parseLimitations(\n record.limitations,\n \"sceneToSensorIrradianceProfile.limitations\"\n )\n };\n}\n\nfunction ensureWithin(\n value: number,\n range: NumericRange,\n path: string\n): void {\n if (\n value < range.minimum ||\n value > range.maximum\n ) {\n throw new InvalidScientificInputError(\n path +\n \" lies outside the optical profile applicability range.\"\n );\n }\n}\n\n/**\n * Internal (2.6; not exported from the package): N_w / N = 1 + m for an ideal symmetric thin lens with unity pupil\n * magnification focused at `objectDistanceM`, m = v/s from `calculateThinLensImageDistance`. The irradiance bridge's\n * `ideal-symmetric-thin-lens` focus uses exactly this factor, and the framing reports derive their close-up light\n * factor 1/(1 + m)² from it, so the two cannot drift apart.\n */\nexport function thinLensWorkingFNumberFactor(focalLengthMm: number, objectDistanceM: number): number {\n return 1 + calculateThinLensImageDistance({ focalLengthMm, objectDistanceM }).value.magnification;\n}\n\nfunction workingFNumber(\n focus:\n OpticalBridgeFocusContext,\n focalLengthMm: number,\n nominalFNumber: number\n): {\n value: number;\n evidence:\n readonly EvidenceProvenance[];\n} {\n if (\n focus.kind === \"infinity-focus\"\n ) {\n return {\n value: nominalFNumber,\n evidence: []\n };\n }\n\n if (\n focus.kind ===\n \"ideal-symmetric-thin-lens\"\n ) {\n if (\n focus\n .pupilMagnificationAssumption !==\n \"unity\"\n ) {\n throw new InvalidScientificInputError(\n 'Ideal symmetric thin-lens focus requires pupilMagnificationAssumption \"unity\".'\n );\n }\n return {\n value:\n nominalFNumber *\n thinLensWorkingFNumberFactor(focalLengthMm, focus.objectDistanceM),\n evidence: []\n };\n }\n\n if (\n focus.kind ===\n \"supplied-working-f-number\"\n ) {\n const value =\n requirePositiveFiniteScientific(\n focus.workingFNumber.value,\n \"focus.workingFNumber.value\"\n );\n return {\n value,\n evidence:\n parseEvidenceList(\n focus.workingFNumber.evidence,\n \"focus.workingFNumber.evidence\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n \"focus.kind is invalid.\"\n );\n}\n\nfunction finiteFocusDistance(\n focus:\n OpticalBridgeFocusContext\n): number | null {\n if (\n focus.kind ===\n \"ideal-symmetric-thin-lens\"\n ) {\n return requirePositiveFiniteScientific(\n focus.objectDistanceM,\n \"focus.objectDistanceM\"\n );\n }\n if (\n focus.kind ===\n \"supplied-working-f-number\" &&\n focus.focus.kind === \"finite\"\n ) {\n return requirePositiveFiniteScientific(\n focus.focus.objectDistanceM,\n \"focus.focus.objectDistanceM\"\n );\n }\n return null;\n}\n\nfunction interpolateTransmission(\n model:\n Extract<\n OpticalTransmissionModel,\n { kind: \"spectral-transmission\" }\n >,\n wavelengthNanometers: number\n): number {\n const samples =\n model.samples.value;\n const first =\n samples[0]!;\n const last =\n samples[\n samples.length - 1\n ]!;\n\n if (\n wavelengthNanometers <\n first.wavelengthNanometers ||\n wavelengthNanometers >\n last.wavelengthNanometers\n ) {\n throw new InvalidScientificInputError(\n \"Scene wavelength lies outside the spectral transmission sample range.\"\n );\n }\n\n if (\n wavelengthNanometers ===\n first.wavelengthNanometers\n ) {\n return first\n .linearTransmissionFactor;\n }\n\n for (\n let index = 1;\n index < samples.length;\n index += 1\n ) {\n const right =\n samples[index]!;\n if (\n wavelengthNanometers <=\n right.wavelengthNanometers\n ) {\n const left =\n samples[index - 1]!;\n if (\n wavelengthNanometers ===\n right.wavelengthNanometers\n ) {\n return right\n .linearTransmissionFactor;\n }\n const phase =\n (wavelengthNanometers -\n left.wavelengthNanometers) /\n (right.wavelengthNanometers -\n left.wavelengthNanometers);\n return (\n left\n .linearTransmissionFactor +\n phase *\n (right\n .linearTransmissionFactor -\n left\n .linearTransmissionFactor)\n );\n }\n }\n\n return last\n .linearTransmissionFactor;\n}\n\nfunction fieldFactor(\n field:\n OpticalBridgeFieldThroughput,\n imagePointMm: {\n x: number;\n y: number;\n }\n): number {\n if (field.kind === \"unity\") {\n return 1;\n }\n if (\n field.kind ===\n \"illumination-vignetting-result\"\n ) {\n const result =\n field.result;\n const scale = Math.max(\n 1,\n Math.abs(imagePointMm.x),\n Math.abs(imagePointMm.y),\n Math.abs(result.imagePointMm.x),\n Math.abs(result.imagePointMm.y)\n );\n const tolerance =\n Number.EPSILON *\n 64 *\n scale;\n if (\n Math.abs(\n result.imagePointMm.x -\n imagePointMm.x\n ) > tolerance ||\n Math.abs(\n result.imagePointMm.y -\n imagePointMm.y\n ) > tolerance\n ) {\n throw new InvalidScientificInputError(\n \"Illumination-vignetting result imagePointMm must match the optical bridge imagePointMm.\"\n );\n }\n const factor =\n result.linearThroughputFactor;\n if (\n !Number.isFinite(factor) ||\n factor <= 0 ||\n factor > 1\n ) {\n throw new InvalidScientificInputError(\n \"Field throughput factor must be finite and in (0, 1].\"\n );\n }\n return factor;\n }\n throw new InvalidConfigurationError(\n \"fieldThroughput.kind is invalid.\"\n );\n}\n\nfunction validateSceneIdentity(\n request:\n SceneRadianceEvaluationRequest,\n result:\n SceneRadianceEvaluationResult\n): void {\n if (\n request.sampleId !==\n result.sampleId ||\n request.providerProfileId !==\n result.providerProfileId ||\n request.sceneId !==\n result.sceneId ||\n request.wavelengthNanometers !==\n result.wavelengthNanometers ||\n request.wavelengthBasis !==\n result.wavelengthBasis\n ) {\n throw new InvalidScientificInputError(\n \"Scene-radiance request/result identity must match exactly.\"\n );\n }\n}\n\n/**\n * The request-independent part of {@link calculateSceneRadianceToSensorIrradiance}: the profile, focal length, f-number,\n * applicability, focus and working f-number checks and the paraxial acceptance, in the public function's order.\n */\ninterface PreparedSceneRadianceToSensorIrradiance {\n profile: ReturnType<typeof parseSceneToSensorIrradianceProfile>;\n focalLengthMm: number;\n nominalFNumber: number;\n working: ReturnType<typeof workingFNumber>;\n workingFNumberValue: number;\n geometricAcceptance: number;\n transmission: ReturnType<typeof parseSceneToSensorIrradianceProfile>[\"transmission\"];\n}\n\nfunction prepareSceneRadianceToSensorIrradiance(\n input: Pick<CalculateSceneRadianceToSensorIrradianceInput, \"profile\" | \"focalLengthMm\" | \"nominalFNumber\" | \"focus\">\n): PreparedSceneRadianceToSensorIrradiance {\n const profile =\n parseSceneToSensorIrradianceProfile(\n input.profile\n );\n const focalLengthMm =\n requirePositiveFiniteScientific(\n input.focalLengthMm,\n \"focalLengthMm\"\n );\n const nominalFNumber =\n requirePositiveFiniteScientific(\n input.nominalFNumber,\n \"nominalFNumber\"\n );\n\n ensureWithin(\n focalLengthMm,\n profile.applicability\n .focalLengthMm,\n \"focalLengthMm\"\n );\n ensureWithin(\n nominalFNumber,\n profile.applicability\n .nominalFNumber,\n \"nominalFNumber\"\n );\n\n const focusDistanceM =\n finiteFocusDistance(input.focus);\n if (\n profile.applicability.focus.kind ===\n \"finite-distance-range\"\n ) {\n if (focusDistanceM === null) {\n throw new InvalidScientificInputError(\n \"Optical profile requires a finite focus distance.\"\n );\n }\n ensureWithin(\n focusDistanceM,\n profile.applicability\n .focus.objectDistanceM,\n \"focus object distance\"\n );\n }\n\n const working =\n workingFNumber(\n input.focus,\n focalLengthMm,\n nominalFNumber\n );\n const workingFNumberValue =\n working.value;\n const geometricAcceptance =\n Math.PI /\n (4 *\n workingFNumberValue *\n workingFNumberValue);\n\n if (\n !Number.isFinite(\n geometricAcceptance\n ) ||\n geometricAcceptance <= 0\n ) {\n throw new InvalidScientificInputError(\n \"Paraxial geometric acceptance must remain finite and greater than zero.\"\n );\n }\n\n const transmission =\n profile.transmission;\n return { profile, focalLengthMm, nominalFNumber, working, workingFNumberValue, geometricAcceptance, transmission };\n}\n\ninterface AppliedSceneRadianceToSensorIrradiance {\n spectralTransmissionFactor: number | null;\n effectiveWorkingTStop: number | null;\n tStopEquivalentTransmissionFactor: number | null;\n effectiveAcceptance: number;\n transmissionEvidence: readonly EvidenceProvenance[];\n frontOfLensFilterCount: number;\n frontOfLensFilterPolarizationModeled: boolean;\n frontOfLensFilterTransmissionFactor: number;\n frontOfLensFilterAttenuationStops: number;\n frontOfLensFilterEvidence: readonly EvidenceProvenance[];\n frontOfLensFilterLimitations: readonly string[];\n fieldThroughputFactor: number;\n sceneRadiance: number;\n irradiance: number;\n}\n\n/**\n * The per-query part of {@link calculateSceneRadianceToSensorIrradiance}, after the request and result are parsed and their\n * identities matched: the wavelength-basis check, the transmission or T-stop path, the front-of-lens filters, the field\n * throughput and the irradiance, with the same operations in the same order.\n */\nfunction applySceneRadianceToSensorIrradiance(\n prepared: PreparedSceneRadianceToSensorIrradiance,\n input: Pick<CalculateSceneRadianceToSensorIrradianceInput, \"imagePointMm\" | \"fieldThroughput\"> &\n { frontOfLensFilters?: readonly FrontOfLensFilterProfile[] | undefined },\n request: SceneRadianceEvaluationRequest,\n sceneResult: SceneRadianceEvaluationResult\n): AppliedSceneRadianceToSensorIrradiance {\n const { workingFNumberValue, geometricAcceptance, transmission } = prepared;\n if (\n request.wavelengthBasis !==\n transmission.wavelengthBasis\n ) {\n throw new InvalidScientificInputError(\n \"Scene-radiance and optical-transmission wavelength bases must match; implicit air/vacuum conversion is not performed.\"\n );\n }\n\n let spectralTransmissionFactor:\n number | null = null;\n let effectiveWorkingTStop:\n number | null = null;\n let tStopEquivalentTransmissionFactor:\n number | null = null;\n let effectiveAcceptance =\n geometricAcceptance;\n let transmissionEvidence:\n readonly EvidenceProvenance[];\n\n if (\n transmission.kind ===\n \"spectral-transmission\"\n ) {\n spectralTransmissionFactor =\n interpolateTransmission(\n transmission,\n request.wavelengthNanometers\n );\n effectiveAcceptance *=\n spectralTransmissionFactor;\n transmissionEvidence =\n transmission.samples.evidence;\n } else {\n ensureWithin(\n request.wavelengthNanometers,\n transmission\n .wavelengthRangeNanometers,\n \"scene wavelength\"\n );\n effectiveWorkingTStop =\n transmission.workingTStop.value;\n if (\n effectiveWorkingTStop <\n workingFNumberValue\n ) {\n throw new InvalidScientificInputError(\n \"effective working T-stop must not be smaller than the geometric working f-number for a passive optical approximation.\"\n );\n }\n tStopEquivalentTransmissionFactor =\n (workingFNumberValue /\n effectiveWorkingTStop) **\n 2;\n effectiveAcceptance =\n Math.PI /\n (4 *\n effectiveWorkingTStop *\n effectiveWorkingTStop);\n transmissionEvidence =\n transmission\n .workingTStop.evidence;\n }\n\n let frontOfLensFilterCount = 0;\n let frontOfLensFilterPolarizationModeled = false;\n let frontOfLensFilterTransmissionFactor = 1;\n let frontOfLensFilterAttenuationStops = 0;\n let frontOfLensFilterEvidence:\n readonly EvidenceProvenance[] = [];\n let frontOfLensFilterLimitations:\n readonly string[] = [];\n\n if (\n input.frontOfLensFilters !==\n undefined\n ) {\n const composed =\n composeFrontOfLensFilterTransmission({\n filters:\n input.frontOfLensFilters,\n wavelengthNanometers:\n request.wavelengthNanometers,\n wavelengthBasis:\n request.wavelengthBasis\n });\n frontOfLensFilterCount =\n composed.appliedFilterCount;\n frontOfLensFilterPolarizationModeled =\n composed.polarizationModeled;\n frontOfLensFilterTransmissionFactor =\n composed\n .combinedLinearTransmissionFactor;\n frontOfLensFilterAttenuationStops =\n composed\n .combinedAttenuationStops;\n frontOfLensFilterEvidence =\n composed.components.flatMap(\n (component) =>\n component.evidence\n );\n frontOfLensFilterLimitations =\n composed.components.flatMap(\n (component) =>\n component.limitations\n );\n effectiveAcceptance *=\n frontOfLensFilterTransmissionFactor;\n }\n\n const fieldThroughputFactor =\n fieldFactor(\n input.fieldThroughput,\n input.imagePointMm\n );\n effectiveAcceptance *=\n fieldThroughputFactor;\n\n const sceneRadiance =\n sceneResult\n .spectralRadianceWattsPerSquareMeterSteradianNanometer;\n const irradiance =\n sceneRadiance *\n effectiveAcceptance;\n\n if (\n !Number.isFinite(irradiance) ||\n irradiance < 0\n ) {\n throw new InvalidScientificInputError(\n \"Sensor-plane spectral irradiance must remain finite and non-negative.\"\n );\n }\n\n return { spectralTransmissionFactor, effectiveWorkingTStop, tStopEquivalentTransmissionFactor, effectiveAcceptance, transmissionEvidence,\n frontOfLensFilterCount, frontOfLensFilterPolarizationModeled, frontOfLensFilterTransmissionFactor, frontOfLensFilterAttenuationStops,\n frontOfLensFilterEvidence, frontOfLensFilterLimitations, fieldThroughputFactor, sceneRadiance, irradiance };\n}\n\n/**\n * Internal (#453; not exported from the package): the sensor-plane spectral irradiance that\n * {@link calculateSceneRadianceToSensorIrradiance} returns, for repeated queries of one optical state. This call runs the\n * request-independent checks once (the public function's own code, in its order); each call of the returned function then\n * runs the public function's per-query steps on an engine-built request and a result already parsed by the caller: the identity\n * match, the wavelength-basis check, the transmission or T-stop path, the front-of-lens filters, the field throughput and\n * the finite, non-negative irradiance, with the same operations, order and errors. It skips only the re-validation of the\n * unchanged optical inputs, the re-parse of the request and result, and the result envelope.\n */\nexport function prepareSceneRadianceToSensorIrradianceValue(\n optics: Pick<CalculateSceneRadianceToSensorIrradianceInput, \"profile\" | \"focalLengthMm\" | \"nominalFNumber\" | \"focus\" | \"frontOfLensFilters\">\n): (request: SceneRadianceEvaluationRequest, result: SceneRadianceEvaluationResult, imagePointMm: { x: number; y: number },\n fieldThroughput: OpticalBridgeFieldThroughput) => number {\n const prepared = prepareSceneRadianceToSensorIrradiance(optics);\n const frontOfLensFilters = optics.frontOfLensFilters;\n return (request, result, imagePointMm, fieldThroughput) => {\n validateSceneIdentity(request, result);\n return applySceneRadianceToSensorIrradiance(prepared, { imagePointMm, fieldThroughput, frontOfLensFilters }, request, result).irradiance;\n };\n}\n\n/**\n * Applies the declared pupil acceptance and exactly one transmission or field-throughput path to\n * spectral scene radiance. Returns the irradiance before the sensor stack, with conservative\n * evidence.\n *\n * The optical bridge turns outgoing scene radiance (W/m²/sr/nm) into irradiance (W/m²/nm) arriving\n * at the front of the sensor stack.\n * - Circular paraxial acceptance is pi/(4*Nworking²). At finite focus with unit pupil magnification,\n * the working f-number is N*(1+m).\n * - Spectral transmission and an effective working T-stop are alternative paths; use one.\n * - Field throughput is applied exactly once.\n * - Sensor response and PSF (point-spread function) blur happen later in the pipeline.\n * @param input - CalculateSceneRadianceToSensorIrradianceInput. See that type for coordinates, units and profile binding.\n * @returns CalculationResult<\n SceneToSensorIrradianceResult\n>. The result states its shape and scientific status; a successful call does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the root export and exact type graph.\n */\nexport function calculateSceneRadianceToSensorIrradiance(\n input:\n CalculateSceneRadianceToSensorIrradianceInput\n): CalculationResult<\n SceneToSensorIrradianceResult\n> {\n requireInputKeys(input, \"calculateSceneRadianceToSensorIrradiance\", [\"sceneRadianceRequest\", \"sceneRadianceResult\", \"profile\", \"focalLengthMm\", \"nominalFNumber\", \"focus\", \"imagePointMm\", \"fieldThroughput\", \"frontOfLensFilters\"]);\n const request =\n parseSceneRadianceEvaluationRequest(\n input.sceneRadianceRequest\n );\n const sceneResult =\n parseSceneRadianceEvaluationResult(\n input.sceneRadianceResult\n );\n validateSceneIdentity(\n request,\n sceneResult\n );\n\n const prepared = prepareSceneRadianceToSensorIrradiance(input);\n const { profile, focalLengthMm, nominalFNumber, working, workingFNumberValue, geometricAcceptance, transmission } = prepared;\n const { spectralTransmissionFactor, effectiveWorkingTStop, tStopEquivalentTransmissionFactor, effectiveAcceptance, transmissionEvidence,\n frontOfLensFilterCount, frontOfLensFilterPolarizationModeled, frontOfLensFilterTransmissionFactor, frontOfLensFilterAttenuationStops,\n frontOfLensFilterEvidence, frontOfLensFilterLimitations, fieldThroughputFactor, sceneRadiance, irradiance } =\n applySceneRadianceToSensorIrradiance(prepared, input, request, sceneResult);\n\n const limitations = [\n ...profile.limitations,\n ...frontOfLensFilterLimitations,\n \"Front-of-lens filter transmission is applied outside the lens throughput path exactly once and does not change geometric working f-number.\",\n \"Paraxial circular-pupil acceptance uses pi/(4*N_working^2); no universal cos^4 field falloff is added.\",\n \"Field throughput is supplied separately and applied exactly once.\",\n \"Geometric distortion area-density/Jacobian correction is not applied by this bridge.\",\n \"PSF/diffraction energy redistribution is downstream and must preserve normalized energy apart from separately owned throughput losses.\",\n \"Sensor optical stack, microlens, CFA, QE/responsivity and charge conversion are downstream.\",\n frontOfLensFilterPolarizationModeled\n ? \"Flare, ghosting, veiling glare, scene-radiance polarization (scene light is treated as unpolarized; the front-of-lens linear polarizer transmits (k1 + k2) / 2), volumetric scattering and wavelength-changing behavior are not modeled.\"\n : \"Flare, ghosting, veiling glare, polarization, volumetric scattering and wavelength-changing behavior are not modeled.\"\n ];\n\n return approximationResult(\n {\n schemaVersion:\n SCENE_TO_SENSOR_IRRADIANCE_PROFILE_SCHEMA_VERSION,\n profileId: profile.profileId,\n profileVersion:\n profile.profileVersion,\n lensProfileId:\n profile.lensProfileId,\n sampleId: request.sampleId,\n providerProfileId:\n request.providerProfileId,\n sceneId: request.sceneId,\n timeSecondsFromExposureStart:\n request\n .timeSecondsFromExposureStart,\n wavelengthNanometers:\n request.wavelengthNanometers,\n wavelengthBasis:\n request.wavelengthBasis,\n inputQuantity:\n \"outgoing-spectral-radiance\",\n inputUnit:\n \"W/m^2/sr/nm\",\n outputQuantity:\n \"sensor-plane-spectral-irradiance\",\n outputUnit: \"W/m^2/nm\",\n sceneSpectralRadianceWattsPerSquareMeterSteradianNanometer:\n sceneRadiance,\n focalLengthMm,\n nominalFNumber,\n workingFNumber:\n workingFNumberValue,\n focusModel: input.focus.kind,\n paraxialGeometricAcceptanceSolidAngleSteradians:\n geometricAcceptance,\n transmissionPath:\n transmission.kind,\n spectralTransmissionFactor,\n effectiveWorkingTStop,\n tStopEquivalentTransmissionFactor,\n frontOfLensFilterCount,\n frontOfLensFilterTransmissionFactor,\n frontOfLensFilterAttenuationStops,\n frontOfLensFilterTransmissionAppliedExactlyOnce:\n true,\n frontOfLensFilterPolarizationModeled,\n fieldThroughputFactor,\n effectiveAcceptanceAfterTransmissionAndFieldSteradians:\n effectiveAcceptance,\n sensorPlaneSpectralIrradianceWattsPerSquareMeterNanometer:\n irradiance,\n scientificStatus:\n \"approximation\",\n sceneRadianceScientificStatus:\n sceneResult.scientificStatus,\n opticalProfileScientificStatus:\n profile.scientificStatus,\n sceneRadianceUncertainty:\n sceneResult.uncertainty,\n opticalUncertainty:\n transmission.uncertainty,\n radianceAmplificationApplied:\n false,\n fieldThroughputAppliedExactlyOnce:\n true,\n universalCos4FalloffApplied:\n false,\n distortionAreaCorrectionApplied:\n false,\n psfRedistributionApplied:\n false,\n sensorOpticalStackApplied:\n false,\n cfaApplied: false,\n quantumEfficiencyApplied: false,\n spectralResponsivityApplied:\n false,\n strayLightApplied: false,\n polarizationModeled: false,\n wavelengthChangingBehaviorModeled:\n false,\n volumetricScatteringModeled:\n false,\n calibratedSensorPlaneIrradianceClaimAuthorized:\n false,\n componentEvidence: {\n sceneRadiance:\n sceneResult.evidence,\n opticalProfile:\n profile.evidence,\n transmission:\n transmissionEvidence,\n workingFNumber:\n working.evidence,\n frontOfLensFilters:\n frontOfLensFilterEvidence\n },\n limitations\n },\n \"scene-radiance-to-sensor-irradiance\",\n \"1.1.0\",\n limitations\n );\n}\n"]}
|
|
@@ -73,6 +73,7 @@ export interface StrayLightIrradiance {
|
|
|
73
73
|
spectralIrradianceWPerM2PerNm: number;
|
|
74
74
|
}[];
|
|
75
75
|
}
|
|
76
|
+
export declare function point(value: unknown): LensFieldPointMm;
|
|
76
77
|
/** Strict generic profile parser. Checks that the profile adds no energy at any source angle in its range. */
|
|
77
78
|
export declare function parseLensStrayLightProfile(value: unknown): LensStrayLightProfile;
|
|
78
79
|
/** Adds off-path spectral irradiance before the sensor integrates the exposure, never after tone mapping.
|
|
@@ -22,7 +22,7 @@ function fraction(value, name) {
|
|
|
22
22
|
throw new InvalidScientificInputError(`${name} must lie in [0,1].`);
|
|
23
23
|
return n;
|
|
24
24
|
}
|
|
25
|
-
function point(value) {
|
|
25
|
+
export function point(value) {
|
|
26
26
|
const p = record(value, ["x", "y"]);
|
|
27
27
|
return { x: finite(p.x, "x"), y: finite(p.y, "y") };
|
|
28
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stray-light.js","sourceRoot":"","sources":["../../src/optics/stray-light.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Stray light (flare and veiling glare) from bright sources.\n *\n * The profile parser is strict. It checks that the profile stays passive (adds no energy) across the\n * whole range of source angles.\n *\n * The model adds off-path spectral irradiance before the sensor integrates the exposure. It never\n * adds it after tone mapping. Source powers are integrated over profile.referenceEntranceAreaMm2 at\n * one instant and one wavelength. The Gaussian templates are independently parameterized\n * approximations, not ray tracing through a lens prescription.\n * @see docs/DIGITAL_OPTICS_FOUNDATION.md for the equations, units, coordinate conventions, known\n * blockers and support limits.\n */\n\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n choice, finite, list, parseEvidence, parseOpticalProfileState, positive, record,\n requireSameState, textValue, type GenericOpticalEvidence, type OpticalProfileState\n} from \"./profile-contract.js\";\nimport type { LensFieldPointMm } from \"./radial-distortion.js\";\nimport { requireInputKeys } from \"../core/record-validation.js\";\n\n/** One ghost (reflection path) or veil (broad scatter) response. Its shape is normalized over the infinite image plane. */\nexport interface ParametricStrayLightResponse {\n id: string;\n kind: \"ghost\" | \"veil\";\n /** Centroid response in mm per degree of the two declared source field angles. */\n centroidMmPerDegree: readonly [number, number, number, number];\n offsetMm: LensFieldPointMm;\n sigmaMm: number;\n /** Fraction of source power entering this response at the axis. */\n axisPowerFraction: number;\n /** Power fraction versus angle: F(rho) = F(0) * (1 + slope * rho²), where rho = source angle / maximum angle. */\n angularSlope: number;\n}\n/** One explicit wavelength and an exact generic camera state. Models clean optics only. */\nexport interface LensStrayLightProfile {\n schemaVersion: \"0.1.0\";\n id: string;\n version: string;\n state: OpticalProfileState;\n evidence: GenericOpticalEvidence;\n interaction: \"lens-reflections\" | \"sensor-lens-reflections\";\n wavelengthNm: number;\n wavelengthBasis: \"air\" | \"vacuum\";\n maximumSourceAngleDegrees: number;\n /** Area over which the caller's incident source power was integrated. */\n referenceEntranceAreaMm2: number;\n responses: readonly ParametricStrayLightResponse[];\n}\n/** The direction can lie outside the active frame. It is not clipped to the crop. */\nexport interface StrayLightSource {\n id: string;\n fieldAngleXDegrees: number;\n fieldAngleYDegrees: number;\n incidentSpectralPowerWPerNm: number;\n /** Fraction of this source let in, for example after a flag or hood decision. Not a global hood factor. */\n admittedFraction: number;\n}\nexport interface StrayLightIrradiance {\n profileId: string;\n profileVersion: string;\n interaction: LensStrayLightProfile[\"interaction\"];\n imagePointMm: LensFieldPointMm;\n wavelengthNm: number;\n wavelengthBasis: \"air\" | \"vacuum\";\n timeSeconds: number;\n primarySpectralIrradianceWPerM2PerNm: number;\n ghostSpectralIrradianceWPerM2PerNm: number;\n veilSpectralIrradianceWPerM2PerNm: number;\n totalSpectralIrradianceWPerM2PerNm: number;\n /** If primary-only, the meter leaves these additions out. The sensor always receives the total. */\n meterDomain: \"primary-only\" | \"primary-plus-stray\";\n meterSpectralIrradianceWPerM2PerNm: number;\n contributions: readonly { sourceId: string; responseId: string; kind: \"ghost\" | \"veil\"; spectralIrradianceWPerM2PerNm: number }[];\n}\nfunction fraction(value: unknown, name: string): number {\n const n = finite(value, name);\n if (n < 0 || n > 1) throw new InvalidScientificInputError(`${name} must lie in [0,1].`);\n return n;\n}\nfunction point(value: unknown): LensFieldPointMm {\n const p = record(value, [\"x\", \"y\"]); return { x: finite(p.x, \"x\"), y: finite(p.y, \"y\") };\n}\n/** Strict generic profile parser. Checks that the profile adds no energy at any source angle in its range. */\nexport function parseLensStrayLightProfile(value: unknown): LensStrayLightProfile {\n const r = record(value, [\"schemaVersion\", \"id\", \"version\", \"state\", \"evidence\", \"interaction\",\n \"wavelengthNm\", \"wavelengthBasis\", \"maximumSourceAngleDegrees\", \"referenceEntranceAreaMm2\", \"responses\"]);\n const responses = list(r.responses, 32).map((value): ParametricStrayLightResponse => {\n const s = record(value, [\"id\", \"kind\", \"centroidMmPerDegree\", \"offsetMm\", \"sigmaMm\", \"axisPowerFraction\", \"angularSlope\"]);\n const matrix = list(s.centroidMmPerDegree, 4).map((v) => finite(v, \"centroid coefficient\"));\n if (matrix.length !== 4) throw new InvalidScientificInputError(\"Centroid matrix requires four entries.\");\n const slope = finite(s.angularSlope, \"angularSlope\");\n if (slope < -1) throw new InvalidScientificInputError(\"Angular response must be nonnegative.\");\n return { id: textValue(s.id), kind: choice(s.kind, [\"ghost\", \"veil\"]),\n centroidMmPerDegree: matrix as [number, number, number, number], offsetMm: point(s.offsetMm),\n sigmaMm: positive(s.sigmaMm, \"sigmaMm\"), axisPowerFraction: fraction(s.axisPowerFraction, \"axisPowerFraction\"), angularSlope: slope };\n });\n if (new Set(responses.map((s) => s.id)).size !== responses.length ||\n responses.reduce((sum, s) => sum+s.axisPowerFraction, 0) > 1 ||\n responses.reduce((sum, s) => sum+s.axisPowerFraction*(1+s.angularSlope), 0) > 1) {\n throw new InvalidScientificInputError(\"Duplicate response or nonpassive total response.\");\n }\n const maximum = positive(r.maximumSourceAngleDegrees, \"maximumSourceAngleDegrees\");\n if (maximum >= 90) throw new InvalidScientificInputError(\"Source angular domain must be below 90 degrees.\");\n return { schemaVersion: choice(r.schemaVersion, [\"0.1.0\"]), id: textValue(r.id), version: textValue(r.version),\n state: parseOpticalProfileState(r.state), evidence: parseEvidence(r.evidence),\n interaction: choice(r.interaction, [\"lens-reflections\", \"sensor-lens-reflections\"]),\n wavelengthNm: positive(r.wavelengthNm, \"wavelengthNm\"), wavelengthBasis: choice(r.wavelengthBasis, [\"air\", \"vacuum\"]),\n maximumSourceAngleDegrees: maximum, referenceEntranceAreaMm2: positive(r.referenceEntranceAreaMm2, \"referenceEntranceAreaMm2\"), responses };\n}\n\n/** Adds off-path spectral irradiance before the sensor integrates the exposure, never after tone mapping.\n * Source powers are integrated over profile.referenceEntranceAreaMm2 at this instant and wavelength.\n * The Gaussian templates are independently parameterized approximations, not ray tracing through a\n * lens prescription.\n */\nexport function calculateLensStrayLightIrradiance(input: {\n profile: LensStrayLightProfile; state: OpticalProfileState; enabled: boolean;\n imagePointMm: LensFieldPointMm; wavelengthNm: number; wavelengthBasis: \"air\" | \"vacuum\";\n timeSeconds: number; primarySpectralIrradianceWPerM2PerNm: number;\n sources: readonly StrayLightSource[]; meterDomain: \"primary-only\" | \"primary-plus-stray\";\n}): CalculationResult<StrayLightIrradiance> {\n requireInputKeys(input, \"calculateLensStrayLightIrradiance\", [\"profile\", \"state\", \"enabled\", \"imagePointMm\", \"wavelengthNm\", \"wavelengthBasis\", \"timeSeconds\", \"primarySpectralIrradianceWPerM2PerNm\", \"sources\", \"meterDomain\"]);\n const profile = parseLensStrayLightProfile(input.profile);\n requireSameState(profile.state, input.state);\n const p = point(input.imagePointMm), time = finite(input.timeSeconds, \"timeSeconds\");\n const primary = finite(input.primarySpectralIrradianceWPerM2PerNm, \"primary irradiance\");\n if (time < 0 || primary < 0 || typeof input.enabled !== \"boolean\") throw new InvalidScientificInputError(\"Invalid time, primary irradiance or enabled state.\");\n if (input.wavelengthNm !== profile.wavelengthNm || input.wavelengthBasis !== profile.wavelengthBasis) {\n throw new InvalidScientificInputError(\"Wavelength/basis mismatch; select an explicit profile slice.\");\n }\n const meter = choice(input.meterDomain, [\"primary-only\", \"primary-plus-stray\"]);\n const sources = list(input.sources, 128).map((value): StrayLightSource => {\n const s = record(value, [\"id\", \"fieldAngleXDegrees\", \"fieldAngleYDegrees\", \"incidentSpectralPowerWPerNm\", \"admittedFraction\"]);\n const x = finite(s.fieldAngleXDegrees, \"source angle X\"), y = finite(s.fieldAngleYDegrees, \"source angle Y\");\n const power = finite(s.incidentSpectralPowerWPerNm, \"source power\");\n if (Math.hypot(x, y) > profile.maximumSourceAngleDegrees || power < 0) throw new InvalidScientificInputError(\"Source outside profile domain or negative power.\");\n return { id: textValue(s.id), fieldAngleXDegrees: x, fieldAngleYDegrees: y,\n incidentSpectralPowerWPerNm: power, admittedFraction: fraction(s.admittedFraction, \"admittedFraction\") };\n });\n if (new Set(sources.map((s) => s.id)).size !== sources.length) throw new InvalidScientificInputError(\"Duplicate source IDs.\");\n const contributions: StrayLightIrradiance[\"contributions\"][number][] = [];\n let ghost = 0, veil = 0;\n if (input.enabled) for (const s of sources) for (const r of profile.responses) {\n const m = r.centroidMmPerDegree;\n const center = { x: m[0]*s.fieldAngleXDegrees+m[1]*s.fieldAngleYDegrees+r.offsetMm.x,\n y: m[2]*s.fieldAngleXDegrees+m[3]*s.fieldAngleYDegrees+r.offsetMm.y };\n const rho = Math.hypot(s.fieldAngleXDegrees, s.fieldAngleYDegrees)/profile.maximumSourceAngleDegrees;\n const response = r.axisPowerFraction*(1+r.angularSlope*rho*rho);\n // Infinite-plane unit-integral density in 1/mm², converted to 1/m².\n const densityPerM2 = Math.exp(-((p.x-center.x)**2+(p.y-center.y)**2)/(2*r.sigmaMm**2))/(2*Math.PI*r.sigmaMm**2)*1e6;\n const irradiance = s.incidentSpectralPowerWPerNm*s.admittedFraction*response*densityPerM2;\n contributions.push({ sourceId: s.id, responseId: r.id, kind: r.kind, spectralIrradianceWPerM2PerNm: irradiance });\n if (r.kind === \"ghost\") ghost += irradiance; else veil += irradiance;\n }\n const total = primary+ghost+veil;\n return approximationResult({ profileId: profile.id, profileVersion: profile.version, interaction: profile.interaction,\n imagePointMm: p, wavelengthNm: profile.wavelengthNm, wavelengthBasis: profile.wavelengthBasis,\n timeSeconds: time, primarySpectralIrradianceWPerM2PerNm: primary, ghostSpectralIrradianceWPerM2PerNm: ghost,\n veilSpectralIrradianceWPerM2PerNm: veil, totalSpectralIrradianceWPerM2PerNm: total,\n meterDomain: meter, meterSpectralIrradianceWPerM2PerNm: meter === \"primary-only\" ? primary : total, contributions },\n \"parametric-clean-lens-stray-light\", \"0.1.0\", [profile.evidence.basis, profile.evidence.residualNote,\n \"Additive pre-sensor irradiance; exposure, saturation and noise remain downstream\",\n \"No diffraction, primary PSF, contamination, cosmetic sprites or calibrated branded signature\",\n \"One instant and one wavelength slice; no implicit temporal or spectral averaging\"]);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"stray-light.js","sourceRoot":"","sources":["../../src/optics/stray-light.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Stray light (flare and veiling glare) from bright sources.\n *\n * The profile parser is strict. It checks that the profile stays passive (adds no energy) across the\n * whole range of source angles.\n *\n * The model adds off-path spectral irradiance before the sensor integrates the exposure. It never\n * adds it after tone mapping. Source powers are integrated over profile.referenceEntranceAreaMm2 at\n * one instant and one wavelength. The Gaussian templates are independently parameterized\n * approximations, not ray tracing through a lens prescription.\n * @see docs/DIGITAL_OPTICS_FOUNDATION.md for the equations, units, coordinate conventions, known\n * blockers and support limits.\n */\n\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n choice, finite, list, parseEvidence, parseOpticalProfileState, positive, record,\n requireSameState, textValue, type GenericOpticalEvidence, type OpticalProfileState\n} from \"./profile-contract.js\";\nimport type { LensFieldPointMm } from \"./radial-distortion.js\";\nimport { requireInputKeys } from \"../core/record-validation.js\";\n\n/** One ghost (reflection path) or veil (broad scatter) response. Its shape is normalized over the infinite image plane. */\nexport interface ParametricStrayLightResponse {\n id: string;\n kind: \"ghost\" | \"veil\";\n /** Centroid response in mm per degree of the two declared source field angles. */\n centroidMmPerDegree: readonly [number, number, number, number];\n offsetMm: LensFieldPointMm;\n sigmaMm: number;\n /** Fraction of source power entering this response at the axis. */\n axisPowerFraction: number;\n /** Power fraction versus angle: F(rho) = F(0) * (1 + slope * rho²), where rho = source angle / maximum angle. */\n angularSlope: number;\n}\n/** One explicit wavelength and an exact generic camera state. Models clean optics only. */\nexport interface LensStrayLightProfile {\n schemaVersion: \"0.1.0\";\n id: string;\n version: string;\n state: OpticalProfileState;\n evidence: GenericOpticalEvidence;\n interaction: \"lens-reflections\" | \"sensor-lens-reflections\";\n wavelengthNm: number;\n wavelengthBasis: \"air\" | \"vacuum\";\n maximumSourceAngleDegrees: number;\n /** Area over which the caller's incident source power was integrated. */\n referenceEntranceAreaMm2: number;\n responses: readonly ParametricStrayLightResponse[];\n}\n/** The direction can lie outside the active frame. It is not clipped to the crop. */\nexport interface StrayLightSource {\n id: string;\n fieldAngleXDegrees: number;\n fieldAngleYDegrees: number;\n incidentSpectralPowerWPerNm: number;\n /** Fraction of this source let in, for example after a flag or hood decision. Not a global hood factor. */\n admittedFraction: number;\n}\nexport interface StrayLightIrradiance {\n profileId: string;\n profileVersion: string;\n interaction: LensStrayLightProfile[\"interaction\"];\n imagePointMm: LensFieldPointMm;\n wavelengthNm: number;\n wavelengthBasis: \"air\" | \"vacuum\";\n timeSeconds: number;\n primarySpectralIrradianceWPerM2PerNm: number;\n ghostSpectralIrradianceWPerM2PerNm: number;\n veilSpectralIrradianceWPerM2PerNm: number;\n totalSpectralIrradianceWPerM2PerNm: number;\n /** If primary-only, the meter leaves these additions out. The sensor always receives the total. */\n meterDomain: \"primary-only\" | \"primary-plus-stray\";\n meterSpectralIrradianceWPerM2PerNm: number;\n contributions: readonly { sourceId: string; responseId: string; kind: \"ghost\" | \"veil\"; spectralIrradianceWPerM2PerNm: number }[];\n}\nfunction fraction(value: unknown, name: string): number {\n const n = finite(value, name);\n if (n < 0 || n > 1) throw new InvalidScientificInputError(`${name} must lie in [0,1].`);\n return n;\n}\nexport function point(value: unknown): LensFieldPointMm {\n const p = record(value, [\"x\", \"y\"]); return { x: finite(p.x, \"x\"), y: finite(p.y, \"y\") };\n}\n/** Strict generic profile parser. Checks that the profile adds no energy at any source angle in its range. */\nexport function parseLensStrayLightProfile(value: unknown): LensStrayLightProfile {\n const r = record(value, [\"schemaVersion\", \"id\", \"version\", \"state\", \"evidence\", \"interaction\",\n \"wavelengthNm\", \"wavelengthBasis\", \"maximumSourceAngleDegrees\", \"referenceEntranceAreaMm2\", \"responses\"]);\n const responses = list(r.responses, 32).map((value): ParametricStrayLightResponse => {\n const s = record(value, [\"id\", \"kind\", \"centroidMmPerDegree\", \"offsetMm\", \"sigmaMm\", \"axisPowerFraction\", \"angularSlope\"]);\n const matrix = list(s.centroidMmPerDegree, 4).map((v) => finite(v, \"centroid coefficient\"));\n if (matrix.length !== 4) throw new InvalidScientificInputError(\"Centroid matrix requires four entries.\");\n const slope = finite(s.angularSlope, \"angularSlope\");\n if (slope < -1) throw new InvalidScientificInputError(\"Angular response must be nonnegative.\");\n return { id: textValue(s.id), kind: choice(s.kind, [\"ghost\", \"veil\"]),\n centroidMmPerDegree: matrix as [number, number, number, number], offsetMm: point(s.offsetMm),\n sigmaMm: positive(s.sigmaMm, \"sigmaMm\"), axisPowerFraction: fraction(s.axisPowerFraction, \"axisPowerFraction\"), angularSlope: slope };\n });\n if (new Set(responses.map((s) => s.id)).size !== responses.length ||\n responses.reduce((sum, s) => sum+s.axisPowerFraction, 0) > 1 ||\n responses.reduce((sum, s) => sum+s.axisPowerFraction*(1+s.angularSlope), 0) > 1) {\n throw new InvalidScientificInputError(\"Duplicate response or nonpassive total response.\");\n }\n const maximum = positive(r.maximumSourceAngleDegrees, \"maximumSourceAngleDegrees\");\n if (maximum >= 90) throw new InvalidScientificInputError(\"Source angular domain must be below 90 degrees.\");\n return { schemaVersion: choice(r.schemaVersion, [\"0.1.0\"]), id: textValue(r.id), version: textValue(r.version),\n state: parseOpticalProfileState(r.state), evidence: parseEvidence(r.evidence),\n interaction: choice(r.interaction, [\"lens-reflections\", \"sensor-lens-reflections\"]),\n wavelengthNm: positive(r.wavelengthNm, \"wavelengthNm\"), wavelengthBasis: choice(r.wavelengthBasis, [\"air\", \"vacuum\"]),\n maximumSourceAngleDegrees: maximum, referenceEntranceAreaMm2: positive(r.referenceEntranceAreaMm2, \"referenceEntranceAreaMm2\"), responses };\n}\n\n/** Adds off-path spectral irradiance before the sensor integrates the exposure, never after tone mapping.\n * Source powers are integrated over profile.referenceEntranceAreaMm2 at this instant and wavelength.\n * The Gaussian templates are independently parameterized approximations, not ray tracing through a\n * lens prescription.\n */\nexport function calculateLensStrayLightIrradiance(input: {\n profile: LensStrayLightProfile; state: OpticalProfileState; enabled: boolean;\n imagePointMm: LensFieldPointMm; wavelengthNm: number; wavelengthBasis: \"air\" | \"vacuum\";\n timeSeconds: number; primarySpectralIrradianceWPerM2PerNm: number;\n sources: readonly StrayLightSource[]; meterDomain: \"primary-only\" | \"primary-plus-stray\";\n}): CalculationResult<StrayLightIrradiance> {\n requireInputKeys(input, \"calculateLensStrayLightIrradiance\", [\"profile\", \"state\", \"enabled\", \"imagePointMm\", \"wavelengthNm\", \"wavelengthBasis\", \"timeSeconds\", \"primarySpectralIrradianceWPerM2PerNm\", \"sources\", \"meterDomain\"]);\n const profile = parseLensStrayLightProfile(input.profile);\n requireSameState(profile.state, input.state);\n const p = point(input.imagePointMm), time = finite(input.timeSeconds, \"timeSeconds\");\n const primary = finite(input.primarySpectralIrradianceWPerM2PerNm, \"primary irradiance\");\n if (time < 0 || primary < 0 || typeof input.enabled !== \"boolean\") throw new InvalidScientificInputError(\"Invalid time, primary irradiance or enabled state.\");\n if (input.wavelengthNm !== profile.wavelengthNm || input.wavelengthBasis !== profile.wavelengthBasis) {\n throw new InvalidScientificInputError(\"Wavelength/basis mismatch; select an explicit profile slice.\");\n }\n const meter = choice(input.meterDomain, [\"primary-only\", \"primary-plus-stray\"]);\n const sources = list(input.sources, 128).map((value): StrayLightSource => {\n const s = record(value, [\"id\", \"fieldAngleXDegrees\", \"fieldAngleYDegrees\", \"incidentSpectralPowerWPerNm\", \"admittedFraction\"]);\n const x = finite(s.fieldAngleXDegrees, \"source angle X\"), y = finite(s.fieldAngleYDegrees, \"source angle Y\");\n const power = finite(s.incidentSpectralPowerWPerNm, \"source power\");\n if (Math.hypot(x, y) > profile.maximumSourceAngleDegrees || power < 0) throw new InvalidScientificInputError(\"Source outside profile domain or negative power.\");\n return { id: textValue(s.id), fieldAngleXDegrees: x, fieldAngleYDegrees: y,\n incidentSpectralPowerWPerNm: power, admittedFraction: fraction(s.admittedFraction, \"admittedFraction\") };\n });\n if (new Set(sources.map((s) => s.id)).size !== sources.length) throw new InvalidScientificInputError(\"Duplicate source IDs.\");\n const contributions: StrayLightIrradiance[\"contributions\"][number][] = [];\n let ghost = 0, veil = 0;\n if (input.enabled) for (const s of sources) for (const r of profile.responses) {\n const m = r.centroidMmPerDegree;\n const center = { x: m[0]*s.fieldAngleXDegrees+m[1]*s.fieldAngleYDegrees+r.offsetMm.x,\n y: m[2]*s.fieldAngleXDegrees+m[3]*s.fieldAngleYDegrees+r.offsetMm.y };\n const rho = Math.hypot(s.fieldAngleXDegrees, s.fieldAngleYDegrees)/profile.maximumSourceAngleDegrees;\n const response = r.axisPowerFraction*(1+r.angularSlope*rho*rho);\n // Infinite-plane unit-integral density in 1/mm², converted to 1/m².\n const densityPerM2 = Math.exp(-((p.x-center.x)**2+(p.y-center.y)**2)/(2*r.sigmaMm**2))/(2*Math.PI*r.sigmaMm**2)*1e6;\n const irradiance = s.incidentSpectralPowerWPerNm*s.admittedFraction*response*densityPerM2;\n contributions.push({ sourceId: s.id, responseId: r.id, kind: r.kind, spectralIrradianceWPerM2PerNm: irradiance });\n if (r.kind === \"ghost\") ghost += irradiance; else veil += irradiance;\n }\n const total = primary+ghost+veil;\n return approximationResult({ profileId: profile.id, profileVersion: profile.version, interaction: profile.interaction,\n imagePointMm: p, wavelengthNm: profile.wavelengthNm, wavelengthBasis: profile.wavelengthBasis,\n timeSeconds: time, primarySpectralIrradianceWPerM2PerNm: primary, ghostSpectralIrradianceWPerM2PerNm: ghost,\n veilSpectralIrradianceWPerM2PerNm: veil, totalSpectralIrradianceWPerM2PerNm: total,\n meterDomain: meter, meterSpectralIrradianceWPerM2PerNm: meter === \"primary-only\" ? primary : total, contributions },\n \"parametric-clean-lens-stray-light\", \"0.1.0\", [profile.evidence.basis, profile.evidence.residualNote,\n \"Additive pre-sensor irradiance; exposure, saturation and noise remain downstream\",\n \"No diffraction, primary PSF, contamination, cosmetic sprites or calibrated branded signature\",\n \"One instant and one wavelength slice; no implicit temporal or spectral averaging\"]);\n}\n"]}
|
|
@@ -39,18 +39,12 @@
|
|
|
39
39
|
*/
|
|
40
40
|
import { approximationResult, calculatedResult } from "../core/calculation-result.js";
|
|
41
41
|
import { InvalidScientificInputError, requirePositiveFinite } from "../core/validation.js";
|
|
42
|
-
import { requireInputKeys } from "../core/record-validation.js";
|
|
42
|
+
import { requireInputKeys, requireNamedFinite as requireFinite } from "../core/record-validation.js";
|
|
43
43
|
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
44
44
|
/** Largest supported Noll index (radial order n <= 10). */
|
|
45
45
|
export const ZERNIKE_MAXIMUM_NOLL_INDEX = 66;
|
|
46
46
|
/** Largest odd pupil grid width: 63 x 63 = 3969 samples, within the complex-pupil 4096 limit. */
|
|
47
47
|
export const ZERNIKE_PUPIL_MAXIMUM_SAMPLES_ACROSS = 63;
|
|
48
|
-
function requireFinite(name, value) {
|
|
49
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
50
|
-
throw new InvalidScientificInputError(`${name} must be finite.`);
|
|
51
|
-
}
|
|
52
|
-
return value;
|
|
53
|
-
}
|
|
54
48
|
function requireNollIndex(value) {
|
|
55
49
|
if (typeof value !== "number" ||
|
|
56
50
|
!Number.isSafeInteger(value) ||
|