@photivra/engine 2.17.0 → 2.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +116 -1
- package/CITATION.cff +1 -1
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/dist/camera/sensor-equivalence.d.ts +156 -0
- package/dist/camera/sensor-equivalence.js +208 -0
- package/dist/camera/sensor-equivalence.js.map +1 -0
- package/dist/capture/focus-control.js +2 -75
- package/dist/capture/focus-control.js.map +1 -1
- package/dist/capture/native-environment-lean.d.ts +1 -0
- package/dist/capture/native-environment-lean.js +1 -1
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-environment-optical-stage.d.ts +1 -0
- package/dist/capture/native-environment-optical-stage.js +2 -5
- package/dist/capture/native-environment-optical-stage.js.map +1 -1
- package/dist/capture/native-environment-raw.js +1 -5
- package/dist/capture/native-environment-raw.js.map +1 -1
- package/dist/capture/native-environment-tabulated.d.ts +5 -0
- package/dist/capture/native-environment-tabulated.js +1 -1
- package/dist/capture/native-environment-tabulated.js.map +1 -1
- package/dist/capture/release-sequence.js +2 -47
- package/dist/capture/release-sequence.js.map +1 -1
- package/dist/color/white-balance.d.ts +2 -0
- package/dist/color/white-balance.js +3 -56
- package/dist/color/white-balance.js.map +1 -1
- package/dist/composition/image-formation-plan.d.ts +1 -0
- package/dist/composition/image-formation-plan.js +3 -49
- package/dist/composition/image-formation-plan.js.map +1 -1
- package/dist/core/evidence-provenance.js +1 -16
- package/dist/core/evidence-provenance.js.map +1 -1
- package/dist/core/record-validation.d.ts +54 -0
- package/dist/core/record-validation.js +176 -0
- package/dist/core/record-validation.js.map +1 -1
- package/dist/core/scientific-assurance.js +1 -18
- package/dist/core/scientific-assurance.js.map +1 -1
- package/dist/core/shared-helpers.d.ts +33 -0
- package/dist/core/shared-helpers.js +34 -0
- package/dist/core/shared-helpers.js.map +1 -0
- package/dist/core/spectral-composition.js +1 -36
- package/dist/core/spectral-composition.js.map +1 -1
- package/dist/core/spectral.js +1 -19
- package/dist/core/spectral.js.map +1 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/equipment/exposure-capabilities.d.ts +1 -0
- package/dist/equipment/exposure-capabilities.js +2 -36
- package/dist/equipment/exposure-capabilities.js.map +1 -1
- package/dist/equipment/ideal-lens.d.ts +25 -0
- package/dist/equipment/ideal-lens.js +21 -0
- package/dist/equipment/ideal-lens.js.map +1 -0
- package/dist/equipment/iso-capabilities.js +2 -49
- package/dist/equipment/iso-capabilities.js.map +1 -1
- package/dist/equipment/metering-capabilities.js +1 -26
- package/dist/equipment/metering-capabilities.js.map +1 -1
- package/dist/equipment/release-capabilities.js +1 -53
- package/dist/equipment/release-capabilities.js.map +1 -1
- package/dist/exposure/auto-iso-slowest-shutter.js +1 -7
- package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
- package/dist/exposure/exposure-duration-control.js +1 -16
- package/dist/exposure/exposure-duration-control.js.map +1 -1
- package/dist/exposure/exposure-mode-resolver.js +1 -17
- package/dist/exposure/exposure-mode-resolver.js.map +1 -1
- package/dist/exposure/filtered-exposure-time.d.ts +93 -0
- package/dist/exposure/filtered-exposure-time.js +129 -0
- package/dist/exposure/filtered-exposure-time.js.map +1 -0
- package/dist/exposure/flash-ttl.d.ts +1 -0
- package/dist/exposure/flash-ttl.js +1 -1
- package/dist/exposure/flash-ttl.js.map +1 -1
- package/dist/exposure/flash.js +1 -49
- package/dist/exposure/flash.js.map +1 -1
- package/dist/exposure/metering-scene-radiance.js +1 -25
- package/dist/exposure/metering-scene-radiance.js.map +1 -1
- package/dist/exposure/metering-target.js +1 -30
- package/dist/exposure/metering-target.js.map +1 -1
- package/dist/exposure/metering-temporal.js +1 -8
- package/dist/exposure/metering-temporal.js.map +1 -1
- package/dist/exposure/metering.js +1 -72
- package/dist/exposure/metering.js.map +1 -1
- package/dist/exposure/program-line.js +1 -45
- package/dist/exposure/program-line.js.map +1 -1
- package/dist/facade/catalogue-lens-signal.d.ts +94 -0
- package/dist/facade/catalogue-lens-signal.js +362 -0
- package/dist/facade/catalogue-lens-signal.js.map +1 -0
- package/dist/facade/defocus-image-space-provider.js +5 -2
- package/dist/facade/defocus-image-space-provider.js.map +1 -1
- package/dist/facade/develop-joined-parts.d.ts +29 -0
- package/dist/facade/develop-joined-parts.js +33 -0
- package/dist/facade/develop-joined-parts.js.map +1 -0
- package/dist/facade/display-view-lens-signal.d.ts +32 -1
- package/dist/facade/display-view-lens-signal.js +85 -10
- package/dist/facade/display-view-lens-signal.js.map +1 -1
- package/dist/facade/expected-signal-setup.d.ts +8 -0
- package/dist/facade/expected-signal-setup.js +7 -0
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/expected-signal.d.ts +5 -0
- package/dist/facade/expected-signal.js +5 -1
- package/dist/facade/expected-signal.js.map +1 -1
- package/dist/facade/generic-lens-capture.d.ts +5 -2
- package/dist/facade/generic-lens-capture.js +5 -2
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +67 -8
- package/dist/facade/simulate-capture.js +262 -126
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.js +1 -1
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.js +1 -3
- package/dist/gpu/chart-composite-gpu.js.map +1 -1
- package/dist/gpu/chart-radiance-gpu.js +1 -5
- package/dist/gpu/chart-radiance-gpu.js.map +1 -1
- package/dist/gpu/display-view-quality-gpu.d.ts +13 -1
- package/dist/gpu/display-view-quality-gpu.js +4 -8
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.js +3 -25
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/gpu/sensor-stage-gpu.js +1 -5
- package/dist/gpu/sensor-stage-gpu.js.map +1 -1
- package/dist/gpu/webgpu-runtime.d.ts +2 -0
- package/dist/gpu/webgpu-runtime.js +3 -0
- package/dist/gpu/webgpu-runtime.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/motion/camera-rotation.js +1 -6
- package/dist/motion/camera-rotation.js.map +1 -1
- package/dist/motion/capture-rotation-inverse-mapping.d.ts +12 -0
- package/dist/motion/capture-rotation-inverse-mapping.js +2 -2
- package/dist/motion/capture-rotation-inverse-mapping.js.map +1 -1
- package/dist/motion/capture-rotation-temporal-quadrature.d.ts +1 -0
- package/dist/motion/capture-rotation-temporal-quadrature.js +1 -1
- package/dist/motion/capture-rotation-temporal-quadrature.js.map +1 -1
- package/dist/motion/capture-rotation-trajectory.js +2 -17
- package/dist/motion/capture-rotation-trajectory.js.map +1 -1
- package/dist/motion/capture-translation-parallax-temporal-quadrature.d.ts +1 -0
- package/dist/motion/capture-translation-parallax-temporal-quadrature.js +3 -14
- package/dist/motion/capture-translation-parallax-temporal-quadrature.js.map +1 -1
- package/dist/motion/extended-object-projection.js +5 -40
- package/dist/motion/extended-object-projection.js.map +1 -1
- package/dist/optics/atmospheric-haze.js +1 -7
- package/dist/optics/atmospheric-haze.js.map +1 -1
- package/dist/optics/brown-conrady-distortion.js +1 -7
- package/dist/optics/brown-conrady-distortion.js.map +1 -1
- package/dist/optics/complex-pupil-psf.d.ts +1 -0
- package/dist/optics/complex-pupil-psf.js +2 -84
- package/dist/optics/complex-pupil-psf.js.map +1 -1
- package/dist/optics/focus-state.js +1 -11
- package/dist/optics/focus-state.js.map +1 -1
- package/dist/optics/front-of-lens-filter.d.ts +5 -0
- package/dist/optics/front-of-lens-filter.js +3 -65
- package/dist/optics/front-of-lens-filter.js.map +1 -1
- package/dist/optics/generic-nd-filter-presets.d.ts +12 -0
- package/dist/optics/generic-nd-filter-presets.js +48 -0
- package/dist/optics/generic-nd-filter-presets.js.map +1 -0
- package/dist/optics/illumination-vignetting.js +1 -6
- package/dist/optics/illumination-vignetting.js.map +1 -1
- package/dist/optics/lens-psf-profile.d.ts +4 -1
- package/dist/optics/lens-psf-profile.js +12 -111
- package/dist/optics/lens-psf-profile.js.map +1 -1
- package/dist/optics/longitudinal-chromatic-aberration.js +1 -7
- package/dist/optics/longitudinal-chromatic-aberration.js.map +1 -1
- package/dist/optics/pupil-vignetting.js +1 -7
- package/dist/optics/pupil-vignetting.js.map +1 -1
- package/dist/optics/radial-distortion.js +1 -6
- package/dist/optics/radial-distortion.js.map +1 -1
- package/dist/optics/scene-to-sensor-irradiance.js +4 -96
- package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
- package/dist/optics/stray-light.d.ts +1 -0
- package/dist/optics/stray-light.js +1 -1
- package/dist/optics/stray-light.js.map +1 -1
- package/dist/optics/zernike-wavefront.js +1 -7
- package/dist/optics/zernike-wavefront.js.map +1 -1
- package/dist/output/capture-geometry.js +1 -8
- package/dist/output/capture-geometry.js.map +1 -1
- package/dist/output/developed-lens-corrections.d.ts +62 -0
- package/dist/output/developed-lens-corrections.js +135 -0
- package/dist/output/developed-lens-corrections.js.map +1 -0
- package/dist/output/generic-lens-corrections.d.ts +2 -6
- package/dist/output/generic-lens-corrections.js +3 -6
- package/dist/output/generic-lens-corrections.js.map +1 -1
- package/dist/output/geometric-transforms.js +1 -4
- package/dist/output/geometric-transforms.js.map +1 -1
- package/dist/output/native-capture-sdr.d.ts +2 -2
- package/dist/output/native-capture-sdr.js +3 -2
- package/dist/output/native-capture-sdr.js.map +1 -1
- package/dist/processing/development-detail.d.ts +29 -0
- package/dist/processing/development-detail.js +128 -107
- package/dist/processing/development-detail.js.map +1 -1
- package/dist/schema/illumination-temporal.js +3 -72
- package/dist/schema/illumination-temporal.js.map +1 -1
- package/dist/schema/illumination.d.ts +1 -0
- package/dist/schema/illumination.js +2 -65
- package/dist/schema/illumination.js.map +1 -1
- package/dist/schema/scene-radiance-temporal.js +2 -14
- package/dist/schema/scene-radiance-temporal.js.map +1 -1
- package/dist/schema/scene-radiance.js +2 -60
- package/dist/schema/scene-radiance.js.map +1 -1
- package/dist/schema/validation.d.ts +2 -0
- package/dist/schema/validation.js +2 -17
- package/dist/schema/validation.js.map +1 -1
- package/dist/sensor/accumulated-charge.js +2 -59
- package/dist/sensor/accumulated-charge.js.map +1 -1
- package/dist/sensor/architecture.js +1 -16
- package/dist/sensor/architecture.js.map +1 -1
- package/dist/sensor/camera-saturation-capacity.d.ts +1 -0
- package/dist/sensor/camera-saturation-capacity.js +3 -55
- package/dist/sensor/camera-saturation-capacity.js.map +1 -1
- package/dist/sensor/capture-color-sampling-binding.js +1 -26
- package/dist/sensor/capture-color-sampling-binding.js.map +1 -1
- package/dist/sensor/capture-mode-timing.js +1 -39
- package/dist/sensor/capture-mode-timing.js.map +1 -1
- package/dist/sensor/capture-mode.js +1 -23
- package/dist/sensor/capture-mode.js.map +1 -1
- package/dist/sensor/color-sampling.js +1 -26
- package/dist/sensor/color-sampling.js.map +1 -1
- package/dist/sensor/constant-rate-temporal-integration.js +2 -46
- package/dist/sensor/constant-rate-temporal-integration.js.map +1 -1
- package/dist/sensor/dark-current.js +2 -45
- package/dist/sensor/dark-current.js.map +1 -1
- package/dist/sensor/eqe-electron-rate.js +2 -35
- package/dist/sensor/eqe-electron-rate.js.map +1 -1
- package/dist/sensor/exposure-window.d.ts +5 -0
- package/dist/sensor/exposure-window.js +7 -22
- package/dist/sensor/exposure-window.js.map +1 -1
- package/dist/sensor/iso-gain.js +1 -7
- package/dist/sensor/iso-gain.js.map +1 -1
- package/dist/sensor/iso-signal-chain.js +1 -43
- package/dist/sensor/iso-signal-chain.js.map +1 -1
- package/dist/sensor/optical-stack.js +1 -25
- package/dist/sensor/optical-stack.js.map +1 -1
- package/dist/sensor/photon-energy.js +1 -26
- package/dist/sensor/photon-energy.js.map +1 -1
- package/dist/sensor/physical-charge-capacity.js +3 -77
- package/dist/sensor/physical-charge-capacity.js.map +1 -1
- package/dist/sensor/radiometry-readiness.js +2 -23
- package/dist/sensor/radiometry-readiness.js.map +1 -1
- package/dist/sensor/raw-readout.d.ts +3 -0
- package/dist/sensor/raw-readout.js +4 -66
- package/dist/sensor/raw-readout.js.map +1 -1
- package/dist/sensor/raw-reconstruction.js +2 -64
- package/dist/sensor/raw-reconstruction.js.map +1 -1
- package/dist/sensor/readout-exposure-linkage.js +1 -9
- package/dist/sensor/readout-exposure-linkage.js.map +1 -1
- package/dist/sensor/readout-timing.js +3 -57
- package/dist/sensor/readout-timing.js.map +1 -1
- package/dist/sensor/response-application-compatibility.d.ts +2 -0
- package/dist/sensor/response-application-compatibility.js +3 -29
- package/dist/sensor/response-application-compatibility.js.map +1 -1
- package/dist/sensor/response-operating-range.d.ts +1 -0
- package/dist/sensor/response-operating-range.js +3 -79
- package/dist/sensor/response-operating-range.js.map +1 -1
- package/dist/sensor/responsivity-photocurrent.js +4 -108
- package/dist/sensor/responsivity-photocurrent.js.map +1 -1
- package/dist/sensor/sampling-aperture.js +1 -25
- package/dist/sensor/sampling-aperture.js.map +1 -1
- package/dist/sensor/spatial-sample-reduction.js +2 -19
- package/dist/sensor/spatial-sample-reduction.js.map +1 -1
- package/dist/sensor/spatial-sampling-quadrature.js +1 -3
- package/dist/sensor/spatial-sampling-quadrature.js.map +1 -1
- package/dist/sensor/spatio-spectral-reduction.js +1 -18
- package/dist/sensor/spatio-spectral-reduction.js.map +1 -1
- package/dist/sensor/spectral-coverage.d.ts +10 -1
- package/dist/sensor/spectral-coverage.js +1 -1
- package/dist/sensor/spectral-coverage.js.map +1 -1
- package/dist/sensor/spectral-quadrature.js +2 -35
- package/dist/sensor/spectral-quadrature.js.map +1 -1
- package/dist/sensor/spectral-response.js +1 -47
- package/dist/sensor/spectral-response.js.map +1 -1
- package/dist/stabilization/camera-shake.js +1 -6
- package/dist/stabilization/camera-shake.js.map +1 -1
- package/dist/stabilization/system.js +2 -80
- package/dist/stabilization/system.js.map +1 -1
- package/docs/AGENT_GUIDE.md +4 -0
- package/docs/API_REFERENCE.md +69 -2
- package/docs/QUICK_START.md +136 -6
- package/docs/USAGE.md +89 -3
- package/docs/api/camera-sensor-equivalence.md +209 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-ideal-lens.md +26 -0
- package/docs/api/exports.json +118 -3
- package/docs/api/exposure-filtered-exposure-time.md +105 -0
- package/docs/api/facade-develop-joined-parts.md +32 -0
- package/docs/api/facade-simulate-capture.md +62 -8
- package/docs/api/optics-generic-nd-filter-presets.md +34 -0
- package/docs/api/optics-lens-psf-profile.md +3 -1
- package/docs/api/output-developed-lens-corrections.md +12 -0
- package/docs/api/output-generic-lens-corrections.md +0 -9
- package/llms.txt +3 -0
- package/package.json +1 -1
|
@@ -16,48 +16,8 @@ import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
|
16
16
|
import { parseEvidenceList } from "../core/evidence-provenance.js";
|
|
17
17
|
import { parseSensorEqeTemporalPhotoSignal } from "./temporal-photo-signal.js";
|
|
18
18
|
import { InvalidScientificInputError } from "../core/validation.js";
|
|
19
|
-
import { requireInputObject, requireInputKeys,
|
|
20
|
-
|
|
21
|
-
if (typeof value !== "object" ||
|
|
22
|
-
value === null ||
|
|
23
|
-
Array.isArray(value)) {
|
|
24
|
-
throw new InvalidConfigurationError(path + " must be an object.");
|
|
25
|
-
}
|
|
26
|
-
if (allowedKeys !== null)
|
|
27
|
-
rejectUnknownFields(value, allowedKeys, path);
|
|
28
|
-
return value;
|
|
29
|
-
}
|
|
30
|
-
function requireNonEmptyString(value, path) {
|
|
31
|
-
if (typeof value !== "string" ||
|
|
32
|
-
value.trim().length === 0) {
|
|
33
|
-
throw new InvalidConfigurationError(path + " must be a non-empty string.");
|
|
34
|
-
}
|
|
35
|
-
return value;
|
|
36
|
-
}
|
|
37
|
-
function requireFinite(value, path) {
|
|
38
|
-
if (typeof value !== "number" ||
|
|
39
|
-
!Number.isFinite(value)) {
|
|
40
|
-
throw new InvalidConfigurationError(path + " must be finite.");
|
|
41
|
-
}
|
|
42
|
-
return value;
|
|
43
|
-
}
|
|
44
|
-
function requireNonNegativeFinite(value, path) {
|
|
45
|
-
const parsed = requireFinite(value, path);
|
|
46
|
-
if (parsed < 0) {
|
|
47
|
-
throw new InvalidConfigurationError(path +
|
|
48
|
-
" must be greater than or equal to zero.");
|
|
49
|
-
}
|
|
50
|
-
return parsed;
|
|
51
|
-
}
|
|
52
|
-
function requireSiteIndex(value, path) {
|
|
53
|
-
if (typeof value !== "number" ||
|
|
54
|
-
!Number.isSafeInteger(value) ||
|
|
55
|
-
value < 0) {
|
|
56
|
-
throw new InvalidConfigurationError(path +
|
|
57
|
-
" must be a non-negative safe integer.");
|
|
58
|
-
}
|
|
59
|
-
return value;
|
|
60
|
-
}
|
|
19
|
+
import { requireInputObject, requireInputKeys, rejectUnknownVariantFields, UNCERTAINTY_VARIANT_FIELDS, requireConfigRecord as requireRecord, requireConfigFinite as requireFinite, requireConfigString as requireNonEmptyString, requireConfigNonNegativeSafeInteger as requireSiteIndex, requireConfigFiniteAtLeastZero as requireNonNegativeFinite } from "../core/record-validation.js";
|
|
20
|
+
import { sameSite } from "../core/shared-helpers.js";
|
|
61
21
|
function parseUncertainty(value, path) {
|
|
62
22
|
const record = requireRecord(value, path, null);
|
|
63
23
|
rejectUnknownVariantFields(record, "kind", UNCERTAINTY_VARIANT_FIELDS, path);
|
|
@@ -255,9 +215,6 @@ export function parseSensorDarkCurrentProfile(value) {
|
|
|
255
215
|
.spatialDarkCurrentNonuniformityModeled
|
|
256
216
|
};
|
|
257
217
|
}
|
|
258
|
-
function sameSite(a, b) {
|
|
259
|
-
return a.x === b.x && a.y === b.y;
|
|
260
|
-
}
|
|
261
218
|
function resolveDarkCurrentRate(model, operatingTemperatureC) {
|
|
262
219
|
if (!Number.isFinite(operatingTemperatureC)) {
|
|
263
220
|
throw new InvalidScientificInputError("operatingTemperatureC must be finite.");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dark-current.js","sourceRoot":"","sources":["../../src/sensor/dark-current.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Dark current: electrons generated by heat in a sensor site even with no light, and the dark charge\n * they add over one exposure.\n *\n * The dark-current rate (electrons per second, before any compensation) is declared separately as a\n * function of temperature. It is given either at an exact temperature or as samples, interpolated\n * log-linearly (or with the legacy linear rule) only within the declared range. No universal\n * \"doubles every N degrees\" law is assumed. Rates are population means, an approximation; hot pixels\n * and DCNU (dark-current non-uniformity) are not modelled.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport {\n approximationResult,\n calculatedResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport type { SensorEqeExposureIntegration } from \"./constant-rate-temporal-integration.js\";\nimport { parseSensorEqeTemporalPhotoSignal } from \"./temporal-photo-signal.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport type {\n SensorEqePhotoExposure, SensorEqeTemporalPhotoSignal\n} from \"./temporal-photo-signal.js\";\nimport type {\n SensorSpectralResponseScientificStatus,\n SensorSpectralResponseUncertainty\n} from \"./spectral-response.js\";\nimport { requireInputObject, requireInputKeys, rejectUnknownFields, rejectUnknownVariantFields, UNCERTAINTY_VARIANT_FIELDS } from \"../core/record-validation.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport type SensorDarkCurrentSiteApplicability =\n | {\n kind: \"exact-site\";\n site: {\n x: number;\n y: number;\n };\n }\n | {\n kind:\n \"uniform-site-mean-approximation\";\n limitation: string;\n evidence:\n readonly EvidenceProvenance[];\n };\n\nexport type SensorDarkCurrentTemperatureModel =\n | {\n kind:\n \"fixed-reference-temperature\";\n referenceTemperatureC: number;\n darkCurrentElectronsPerSecond:\n number;\n }\n | {\n /** Legacy interpolation, linear in rate. Between samples of an exponential curve it gives values that are too high. */\n kind:\n \"piecewise-linear-temperature-table\";\n interpolation: \"piecewise-linear\";\n outsideRangeBehavior:\n \"fail-closed\";\n samples: readonly {\n temperatureC: number;\n darkCurrentElectronsPerSecond:\n number;\n }[];\n }\n | {\n /**\n * Linear interpolation of ln(rate) between neighbouring samples. That makes the rate exponential\n * in temperature within each interval, matching the roughly exponential (Arrhenius, or\n * \"doubling temperature\") behaviour of thermally generated dark current. Rates must be strictly\n * positive. No single doubling temperature is imposed; each interval follows its own two samples.\n */\n kind:\n \"log-linear-temperature-table\";\n interpolation: \"log-linear\";\n outsideRangeBehavior:\n \"fail-closed\";\n samples: readonly {\n temperatureC: number;\n darkCurrentElectronsPerSecond:\n number;\n }[];\n };\n\nexport interface SensorDarkCurrentProfile {\n schemaVersion: \"0.1.0\";\n profileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n scientificStatus:\n SensorSpectralResponseScientificStatus;\n uncertainty:\n SensorSpectralResponseUncertainty;\n evidence: readonly EvidenceProvenance[];\n chargeMeaning:\n \"pre-compensation-thermally-generated-electrons\";\n siteApplicability:\n SensorDarkCurrentSiteApplicability;\n temperatureModel:\n SensorDarkCurrentTemperatureModel;\n darkCurrentCompensationIncluded: false;\n spatialDarkCurrentNonuniformityModeled:\n boolean;\n}\n\nexport interface CalculateSensorDarkCurrentChargeInput<Exposure extends SensorEqePhotoExposure = SensorEqeExposureIntegration> {\n exposure:\n Exposure;\n darkCurrentProfile:\n SensorDarkCurrentProfile;\n operatingTemperatureC: number;\n}\n\nexport interface SensorDarkCurrentCharge {\n darkCurrentProfileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n stationarityProfileId: string;\n temporalIntegrationId?: never;\n timeReference:\n \"first-opening-boundary-phase\";\n startOffsetSecondsFromOpeningReference:\n number;\n endOffsetSecondsFromOpeningReference:\n number;\n operatingTemperatureC: number;\n temperatureModel:\n SensorDarkCurrentTemperatureModel[\"kind\"];\n temperatureInterpolationUsed:\n boolean;\n darkCurrentElectronsPerSecond:\n number;\n localExposureDurationSeconds:\n number;\n expectedDarkElectronCount:\n number;\n countMeaning:\n \"expected-thermally-generated-electrons\";\n expectationValueOnly: true;\n integerDarkElectronCountSampled:\n false;\n darkShotNoiseApplied: false;\n darkCurrentCompensationApplied:\n false;\n spatialDarkCurrentNonuniformityModeled:\n boolean;\n photoSignalIncluded: false;\n otherChargeIncluded: false;\n physicalFullWellAssessmentAuthorized:\n false;\n saturationAssessed: false;\n componentEvidence: {\n darkCurrent:\n readonly EvidenceProvenance[];\n siteApproximation:\n readonly EvidenceProvenance[];\n exposure:\n SensorEqeExposureIntegration[\"componentEvidence\"];\n };\n}\n\n/** Dark charge for the exact same time-resolved photo event. It has no stationarity ID. */\nexport type SensorTemporalDarkCurrentCharge = Omit<SensorDarkCurrentCharge, \"stationarityProfileId\" | \"temporalIntegrationId\" | \"componentEvidence\"> & {\n stationarityProfileId?: never;\n temporalIntegrationId: string;\n componentEvidence: Omit<SensorDarkCurrentCharge[\"componentEvidence\"], \"exposure\"> & { exposure: SensorEqeTemporalPhotoSignal[\"componentEvidence\"] };\n};\nexport type SensorPhotoDarkCurrentCharge = SensorDarkCurrentCharge | SensorTemporalDarkCurrentCharge;\n\nfunction requireRecord(\n value: unknown,\n path: string,\n allowedKeys: readonly string[] | null\n): UnknownRecord {\n if (\n typeof value !== \"object\" ||\n value === null ||\n Array.isArray(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be an object.\"\n );\n }\n if (allowedKeys !== null) rejectUnknownFields(value, allowedKeys, path);\n return value as UnknownRecord;\n}\n\nfunction requireNonEmptyString(\n value: unknown,\n path: string\n): string {\n if (\n typeof value !== \"string\" ||\n value.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path + \" must be a non-empty string.\"\n );\n }\n return value;\n}\n\nfunction 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 requireNonNegativeFinite(\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 or equal to zero.\"\n );\n }\n return parsed;\n}\n\nfunction requireSiteIndex(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isSafeInteger(value) ||\n value < 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must be a non-negative safe integer.\"\n );\n }\n return value;\n}\n\nfunction parseUncertainty(\n value: unknown,\n path: string\n): SensorSpectralResponseUncertainty {\n const record =\n requireRecord(value, path, null);\n rejectUnknownVariantFields(record, \"kind\", UNCERTAINTY_VARIANT_FIELDS, path);\n\n if (record.kind === \"relative\") {\n const fraction =\n requireNonNegativeFinite(\n record.fraction,\n path + \".fraction\"\n );\n if (\n typeof record.basis !== \"string\" ||\n record.basis.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \".basis must be a non-empty string.\"\n );\n }\n return {\n kind: \"relative\",\n fraction,\n basis: record.basis\n };\n }\n\n if (\n record.kind === \"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 parseSiteApplicability(\n value: unknown\n): SensorDarkCurrentSiteApplicability {\n const record = requireRecord(\n value,\n \"sensorDarkCurrent.siteApplicability\", null\n );\n rejectUnknownVariantFields(record, \"kind\", { \"exact-site\": [\"kind\", \"site\"], \"uniform-site-mean-approximation\": [\"kind\", \"limitation\", \"evidence\"] }, \"sensorDarkCurrent.siteApplicability\");\n\n if (record.kind === \"exact-site\") {\n const site = requireRecord(\n record.site,\n \"sensorDarkCurrent.siteApplicability.site\", [\"x\", \"y\"]\n );\n return {\n kind: \"exact-site\",\n site: {\n x: requireSiteIndex(\n site.x,\n \"sensorDarkCurrent.siteApplicability.site.x\"\n ),\n y: requireSiteIndex(\n site.y,\n \"sensorDarkCurrent.siteApplicability.site.y\"\n )\n }\n };\n }\n\n if (\n record.kind ===\n \"uniform-site-mean-approximation\"\n ) {\n return {\n kind:\n \"uniform-site-mean-approximation\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n \"sensorDarkCurrent.siteApplicability.limitation\"\n ),\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorDarkCurrent.siteApplicability.evidence\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.siteApplicability.kind is invalid.\"\n );\n}\n\nfunction parseTemperatureModel(\n value: unknown\n): SensorDarkCurrentTemperatureModel {\n const record = requireRecord(\n value,\n \"sensorDarkCurrent.temperatureModel\", null\n );\n rejectUnknownVariantFields(record, \"kind\", {\n \"fixed-reference-temperature\": [\"kind\", \"referenceTemperatureC\", \"darkCurrentElectronsPerSecond\"],\n \"piecewise-linear-temperature-table\": [\"kind\", \"interpolation\", \"outsideRangeBehavior\", \"samples\"],\n \"log-linear-temperature-table\": [\"kind\", \"interpolation\", \"outsideRangeBehavior\", \"samples\"]\n }, \"sensorDarkCurrent.temperatureModel\");\n\n if (\n record.kind ===\n \"fixed-reference-temperature\"\n ) {\n return {\n kind:\n \"fixed-reference-temperature\",\n referenceTemperatureC:\n requireFinite(\n record.referenceTemperatureC,\n \"sensorDarkCurrent.temperatureModel.referenceTemperatureC\"\n ),\n darkCurrentElectronsPerSecond:\n requireNonNegativeFinite(\n record.darkCurrentElectronsPerSecond,\n \"sensorDarkCurrent.temperatureModel.darkCurrentElectronsPerSecond\"\n )\n };\n }\n\n const logLinear =\n record.kind ===\n \"log-linear-temperature-table\";\n if (\n record.kind !==\n \"piecewise-linear-temperature-table\" &&\n !logLinear\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.temperatureModel.kind is invalid.\"\n );\n }\n if (\n record.interpolation !==\n (logLinear ? \"log-linear\" : \"piecewise-linear\")\n ) {\n throw new InvalidConfigurationError(\n logLinear\n ? 'sensorDarkCurrent.temperatureModel.interpolation must be \"log-linear\".'\n : 'sensorDarkCurrent.temperatureModel.interpolation must be \"piecewise-linear\".'\n );\n }\n if (\n record.outsideRangeBehavior !==\n \"fail-closed\"\n ) {\n throw new InvalidConfigurationError(\n 'sensorDarkCurrent.temperatureModel.outsideRangeBehavior must be \"fail-closed\".'\n );\n }\n if (\n !Array.isArray(record.samples) ||\n record.samples.length < 2\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.temperatureModel.samples must contain at least two samples.\"\n );\n }\n\n let previousTemperature =\n Number.NEGATIVE_INFINITY;\n const samples =\n record.samples.map(\n (sample, index) => {\n const parsed = requireRecord(\n sample,\n \"sensorDarkCurrent.temperatureModel.samples[\" +\n index +\n \"]\", [\"temperatureC\", \"darkCurrentElectronsPerSecond\"]\n );\n const temperatureC =\n requireFinite(\n parsed.temperatureC,\n \"sensorDarkCurrent.temperatureModel.samples[\" +\n index +\n \"].temperatureC\"\n );\n if (\n temperatureC <=\n previousTemperature\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.temperatureModel.samples must be strictly increasing in temperature.\"\n );\n }\n previousTemperature =\n temperatureC;\n return {\n temperatureC,\n darkCurrentElectronsPerSecond:\n requireNonNegativeFinite(\n parsed.darkCurrentElectronsPerSecond,\n \"sensorDarkCurrent.temperatureModel.samples[\" +\n index +\n \"].darkCurrentElectronsPerSecond\"\n )\n };\n }\n );\n\n if (\n logLinear &&\n samples.some(\n (sample) =>\n sample\n .darkCurrentElectronsPerSecond <=\n 0\n )\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.temperatureModel log-linear samples require strictly positive rates.\"\n );\n }\n\n return logLinear\n ? {\n kind:\n \"log-linear-temperature-table\",\n interpolation: \"log-linear\",\n outsideRangeBehavior:\n \"fail-closed\",\n samples\n }\n : {\n kind:\n \"piecewise-linear-temperature-table\",\n interpolation: \"piecewise-linear\",\n outsideRangeBehavior:\n \"fail-closed\",\n samples\n };\n}\n\n/**\n * Validates untrusted input and returns it as a normalized, typed value. Unknown enum values,\n * missing required fields and inconsistent scientific data are rejected.\n *\n * The profile declares the dark-current rate (before compensation) against temperature, at an exact\n * temperature or as samples. Interpolation (log-linear, or the legacy linear rule) is allowed only\n * within the declared range. No universal doubling-temperature law is assumed.\n * @param value - Untrusted data. TypeScript types alone are not validation.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function parseSensorDarkCurrentProfile(\n value: unknown\n): SensorDarkCurrentProfile {\n const record = requireRecord(\n value,\n \"sensorDarkCurrent\", [\"schemaVersion\", \"chargeMeaning\", \"scientificStatus\", \"darkCurrentCompensationIncluded\", \"spatialDarkCurrentNonuniformityModeled\", \"uncertainty\", \"siteApplicability\", \"profileId\", \"colorSamplingProfileId\", \"channelId\", \"evidence\", \"temperatureModel\"]\n );\n\n if (\n record.schemaVersion !== \"0.1.0\"\n ) {\n throw new InvalidConfigurationError(\n 'sensorDarkCurrent.schemaVersion must be \"0.1.0\".'\n );\n }\n if (\n record.chargeMeaning !==\n \"pre-compensation-thermally-generated-electrons\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.chargeMeaning is invalid.\"\n );\n }\n if (\n record.scientificStatus !==\n \"calibrated\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.scientificStatus is invalid.\"\n );\n }\n if (\n record.darkCurrentCompensationIncluded !==\n false\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.darkCurrentCompensationIncluded must be false.\"\n );\n }\n if (\n typeof record\n .spatialDarkCurrentNonuniformityModeled !==\n \"boolean\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.spatialDarkCurrentNonuniformityModeled must be boolean.\"\n );\n }\n\n const uncertainty =\n parseUncertainty(\n record.uncertainty,\n \"sensorDarkCurrent.uncertainty\"\n );\n if (\n record.scientificStatus ===\n \"calibrated\" &&\n uncertainty.kind ===\n \"not-quantified\"\n ) {\n throw new InvalidConfigurationError(\n \"A calibrated dark-current profile must declare quantified relative uncertainty.\"\n );\n }\n\n const siteApplicability =\n parseSiteApplicability(\n record.siteApplicability\n );\n if (\n siteApplicability.kind ===\n \"uniform-site-mean-approximation\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"Uniform site-mean dark current must remain an approximation.\"\n );\n }\n if (\n siteApplicability.kind ===\n \"uniform-site-mean-approximation\" &&\n record\n .spatialDarkCurrentNonuniformityModeled !==\n false\n ) {\n throw new InvalidConfigurationError(\n \"Uniform site-mean dark current cannot claim spatial dark-current nonuniformity is modeled.\"\n );\n }\n\n return {\n schemaVersion: \"0.1.0\",\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"sensorDarkCurrent.profileId\"\n ),\n colorSamplingProfileId:\n requireNonEmptyString(\n record.colorSamplingProfileId,\n \"sensorDarkCurrent.colorSamplingProfileId\"\n ),\n channelId:\n requireNonEmptyString(\n record.channelId,\n \"sensorDarkCurrent.channelId\"\n ),\n scientificStatus:\n record.scientificStatus,\n uncertainty,\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorDarkCurrent.evidence\"\n ),\n chargeMeaning:\n \"pre-compensation-thermally-generated-electrons\",\n siteApplicability,\n temperatureModel:\n parseTemperatureModel(\n record.temperatureModel\n ),\n darkCurrentCompensationIncluded:\n false,\n spatialDarkCurrentNonuniformityModeled:\n record\n .spatialDarkCurrentNonuniformityModeled\n };\n}\n\nfunction sameSite(\n a: { x: number; y: number },\n b: { x: number; y: number }\n): boolean {\n return a.x === b.x && a.y === b.y;\n}\n\nfunction resolveDarkCurrentRate(\n model:\n SensorDarkCurrentTemperatureModel,\n operatingTemperatureC: number\n): {\n rate: number;\n interpolationUsed: boolean;\n} {\n if (\n !Number.isFinite(\n operatingTemperatureC\n )\n ) {\n throw new InvalidScientificInputError(\n \"operatingTemperatureC must be finite.\"\n );\n }\n\n if (\n model.kind ===\n \"fixed-reference-temperature\"\n ) {\n if (\n operatingTemperatureC !==\n model.referenceTemperatureC\n ) {\n throw new InvalidScientificInputError(\n \"Fixed-reference dark current may only be used at its exact reference temperature.\"\n );\n }\n return {\n rate:\n model\n .darkCurrentElectronsPerSecond,\n interpolationUsed: false\n };\n }\n\n const first =\n model.samples[0]!;\n const last =\n model.samples[\n model.samples.length - 1\n ]!;\n if (\n operatingTemperatureC <\n first.temperatureC ||\n operatingTemperatureC >\n last.temperatureC\n ) {\n throw new InvalidScientificInputError(\n \"operatingTemperatureC is outside the measured dark-current temperature range; extrapolation is not permitted.\"\n );\n }\n\n const exact =\n model.samples.find(\n (sample) =>\n sample.temperatureC ===\n operatingTemperatureC\n );\n if (exact !== undefined) {\n return {\n rate:\n exact\n .darkCurrentElectronsPerSecond,\n interpolationUsed: false\n };\n }\n\n const upperIndex =\n model.samples.findIndex(\n (sample) =>\n sample.temperatureC >\n operatingTemperatureC\n );\n const upper =\n model.samples[upperIndex]!;\n const lower =\n model.samples[upperIndex - 1]!;\n const alpha =\n (operatingTemperatureC -\n lower.temperatureC) /\n (upper.temperatureC -\n lower.temperatureC);\n if (\n model.kind ===\n \"log-linear-temperature-table\"\n ) {\n // r = r0^(1-alpha) * r1^alpha, written as r0 * (r1/r0)^alpha.\n return {\n rate:\n lower\n .darkCurrentElectronsPerSecond *\n (upper\n .darkCurrentElectronsPerSecond /\n lower\n .darkCurrentElectronsPerSecond) **\n alpha,\n interpolationUsed: true\n };\n }\n const rate =\n lower\n .darkCurrentElectronsPerSecond +\n alpha *\n (\n upper\n .darkCurrentElectronsPerSecond -\n lower\n .darkCurrentElectronsPerSecond\n );\n\n return {\n rate,\n interpolationUsed: true\n };\n}\n\nfunction validateExposure(\n exposure:\n SensorEqePhotoExposure\n): void {\n requireInputObject(exposure, \"exposure input\");\n if (exposure.kind === \"eqe-temporal-photo-signal\") parseSensorEqeTemporalPhotoSignal(exposure);\n if (\n (exposure.kind !== \"eqe-expected-counts\" && exposure.kind !== \"eqe-temporal-photo-signal\") ||\n exposure.temporalIntegrationApplied !==\n true ||\n exposure.exposureDurationApplied !==\n true ||\n exposure.accumulatedSignalCompleteness !==\n \"photo-signal-only\" ||\n exposure.darkChargeIncluded !== false ||\n exposure.otherChargeIncluded !== false ||\n exposure\n .physicalFullWellAssessmentAuthorized !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"Dark-current composition requires an unmodified photo-signal-only EQE exposure integration.\"\n );\n }\n\n if (\n typeof exposure\n .localExposureDurationSeconds !==\n \"number\" ||\n !Number.isFinite(\n exposure\n .localExposureDurationSeconds\n ) ||\n exposure\n .localExposureDurationSeconds <=\n 0\n ) {\n throw new InvalidScientificInputError(\n \"EQE exposure local duration must be finite and positive.\"\n );\n }\n}\n\n/**\n * Evaluates the declared dark-current rate at the sensor temperature and integrates it over the exact\n * local exposure of the photo signal.\n *\n * The rate is a population mean, an approximation. Hot pixels and DCNU (dark-current non-uniformity)\n * are not modelled. No universal doubling-temperature law is assumed.\n * @param input - Dark-current input whose exposure is a per-exposure photo signal (SensorEqeExposureIntegration); see `CalculateSensorDarkCurrentChargeInput`.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function calculateSensorDarkCurrentCharge(\n input: Omit<CalculateSensorDarkCurrentChargeInput, \"exposure\"> & { exposure: SensorEqeExposureIntegration }\n): CalculationResult<SensorDarkCurrentCharge>;\n/**\n * Evaluates the declared dark-current rate at the sensor temperature and integrates it over the exact\n * local exposure of the photo signal.\n *\n * The rate is a population mean, an approximation. Hot pixels and DCNU (dark-current non-uniformity)\n * are not modelled. No universal doubling-temperature law is assumed.\n * @param input - Dark-current input whose exposure is a time-resolved photo signal (SensorEqeTemporalPhotoSignal); see `CalculateSensorDarkCurrentChargeInput`.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function calculateSensorDarkCurrentCharge(\n input: Omit<CalculateSensorDarkCurrentChargeInput, \"exposure\"> & { exposure: SensorEqeTemporalPhotoSignal }\n): CalculationResult<SensorTemporalDarkCurrentCharge>;\n/**\n * Evaluates the declared dark-current rate at the sensor temperature and integrates it over the exact\n * local exposure of the photo signal.\n *\n * The rate is a population mean, an approximation. Hot pixels and DCNU (dark-current non-uniformity)\n * are not modelled. No universal doubling-temperature law is assumed.\n * @param input - The input; see `CalculateSensorDarkCurrentChargeInput` for its fields, units and coordinates.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function calculateSensorDarkCurrentCharge(\n input: CalculateSensorDarkCurrentChargeInput<SensorEqePhotoExposure>\n): CalculationResult<SensorPhotoDarkCurrentCharge>;\n/**\n * Evaluates the declared dark-current rate at the sensor temperature and integrates it over the exact\n * local exposure of the photo signal.\n *\n * The rate is a population mean, an approximation. Hot pixels and DCNU (dark-current non-uniformity)\n * are not modelled. No universal doubling-temperature law is assumed.\n * @param input - The input; see `CalculateSensorDarkCurrentChargeInput` for its fields, units and coordinates.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function calculateSensorDarkCurrentCharge(\n input:\n CalculateSensorDarkCurrentChargeInput<SensorEqePhotoExposure>\n): CalculationResult<SensorPhotoDarkCurrentCharge> {\n requireInputKeys(input, \"calculateSensorDarkCurrentCharge\", [\"exposure\", \"darkCurrentProfile\", \"operatingTemperatureC\"]);\n validateExposure(input.exposure);\n const profile =\n parseSensorDarkCurrentProfile(\n input.darkCurrentProfile\n );\n\n if (\n profile.colorSamplingProfileId !==\n input.exposure\n .colorSamplingProfileId ||\n profile.channelId !==\n input.exposure.channelId\n ) {\n throw new InvalidScientificInputError(\n \"Dark-current profile color/channel identity must match the EQE exposure.\"\n );\n }\n\n const site =\n input.exposure.site;\n if (\n profile.siteApplicability.kind ===\n \"exact-site\" &&\n !sameSite(\n profile.siteApplicability.site,\n site\n )\n ) {\n throw new InvalidScientificInputError(\n \"Exact-site dark-current profile does not apply to the exposure site.\"\n );\n }\n\n const resolved =\n resolveDarkCurrentRate(\n profile.temperatureModel,\n input.operatingTemperatureC\n );\n const expectedDarkElectronCount =\n resolved.rate *\n input.exposure\n .localExposureDurationSeconds;\n\n if (\n !Number.isFinite(\n expectedDarkElectronCount\n ) ||\n expectedDarkElectronCount < 0\n ) {\n throw new InvalidScientificInputError(\n \"Expected dark-electron count must remain finite and nonnegative.\"\n );\n }\n\n const result:\n SensorPhotoDarkCurrentCharge = {\n darkCurrentProfileId:\n profile.profileId,\n colorSamplingProfileId:\n input.exposure\n .colorSamplingProfileId,\n channelId:\n input.exposure.channelId,\n site: {\n ...site\n },\n bindingId:\n input.exposure.bindingId,\n ...(input.exposure.kind === \"eqe-temporal-photo-signal\"\n ? { temporalIntegrationId: input.exposure.temporalIntegrationId }\n : { stationarityProfileId: input.exposure.stationarityProfileId }),\n timeReference:\n input.exposure.timeReference,\n startOffsetSecondsFromOpeningReference:\n input.exposure\n .startOffsetSecondsFromOpeningReference,\n endOffsetSecondsFromOpeningReference:\n input.exposure\n .endOffsetSecondsFromOpeningReference,\n operatingTemperatureC:\n input.operatingTemperatureC,\n temperatureModel:\n profile.temperatureModel.kind,\n temperatureInterpolationUsed:\n resolved.interpolationUsed,\n darkCurrentElectronsPerSecond:\n resolved.rate,\n localExposureDurationSeconds:\n input.exposure\n .localExposureDurationSeconds,\n expectedDarkElectronCount,\n countMeaning:\n \"expected-thermally-generated-electrons\",\n expectationValueOnly: true,\n integerDarkElectronCountSampled:\n false,\n darkShotNoiseApplied: false,\n darkCurrentCompensationApplied:\n false,\n spatialDarkCurrentNonuniformityModeled:\n profile\n .spatialDarkCurrentNonuniformityModeled,\n photoSignalIncluded: false,\n otherChargeIncluded: false,\n physicalFullWellAssessmentAuthorized:\n false,\n saturationAssessed: false,\n componentEvidence: {\n darkCurrent:\n profile.evidence,\n siteApproximation:\n profile.siteApplicability.kind ===\n \"uniform-site-mean-approximation\"\n ? profile\n .siteApplicability\n .evidence\n : [],\n exposure:\n input.exposure\n .componentEvidence\n }\n } as SensorPhotoDarkCurrentCharge;\n\n const assumptions = [\n \"Dark current is modeled as pre-compensation thermally generated electron rate in electrons per second.\",\n \"Release 4.0 style temperature handling is data-driven: no universal exponential or doubling-temperature law is inferred.\",\n profile.temperatureModel.kind ===\n \"log-linear-temperature-table\"\n ? \"Log-linear (exponential-in-temperature) interpolation between adjacent declared samples is allowed only inside the sampled table; extrapolation fails closed.\"\n : \"Piecewise-linear interpolation is allowed only inside an explicitly sampled temperature table; extrapolation fails closed.\",\n \"Dark-current compensation, dark offset, hot-pixel/defect excess, leakage and charge injection remain separate from this thermal dark-current expectation.\",\n \"Expected dark-electron count is rate × the exact local exposure duration; no integer sampling or dark-current shot-noise realization is performed.\",\n \"This result is dark charge only. Photo-signal and other accumulated charge are not included, so physical full-well assessment remains unauthorized.\",\n \"A/W current-domain exposure results cannot enter this electron-storage path without a separate carrier/storage mapping.\"\n ];\n\n return profile.scientificStatus ===\n \"calibrated\"\n ? calculatedResult(\n result,\n \"sensor-dark-current-charge\",\n input.exposure.kind === \"eqe-temporal-photo-signal\" ? \"1.1.0\" : \"1.0.0\",\n assumptions\n )\n : approximationResult(\n result,\n \"sensor-dark-current-charge\",\n input.exposure.kind === \"eqe-temporal-photo-signal\" ? \"1.1.0\" : \"1.0.0\",\n [\n ...assumptions,\n ...(profile\n .siteApplicability.kind ===\n \"uniform-site-mean-approximation\"\n ? [\n profile\n .siteApplicability\n .limitation\n ]\n : [])\n ]\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dark-current.js","sourceRoot":"","sources":["../../src/sensor/dark-current.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Dark current: electrons generated by heat in a sensor site even with no light, and the dark charge\n * they add over one exposure.\n *\n * The dark-current rate (electrons per second, before any compensation) is declared separately as a\n * function of temperature. It is given either at an exact temperature or as samples, interpolated\n * log-linearly (or with the legacy linear rule) only within the declared range. No universal\n * \"doubles every N degrees\" law is assumed. Rates are population means, an approximation; hot pixels\n * and DCNU (dark-current non-uniformity) are not modelled.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport {\n approximationResult,\n calculatedResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport type { SensorEqeExposureIntegration } from \"./constant-rate-temporal-integration.js\";\nimport { parseSensorEqeTemporalPhotoSignal } from \"./temporal-photo-signal.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport type {\n SensorEqePhotoExposure, SensorEqeTemporalPhotoSignal\n} from \"./temporal-photo-signal.js\";\nimport type {\n SensorSpectralResponseScientificStatus,\n SensorSpectralResponseUncertainty\n} from \"./spectral-response.js\";\nimport { requireInputObject, requireInputKeys, rejectUnknownVariantFields, UNCERTAINTY_VARIANT_FIELDS, requireConfigRecord as requireRecord, requireConfigFinite as requireFinite, requireConfigString as requireNonEmptyString, requireConfigNonNegativeSafeInteger as requireSiteIndex, requireConfigFiniteAtLeastZero as requireNonNegativeFinite } from \"../core/record-validation.js\";\nimport { sameSite } from \"../core/shared-helpers.js\";\n\nexport type SensorDarkCurrentSiteApplicability =\n | {\n kind: \"exact-site\";\n site: {\n x: number;\n y: number;\n };\n }\n | {\n kind:\n \"uniform-site-mean-approximation\";\n limitation: string;\n evidence:\n readonly EvidenceProvenance[];\n };\n\nexport type SensorDarkCurrentTemperatureModel =\n | {\n kind:\n \"fixed-reference-temperature\";\n referenceTemperatureC: number;\n darkCurrentElectronsPerSecond:\n number;\n }\n | {\n /** Legacy interpolation, linear in rate. Between samples of an exponential curve it gives values that are too high. */\n kind:\n \"piecewise-linear-temperature-table\";\n interpolation: \"piecewise-linear\";\n outsideRangeBehavior:\n \"fail-closed\";\n samples: readonly {\n temperatureC: number;\n darkCurrentElectronsPerSecond:\n number;\n }[];\n }\n | {\n /**\n * Linear interpolation of ln(rate) between neighbouring samples. That makes the rate exponential\n * in temperature within each interval, matching the roughly exponential (Arrhenius, or\n * \"doubling temperature\") behaviour of thermally generated dark current. Rates must be strictly\n * positive. No single doubling temperature is imposed; each interval follows its own two samples.\n */\n kind:\n \"log-linear-temperature-table\";\n interpolation: \"log-linear\";\n outsideRangeBehavior:\n \"fail-closed\";\n samples: readonly {\n temperatureC: number;\n darkCurrentElectronsPerSecond:\n number;\n }[];\n };\n\nexport interface SensorDarkCurrentProfile {\n schemaVersion: \"0.1.0\";\n profileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n scientificStatus:\n SensorSpectralResponseScientificStatus;\n uncertainty:\n SensorSpectralResponseUncertainty;\n evidence: readonly EvidenceProvenance[];\n chargeMeaning:\n \"pre-compensation-thermally-generated-electrons\";\n siteApplicability:\n SensorDarkCurrentSiteApplicability;\n temperatureModel:\n SensorDarkCurrentTemperatureModel;\n darkCurrentCompensationIncluded: false;\n spatialDarkCurrentNonuniformityModeled:\n boolean;\n}\n\nexport interface CalculateSensorDarkCurrentChargeInput<Exposure extends SensorEqePhotoExposure = SensorEqeExposureIntegration> {\n exposure:\n Exposure;\n darkCurrentProfile:\n SensorDarkCurrentProfile;\n operatingTemperatureC: number;\n}\n\nexport interface SensorDarkCurrentCharge {\n darkCurrentProfileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n stationarityProfileId: string;\n temporalIntegrationId?: never;\n timeReference:\n \"first-opening-boundary-phase\";\n startOffsetSecondsFromOpeningReference:\n number;\n endOffsetSecondsFromOpeningReference:\n number;\n operatingTemperatureC: number;\n temperatureModel:\n SensorDarkCurrentTemperatureModel[\"kind\"];\n temperatureInterpolationUsed:\n boolean;\n darkCurrentElectronsPerSecond:\n number;\n localExposureDurationSeconds:\n number;\n expectedDarkElectronCount:\n number;\n countMeaning:\n \"expected-thermally-generated-electrons\";\n expectationValueOnly: true;\n integerDarkElectronCountSampled:\n false;\n darkShotNoiseApplied: false;\n darkCurrentCompensationApplied:\n false;\n spatialDarkCurrentNonuniformityModeled:\n boolean;\n photoSignalIncluded: false;\n otherChargeIncluded: false;\n physicalFullWellAssessmentAuthorized:\n false;\n saturationAssessed: false;\n componentEvidence: {\n darkCurrent:\n readonly EvidenceProvenance[];\n siteApproximation:\n readonly EvidenceProvenance[];\n exposure:\n SensorEqeExposureIntegration[\"componentEvidence\"];\n };\n}\n\n/** Dark charge for the exact same time-resolved photo event. It has no stationarity ID. */\nexport type SensorTemporalDarkCurrentCharge = Omit<SensorDarkCurrentCharge, \"stationarityProfileId\" | \"temporalIntegrationId\" | \"componentEvidence\"> & {\n stationarityProfileId?: never;\n temporalIntegrationId: string;\n componentEvidence: Omit<SensorDarkCurrentCharge[\"componentEvidence\"], \"exposure\"> & { exposure: SensorEqeTemporalPhotoSignal[\"componentEvidence\"] };\n};\nexport type SensorPhotoDarkCurrentCharge = SensorDarkCurrentCharge | SensorTemporalDarkCurrentCharge;\n\nfunction parseUncertainty(\n value: unknown,\n path: string\n): SensorSpectralResponseUncertainty {\n const record =\n requireRecord(value, path, null);\n rejectUnknownVariantFields(record, \"kind\", UNCERTAINTY_VARIANT_FIELDS, path);\n\n if (record.kind === \"relative\") {\n const fraction =\n requireNonNegativeFinite(\n record.fraction,\n path + \".fraction\"\n );\n if (\n typeof record.basis !== \"string\" ||\n record.basis.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \".basis must be a non-empty string.\"\n );\n }\n return {\n kind: \"relative\",\n fraction,\n basis: record.basis\n };\n }\n\n if (\n record.kind === \"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 parseSiteApplicability(\n value: unknown\n): SensorDarkCurrentSiteApplicability {\n const record = requireRecord(\n value,\n \"sensorDarkCurrent.siteApplicability\", null\n );\n rejectUnknownVariantFields(record, \"kind\", { \"exact-site\": [\"kind\", \"site\"], \"uniform-site-mean-approximation\": [\"kind\", \"limitation\", \"evidence\"] }, \"sensorDarkCurrent.siteApplicability\");\n\n if (record.kind === \"exact-site\") {\n const site = requireRecord(\n record.site,\n \"sensorDarkCurrent.siteApplicability.site\", [\"x\", \"y\"]\n );\n return {\n kind: \"exact-site\",\n site: {\n x: requireSiteIndex(\n site.x,\n \"sensorDarkCurrent.siteApplicability.site.x\"\n ),\n y: requireSiteIndex(\n site.y,\n \"sensorDarkCurrent.siteApplicability.site.y\"\n )\n }\n };\n }\n\n if (\n record.kind ===\n \"uniform-site-mean-approximation\"\n ) {\n return {\n kind:\n \"uniform-site-mean-approximation\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n \"sensorDarkCurrent.siteApplicability.limitation\"\n ),\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorDarkCurrent.siteApplicability.evidence\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.siteApplicability.kind is invalid.\"\n );\n}\n\nfunction parseTemperatureModel(\n value: unknown\n): SensorDarkCurrentTemperatureModel {\n const record = requireRecord(\n value,\n \"sensorDarkCurrent.temperatureModel\", null\n );\n rejectUnknownVariantFields(record, \"kind\", {\n \"fixed-reference-temperature\": [\"kind\", \"referenceTemperatureC\", \"darkCurrentElectronsPerSecond\"],\n \"piecewise-linear-temperature-table\": [\"kind\", \"interpolation\", \"outsideRangeBehavior\", \"samples\"],\n \"log-linear-temperature-table\": [\"kind\", \"interpolation\", \"outsideRangeBehavior\", \"samples\"]\n }, \"sensorDarkCurrent.temperatureModel\");\n\n if (\n record.kind ===\n \"fixed-reference-temperature\"\n ) {\n return {\n kind:\n \"fixed-reference-temperature\",\n referenceTemperatureC:\n requireFinite(\n record.referenceTemperatureC,\n \"sensorDarkCurrent.temperatureModel.referenceTemperatureC\"\n ),\n darkCurrentElectronsPerSecond:\n requireNonNegativeFinite(\n record.darkCurrentElectronsPerSecond,\n \"sensorDarkCurrent.temperatureModel.darkCurrentElectronsPerSecond\"\n )\n };\n }\n\n const logLinear =\n record.kind ===\n \"log-linear-temperature-table\";\n if (\n record.kind !==\n \"piecewise-linear-temperature-table\" &&\n !logLinear\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.temperatureModel.kind is invalid.\"\n );\n }\n if (\n record.interpolation !==\n (logLinear ? \"log-linear\" : \"piecewise-linear\")\n ) {\n throw new InvalidConfigurationError(\n logLinear\n ? 'sensorDarkCurrent.temperatureModel.interpolation must be \"log-linear\".'\n : 'sensorDarkCurrent.temperatureModel.interpolation must be \"piecewise-linear\".'\n );\n }\n if (\n record.outsideRangeBehavior !==\n \"fail-closed\"\n ) {\n throw new InvalidConfigurationError(\n 'sensorDarkCurrent.temperatureModel.outsideRangeBehavior must be \"fail-closed\".'\n );\n }\n if (\n !Array.isArray(record.samples) ||\n record.samples.length < 2\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.temperatureModel.samples must contain at least two samples.\"\n );\n }\n\n let previousTemperature =\n Number.NEGATIVE_INFINITY;\n const samples =\n record.samples.map(\n (sample, index) => {\n const parsed = requireRecord(\n sample,\n \"sensorDarkCurrent.temperatureModel.samples[\" +\n index +\n \"]\", [\"temperatureC\", \"darkCurrentElectronsPerSecond\"]\n );\n const temperatureC =\n requireFinite(\n parsed.temperatureC,\n \"sensorDarkCurrent.temperatureModel.samples[\" +\n index +\n \"].temperatureC\"\n );\n if (\n temperatureC <=\n previousTemperature\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.temperatureModel.samples must be strictly increasing in temperature.\"\n );\n }\n previousTemperature =\n temperatureC;\n return {\n temperatureC,\n darkCurrentElectronsPerSecond:\n requireNonNegativeFinite(\n parsed.darkCurrentElectronsPerSecond,\n \"sensorDarkCurrent.temperatureModel.samples[\" +\n index +\n \"].darkCurrentElectronsPerSecond\"\n )\n };\n }\n );\n\n if (\n logLinear &&\n samples.some(\n (sample) =>\n sample\n .darkCurrentElectronsPerSecond <=\n 0\n )\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.temperatureModel log-linear samples require strictly positive rates.\"\n );\n }\n\n return logLinear\n ? {\n kind:\n \"log-linear-temperature-table\",\n interpolation: \"log-linear\",\n outsideRangeBehavior:\n \"fail-closed\",\n samples\n }\n : {\n kind:\n \"piecewise-linear-temperature-table\",\n interpolation: \"piecewise-linear\",\n outsideRangeBehavior:\n \"fail-closed\",\n samples\n };\n}\n\n/**\n * Validates untrusted input and returns it as a normalized, typed value. Unknown enum values,\n * missing required fields and inconsistent scientific data are rejected.\n *\n * The profile declares the dark-current rate (before compensation) against temperature, at an exact\n * temperature or as samples. Interpolation (log-linear, or the legacy linear rule) is allowed only\n * within the declared range. No universal doubling-temperature law is assumed.\n * @param value - Untrusted data. TypeScript types alone are not validation.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function parseSensorDarkCurrentProfile(\n value: unknown\n): SensorDarkCurrentProfile {\n const record = requireRecord(\n value,\n \"sensorDarkCurrent\", [\"schemaVersion\", \"chargeMeaning\", \"scientificStatus\", \"darkCurrentCompensationIncluded\", \"spatialDarkCurrentNonuniformityModeled\", \"uncertainty\", \"siteApplicability\", \"profileId\", \"colorSamplingProfileId\", \"channelId\", \"evidence\", \"temperatureModel\"]\n );\n\n if (\n record.schemaVersion !== \"0.1.0\"\n ) {\n throw new InvalidConfigurationError(\n 'sensorDarkCurrent.schemaVersion must be \"0.1.0\".'\n );\n }\n if (\n record.chargeMeaning !==\n \"pre-compensation-thermally-generated-electrons\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.chargeMeaning is invalid.\"\n );\n }\n if (\n record.scientificStatus !==\n \"calibrated\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.scientificStatus is invalid.\"\n );\n }\n if (\n record.darkCurrentCompensationIncluded !==\n false\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.darkCurrentCompensationIncluded must be false.\"\n );\n }\n if (\n typeof record\n .spatialDarkCurrentNonuniformityModeled !==\n \"boolean\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorDarkCurrent.spatialDarkCurrentNonuniformityModeled must be boolean.\"\n );\n }\n\n const uncertainty =\n parseUncertainty(\n record.uncertainty,\n \"sensorDarkCurrent.uncertainty\"\n );\n if (\n record.scientificStatus ===\n \"calibrated\" &&\n uncertainty.kind ===\n \"not-quantified\"\n ) {\n throw new InvalidConfigurationError(\n \"A calibrated dark-current profile must declare quantified relative uncertainty.\"\n );\n }\n\n const siteApplicability =\n parseSiteApplicability(\n record.siteApplicability\n );\n if (\n siteApplicability.kind ===\n \"uniform-site-mean-approximation\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"Uniform site-mean dark current must remain an approximation.\"\n );\n }\n if (\n siteApplicability.kind ===\n \"uniform-site-mean-approximation\" &&\n record\n .spatialDarkCurrentNonuniformityModeled !==\n false\n ) {\n throw new InvalidConfigurationError(\n \"Uniform site-mean dark current cannot claim spatial dark-current nonuniformity is modeled.\"\n );\n }\n\n return {\n schemaVersion: \"0.1.0\",\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"sensorDarkCurrent.profileId\"\n ),\n colorSamplingProfileId:\n requireNonEmptyString(\n record.colorSamplingProfileId,\n \"sensorDarkCurrent.colorSamplingProfileId\"\n ),\n channelId:\n requireNonEmptyString(\n record.channelId,\n \"sensorDarkCurrent.channelId\"\n ),\n scientificStatus:\n record.scientificStatus,\n uncertainty,\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorDarkCurrent.evidence\"\n ),\n chargeMeaning:\n \"pre-compensation-thermally-generated-electrons\",\n siteApplicability,\n temperatureModel:\n parseTemperatureModel(\n record.temperatureModel\n ),\n darkCurrentCompensationIncluded:\n false,\n spatialDarkCurrentNonuniformityModeled:\n record\n .spatialDarkCurrentNonuniformityModeled\n };\n}\n\nfunction resolveDarkCurrentRate(\n model:\n SensorDarkCurrentTemperatureModel,\n operatingTemperatureC: number\n): {\n rate: number;\n interpolationUsed: boolean;\n} {\n if (\n !Number.isFinite(\n operatingTemperatureC\n )\n ) {\n throw new InvalidScientificInputError(\n \"operatingTemperatureC must be finite.\"\n );\n }\n\n if (\n model.kind ===\n \"fixed-reference-temperature\"\n ) {\n if (\n operatingTemperatureC !==\n model.referenceTemperatureC\n ) {\n throw new InvalidScientificInputError(\n \"Fixed-reference dark current may only be used at its exact reference temperature.\"\n );\n }\n return {\n rate:\n model\n .darkCurrentElectronsPerSecond,\n interpolationUsed: false\n };\n }\n\n const first =\n model.samples[0]!;\n const last =\n model.samples[\n model.samples.length - 1\n ]!;\n if (\n operatingTemperatureC <\n first.temperatureC ||\n operatingTemperatureC >\n last.temperatureC\n ) {\n throw new InvalidScientificInputError(\n \"operatingTemperatureC is outside the measured dark-current temperature range; extrapolation is not permitted.\"\n );\n }\n\n const exact =\n model.samples.find(\n (sample) =>\n sample.temperatureC ===\n operatingTemperatureC\n );\n if (exact !== undefined) {\n return {\n rate:\n exact\n .darkCurrentElectronsPerSecond,\n interpolationUsed: false\n };\n }\n\n const upperIndex =\n model.samples.findIndex(\n (sample) =>\n sample.temperatureC >\n operatingTemperatureC\n );\n const upper =\n model.samples[upperIndex]!;\n const lower =\n model.samples[upperIndex - 1]!;\n const alpha =\n (operatingTemperatureC -\n lower.temperatureC) /\n (upper.temperatureC -\n lower.temperatureC);\n if (\n model.kind ===\n \"log-linear-temperature-table\"\n ) {\n // r = r0^(1-alpha) * r1^alpha, written as r0 * (r1/r0)^alpha.\n return {\n rate:\n lower\n .darkCurrentElectronsPerSecond *\n (upper\n .darkCurrentElectronsPerSecond /\n lower\n .darkCurrentElectronsPerSecond) **\n alpha,\n interpolationUsed: true\n };\n }\n const rate =\n lower\n .darkCurrentElectronsPerSecond +\n alpha *\n (\n upper\n .darkCurrentElectronsPerSecond -\n lower\n .darkCurrentElectronsPerSecond\n );\n\n return {\n rate,\n interpolationUsed: true\n };\n}\n\nfunction validateExposure(\n exposure:\n SensorEqePhotoExposure\n): void {\n requireInputObject(exposure, \"exposure input\");\n if (exposure.kind === \"eqe-temporal-photo-signal\") parseSensorEqeTemporalPhotoSignal(exposure);\n if (\n (exposure.kind !== \"eqe-expected-counts\" && exposure.kind !== \"eqe-temporal-photo-signal\") ||\n exposure.temporalIntegrationApplied !==\n true ||\n exposure.exposureDurationApplied !==\n true ||\n exposure.accumulatedSignalCompleteness !==\n \"photo-signal-only\" ||\n exposure.darkChargeIncluded !== false ||\n exposure.otherChargeIncluded !== false ||\n exposure\n .physicalFullWellAssessmentAuthorized !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"Dark-current composition requires an unmodified photo-signal-only EQE exposure integration.\"\n );\n }\n\n if (\n typeof exposure\n .localExposureDurationSeconds !==\n \"number\" ||\n !Number.isFinite(\n exposure\n .localExposureDurationSeconds\n ) ||\n exposure\n .localExposureDurationSeconds <=\n 0\n ) {\n throw new InvalidScientificInputError(\n \"EQE exposure local duration must be finite and positive.\"\n );\n }\n}\n\n/**\n * Evaluates the declared dark-current rate at the sensor temperature and integrates it over the exact\n * local exposure of the photo signal.\n *\n * The rate is a population mean, an approximation. Hot pixels and DCNU (dark-current non-uniformity)\n * are not modelled. No universal doubling-temperature law is assumed.\n * @param input - Dark-current input whose exposure is a per-exposure photo signal (SensorEqeExposureIntegration); see `CalculateSensorDarkCurrentChargeInput`.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function calculateSensorDarkCurrentCharge(\n input: Omit<CalculateSensorDarkCurrentChargeInput, \"exposure\"> & { exposure: SensorEqeExposureIntegration }\n): CalculationResult<SensorDarkCurrentCharge>;\n/**\n * Evaluates the declared dark-current rate at the sensor temperature and integrates it over the exact\n * local exposure of the photo signal.\n *\n * The rate is a population mean, an approximation. Hot pixels and DCNU (dark-current non-uniformity)\n * are not modelled. No universal doubling-temperature law is assumed.\n * @param input - Dark-current input whose exposure is a time-resolved photo signal (SensorEqeTemporalPhotoSignal); see `CalculateSensorDarkCurrentChargeInput`.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function calculateSensorDarkCurrentCharge(\n input: Omit<CalculateSensorDarkCurrentChargeInput, \"exposure\"> & { exposure: SensorEqeTemporalPhotoSignal }\n): CalculationResult<SensorTemporalDarkCurrentCharge>;\n/**\n * Evaluates the declared dark-current rate at the sensor temperature and integrates it over the exact\n * local exposure of the photo signal.\n *\n * The rate is a population mean, an approximation. Hot pixels and DCNU (dark-current non-uniformity)\n * are not modelled. No universal doubling-temperature law is assumed.\n * @param input - The input; see `CalculateSensorDarkCurrentChargeInput` for its fields, units and coordinates.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function calculateSensorDarkCurrentCharge(\n input: CalculateSensorDarkCurrentChargeInput<SensorEqePhotoExposure>\n): CalculationResult<SensorPhotoDarkCurrentCharge>;\n/**\n * Evaluates the declared dark-current rate at the sensor temperature and integrates it over the exact\n * local exposure of the photo signal.\n *\n * The rate is a population mean, an approximation. Hot pixels and DCNU (dark-current non-uniformity)\n * are not modelled. No universal doubling-temperature law is assumed.\n * @param input - The input; see `CalculateSensorDarkCurrentChargeInput` for its fields, units and coordinates.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function calculateSensorDarkCurrentCharge(\n input:\n CalculateSensorDarkCurrentChargeInput<SensorEqePhotoExposure>\n): CalculationResult<SensorPhotoDarkCurrentCharge> {\n requireInputKeys(input, \"calculateSensorDarkCurrentCharge\", [\"exposure\", \"darkCurrentProfile\", \"operatingTemperatureC\"]);\n validateExposure(input.exposure);\n const profile =\n parseSensorDarkCurrentProfile(\n input.darkCurrentProfile\n );\n\n if (\n profile.colorSamplingProfileId !==\n input.exposure\n .colorSamplingProfileId ||\n profile.channelId !==\n input.exposure.channelId\n ) {\n throw new InvalidScientificInputError(\n \"Dark-current profile color/channel identity must match the EQE exposure.\"\n );\n }\n\n const site =\n input.exposure.site;\n if (\n profile.siteApplicability.kind ===\n \"exact-site\" &&\n !sameSite(\n profile.siteApplicability.site,\n site\n )\n ) {\n throw new InvalidScientificInputError(\n \"Exact-site dark-current profile does not apply to the exposure site.\"\n );\n }\n\n const resolved =\n resolveDarkCurrentRate(\n profile.temperatureModel,\n input.operatingTemperatureC\n );\n const expectedDarkElectronCount =\n resolved.rate *\n input.exposure\n .localExposureDurationSeconds;\n\n if (\n !Number.isFinite(\n expectedDarkElectronCount\n ) ||\n expectedDarkElectronCount < 0\n ) {\n throw new InvalidScientificInputError(\n \"Expected dark-electron count must remain finite and nonnegative.\"\n );\n }\n\n const result:\n SensorPhotoDarkCurrentCharge = {\n darkCurrentProfileId:\n profile.profileId,\n colorSamplingProfileId:\n input.exposure\n .colorSamplingProfileId,\n channelId:\n input.exposure.channelId,\n site: {\n ...site\n },\n bindingId:\n input.exposure.bindingId,\n ...(input.exposure.kind === \"eqe-temporal-photo-signal\"\n ? { temporalIntegrationId: input.exposure.temporalIntegrationId }\n : { stationarityProfileId: input.exposure.stationarityProfileId }),\n timeReference:\n input.exposure.timeReference,\n startOffsetSecondsFromOpeningReference:\n input.exposure\n .startOffsetSecondsFromOpeningReference,\n endOffsetSecondsFromOpeningReference:\n input.exposure\n .endOffsetSecondsFromOpeningReference,\n operatingTemperatureC:\n input.operatingTemperatureC,\n temperatureModel:\n profile.temperatureModel.kind,\n temperatureInterpolationUsed:\n resolved.interpolationUsed,\n darkCurrentElectronsPerSecond:\n resolved.rate,\n localExposureDurationSeconds:\n input.exposure\n .localExposureDurationSeconds,\n expectedDarkElectronCount,\n countMeaning:\n \"expected-thermally-generated-electrons\",\n expectationValueOnly: true,\n integerDarkElectronCountSampled:\n false,\n darkShotNoiseApplied: false,\n darkCurrentCompensationApplied:\n false,\n spatialDarkCurrentNonuniformityModeled:\n profile\n .spatialDarkCurrentNonuniformityModeled,\n photoSignalIncluded: false,\n otherChargeIncluded: false,\n physicalFullWellAssessmentAuthorized:\n false,\n saturationAssessed: false,\n componentEvidence: {\n darkCurrent:\n profile.evidence,\n siteApproximation:\n profile.siteApplicability.kind ===\n \"uniform-site-mean-approximation\"\n ? profile\n .siteApplicability\n .evidence\n : [],\n exposure:\n input.exposure\n .componentEvidence\n }\n } as SensorPhotoDarkCurrentCharge;\n\n const assumptions = [\n \"Dark current is modeled as pre-compensation thermally generated electron rate in electrons per second.\",\n \"Release 4.0 style temperature handling is data-driven: no universal exponential or doubling-temperature law is inferred.\",\n profile.temperatureModel.kind ===\n \"log-linear-temperature-table\"\n ? \"Log-linear (exponential-in-temperature) interpolation between adjacent declared samples is allowed only inside the sampled table; extrapolation fails closed.\"\n : \"Piecewise-linear interpolation is allowed only inside an explicitly sampled temperature table; extrapolation fails closed.\",\n \"Dark-current compensation, dark offset, hot-pixel/defect excess, leakage and charge injection remain separate from this thermal dark-current expectation.\",\n \"Expected dark-electron count is rate × the exact local exposure duration; no integer sampling or dark-current shot-noise realization is performed.\",\n \"This result is dark charge only. Photo-signal and other accumulated charge are not included, so physical full-well assessment remains unauthorized.\",\n \"A/W current-domain exposure results cannot enter this electron-storage path without a separate carrier/storage mapping.\"\n ];\n\n return profile.scientificStatus ===\n \"calibrated\"\n ? calculatedResult(\n result,\n \"sensor-dark-current-charge\",\n input.exposure.kind === \"eqe-temporal-photo-signal\" ? \"1.1.0\" : \"1.0.0\",\n assumptions\n )\n : approximationResult(\n result,\n \"sensor-dark-current-charge\",\n input.exposure.kind === \"eqe-temporal-photo-signal\" ? \"1.1.0\" : \"1.0.0\",\n [\n ...assumptions,\n ...(profile\n .siteApplicability.kind ===\n \"uniform-site-mean-approximation\"\n ? [\n profile\n .siteApplicability\n .limitation\n ]\n : [])\n ]\n );\n}\n"]}
|
|
@@ -15,43 +15,10 @@ import { InvalidScientificInputError } from "../core/validation.js";
|
|
|
15
15
|
import { calculatePhotonEnergyFromWavelength } from "./photon-energy.js";
|
|
16
16
|
import { parseSensorColorSamplingProfile } from "./color-sampling.js";
|
|
17
17
|
import { createSensorSpectralResponseChannelBinding, requireCurrentSensorSpectralResponseChannelBinding, parseSensorSpectralResponseProfile, resolveParsedSensorSpectralResponseAtWavelength } from "./spectral-response.js";
|
|
18
|
-
import { requireInputObject, requireInputKeys } from "../core/record-validation.js";
|
|
18
|
+
import { requireInputObject, requireInputKeys, requireInputNonNegativeFinite as requireNonNegativeFinite, requireInputPositiveFinite as requirePositiveFinite, requireInputNonNegativeSafeInteger as requireSafeNonNegativeInteger } from "../core/record-validation.js";
|
|
19
19
|
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
20
|
+
import { addCompensated } from "../core/shared-helpers.js";
|
|
20
21
|
const REDUCTION_TOLERANCE = 1e-10;
|
|
21
|
-
function addCompensated(state, value) {
|
|
22
|
-
const adjusted = value - state.correction;
|
|
23
|
-
const next = state.sum + adjusted;
|
|
24
|
-
state.correction =
|
|
25
|
-
(next - state.sum) - adjusted;
|
|
26
|
-
state.sum = next;
|
|
27
|
-
}
|
|
28
|
-
function requireNonNegativeFinite(value, path) {
|
|
29
|
-
if (typeof value !== "number" ||
|
|
30
|
-
!Number.isFinite(value) ||
|
|
31
|
-
value < 0) {
|
|
32
|
-
throw new InvalidScientificInputError(path +
|
|
33
|
-
" must be finite and greater than or equal to zero.");
|
|
34
|
-
}
|
|
35
|
-
return value;
|
|
36
|
-
}
|
|
37
|
-
function requirePositiveFinite(value, path) {
|
|
38
|
-
if (typeof value !== "number" ||
|
|
39
|
-
!Number.isFinite(value) ||
|
|
40
|
-
value <= 0) {
|
|
41
|
-
throw new InvalidScientificInputError(path +
|
|
42
|
-
" must be finite and greater than zero.");
|
|
43
|
-
}
|
|
44
|
-
return value;
|
|
45
|
-
}
|
|
46
|
-
function requireSafeNonNegativeInteger(value, path) {
|
|
47
|
-
if (typeof value !== "number" ||
|
|
48
|
-
!Number.isSafeInteger(value) ||
|
|
49
|
-
value < 0) {
|
|
50
|
-
throw new InvalidScientificInputError(path +
|
|
51
|
-
" must be a non-negative safe integer.");
|
|
52
|
-
}
|
|
53
|
-
return value;
|
|
54
|
-
}
|
|
55
22
|
function approximatelyEqual(actual, expected) {
|
|
56
23
|
const scale = Math.max(1, Math.abs(actual), Math.abs(expected));
|
|
57
24
|
return (Math.abs(actual - expected) <=
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eqe-electron-rate.js","sourceRoot":"","sources":["../../src/sensor/eqe-electron-rate.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * EQE electron rate: converts the light power reaching a sensor site into a photon rate and an\n * expected electron rate, using the sensor's EQE (external quantum efficiency, the fraction of\n * incident photons that produce a collected electron).\n *\n * The response is applied at each wavelength node. One broadband-average QE is never applied to the\n * total power. Outputs are rates per second; integrating over the exposure time happens later.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport type {\n EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport { stringifyCanonicalJson } from \"../core/canonical-json.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n calculatePhotonEnergyFromWavelength,\n type AirRefractiveIndexConditionPolicy,\n type AirRefractiveIndexReferenceConditions,\n type SourcedAirPhaseRefractiveIndex\n} from \"./photon-energy.js\";\nimport type {\n SensorResponseApplicationCompatibilityAssessment\n} from \"./response-application-compatibility.js\";\nimport type {\n SensorResponseOperatingRangeAssessment\n} from \"./response-operating-range.js\";\nimport {\n parseSensorColorSamplingProfile,\n type SensorColorSamplingProfile\n} from \"./color-sampling.js\";\nimport {\n createSensorSpectralResponseChannelBinding,\n requireCurrentSensorSpectralResponseChannelBinding,\n parseSensorSpectralResponseProfile,\n resolveParsedSensorSpectralResponseAtWavelength,\n type ResolvedSensorSpectralResponseValue,\n type SensorSpectralResponseProfile\n} from \"./spectral-response.js\";\nimport type {\n SensorSpatioSpectralIrradianceReduction,\n SensorSpatioSpectralWavelengthReduction\n} from \"./spatio-spectral-reduction.js\";\nimport { requireInputObject, requireInputKeys } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\nconst REDUCTION_TOLERANCE = 1e-10;\n\nexport interface SensorEqeAirRefractiveIndexSample {\n spectralSampleIndex: number;\n refractiveIndex:\n SourcedAirPhaseRefractiveIndex;\n}\n\nexport interface SensorEqeAirPhotonEnergyContext {\n samples:\n readonly SensorEqeAirRefractiveIndexSample[];\n operatingConditions?:\n AirRefractiveIndexReferenceConditions;\n conditionPolicy:\n AirRefractiveIndexConditionPolicy;\n}\n\nexport interface CalculateSensorEqeElectronRateInput {\n reduction:\n SensorSpatioSpectralIrradianceReduction;\n compatibility:\n SensorResponseApplicationCompatibilityAssessment;\n operatingRange:\n SensorResponseOperatingRangeAssessment;\n colorSamplingProfile:\n SensorColorSamplingProfile;\n spectralResponseProfile:\n SensorSpectralResponseProfile;\n airPhotonEnergyContext?:\n SensorEqeAirPhotonEnergyContext;\n}\n\nexport interface SensorEqeWavelengthRateContribution {\n spectralSampleIndex: number;\n wavelengthNanometers: number;\n wavelengthMeasureNanometers: number;\n geometricApertureIncidentSpectralFluxWattsPerNanometer:\n number;\n radiantPowerContributionWatts: number;\n vacuumWavelengthNanometers: number;\n photonEnergyJoules: number;\n wavelengthConversion:\n | \"vacuum-identity\"\n | \"air-to-vacuum-via-phase-refractive-index\";\n phaseRefractiveIndexUsed?: number;\n airConditionCompatibility?:\n | \"exact-match\"\n | \"assumed-compatible\";\n airRefractiveIndexEvidence?:\n readonly EvidenceProvenance[];\n incidentPhotonRatePerSecond: number;\n effectiveExternalQuantumEfficiency: number;\n responseComposition:\n | \"direct-effective-response\"\n | \"channel-filter-transmittance-times-detector-eqe\";\n channelFilterTransmittance?: number;\n detectorExternalQuantumEfficiency?: number;\n responseInterpolationUsed: boolean;\n expectedGeneratedElectronRatePerSecond:\n number;\n}\n\nexport interface SensorEqeElectronRate {\n colorSamplingProfileId: string;\n responseProfileId: string;\n responseApplicationProfileId: string;\n operatingRangeProfileId: string;\n /** The exact source colour site. Results produced by the engine fill this in. */\n site?: {\n x: number;\n y: number;\n };\n channelId: string;\n sourceResponseKind:\n | \"effective-external-quantum-efficiency\"\n | \"separable-channel-filter-and-detector-eqe\";\n responseScope:\n SensorSpatioSpectralIrradianceReduction[\"responseScope\"];\n responseReferencePlane:\n SensorResponseApplicationCompatibilityAssessment[\"requiredSourcePlane\"];\n wavelengthBasis:\n SensorSpatioSpectralIrradianceReduction[\"wavelengthBasis\"];\n wavelengthRangeNanometers:\n SensorSpatioSpectralIrradianceReduction[\"wavelengthRangeNanometers\"];\n spectralNodeCount: number;\n perWavelength:\n readonly SensorEqeWavelengthRateContribution[];\n incidentPhotonRatePerSecond: number;\n expectedGeneratedElectronRatePerSecond:\n number;\n summationMethod:\n \"kahan-compensated\";\n responseApplicationPerformed: true;\n quantumEfficiencyApplied: true;\n spectralResponsivityApplied: false;\n channelFilterTransmissionApplied:\n boolean;\n photonRateCalculated: true;\n electronRateCalculated: true;\n photonCountCalculated: false;\n electronCountCalculated: false;\n currentCalculated: false;\n temporalIntegrationApplied: false;\n exposureDurationApplied: false;\n saturationAssessed: false;\n shotNoiseApplied: false;\n readNoiseApplied: false;\n adcQuantizationApplied: false;\n rawCodeValueProduced: false;\n responseUncertaintyPropagated: false;\n photonEnergyUncertaintyPropagated: false;\n quadratureConvergenceErrorEstimated:\n false;\n}\n\ninterface CompensatedSum {\n sum: number;\n correction: number;\n}\n\nfunction addCompensated(\n state: CompensatedSum,\n value: number\n): void {\n const adjusted =\n value - state.correction;\n const next =\n state.sum + adjusted;\n state.correction =\n (next - state.sum) - adjusted;\n state.sum = next;\n}\n\nfunction requireNonNegativeFinite(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n value < 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be finite and greater than or equal to zero.\"\n );\n }\n return value;\n}\n\nfunction requirePositiveFinite(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n value <= 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be finite and greater than zero.\"\n );\n }\n return value;\n}\n\nfunction requireSafeNonNegativeInteger(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isSafeInteger(value) ||\n value < 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be a non-negative safe integer.\"\n );\n }\n return value;\n}\n\nfunction approximatelyEqual(\n actual: number,\n expected: number\n): boolean {\n const scale = Math.max(\n 1,\n Math.abs(actual),\n Math.abs(expected)\n );\n return (\n Math.abs(actual - expected) <=\n REDUCTION_TOLERANCE * scale\n );\n}\n\n/**\n * Identity string for parsed plain data that ignores key order, so records with\n * the same content compare equal whatever order their properties were added in.\n */\nfunction canonicalJson(\n value: unknown\n): string {\n if (value === undefined) return \"undefined\";\n return stringifyCanonicalJson(value, {\n undefinedObjectProperties: \"omit\",\n nonFiniteNumberMessage:\n \"EQE electron-rate identity data must contain finite numbers.\",\n unsupportedValueMessage:\n \"EQE electron-rate identity data must be plain JSON data.\"\n });\n}\n\nfunction requireEquivalent(\n actual: unknown,\n expected: unknown,\n message: string\n): void {\n if (\n canonicalJson(actual) !==\n canonicalJson(expected)\n ) {\n throw new InvalidScientificInputError(\n message\n );\n }\n}\n\nfunction validateAuthorizationChain(\n reduction:\n SensorSpatioSpectralIrradianceReduction,\n compatibility:\n SensorResponseApplicationCompatibilityAssessment,\n operatingRange:\n SensorResponseOperatingRangeAssessment\n): void {\n requireInputObject(reduction, \"reduction input\");\n if (\n reduction.outputMeaning !==\n \"pre-response-spatio-spectral-radiometric-reduction\" ||\n reduction\n .spectralResponseApplicationPerformed !==\n false ||\n reduction.quantumEfficiencyApplied !==\n false ||\n reduction.temporalIntegrationApplied !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"reduction must remain pre-response and pre-temporal before EQE rate conversion.\"\n );\n }\n\n if (\n !compatibility\n .structuralCompatibilityEstablished ||\n compatibility.compatibilityStatus ===\n \"blocked\" ||\n compatibility.blockers.length !== 0 ||\n !compatibility.sourcePlaneMatched ||\n compatibility.requiredSignalPath !==\n \"photon-rate-to-electrons\"\n ) {\n throw new InvalidScientificInputError(\n \"compatibility must establish a non-blocked photon-rate-to-electrons response path.\"\n );\n }\n\n if (\n compatibility.spectralResponseProfileId !==\n reduction.responseProfileId ||\n compatibility.colorSamplingProfileId !==\n reduction.colorSamplingProfileId ||\n compatibility.channelId !==\n reduction.channelId ||\n compatibility.responseScope !==\n reduction.responseScope\n ) {\n throw new InvalidScientificInputError(\n \"compatibility identities must exactly match the reduction.\"\n );\n }\n\n if (\n !operatingRange\n .operatingRangeCompatibilityAssessed ||\n !operatingRange\n .instantaneousResponseRangeCompatible ||\n !operatingRange\n .responseRateConversionAuthorized ||\n operatingRange.status === \"blocked\" ||\n operatingRange.blockers.length !== 0 ||\n operatingRange.requiredSignalPath !==\n \"photon-rate-to-electrons\" ||\n operatingRange\n .responseApplicationPerformed !==\n false ||\n operatingRange\n .photonConversionPerformed !== false ||\n operatingRange\n .electronConversionPerformed !== false ||\n operatingRange\n .temporalIntegrationAuthorized !== false\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange must authorize a clean photon-rate-to-electrons conversion path.\"\n );\n }\n\n if (\n !operatingRange\n .spectralNodeRangeCompatibilityAssessed ||\n operatingRange\n .spectralInputModel.kind !==\n \"per-spectral-bin\"\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange must establish per-spectral-bin input-range applicability before wavelength-dependent EQE conversion.\"\n );\n }\n\n if (\n operatingRange\n .spatialLinearityModel.kind !==\n \"linear-superposition-over-geometric-aperture\"\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange must establish linear superposition over the geometric aperture.\"\n );\n }\n\n if (\n operatingRange.spectralResponseProfileId !==\n reduction.responseProfileId ||\n operatingRange.colorSamplingProfileId !==\n reduction.colorSamplingProfileId ||\n operatingRange.channelId !==\n reduction.channelId ||\n operatingRange.responseApplicationProfileId !==\n compatibility.applicationProfileId\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange identities must exactly match the reduction and compatibility assessment.\"\n );\n }\n}\n\nfunction validateReductionSpectralNodes(\n reduction:\n SensorSpatioSpectralIrradianceReduction\n): readonly SensorSpatioSpectralWavelengthReduction[] {\n if (\n !Array.isArray(\n reduction.perWavelength\n ) ||\n !Number.isSafeInteger(\n reduction.spectralNodeCount\n ) ||\n reduction.spectralNodeCount <= 0 ||\n reduction.perWavelength.length !==\n reduction.spectralNodeCount\n ) {\n throw new InvalidScientificInputError(\n \"reduction.perWavelength must contain exactly spectralNodeCount entries.\"\n );\n }\n\n let previousWavelength =\n Number.NEGATIVE_INFINITY;\n let normalizedWeightSum = 0;\n const integratedPower:\n CompensatedSum = {\n sum: 0,\n correction: 0\n };\n\n reduction.perWavelength.forEach(\n (entry, arrayIndex) => {\n const sampleIndex =\n requireSafeNonNegativeInteger(\n entry.spectralSampleIndex,\n \"reduction.perWavelength[\" +\n arrayIndex +\n \"].spectralSampleIndex\"\n );\n if (sampleIndex !== arrayIndex) {\n throw new InvalidScientificInputError(\n \"reduction spectralSampleIndex values must form the exact contiguous range 0..spectralNodeCount-1.\"\n );\n }\n\n const wavelength =\n requirePositiveFinite(\n entry.wavelengthNanometers,\n \"reduction.perWavelength[\" +\n arrayIndex +\n \"].wavelengthNanometers\"\n );\n if (\n wavelength <=\n previousWavelength\n ) {\n throw new InvalidScientificInputError(\n \"reduction per-wavelength samples must be strictly increasing in wavelength.\"\n );\n }\n previousWavelength =\n wavelength;\n\n const measure =\n requirePositiveFinite(\n entry.wavelengthMeasureNanometers,\n \"reduction.perWavelength[\" +\n arrayIndex +\n \"].wavelengthMeasureNanometers\"\n );\n const normalizedWeight =\n requirePositiveFinite(\n entry.normalizedWavelengthWeight,\n \"reduction.perWavelength[\" +\n arrayIndex +\n \"].normalizedWavelengthWeight\"\n );\n const fluxDensity =\n requireNonNegativeFinite(\n entry\n .geometricApertureIncidentSpectralFluxWattsPerNanometer,\n \"reduction.perWavelength[\" +\n arrayIndex +\n \"].geometricApertureIncidentSpectralFluxWattsPerNanometer\"\n );\n const contribution =\n requireNonNegativeFinite(\n entry\n .wavelengthIntegratedGeometricApertureIncidentFluxContributionWatts,\n \"reduction.perWavelength[\" +\n arrayIndex +\n \"].wavelengthIntegratedGeometricApertureIncidentFluxContributionWatts\"\n );\n const expectedContribution =\n fluxDensity * measure;\n if (\n !Number.isFinite(\n expectedContribution\n ) ||\n !approximatelyEqual(\n contribution,\n expectedContribution\n )\n ) {\n throw new InvalidScientificInputError(\n \"reduction per-wavelength radiant-power contribution must equal spectral flux density × d-lambda.\"\n );\n }\n\n normalizedWeightSum +=\n normalizedWeight;\n if (\n !Number.isFinite(\n normalizedWeightSum\n )\n ) {\n throw new InvalidScientificInputError(\n \"reduction normalized wavelength weights must remain finite.\"\n );\n }\n addCompensated(\n integratedPower,\n contribution\n );\n }\n );\n\n if (\n !approximatelyEqual(\n normalizedWeightSum,\n 1\n )\n ) {\n throw new InvalidScientificInputError(\n \"reduction normalized wavelength weights must sum to 1.\"\n );\n }\n\n const totalPower =\n requireNonNegativeFinite(\n reduction\n .wavelengthIntegratedGeometricApertureIncidentFluxWatts,\n \"reduction.wavelengthIntegratedGeometricApertureIncidentFluxWatts\"\n );\n if (\n !approximatelyEqual(\n integratedPower.sum,\n totalPower\n )\n ) {\n throw new InvalidScientificInputError(\n \"reduction total geometric-aperture radiant power must match the sum of per-wavelength contributions.\"\n );\n }\n\n return reduction.perWavelength;\n}\n\nfunction validateOperatingRangeNodeBinding(\n reduction:\n SensorSpatioSpectralIrradianceReduction,\n nodes:\n readonly SensorSpatioSpectralWavelengthReduction[],\n operatingRange:\n SensorResponseOperatingRangeAssessment\n): void {\n const evaluated =\n operatingRange\n .evaluatedSpectralNodeInputs;\n\n if (\n evaluated === undefined ||\n evaluated.length !== nodes.length\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange must carry one evaluated spectral-node input for every reduction wavelength node.\"\n );\n }\n\n if (\n operatingRange\n .evaluatedWavelengthRangeNanometers\n .minimum !==\n reduction.wavelengthRangeNanometers\n .minimum ||\n operatingRange\n .evaluatedWavelengthRangeNanometers\n .maximum !==\n reduction.wavelengthRangeNanometers\n .maximum\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange wavelength range must exactly match the reduction.\"\n );\n }\n\n evaluated.forEach(\n (entry, index) => {\n const node = nodes[index]!;\n if (\n entry.spectralSampleIndex !==\n node.spectralSampleIndex ||\n entry.wavelengthNanometers !==\n node.wavelengthNanometers ||\n entry.wavelengthMeasureNanometers !==\n node.wavelengthMeasureNanometers\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange spectral-node identities must exactly match the reduction.\"\n );\n }\n\n const expected =\n operatingRange.inputRange.kind ===\n \"wavelength-integrated-geometric-aperture-radiant-power\"\n ? node\n .wavelengthIntegratedGeometricApertureIncidentFluxContributionWatts\n : node\n .wavelengthIntegratedSpatialAverageContributionWattsPerSquareMeter;\n\n if (\n entry.kind !==\n operatingRange.inputRange.kind ||\n entry.unit !==\n operatingRange.inputRange.unit ||\n !approximatelyEqual(\n entry.value,\n expected\n )\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange spectral-node values must match the reduction values in the calibrated input domain.\"\n );\n }\n }\n );\n}\n\nfunction parseAirContext(\n reduction:\n SensorSpatioSpectralIrradianceReduction,\n nodes:\n readonly SensorSpatioSpectralWavelengthReduction[],\n context:\n SensorEqeAirPhotonEnergyContext |\n undefined\n): ReadonlyMap<\n number,\n SourcedAirPhaseRefractiveIndex\n> {\n if (\n reduction.wavelengthBasis ===\n \"vacuum\"\n ) {\n if (context !== undefined) {\n throw new InvalidScientificInputError(\n \"airPhotonEnergyContext must be omitted for vacuum-basis EQE conversion.\"\n );\n }\n return new Map();\n }\n\n if (\n reduction.wavelengthBasis !== \"air\"\n ) {\n throw new InvalidScientificInputError(\n \"EQE electron-rate conversion requires a resolved air or vacuum wavelength basis.\"\n );\n }\n\n if (\n context === undefined ||\n !Array.isArray(context.samples) ||\n context.samples.length !==\n nodes.length\n ) {\n throw new InvalidScientificInputError(\n \"Air-basis EQE conversion requires exactly one refractive-index sample for every spectral node.\"\n );\n }\n\n const byIndex =\n new Map<\n number,\n SourcedAirPhaseRefractiveIndex\n >();\n\n context.samples.forEach(\n (entry, arrayIndex) => {\n if (\n typeof entry !== \"object\" ||\n entry === null\n ) {\n throw new InvalidConfigurationError(\n \"airPhotonEnergyContext.samples[\" +\n arrayIndex +\n \"] must be an object.\"\n );\n }\n\n const spectralSampleIndex =\n requireSafeNonNegativeInteger(\n entry.spectralSampleIndex,\n \"airPhotonEnergyContext.samples[\" +\n arrayIndex +\n \"].spectralSampleIndex\"\n );\n if (\n spectralSampleIndex >=\n nodes.length ||\n byIndex.has(\n spectralSampleIndex\n )\n ) {\n throw new InvalidScientificInputError(\n \"airPhotonEnergyContext must contain unique in-range spectral sample identities.\"\n );\n }\n byIndex.set(\n spectralSampleIndex,\n entry.refractiveIndex\n );\n }\n );\n\n return byIndex;\n}\n\nfunction responseEvidenceMatches(\n actual:\n readonly EvidenceProvenance[],\n expected:\n readonly EvidenceProvenance[]\n): boolean {\n return (\n canonicalJson(actual) ===\n canonicalJson(expected)\n );\n}\n\nfunction validateResponseBinding(\n resolved:\n ReturnType<\n typeof resolveParsedSensorSpectralResponseAtWavelength\n >[\"value\"],\n reduction:\n SensorSpatioSpectralIrradianceReduction\n): void {\n if (\n resolved.profileId !==\n reduction.responseProfileId ||\n resolved.colorSamplingProfileId !==\n reduction.colorSamplingProfileId ||\n resolved.channelId !==\n reduction.channelId ||\n resolved.sourceResponseKind !==\n reduction.sourceResponseKind ||\n resolved.responseScope !==\n reduction.responseScope ||\n resolved.scientificStatus !==\n reduction.responseScientificStatus\n ) {\n throw new InvalidScientificInputError(\n \"Resolved spectral response metadata must exactly match the response used to construct the reduction.\"\n );\n }\n\n requireEquivalent(\n resolved.uncertainty,\n reduction.responseUncertainty,\n \"Resolved spectral response uncertainty must match the reduction.\"\n );\n\n const expectedEvidence =\n reduction.componentEvidence.spectral;\n if (\n !responseEvidenceMatches(\n resolved.componentEvidence.profile,\n expectedEvidence.profile\n ) ||\n !responseEvidenceMatches(\n resolved.componentEvidence.channel,\n expectedEvidence.channel\n ) ||\n canonicalJson(\n resolved.componentEvidence.curves\n ) !==\n canonicalJson(\n expectedEvidence.curves\n )\n ) {\n throw new InvalidScientificInputError(\n \"Resolved spectral response evidence must match the calibration evidence carried by the reduction.\"\n );\n }\n}\n\nfunction validateExactResponseDataBinding(\n responseProfile:\n SensorSpectralResponseProfile,\n reduction:\n SensorSpatioSpectralIrradianceReduction\n): void {\n const response =\n responseProfile.channels.find(\n (entry) =>\n entry.channelId ===\n reduction.channelId\n );\n if (reduction.responseChannelBinding !== undefined) {\n requireCurrentSensorSpectralResponseChannelBinding(\n reduction.responseChannelBinding\n );\n }\n if (\n response === undefined ||\n reduction.responseChannelBinding ===\n undefined ||\n canonicalJson(\n createSensorSpectralResponseChannelBinding(\n response\n )\n ) !==\n canonicalJson(\n reduction.responseChannelBinding\n )\n ) {\n throw new InvalidScientificInputError(\n \"Supplied spectral response channel data must exactly match the response-channel binding carried by the reduction.\"\n );\n }\n}\n\nfunction requireEqeResponse(\n response:\n ResolvedSensorSpectralResponseValue\n): Extract<\n ResolvedSensorSpectralResponseValue,\n {\n kind:\n \"effective-external-quantum-efficiency\";\n }\n> {\n if (\n response.kind !==\n \"effective-external-quantum-efficiency\"\n ) {\n throw new InvalidScientificInputError(\n \"EQE electron-rate conversion cannot consume A/W spectral responsivity.\"\n );\n }\n return response;\n}\n\n/**\n * Converts the spectral radiant power collected by the site's geometric aperture\n * (before the sensor response) into an incident photon rate and an expected\n * generated electron rate.\n *\n * The response is applied at each wavelength node. One broadband-average QE is\n * never applied to the total power.\n *\n * Integration over exposure time happens later, so all outputs are rates per\n * second, not photon or electron counts.\n */\nexport function calculateSensorEqeElectronRate(\n input:\n CalculateSensorEqeElectronRateInput\n): CalculationResult<SensorEqeElectronRate> {\n requireInputKeys(input, \"calculateSensorEqeElectronRate\", [\"reduction\", \"compatibility\", \"operatingRange\", \"colorSamplingProfile\", \"spectralResponseProfile\", \"airPhotonEnergyContext\"]);\n validateAuthorizationChain(\n input.reduction,\n input.compatibility,\n input.operatingRange\n );\n\n if (\n input.reduction\n .sourceResponseKind ===\n \"effective-spectral-responsivity\"\n ) {\n throw new InvalidScientificInputError(\n \"EQE electron-rate conversion requires an EQE or filter×detector-EQE response, not A/W responsivity.\"\n );\n }\n\n const colorProfile =\n parseSensorColorSamplingProfile(\n input.colorSamplingProfile\n );\n const responseProfile =\n parseSensorSpectralResponseProfile(\n input.spectralResponseProfile\n );\n\n if (\n colorProfile.profileId !==\n input.reduction\n .colorSamplingProfileId ||\n responseProfile.profileId !==\n input.reduction\n .responseProfileId ||\n responseProfile\n .colorSamplingProfileId !==\n colorProfile.profileId\n ) {\n throw new InvalidScientificInputError(\n \"Supplied color/spectral response profiles must exactly match the reduction identities.\"\n );\n }\n\n validateExactResponseDataBinding(\n responseProfile,\n input.reduction\n );\n\n const nodes =\n validateReductionSpectralNodes(\n input.reduction\n );\n validateOperatingRangeNodeBinding(\n input.reduction,\n nodes,\n input.operatingRange\n );\n\n const airIndices =\n parseAirContext(\n input.reduction,\n nodes,\n input.airPhotonEnergyContext\n );\n\n const photonRateSum:\n CompensatedSum = {\n sum: 0,\n correction: 0\n };\n const electronRateSum:\n CompensatedSum = {\n sum: 0,\n correction: 0\n };\n const perWavelength:\n SensorEqeWavelengthRateContribution[] =\n [];\n\n let responseBindingValidated =\n false;\n let channelFilterTransmissionApplied =\n false;\n\n for (const node of nodes) {\n const resolved =\n resolveParsedSensorSpectralResponseAtWavelength({\n colorSamplingProfile:\n colorProfile,\n spectralResponseProfile:\n responseProfile,\n channelId:\n input.reduction.channelId,\n wavelengthNanometers:\n node.wavelengthNanometers,\n wavelengthBasis:\n input.reduction\n .wavelengthBasis\n });\n\n if (!responseBindingValidated) {\n validateResponseBinding(\n resolved.value,\n input.reduction\n );\n responseBindingValidated =\n true;\n }\n\n const eqe =\n requireEqeResponse(\n resolved.value.response\n );\n\n const airIndex =\n airIndices.get(\n node.spectralSampleIndex\n );\n const photonEnergy =\n calculatePhotonEnergyFromWavelength({\n wavelengthNanometers:\n node.wavelengthNanometers,\n wavelengthBasis:\n input.reduction\n .wavelengthBasis,\n ...(input.reduction\n .wavelengthBasis === \"air\"\n ? {\n airPhaseRefractiveIndex:\n airIndex!,\n ...(input.airPhotonEnergyContext!\n .operatingConditions ===\n undefined\n ? {}\n : {\n airOperatingConditions:\n input\n .airPhotonEnergyContext!\n .operatingConditions\n }),\n airConditionPolicy:\n input.airPhotonEnergyContext!\n .conditionPolicy\n }\n : {})\n }).value;\n\n const radiantPowerContributionWatts =\n node\n .geometricApertureIncidentSpectralFluxWattsPerNanometer *\n node.wavelengthMeasureNanometers;\n const incidentPhotonRatePerSecond =\n radiantPowerContributionWatts /\n photonEnergy.photonEnergyJoules;\n const expectedGeneratedElectronRatePerSecond =\n incidentPhotonRatePerSecond *\n eqe.externalQuantumEfficiency;\n\n if (\n !Number.isFinite(\n radiantPowerContributionWatts\n ) ||\n !Number.isFinite(\n incidentPhotonRatePerSecond\n ) ||\n !Number.isFinite(\n expectedGeneratedElectronRatePerSecond\n )\n ) {\n throw new InvalidScientificInputError(\n \"EQE wavelength-node rate calculation must remain finite.\"\n );\n }\n\n addCompensated(\n photonRateSum,\n incidentPhotonRatePerSecond\n );\n addCompensated(\n electronRateSum,\n expectedGeneratedElectronRatePerSecond\n );\n\n if (\n eqe.composition ===\n \"channel-filter-transmittance-times-detector-eqe\"\n ) {\n channelFilterTransmissionApplied =\n true;\n }\n\n perWavelength.push({\n spectralSampleIndex:\n node.spectralSampleIndex,\n wavelengthNanometers:\n node.wavelengthNanometers,\n wavelengthMeasureNanometers:\n node.wavelengthMeasureNanometers,\n geometricApertureIncidentSpectralFluxWattsPerNanometer:\n node\n .geometricApertureIncidentSpectralFluxWattsPerNanometer,\n radiantPowerContributionWatts,\n vacuumWavelengthNanometers:\n photonEnergy\n .vacuumWavelengthNanometers,\n photonEnergyJoules:\n photonEnergy\n .photonEnergyJoules,\n wavelengthConversion:\n photonEnergy\n .wavelengthConversion,\n ...(photonEnergy\n .phaseRefractiveIndexUsed ===\n undefined\n ? {}\n : {\n phaseRefractiveIndexUsed:\n photonEnergy\n .phaseRefractiveIndexUsed\n }),\n ...(photonEnergy\n .airConditionCompatibility ===\n undefined\n ? {}\n : {\n airConditionCompatibility:\n photonEnergy\n .airConditionCompatibility\n }),\n ...(photonEnergy\n .airRefractiveIndexEvidence ===\n undefined\n ? {}\n : {\n airRefractiveIndexEvidence:\n photonEnergy\n .airRefractiveIndexEvidence\n }),\n incidentPhotonRatePerSecond,\n effectiveExternalQuantumEfficiency:\n eqe.externalQuantumEfficiency,\n responseComposition:\n eqe.composition,\n ...(eqe.channelFilterTransmittance ===\n undefined\n ? {}\n : {\n channelFilterTransmittance:\n eqe.channelFilterTransmittance\n }),\n ...(eqe\n .detectorExternalQuantumEfficiency ===\n undefined\n ? {}\n : {\n detectorExternalQuantumEfficiency:\n eqe\n .detectorExternalQuantumEfficiency\n }),\n responseInterpolationUsed:\n resolved.value\n .interpolationUsed,\n expectedGeneratedElectronRatePerSecond\n });\n }\n\n if (\n !Number.isFinite(\n photonRateSum.sum\n ) ||\n !Number.isFinite(\n electronRateSum.sum\n )\n ) {\n throw new InvalidScientificInputError(\n \"Total EQE photon/electron rates must remain finite.\"\n );\n }\n\n return approximationResult(\n {\n colorSamplingProfileId:\n input.reduction\n .colorSamplingProfileId,\n responseProfileId:\n input.reduction\n .responseProfileId,\n responseApplicationProfileId:\n input.compatibility\n .applicationProfileId,\n operatingRangeProfileId:\n input.operatingRange\n .operatingRangeProfileId,\n site: {\n ...input.reduction.site\n },\n channelId:\n input.reduction.channelId,\n sourceResponseKind:\n input.reduction\n .sourceResponseKind,\n responseScope:\n input.reduction.responseScope,\n responseReferencePlane:\n input.compatibility\n .requiredSourcePlane,\n wavelengthBasis:\n input.reduction\n .wavelengthBasis,\n wavelengthRangeNanometers: {\n ...input.reduction\n .wavelengthRangeNanometers\n },\n spectralNodeCount:\n nodes.length,\n perWavelength,\n incidentPhotonRatePerSecond:\n photonRateSum.sum,\n expectedGeneratedElectronRatePerSecond:\n electronRateSum.sum,\n summationMethod:\n \"kahan-compensated\",\n responseApplicationPerformed:\n true,\n quantumEfficiencyApplied:\n true,\n spectralResponsivityApplied:\n false,\n channelFilterTransmissionApplied,\n photonRateCalculated: true,\n electronRateCalculated: true,\n photonCountCalculated: false,\n electronCountCalculated: false,\n currentCalculated: false,\n temporalIntegrationApplied:\n false,\n exposureDurationApplied: false,\n saturationAssessed: false,\n shotNoiseApplied: false,\n readNoiseApplied: false,\n adcQuantizationApplied:\n false,\n rawCodeValueProduced: false,\n responseUncertaintyPropagated:\n false,\n photonEnergyUncertaintyPropagated:\n false,\n quadratureConvergenceErrorEstimated:\n false\n },\n \"sensor-eqe-electron-rate\",\n \"1.0.0\",\n [\n \"Response is evaluated independently at every wavelength quadrature node; broadband radiant power is never multiplied by an average QE.\",\n \"Each node converts radiant power to photon rate using photon energy hν, then applies effective external QE to obtain expected generated-electron rate.\",\n \"Direct effective EQE is preserved as direct response; explicitly separable channel-filter transmittance × detector EQE is multiplied only through the existing spectral-response resolver.\",\n \"The response calibration metadata/evidence must match the spectral evidence carried by the pre-response reduction; matching profile IDs alone is insufficient.\",\n \"The structural compatibility and operating-range assessments must both be non-blocked and authorize the photon-rate→electron path.\",\n \"Operating-range authorization must be per spectral bin, with spectral-node identities and calibrated-domain values bound back to the exact reduction used for conversion.\",\n \"Operating-range authorization must establish linear superposition over the geometric aperture so spatial reduction may precede response application without hiding sub-aperture nonlinear behavior.\",\n \"Air-basis spectral nodes require one exact-wavelength sourced phase refractive index per spectral sample plus the photon-energy atmosphere compatibility policy.\",\n \"Kahan compensated summation is used across wavelength nodes to reduce loss of small contributions when rates span large dynamic ranges.\",\n \"Outputs are instantaneous expected rates only. Exposure duration, photon/electron counts, full-well saturation, shot/read noise, ADC/RAW conversion and reconstruction remain downstream.\",\n \"Response/refractive-index uncertainties and quadrature convergence error are not propagated into a combined output uncertainty by this version.\"\n ]\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"eqe-electron-rate.js","sourceRoot":"","sources":["../../src/sensor/eqe-electron-rate.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * EQE electron rate: converts the light power reaching a sensor site into a photon rate and an\n * expected electron rate, using the sensor's EQE (external quantum efficiency, the fraction of\n * incident photons that produce a collected electron).\n *\n * The response is applied at each wavelength node. One broadband-average QE is never applied to the\n * total power. Outputs are rates per second; integrating over the exposure time happens later.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport type {\n EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport { stringifyCanonicalJson } from \"../core/canonical-json.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n calculatePhotonEnergyFromWavelength,\n type AirRefractiveIndexConditionPolicy,\n type AirRefractiveIndexReferenceConditions,\n type SourcedAirPhaseRefractiveIndex\n} from \"./photon-energy.js\";\nimport type {\n SensorResponseApplicationCompatibilityAssessment\n} from \"./response-application-compatibility.js\";\nimport type {\n SensorResponseOperatingRangeAssessment\n} from \"./response-operating-range.js\";\nimport {\n parseSensorColorSamplingProfile,\n type SensorColorSamplingProfile\n} from \"./color-sampling.js\";\nimport {\n createSensorSpectralResponseChannelBinding,\n requireCurrentSensorSpectralResponseChannelBinding,\n parseSensorSpectralResponseProfile,\n resolveParsedSensorSpectralResponseAtWavelength,\n type ResolvedSensorSpectralResponseValue,\n type SensorSpectralResponseProfile\n} from \"./spectral-response.js\";\nimport type {\n SensorSpatioSpectralIrradianceReduction,\n SensorSpatioSpectralWavelengthReduction\n} from \"./spatio-spectral-reduction.js\";\nimport { requireInputObject, requireInputKeys, requireInputNonNegativeFinite as requireNonNegativeFinite, requireInputPositiveFinite as requirePositiveFinite, requireInputNonNegativeSafeInteger as requireSafeNonNegativeInteger } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { addCompensated } from \"../core/shared-helpers.js\";\n\nconst REDUCTION_TOLERANCE = 1e-10;\n\nexport interface SensorEqeAirRefractiveIndexSample {\n spectralSampleIndex: number;\n refractiveIndex:\n SourcedAirPhaseRefractiveIndex;\n}\n\nexport interface SensorEqeAirPhotonEnergyContext {\n samples:\n readonly SensorEqeAirRefractiveIndexSample[];\n operatingConditions?:\n AirRefractiveIndexReferenceConditions;\n conditionPolicy:\n AirRefractiveIndexConditionPolicy;\n}\n\nexport interface CalculateSensorEqeElectronRateInput {\n reduction:\n SensorSpatioSpectralIrradianceReduction;\n compatibility:\n SensorResponseApplicationCompatibilityAssessment;\n operatingRange:\n SensorResponseOperatingRangeAssessment;\n colorSamplingProfile:\n SensorColorSamplingProfile;\n spectralResponseProfile:\n SensorSpectralResponseProfile;\n airPhotonEnergyContext?:\n SensorEqeAirPhotonEnergyContext;\n}\n\nexport interface SensorEqeWavelengthRateContribution {\n spectralSampleIndex: number;\n wavelengthNanometers: number;\n wavelengthMeasureNanometers: number;\n geometricApertureIncidentSpectralFluxWattsPerNanometer:\n number;\n radiantPowerContributionWatts: number;\n vacuumWavelengthNanometers: number;\n photonEnergyJoules: number;\n wavelengthConversion:\n | \"vacuum-identity\"\n | \"air-to-vacuum-via-phase-refractive-index\";\n phaseRefractiveIndexUsed?: number;\n airConditionCompatibility?:\n | \"exact-match\"\n | \"assumed-compatible\";\n airRefractiveIndexEvidence?:\n readonly EvidenceProvenance[];\n incidentPhotonRatePerSecond: number;\n effectiveExternalQuantumEfficiency: number;\n responseComposition:\n | \"direct-effective-response\"\n | \"channel-filter-transmittance-times-detector-eqe\";\n channelFilterTransmittance?: number;\n detectorExternalQuantumEfficiency?: number;\n responseInterpolationUsed: boolean;\n expectedGeneratedElectronRatePerSecond:\n number;\n}\n\nexport interface SensorEqeElectronRate {\n colorSamplingProfileId: string;\n responseProfileId: string;\n responseApplicationProfileId: string;\n operatingRangeProfileId: string;\n /** The exact source colour site. Results produced by the engine fill this in. */\n site?: {\n x: number;\n y: number;\n };\n channelId: string;\n sourceResponseKind:\n | \"effective-external-quantum-efficiency\"\n | \"separable-channel-filter-and-detector-eqe\";\n responseScope:\n SensorSpatioSpectralIrradianceReduction[\"responseScope\"];\n responseReferencePlane:\n SensorResponseApplicationCompatibilityAssessment[\"requiredSourcePlane\"];\n wavelengthBasis:\n SensorSpatioSpectralIrradianceReduction[\"wavelengthBasis\"];\n wavelengthRangeNanometers:\n SensorSpatioSpectralIrradianceReduction[\"wavelengthRangeNanometers\"];\n spectralNodeCount: number;\n perWavelength:\n readonly SensorEqeWavelengthRateContribution[];\n incidentPhotonRatePerSecond: number;\n expectedGeneratedElectronRatePerSecond:\n number;\n summationMethod:\n \"kahan-compensated\";\n responseApplicationPerformed: true;\n quantumEfficiencyApplied: true;\n spectralResponsivityApplied: false;\n channelFilterTransmissionApplied:\n boolean;\n photonRateCalculated: true;\n electronRateCalculated: true;\n photonCountCalculated: false;\n electronCountCalculated: false;\n currentCalculated: false;\n temporalIntegrationApplied: false;\n exposureDurationApplied: false;\n saturationAssessed: false;\n shotNoiseApplied: false;\n readNoiseApplied: false;\n adcQuantizationApplied: false;\n rawCodeValueProduced: false;\n responseUncertaintyPropagated: false;\n photonEnergyUncertaintyPropagated: false;\n quadratureConvergenceErrorEstimated:\n false;\n}\n\ninterface CompensatedSum {\n sum: number;\n correction: number;\n}\n\nfunction approximatelyEqual(\n actual: number,\n expected: number\n): boolean {\n const scale = Math.max(\n 1,\n Math.abs(actual),\n Math.abs(expected)\n );\n return (\n Math.abs(actual - expected) <=\n REDUCTION_TOLERANCE * scale\n );\n}\n\n/**\n * Identity string for parsed plain data that ignores key order, so records with\n * the same content compare equal whatever order their properties were added in.\n */\nfunction canonicalJson(\n value: unknown\n): string {\n if (value === undefined) return \"undefined\";\n return stringifyCanonicalJson(value, {\n undefinedObjectProperties: \"omit\",\n nonFiniteNumberMessage:\n \"EQE electron-rate identity data must contain finite numbers.\",\n unsupportedValueMessage:\n \"EQE electron-rate identity data must be plain JSON data.\"\n });\n}\n\nfunction requireEquivalent(\n actual: unknown,\n expected: unknown,\n message: string\n): void {\n if (\n canonicalJson(actual) !==\n canonicalJson(expected)\n ) {\n throw new InvalidScientificInputError(\n message\n );\n }\n}\n\nfunction validateAuthorizationChain(\n reduction:\n SensorSpatioSpectralIrradianceReduction,\n compatibility:\n SensorResponseApplicationCompatibilityAssessment,\n operatingRange:\n SensorResponseOperatingRangeAssessment\n): void {\n requireInputObject(reduction, \"reduction input\");\n if (\n reduction.outputMeaning !==\n \"pre-response-spatio-spectral-radiometric-reduction\" ||\n reduction\n .spectralResponseApplicationPerformed !==\n false ||\n reduction.quantumEfficiencyApplied !==\n false ||\n reduction.temporalIntegrationApplied !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"reduction must remain pre-response and pre-temporal before EQE rate conversion.\"\n );\n }\n\n if (\n !compatibility\n .structuralCompatibilityEstablished ||\n compatibility.compatibilityStatus ===\n \"blocked\" ||\n compatibility.blockers.length !== 0 ||\n !compatibility.sourcePlaneMatched ||\n compatibility.requiredSignalPath !==\n \"photon-rate-to-electrons\"\n ) {\n throw new InvalidScientificInputError(\n \"compatibility must establish a non-blocked photon-rate-to-electrons response path.\"\n );\n }\n\n if (\n compatibility.spectralResponseProfileId !==\n reduction.responseProfileId ||\n compatibility.colorSamplingProfileId !==\n reduction.colorSamplingProfileId ||\n compatibility.channelId !==\n reduction.channelId ||\n compatibility.responseScope !==\n reduction.responseScope\n ) {\n throw new InvalidScientificInputError(\n \"compatibility identities must exactly match the reduction.\"\n );\n }\n\n if (\n !operatingRange\n .operatingRangeCompatibilityAssessed ||\n !operatingRange\n .instantaneousResponseRangeCompatible ||\n !operatingRange\n .responseRateConversionAuthorized ||\n operatingRange.status === \"blocked\" ||\n operatingRange.blockers.length !== 0 ||\n operatingRange.requiredSignalPath !==\n \"photon-rate-to-electrons\" ||\n operatingRange\n .responseApplicationPerformed !==\n false ||\n operatingRange\n .photonConversionPerformed !== false ||\n operatingRange\n .electronConversionPerformed !== false ||\n operatingRange\n .temporalIntegrationAuthorized !== false\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange must authorize a clean photon-rate-to-electrons conversion path.\"\n );\n }\n\n if (\n !operatingRange\n .spectralNodeRangeCompatibilityAssessed ||\n operatingRange\n .spectralInputModel.kind !==\n \"per-spectral-bin\"\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange must establish per-spectral-bin input-range applicability before wavelength-dependent EQE conversion.\"\n );\n }\n\n if (\n operatingRange\n .spatialLinearityModel.kind !==\n \"linear-superposition-over-geometric-aperture\"\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange must establish linear superposition over the geometric aperture.\"\n );\n }\n\n if (\n operatingRange.spectralResponseProfileId !==\n reduction.responseProfileId ||\n operatingRange.colorSamplingProfileId !==\n reduction.colorSamplingProfileId ||\n operatingRange.channelId !==\n reduction.channelId ||\n operatingRange.responseApplicationProfileId !==\n compatibility.applicationProfileId\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange identities must exactly match the reduction and compatibility assessment.\"\n );\n }\n}\n\nfunction validateReductionSpectralNodes(\n reduction:\n SensorSpatioSpectralIrradianceReduction\n): readonly SensorSpatioSpectralWavelengthReduction[] {\n if (\n !Array.isArray(\n reduction.perWavelength\n ) ||\n !Number.isSafeInteger(\n reduction.spectralNodeCount\n ) ||\n reduction.spectralNodeCount <= 0 ||\n reduction.perWavelength.length !==\n reduction.spectralNodeCount\n ) {\n throw new InvalidScientificInputError(\n \"reduction.perWavelength must contain exactly spectralNodeCount entries.\"\n );\n }\n\n let previousWavelength =\n Number.NEGATIVE_INFINITY;\n let normalizedWeightSum = 0;\n const integratedPower:\n CompensatedSum = {\n sum: 0,\n correction: 0\n };\n\n reduction.perWavelength.forEach(\n (entry, arrayIndex) => {\n const sampleIndex =\n requireSafeNonNegativeInteger(\n entry.spectralSampleIndex,\n \"reduction.perWavelength[\" +\n arrayIndex +\n \"].spectralSampleIndex\"\n );\n if (sampleIndex !== arrayIndex) {\n throw new InvalidScientificInputError(\n \"reduction spectralSampleIndex values must form the exact contiguous range 0..spectralNodeCount-1.\"\n );\n }\n\n const wavelength =\n requirePositiveFinite(\n entry.wavelengthNanometers,\n \"reduction.perWavelength[\" +\n arrayIndex +\n \"].wavelengthNanometers\"\n );\n if (\n wavelength <=\n previousWavelength\n ) {\n throw new InvalidScientificInputError(\n \"reduction per-wavelength samples must be strictly increasing in wavelength.\"\n );\n }\n previousWavelength =\n wavelength;\n\n const measure =\n requirePositiveFinite(\n entry.wavelengthMeasureNanometers,\n \"reduction.perWavelength[\" +\n arrayIndex +\n \"].wavelengthMeasureNanometers\"\n );\n const normalizedWeight =\n requirePositiveFinite(\n entry.normalizedWavelengthWeight,\n \"reduction.perWavelength[\" +\n arrayIndex +\n \"].normalizedWavelengthWeight\"\n );\n const fluxDensity =\n requireNonNegativeFinite(\n entry\n .geometricApertureIncidentSpectralFluxWattsPerNanometer,\n \"reduction.perWavelength[\" +\n arrayIndex +\n \"].geometricApertureIncidentSpectralFluxWattsPerNanometer\"\n );\n const contribution =\n requireNonNegativeFinite(\n entry\n .wavelengthIntegratedGeometricApertureIncidentFluxContributionWatts,\n \"reduction.perWavelength[\" +\n arrayIndex +\n \"].wavelengthIntegratedGeometricApertureIncidentFluxContributionWatts\"\n );\n const expectedContribution =\n fluxDensity * measure;\n if (\n !Number.isFinite(\n expectedContribution\n ) ||\n !approximatelyEqual(\n contribution,\n expectedContribution\n )\n ) {\n throw new InvalidScientificInputError(\n \"reduction per-wavelength radiant-power contribution must equal spectral flux density × d-lambda.\"\n );\n }\n\n normalizedWeightSum +=\n normalizedWeight;\n if (\n !Number.isFinite(\n normalizedWeightSum\n )\n ) {\n throw new InvalidScientificInputError(\n \"reduction normalized wavelength weights must remain finite.\"\n );\n }\n addCompensated(\n integratedPower,\n contribution\n );\n }\n );\n\n if (\n !approximatelyEqual(\n normalizedWeightSum,\n 1\n )\n ) {\n throw new InvalidScientificInputError(\n \"reduction normalized wavelength weights must sum to 1.\"\n );\n }\n\n const totalPower =\n requireNonNegativeFinite(\n reduction\n .wavelengthIntegratedGeometricApertureIncidentFluxWatts,\n \"reduction.wavelengthIntegratedGeometricApertureIncidentFluxWatts\"\n );\n if (\n !approximatelyEqual(\n integratedPower.sum,\n totalPower\n )\n ) {\n throw new InvalidScientificInputError(\n \"reduction total geometric-aperture radiant power must match the sum of per-wavelength contributions.\"\n );\n }\n\n return reduction.perWavelength;\n}\n\nfunction validateOperatingRangeNodeBinding(\n reduction:\n SensorSpatioSpectralIrradianceReduction,\n nodes:\n readonly SensorSpatioSpectralWavelengthReduction[],\n operatingRange:\n SensorResponseOperatingRangeAssessment\n): void {\n const evaluated =\n operatingRange\n .evaluatedSpectralNodeInputs;\n\n if (\n evaluated === undefined ||\n evaluated.length !== nodes.length\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange must carry one evaluated spectral-node input for every reduction wavelength node.\"\n );\n }\n\n if (\n operatingRange\n .evaluatedWavelengthRangeNanometers\n .minimum !==\n reduction.wavelengthRangeNanometers\n .minimum ||\n operatingRange\n .evaluatedWavelengthRangeNanometers\n .maximum !==\n reduction.wavelengthRangeNanometers\n .maximum\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange wavelength range must exactly match the reduction.\"\n );\n }\n\n evaluated.forEach(\n (entry, index) => {\n const node = nodes[index]!;\n if (\n entry.spectralSampleIndex !==\n node.spectralSampleIndex ||\n entry.wavelengthNanometers !==\n node.wavelengthNanometers ||\n entry.wavelengthMeasureNanometers !==\n node.wavelengthMeasureNanometers\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange spectral-node identities must exactly match the reduction.\"\n );\n }\n\n const expected =\n operatingRange.inputRange.kind ===\n \"wavelength-integrated-geometric-aperture-radiant-power\"\n ? node\n .wavelengthIntegratedGeometricApertureIncidentFluxContributionWatts\n : node\n .wavelengthIntegratedSpatialAverageContributionWattsPerSquareMeter;\n\n if (\n entry.kind !==\n operatingRange.inputRange.kind ||\n entry.unit !==\n operatingRange.inputRange.unit ||\n !approximatelyEqual(\n entry.value,\n expected\n )\n ) {\n throw new InvalidScientificInputError(\n \"operatingRange spectral-node values must match the reduction values in the calibrated input domain.\"\n );\n }\n }\n );\n}\n\nfunction parseAirContext(\n reduction:\n SensorSpatioSpectralIrradianceReduction,\n nodes:\n readonly SensorSpatioSpectralWavelengthReduction[],\n context:\n SensorEqeAirPhotonEnergyContext |\n undefined\n): ReadonlyMap<\n number,\n SourcedAirPhaseRefractiveIndex\n> {\n if (\n reduction.wavelengthBasis ===\n \"vacuum\"\n ) {\n if (context !== undefined) {\n throw new InvalidScientificInputError(\n \"airPhotonEnergyContext must be omitted for vacuum-basis EQE conversion.\"\n );\n }\n return new Map();\n }\n\n if (\n reduction.wavelengthBasis !== \"air\"\n ) {\n throw new InvalidScientificInputError(\n \"EQE electron-rate conversion requires a resolved air or vacuum wavelength basis.\"\n );\n }\n\n if (\n context === undefined ||\n !Array.isArray(context.samples) ||\n context.samples.length !==\n nodes.length\n ) {\n throw new InvalidScientificInputError(\n \"Air-basis EQE conversion requires exactly one refractive-index sample for every spectral node.\"\n );\n }\n\n const byIndex =\n new Map<\n number,\n SourcedAirPhaseRefractiveIndex\n >();\n\n context.samples.forEach(\n (entry, arrayIndex) => {\n if (\n typeof entry !== \"object\" ||\n entry === null\n ) {\n throw new InvalidConfigurationError(\n \"airPhotonEnergyContext.samples[\" +\n arrayIndex +\n \"] must be an object.\"\n );\n }\n\n const spectralSampleIndex =\n requireSafeNonNegativeInteger(\n entry.spectralSampleIndex,\n \"airPhotonEnergyContext.samples[\" +\n arrayIndex +\n \"].spectralSampleIndex\"\n );\n if (\n spectralSampleIndex >=\n nodes.length ||\n byIndex.has(\n spectralSampleIndex\n )\n ) {\n throw new InvalidScientificInputError(\n \"airPhotonEnergyContext must contain unique in-range spectral sample identities.\"\n );\n }\n byIndex.set(\n spectralSampleIndex,\n entry.refractiveIndex\n );\n }\n );\n\n return byIndex;\n}\n\nfunction responseEvidenceMatches(\n actual:\n readonly EvidenceProvenance[],\n expected:\n readonly EvidenceProvenance[]\n): boolean {\n return (\n canonicalJson(actual) ===\n canonicalJson(expected)\n );\n}\n\nfunction validateResponseBinding(\n resolved:\n ReturnType<\n typeof resolveParsedSensorSpectralResponseAtWavelength\n >[\"value\"],\n reduction:\n SensorSpatioSpectralIrradianceReduction\n): void {\n if (\n resolved.profileId !==\n reduction.responseProfileId ||\n resolved.colorSamplingProfileId !==\n reduction.colorSamplingProfileId ||\n resolved.channelId !==\n reduction.channelId ||\n resolved.sourceResponseKind !==\n reduction.sourceResponseKind ||\n resolved.responseScope !==\n reduction.responseScope ||\n resolved.scientificStatus !==\n reduction.responseScientificStatus\n ) {\n throw new InvalidScientificInputError(\n \"Resolved spectral response metadata must exactly match the response used to construct the reduction.\"\n );\n }\n\n requireEquivalent(\n resolved.uncertainty,\n reduction.responseUncertainty,\n \"Resolved spectral response uncertainty must match the reduction.\"\n );\n\n const expectedEvidence =\n reduction.componentEvidence.spectral;\n if (\n !responseEvidenceMatches(\n resolved.componentEvidence.profile,\n expectedEvidence.profile\n ) ||\n !responseEvidenceMatches(\n resolved.componentEvidence.channel,\n expectedEvidence.channel\n ) ||\n canonicalJson(\n resolved.componentEvidence.curves\n ) !==\n canonicalJson(\n expectedEvidence.curves\n )\n ) {\n throw new InvalidScientificInputError(\n \"Resolved spectral response evidence must match the calibration evidence carried by the reduction.\"\n );\n }\n}\n\nfunction validateExactResponseDataBinding(\n responseProfile:\n SensorSpectralResponseProfile,\n reduction:\n SensorSpatioSpectralIrradianceReduction\n): void {\n const response =\n responseProfile.channels.find(\n (entry) =>\n entry.channelId ===\n reduction.channelId\n );\n if (reduction.responseChannelBinding !== undefined) {\n requireCurrentSensorSpectralResponseChannelBinding(\n reduction.responseChannelBinding\n );\n }\n if (\n response === undefined ||\n reduction.responseChannelBinding ===\n undefined ||\n canonicalJson(\n createSensorSpectralResponseChannelBinding(\n response\n )\n ) !==\n canonicalJson(\n reduction.responseChannelBinding\n )\n ) {\n throw new InvalidScientificInputError(\n \"Supplied spectral response channel data must exactly match the response-channel binding carried by the reduction.\"\n );\n }\n}\n\nfunction requireEqeResponse(\n response:\n ResolvedSensorSpectralResponseValue\n): Extract<\n ResolvedSensorSpectralResponseValue,\n {\n kind:\n \"effective-external-quantum-efficiency\";\n }\n> {\n if (\n response.kind !==\n \"effective-external-quantum-efficiency\"\n ) {\n throw new InvalidScientificInputError(\n \"EQE electron-rate conversion cannot consume A/W spectral responsivity.\"\n );\n }\n return response;\n}\n\n/**\n * Converts the spectral radiant power collected by the site's geometric aperture\n * (before the sensor response) into an incident photon rate and an expected\n * generated electron rate.\n *\n * The response is applied at each wavelength node. One broadband-average QE is\n * never applied to the total power.\n *\n * Integration over exposure time happens later, so all outputs are rates per\n * second, not photon or electron counts.\n */\nexport function calculateSensorEqeElectronRate(\n input:\n CalculateSensorEqeElectronRateInput\n): CalculationResult<SensorEqeElectronRate> {\n requireInputKeys(input, \"calculateSensorEqeElectronRate\", [\"reduction\", \"compatibility\", \"operatingRange\", \"colorSamplingProfile\", \"spectralResponseProfile\", \"airPhotonEnergyContext\"]);\n validateAuthorizationChain(\n input.reduction,\n input.compatibility,\n input.operatingRange\n );\n\n if (\n input.reduction\n .sourceResponseKind ===\n \"effective-spectral-responsivity\"\n ) {\n throw new InvalidScientificInputError(\n \"EQE electron-rate conversion requires an EQE or filter×detector-EQE response, not A/W responsivity.\"\n );\n }\n\n const colorProfile =\n parseSensorColorSamplingProfile(\n input.colorSamplingProfile\n );\n const responseProfile =\n parseSensorSpectralResponseProfile(\n input.spectralResponseProfile\n );\n\n if (\n colorProfile.profileId !==\n input.reduction\n .colorSamplingProfileId ||\n responseProfile.profileId !==\n input.reduction\n .responseProfileId ||\n responseProfile\n .colorSamplingProfileId !==\n colorProfile.profileId\n ) {\n throw new InvalidScientificInputError(\n \"Supplied color/spectral response profiles must exactly match the reduction identities.\"\n );\n }\n\n validateExactResponseDataBinding(\n responseProfile,\n input.reduction\n );\n\n const nodes =\n validateReductionSpectralNodes(\n input.reduction\n );\n validateOperatingRangeNodeBinding(\n input.reduction,\n nodes,\n input.operatingRange\n );\n\n const airIndices =\n parseAirContext(\n input.reduction,\n nodes,\n input.airPhotonEnergyContext\n );\n\n const photonRateSum:\n CompensatedSum = {\n sum: 0,\n correction: 0\n };\n const electronRateSum:\n CompensatedSum = {\n sum: 0,\n correction: 0\n };\n const perWavelength:\n SensorEqeWavelengthRateContribution[] =\n [];\n\n let responseBindingValidated =\n false;\n let channelFilterTransmissionApplied =\n false;\n\n for (const node of nodes) {\n const resolved =\n resolveParsedSensorSpectralResponseAtWavelength({\n colorSamplingProfile:\n colorProfile,\n spectralResponseProfile:\n responseProfile,\n channelId:\n input.reduction.channelId,\n wavelengthNanometers:\n node.wavelengthNanometers,\n wavelengthBasis:\n input.reduction\n .wavelengthBasis\n });\n\n if (!responseBindingValidated) {\n validateResponseBinding(\n resolved.value,\n input.reduction\n );\n responseBindingValidated =\n true;\n }\n\n const eqe =\n requireEqeResponse(\n resolved.value.response\n );\n\n const airIndex =\n airIndices.get(\n node.spectralSampleIndex\n );\n const photonEnergy =\n calculatePhotonEnergyFromWavelength({\n wavelengthNanometers:\n node.wavelengthNanometers,\n wavelengthBasis:\n input.reduction\n .wavelengthBasis,\n ...(input.reduction\n .wavelengthBasis === \"air\"\n ? {\n airPhaseRefractiveIndex:\n airIndex!,\n ...(input.airPhotonEnergyContext!\n .operatingConditions ===\n undefined\n ? {}\n : {\n airOperatingConditions:\n input\n .airPhotonEnergyContext!\n .operatingConditions\n }),\n airConditionPolicy:\n input.airPhotonEnergyContext!\n .conditionPolicy\n }\n : {})\n }).value;\n\n const radiantPowerContributionWatts =\n node\n .geometricApertureIncidentSpectralFluxWattsPerNanometer *\n node.wavelengthMeasureNanometers;\n const incidentPhotonRatePerSecond =\n radiantPowerContributionWatts /\n photonEnergy.photonEnergyJoules;\n const expectedGeneratedElectronRatePerSecond =\n incidentPhotonRatePerSecond *\n eqe.externalQuantumEfficiency;\n\n if (\n !Number.isFinite(\n radiantPowerContributionWatts\n ) ||\n !Number.isFinite(\n incidentPhotonRatePerSecond\n ) ||\n !Number.isFinite(\n expectedGeneratedElectronRatePerSecond\n )\n ) {\n throw new InvalidScientificInputError(\n \"EQE wavelength-node rate calculation must remain finite.\"\n );\n }\n\n addCompensated(\n photonRateSum,\n incidentPhotonRatePerSecond\n );\n addCompensated(\n electronRateSum,\n expectedGeneratedElectronRatePerSecond\n );\n\n if (\n eqe.composition ===\n \"channel-filter-transmittance-times-detector-eqe\"\n ) {\n channelFilterTransmissionApplied =\n true;\n }\n\n perWavelength.push({\n spectralSampleIndex:\n node.spectralSampleIndex,\n wavelengthNanometers:\n node.wavelengthNanometers,\n wavelengthMeasureNanometers:\n node.wavelengthMeasureNanometers,\n geometricApertureIncidentSpectralFluxWattsPerNanometer:\n node\n .geometricApertureIncidentSpectralFluxWattsPerNanometer,\n radiantPowerContributionWatts,\n vacuumWavelengthNanometers:\n photonEnergy\n .vacuumWavelengthNanometers,\n photonEnergyJoules:\n photonEnergy\n .photonEnergyJoules,\n wavelengthConversion:\n photonEnergy\n .wavelengthConversion,\n ...(photonEnergy\n .phaseRefractiveIndexUsed ===\n undefined\n ? {}\n : {\n phaseRefractiveIndexUsed:\n photonEnergy\n .phaseRefractiveIndexUsed\n }),\n ...(photonEnergy\n .airConditionCompatibility ===\n undefined\n ? {}\n : {\n airConditionCompatibility:\n photonEnergy\n .airConditionCompatibility\n }),\n ...(photonEnergy\n .airRefractiveIndexEvidence ===\n undefined\n ? {}\n : {\n airRefractiveIndexEvidence:\n photonEnergy\n .airRefractiveIndexEvidence\n }),\n incidentPhotonRatePerSecond,\n effectiveExternalQuantumEfficiency:\n eqe.externalQuantumEfficiency,\n responseComposition:\n eqe.composition,\n ...(eqe.channelFilterTransmittance ===\n undefined\n ? {}\n : {\n channelFilterTransmittance:\n eqe.channelFilterTransmittance\n }),\n ...(eqe\n .detectorExternalQuantumEfficiency ===\n undefined\n ? {}\n : {\n detectorExternalQuantumEfficiency:\n eqe\n .detectorExternalQuantumEfficiency\n }),\n responseInterpolationUsed:\n resolved.value\n .interpolationUsed,\n expectedGeneratedElectronRatePerSecond\n });\n }\n\n if (\n !Number.isFinite(\n photonRateSum.sum\n ) ||\n !Number.isFinite(\n electronRateSum.sum\n )\n ) {\n throw new InvalidScientificInputError(\n \"Total EQE photon/electron rates must remain finite.\"\n );\n }\n\n return approximationResult(\n {\n colorSamplingProfileId:\n input.reduction\n .colorSamplingProfileId,\n responseProfileId:\n input.reduction\n .responseProfileId,\n responseApplicationProfileId:\n input.compatibility\n .applicationProfileId,\n operatingRangeProfileId:\n input.operatingRange\n .operatingRangeProfileId,\n site: {\n ...input.reduction.site\n },\n channelId:\n input.reduction.channelId,\n sourceResponseKind:\n input.reduction\n .sourceResponseKind,\n responseScope:\n input.reduction.responseScope,\n responseReferencePlane:\n input.compatibility\n .requiredSourcePlane,\n wavelengthBasis:\n input.reduction\n .wavelengthBasis,\n wavelengthRangeNanometers: {\n ...input.reduction\n .wavelengthRangeNanometers\n },\n spectralNodeCount:\n nodes.length,\n perWavelength,\n incidentPhotonRatePerSecond:\n photonRateSum.sum,\n expectedGeneratedElectronRatePerSecond:\n electronRateSum.sum,\n summationMethod:\n \"kahan-compensated\",\n responseApplicationPerformed:\n true,\n quantumEfficiencyApplied:\n true,\n spectralResponsivityApplied:\n false,\n channelFilterTransmissionApplied,\n photonRateCalculated: true,\n electronRateCalculated: true,\n photonCountCalculated: false,\n electronCountCalculated: false,\n currentCalculated: false,\n temporalIntegrationApplied:\n false,\n exposureDurationApplied: false,\n saturationAssessed: false,\n shotNoiseApplied: false,\n readNoiseApplied: false,\n adcQuantizationApplied:\n false,\n rawCodeValueProduced: false,\n responseUncertaintyPropagated:\n false,\n photonEnergyUncertaintyPropagated:\n false,\n quadratureConvergenceErrorEstimated:\n false\n },\n \"sensor-eqe-electron-rate\",\n \"1.0.0\",\n [\n \"Response is evaluated independently at every wavelength quadrature node; broadband radiant power is never multiplied by an average QE.\",\n \"Each node converts radiant power to photon rate using photon energy hν, then applies effective external QE to obtain expected generated-electron rate.\",\n \"Direct effective EQE is preserved as direct response; explicitly separable channel-filter transmittance × detector EQE is multiplied only through the existing spectral-response resolver.\",\n \"The response calibration metadata/evidence must match the spectral evidence carried by the pre-response reduction; matching profile IDs alone is insufficient.\",\n \"The structural compatibility and operating-range assessments must both be non-blocked and authorize the photon-rate→electron path.\",\n \"Operating-range authorization must be per spectral bin, with spectral-node identities and calibrated-domain values bound back to the exact reduction used for conversion.\",\n \"Operating-range authorization must establish linear superposition over the geometric aperture so spatial reduction may precede response application without hiding sub-aperture nonlinear behavior.\",\n \"Air-basis spectral nodes require one exact-wavelength sourced phase refractive index per spectral sample plus the photon-energy atmosphere compatibility policy.\",\n \"Kahan compensated summation is used across wavelength nodes to reduce loss of small contributions when rates span large dynamic ranges.\",\n \"Outputs are instantaneous expected rates only. Exposure duration, photon/electron counts, full-well saturation, shot/read noise, ADC/RAW conversion and reconstruction remain downstream.\",\n \"Response/refractive-index uncertainties and quadrature convergence error are not propagated into a combined output uncertainty by this version.\"\n ]\n );\n}\n"]}
|
|
@@ -99,10 +99,15 @@ export interface CaptureExposureWindows {
|
|
|
99
99
|
};
|
|
100
100
|
samples: readonly CaptureExposureWindowSample[];
|
|
101
101
|
}
|
|
102
|
+
export declare function requireNonNegativeInteger(name: string, value: number): void;
|
|
103
|
+
export declare function requirePositiveFinite(name: string, value: unknown): number;
|
|
102
104
|
/** Parses a sourced duration in seconds (`{ unit: "s", value, evidence }`). Not a root export. */
|
|
103
105
|
export declare function parseSecondsFact(value: unknown, path: string): SourcedCaptureTimingSeconds;
|
|
104
106
|
/** Parses one exposure boundary schedule (opening or closing). Not a root export. */
|
|
105
107
|
export declare function parseBoundarySchedule(value: unknown, path: string): ExposureBoundarySchedule;
|
|
108
|
+
export declare function validateNativeRaster(raster: NativeImageRaster): void;
|
|
109
|
+
export declare function scanVector(direction: NativeSensorReadoutScanDirection): RasterVector;
|
|
110
|
+
export declare function normalizedScanPosition(point: RasterPoint, rect: RasterRect, direction: NativeSensorReadoutScanDirection): number;
|
|
106
111
|
/**
|
|
107
112
|
* Works out each point's local exposure window from separate opening and
|
|
108
113
|
* closing boundary schedules.
|