@photivra/engine 2.18.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.
Files changed (261) hide show
  1. package/CHANGELOG.md +68 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +6 -6
  4. package/SECURITY.md +1 -1
  5. package/dist/capture/focus-control.js +2 -75
  6. package/dist/capture/focus-control.js.map +1 -1
  7. package/dist/capture/native-environment-lean.d.ts +1 -0
  8. package/dist/capture/native-environment-lean.js +1 -1
  9. package/dist/capture/native-environment-lean.js.map +1 -1
  10. package/dist/capture/native-environment-optical-stage.d.ts +1 -0
  11. package/dist/capture/native-environment-optical-stage.js +2 -5
  12. package/dist/capture/native-environment-optical-stage.js.map +1 -1
  13. package/dist/capture/native-environment-raw.js +1 -5
  14. package/dist/capture/native-environment-raw.js.map +1 -1
  15. package/dist/capture/release-sequence.js +2 -47
  16. package/dist/capture/release-sequence.js.map +1 -1
  17. package/dist/color/white-balance.d.ts +2 -0
  18. package/dist/color/white-balance.js +3 -56
  19. package/dist/color/white-balance.js.map +1 -1
  20. package/dist/composition/image-formation-plan.d.ts +1 -0
  21. package/dist/composition/image-formation-plan.js +3 -49
  22. package/dist/composition/image-formation-plan.js.map +1 -1
  23. package/dist/core/evidence-provenance.js +1 -16
  24. package/dist/core/evidence-provenance.js.map +1 -1
  25. package/dist/core/record-validation.d.ts +54 -0
  26. package/dist/core/record-validation.js +176 -0
  27. package/dist/core/record-validation.js.map +1 -1
  28. package/dist/core/scientific-assurance.js +1 -18
  29. package/dist/core/scientific-assurance.js.map +1 -1
  30. package/dist/core/shared-helpers.d.ts +33 -0
  31. package/dist/core/shared-helpers.js +34 -0
  32. package/dist/core/shared-helpers.js.map +1 -0
  33. package/dist/core/spectral-composition.js +1 -36
  34. package/dist/core/spectral-composition.js.map +1 -1
  35. package/dist/core/spectral.js +1 -19
  36. package/dist/core/spectral.js.map +1 -1
  37. package/dist/core/version.d.ts +1 -1
  38. package/dist/core/version.js +1 -1
  39. package/dist/core/version.js.map +1 -1
  40. package/dist/equipment/exposure-capabilities.d.ts +1 -0
  41. package/dist/equipment/exposure-capabilities.js +2 -36
  42. package/dist/equipment/exposure-capabilities.js.map +1 -1
  43. package/dist/equipment/iso-capabilities.js +2 -49
  44. package/dist/equipment/iso-capabilities.js.map +1 -1
  45. package/dist/equipment/metering-capabilities.js +1 -26
  46. package/dist/equipment/metering-capabilities.js.map +1 -1
  47. package/dist/equipment/release-capabilities.js +1 -53
  48. package/dist/equipment/release-capabilities.js.map +1 -1
  49. package/dist/exposure/auto-iso-slowest-shutter.js +1 -7
  50. package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
  51. package/dist/exposure/exposure-duration-control.js +1 -16
  52. package/dist/exposure/exposure-duration-control.js.map +1 -1
  53. package/dist/exposure/exposure-mode-resolver.js +1 -17
  54. package/dist/exposure/exposure-mode-resolver.js.map +1 -1
  55. package/dist/exposure/filtered-exposure-time.d.ts +93 -0
  56. package/dist/exposure/filtered-exposure-time.js +129 -0
  57. package/dist/exposure/filtered-exposure-time.js.map +1 -0
  58. package/dist/exposure/flash-ttl.d.ts +1 -0
  59. package/dist/exposure/flash-ttl.js +1 -1
  60. package/dist/exposure/flash-ttl.js.map +1 -1
  61. package/dist/exposure/flash.js +1 -49
  62. package/dist/exposure/flash.js.map +1 -1
  63. package/dist/exposure/metering-scene-radiance.js +1 -25
  64. package/dist/exposure/metering-scene-radiance.js.map +1 -1
  65. package/dist/exposure/metering-target.js +1 -30
  66. package/dist/exposure/metering-target.js.map +1 -1
  67. package/dist/exposure/metering-temporal.js +1 -8
  68. package/dist/exposure/metering-temporal.js.map +1 -1
  69. package/dist/exposure/metering.js +1 -72
  70. package/dist/exposure/metering.js.map +1 -1
  71. package/dist/exposure/program-line.js +1 -45
  72. package/dist/exposure/program-line.js.map +1 -1
  73. package/dist/facade/develop-joined-parts.d.ts +29 -0
  74. package/dist/facade/develop-joined-parts.js +33 -0
  75. package/dist/facade/develop-joined-parts.js.map +1 -0
  76. package/dist/facade/generic-lens-capture.d.ts +4 -2
  77. package/dist/facade/generic-lens-capture.js +4 -2
  78. package/dist/facade/generic-lens-capture.js.map +1 -1
  79. package/dist/facade/simulate-capture.d.ts +37 -5
  80. package/dist/facade/simulate-capture.js +153 -96
  81. package/dist/facade/simulate-capture.js.map +1 -1
  82. package/dist/gpu/chart-composite-gpu.js +1 -3
  83. package/dist/gpu/chart-composite-gpu.js.map +1 -1
  84. package/dist/gpu/chart-radiance-gpu.js +1 -5
  85. package/dist/gpu/chart-radiance-gpu.js.map +1 -1
  86. package/dist/gpu/display-view-quality-gpu.d.ts +13 -1
  87. package/dist/gpu/display-view-quality-gpu.js +4 -8
  88. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  89. package/dist/gpu/display-view-speed-gpu.js +3 -25
  90. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  91. package/dist/gpu/sensor-stage-gpu.js +1 -5
  92. package/dist/gpu/sensor-stage-gpu.js.map +1 -1
  93. package/dist/gpu/webgpu-runtime.d.ts +2 -0
  94. package/dist/gpu/webgpu-runtime.js +3 -0
  95. package/dist/gpu/webgpu-runtime.js.map +1 -1
  96. package/dist/index.d.ts +4 -1
  97. package/dist/index.js +3 -0
  98. package/dist/index.js.map +1 -1
  99. package/dist/motion/camera-rotation.js +1 -6
  100. package/dist/motion/camera-rotation.js.map +1 -1
  101. package/dist/motion/capture-rotation-inverse-mapping.d.ts +12 -0
  102. package/dist/motion/capture-rotation-inverse-mapping.js +2 -2
  103. package/dist/motion/capture-rotation-inverse-mapping.js.map +1 -1
  104. package/dist/motion/capture-rotation-temporal-quadrature.d.ts +1 -0
  105. package/dist/motion/capture-rotation-temporal-quadrature.js +1 -1
  106. package/dist/motion/capture-rotation-temporal-quadrature.js.map +1 -1
  107. package/dist/motion/capture-rotation-trajectory.js +2 -17
  108. package/dist/motion/capture-rotation-trajectory.js.map +1 -1
  109. package/dist/motion/capture-translation-parallax-temporal-quadrature.d.ts +1 -0
  110. package/dist/motion/capture-translation-parallax-temporal-quadrature.js +3 -14
  111. package/dist/motion/capture-translation-parallax-temporal-quadrature.js.map +1 -1
  112. package/dist/motion/extended-object-projection.js +5 -40
  113. package/dist/motion/extended-object-projection.js.map +1 -1
  114. package/dist/optics/atmospheric-haze.js +1 -7
  115. package/dist/optics/atmospheric-haze.js.map +1 -1
  116. package/dist/optics/brown-conrady-distortion.js +1 -7
  117. package/dist/optics/brown-conrady-distortion.js.map +1 -1
  118. package/dist/optics/complex-pupil-psf.d.ts +1 -0
  119. package/dist/optics/complex-pupil-psf.js +2 -84
  120. package/dist/optics/complex-pupil-psf.js.map +1 -1
  121. package/dist/optics/focus-state.js +1 -11
  122. package/dist/optics/focus-state.js.map +1 -1
  123. package/dist/optics/front-of-lens-filter.d.ts +5 -0
  124. package/dist/optics/front-of-lens-filter.js +3 -65
  125. package/dist/optics/front-of-lens-filter.js.map +1 -1
  126. package/dist/optics/generic-nd-filter-presets.d.ts +12 -0
  127. package/dist/optics/generic-nd-filter-presets.js +48 -0
  128. package/dist/optics/generic-nd-filter-presets.js.map +1 -0
  129. package/dist/optics/illumination-vignetting.js +1 -6
  130. package/dist/optics/illumination-vignetting.js.map +1 -1
  131. package/dist/optics/lens-psf-profile.d.ts +4 -1
  132. package/dist/optics/lens-psf-profile.js +12 -111
  133. package/dist/optics/lens-psf-profile.js.map +1 -1
  134. package/dist/optics/longitudinal-chromatic-aberration.js +1 -7
  135. package/dist/optics/longitudinal-chromatic-aberration.js.map +1 -1
  136. package/dist/optics/pupil-vignetting.js +1 -7
  137. package/dist/optics/pupil-vignetting.js.map +1 -1
  138. package/dist/optics/radial-distortion.js +1 -6
  139. package/dist/optics/radial-distortion.js.map +1 -1
  140. package/dist/optics/scene-to-sensor-irradiance.js +4 -96
  141. package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
  142. package/dist/optics/stray-light.d.ts +1 -0
  143. package/dist/optics/stray-light.js +1 -1
  144. package/dist/optics/stray-light.js.map +1 -1
  145. package/dist/optics/zernike-wavefront.js +1 -7
  146. package/dist/optics/zernike-wavefront.js.map +1 -1
  147. package/dist/output/capture-geometry.js +1 -8
  148. package/dist/output/capture-geometry.js.map +1 -1
  149. package/dist/output/developed-lens-corrections.d.ts +62 -0
  150. package/dist/output/developed-lens-corrections.js +135 -0
  151. package/dist/output/developed-lens-corrections.js.map +1 -0
  152. package/dist/output/generic-lens-corrections.d.ts +2 -6
  153. package/dist/output/generic-lens-corrections.js +3 -6
  154. package/dist/output/generic-lens-corrections.js.map +1 -1
  155. package/dist/output/geometric-transforms.js +1 -4
  156. package/dist/output/geometric-transforms.js.map +1 -1
  157. package/dist/output/native-capture-sdr.d.ts +2 -2
  158. package/dist/output/native-capture-sdr.js +3 -2
  159. package/dist/output/native-capture-sdr.js.map +1 -1
  160. package/dist/processing/development-detail.d.ts +29 -0
  161. package/dist/processing/development-detail.js +128 -107
  162. package/dist/processing/development-detail.js.map +1 -1
  163. package/dist/schema/illumination-temporal.js +3 -72
  164. package/dist/schema/illumination-temporal.js.map +1 -1
  165. package/dist/schema/illumination.d.ts +1 -0
  166. package/dist/schema/illumination.js +2 -65
  167. package/dist/schema/illumination.js.map +1 -1
  168. package/dist/schema/scene-radiance-temporal.js +2 -14
  169. package/dist/schema/scene-radiance-temporal.js.map +1 -1
  170. package/dist/schema/scene-radiance.js +2 -60
  171. package/dist/schema/scene-radiance.js.map +1 -1
  172. package/dist/schema/validation.d.ts +2 -0
  173. package/dist/schema/validation.js +2 -17
  174. package/dist/schema/validation.js.map +1 -1
  175. package/dist/sensor/accumulated-charge.js +2 -59
  176. package/dist/sensor/accumulated-charge.js.map +1 -1
  177. package/dist/sensor/architecture.js +1 -16
  178. package/dist/sensor/architecture.js.map +1 -1
  179. package/dist/sensor/camera-saturation-capacity.d.ts +1 -0
  180. package/dist/sensor/camera-saturation-capacity.js +3 -55
  181. package/dist/sensor/camera-saturation-capacity.js.map +1 -1
  182. package/dist/sensor/capture-color-sampling-binding.js +1 -26
  183. package/dist/sensor/capture-color-sampling-binding.js.map +1 -1
  184. package/dist/sensor/capture-mode-timing.js +1 -39
  185. package/dist/sensor/capture-mode-timing.js.map +1 -1
  186. package/dist/sensor/capture-mode.js +1 -23
  187. package/dist/sensor/capture-mode.js.map +1 -1
  188. package/dist/sensor/color-sampling.js +1 -26
  189. package/dist/sensor/color-sampling.js.map +1 -1
  190. package/dist/sensor/constant-rate-temporal-integration.js +2 -46
  191. package/dist/sensor/constant-rate-temporal-integration.js.map +1 -1
  192. package/dist/sensor/dark-current.js +2 -45
  193. package/dist/sensor/dark-current.js.map +1 -1
  194. package/dist/sensor/eqe-electron-rate.js +2 -35
  195. package/dist/sensor/eqe-electron-rate.js.map +1 -1
  196. package/dist/sensor/exposure-window.d.ts +5 -0
  197. package/dist/sensor/exposure-window.js +7 -22
  198. package/dist/sensor/exposure-window.js.map +1 -1
  199. package/dist/sensor/iso-gain.js +1 -7
  200. package/dist/sensor/iso-gain.js.map +1 -1
  201. package/dist/sensor/iso-signal-chain.js +1 -43
  202. package/dist/sensor/iso-signal-chain.js.map +1 -1
  203. package/dist/sensor/optical-stack.js +1 -25
  204. package/dist/sensor/optical-stack.js.map +1 -1
  205. package/dist/sensor/photon-energy.js +1 -26
  206. package/dist/sensor/photon-energy.js.map +1 -1
  207. package/dist/sensor/physical-charge-capacity.js +3 -77
  208. package/dist/sensor/physical-charge-capacity.js.map +1 -1
  209. package/dist/sensor/radiometry-readiness.js +2 -23
  210. package/dist/sensor/radiometry-readiness.js.map +1 -1
  211. package/dist/sensor/raw-readout.d.ts +3 -0
  212. package/dist/sensor/raw-readout.js +4 -66
  213. package/dist/sensor/raw-readout.js.map +1 -1
  214. package/dist/sensor/raw-reconstruction.js +2 -64
  215. package/dist/sensor/raw-reconstruction.js.map +1 -1
  216. package/dist/sensor/readout-exposure-linkage.js +1 -9
  217. package/dist/sensor/readout-exposure-linkage.js.map +1 -1
  218. package/dist/sensor/readout-timing.js +3 -57
  219. package/dist/sensor/readout-timing.js.map +1 -1
  220. package/dist/sensor/response-application-compatibility.d.ts +2 -0
  221. package/dist/sensor/response-application-compatibility.js +3 -29
  222. package/dist/sensor/response-application-compatibility.js.map +1 -1
  223. package/dist/sensor/response-operating-range.d.ts +1 -0
  224. package/dist/sensor/response-operating-range.js +3 -79
  225. package/dist/sensor/response-operating-range.js.map +1 -1
  226. package/dist/sensor/responsivity-photocurrent.js +4 -108
  227. package/dist/sensor/responsivity-photocurrent.js.map +1 -1
  228. package/dist/sensor/sampling-aperture.js +1 -25
  229. package/dist/sensor/sampling-aperture.js.map +1 -1
  230. package/dist/sensor/spatial-sample-reduction.js +2 -19
  231. package/dist/sensor/spatial-sample-reduction.js.map +1 -1
  232. package/dist/sensor/spatial-sampling-quadrature.js +1 -3
  233. package/dist/sensor/spatial-sampling-quadrature.js.map +1 -1
  234. package/dist/sensor/spatio-spectral-reduction.js +1 -18
  235. package/dist/sensor/spatio-spectral-reduction.js.map +1 -1
  236. package/dist/sensor/spectral-coverage.d.ts +10 -1
  237. package/dist/sensor/spectral-coverage.js +1 -1
  238. package/dist/sensor/spectral-coverage.js.map +1 -1
  239. package/dist/sensor/spectral-quadrature.js +2 -35
  240. package/dist/sensor/spectral-quadrature.js.map +1 -1
  241. package/dist/sensor/spectral-response.js +1 -47
  242. package/dist/sensor/spectral-response.js.map +1 -1
  243. package/dist/stabilization/camera-shake.js +1 -6
  244. package/dist/stabilization/camera-shake.js.map +1 -1
  245. package/dist/stabilization/system.js +2 -80
  246. package/dist/stabilization/system.js.map +1 -1
  247. package/docs/AGENT_GUIDE.md +1 -0
  248. package/docs/API_REFERENCE.md +41 -2
  249. package/docs/QUICK_START.md +90 -8
  250. package/docs/USAGE.md +47 -2
  251. package/docs/api/core-version.md +1 -1
  252. package/docs/api/exports.json +53 -3
  253. package/docs/api/exposure-filtered-exposure-time.md +105 -0
  254. package/docs/api/facade-develop-joined-parts.md +32 -0
  255. package/docs/api/facade-simulate-capture.md +36 -5
  256. package/docs/api/optics-generic-nd-filter-presets.md +34 -0
  257. package/docs/api/optics-lens-psf-profile.md +3 -1
  258. package/docs/api/output-developed-lens-corrections.md +12 -0
  259. package/docs/api/output-generic-lens-corrections.md +0 -9
  260. package/llms.txt +1 -0
  261. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"accumulated-charge.js","sourceRoot":"","sources":["../../src/sensor/accumulated-charge.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Accumulated charge: adds up the expected electrons stored in one sensor site during one exposure.\n *\n * The parts are photo charge (from light), dark charge (from dark current) and any other stored\n * charge declared explicitly as an addition. All parts must belong to the exact same site, colour\n * channel and local shutter event. Whether the list of parts is complete is a declaration backed by\n * evidence; an empty list does not mean \"complete\".\n *\n * The result is the expected (mean) charge. It is separate from a random (noise) realization and from\n * the non-linear full-well clamp, which later steps apply.\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 { parseSensorEqeTemporalPhotoSignal } from \"./temporal-photo-signal.js\";\nimport type { SensorEqeExposureIntegration } from \"./constant-rate-temporal-integration.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport type {\n SensorEqePhotoExposure, SensorEqeTemporalPhotoSignal\n} from \"./temporal-photo-signal.js\";\nimport type {\n SensorPhotoDarkCurrentCharge, SensorDarkCurrentCharge, SensorTemporalDarkCurrentCharge\n} from \"./dark-current.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 SensorAdditionalStoredChargeKind =\n | \"hot-pixel-defect-excess\"\n | \"leakage\"\n | \"charge-injection\"\n | \"clock-induced-charge\"\n | \"other\";\n\nexport interface SensorAdditionalStoredChargeComponent {\n componentId: string;\n kind:\n SensorAdditionalStoredChargeKind;\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n timeReference:\n \"first-opening-boundary-phase\";\n startOffsetSecondsFromOpeningReference:\n number;\n endOffsetSecondsFromOpeningReference:\n number;\n accountingMeaning:\n \"incremental-stored-electrons-beyond-photo-and-modeled-dark-current\";\n expectedElectronCount: number;\n scientificStatus:\n SensorSpectralResponseScientificStatus;\n uncertainty:\n SensorSpectralResponseUncertainty;\n evidence:\n readonly EvidenceProvenance[];\n}\n\nexport interface SensorAccumulatedChargeCompletenessProfile {\n schemaVersion: \"0.1.0\";\n profileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n timeReference:\n \"first-opening-boundary-phase\";\n startOffsetSecondsFromOpeningReference:\n number;\n endOffsetSecondsFromOpeningReference:\n number;\n darkCurrentProfileId: string;\n includedAdditionalComponentIds:\n readonly string[];\n coverageMeaning:\n \"all-material-stored-electron-contributors-accounted-for\";\n scientificStatus:\n SensorSpectralResponseScientificStatus;\n evidence:\n readonly EvidenceProvenance[];\n limitation?: string;\n}\n\nexport interface ComposeSensorAccumulatedChargeInput<\n Photo extends SensorEqePhotoExposure = SensorEqeExposureIntegration,\n Dark extends SensorPhotoDarkCurrentCharge = SensorDarkCurrentCharge\n> {\n photoSignal:\n Photo;\n darkCharge:\n Dark;\n additionalChargeComponents?:\n readonly SensorAdditionalStoredChargeComponent[];\n completenessProfile:\n SensorAccumulatedChargeCompletenessProfile;\n}\n\nexport type ComposeSensorPhotoAccumulatedChargeInput = ComposeSensorAccumulatedChargeInput<SensorEqePhotoExposure, SensorPhotoDarkCurrentCharge>;\n\nexport interface SensorAccumulatedChargeComposition<\n Photo extends SensorEqePhotoExposure = SensorEqeExposureIntegration,\n Dark extends SensorPhotoDarkCurrentCharge = SensorDarkCurrentCharge\n> {\n completenessProfileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n timeReference:\n \"first-opening-boundary-phase\";\n startOffsetSecondsFromOpeningReference:\n number;\n endOffsetSecondsFromOpeningReference:\n number;\n localExposureDurationSeconds:\n number;\n photoExpectedElectronCount:\n number;\n darkExpectedElectronCount:\n number;\n additionalExpectedElectronCount:\n number;\n totalExpectedStoredElectronCount:\n number;\n additionalComponents:\n readonly {\n componentId: string;\n kind:\n SensorAdditionalStoredChargeKind;\n expectedElectronCount: number;\n scientificStatus:\n SensorSpectralResponseScientificStatus;\n uncertainty:\n SensorSpectralResponseUncertainty;\n evidence:\n readonly EvidenceProvenance[];\n }[];\n accumulatedChargeCompleteness:\n \"complete-for-physical-storage-capacity-assessment\";\n completenessScientificStatus:\n SensorSpectralResponseScientificStatus;\n allCountsAreExpectationValues: true;\n integerChargeSampled: false;\n photoSignalIncluded: true;\n darkChargeIncluded: true;\n otherChargeIncluded: boolean;\n darkShotNoiseApplied: false;\n photoShotNoiseApplied: false;\n readNoiseApplied: false;\n physicalFullWellAssessmentAuthorized:\n true;\n cameraSaturationAssessmentAuthorized:\n false;\n saturationAssessed: false;\n bloomingModeled: false;\n adcQuantizationApplied: false;\n rawCodeValueProduced: false;\n componentEvidence: {\n completeness:\n readonly EvidenceProvenance[];\n photoSignal:\n Photo[\"componentEvidence\"];\n darkCurrent:\n Dark[\"componentEvidence\"];\n };\n}\n\nexport type SensorPhotoAccumulatedChargeComposition = SensorAccumulatedChargeComposition<SensorEqePhotoExposure, SensorPhotoDarkCurrentCharge>;\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 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 return {\n kind: \"relative\",\n fraction,\n basis: requireNonEmptyString(\n record.basis,\n path + \".basis\"\n )\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 parseStatus(\n value: unknown,\n path: string\n): SensorSpectralResponseScientificStatus {\n if (\n value !== \"calibrated\" &&\n value !== \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n path + \" is invalid.\"\n );\n }\n return value;\n}\n\nfunction parseWindowIdentity(\n record: UnknownRecord,\n path: string\n): {\n bindingId: string;\n timeReference:\n \"first-opening-boundary-phase\";\n start:\n number;\n end:\n number;\n} {\n if (\n record.timeReference !==\n \"first-opening-boundary-phase\"\n ) {\n throw new InvalidConfigurationError(\n path +\n '.timeReference must be \"first-opening-boundary-phase\".'\n );\n }\n const start =\n requireFinite(\n record.startOffsetSecondsFromOpeningReference,\n path +\n \".startOffsetSecondsFromOpeningReference\"\n );\n const end =\n requireFinite(\n record.endOffsetSecondsFromOpeningReference,\n path +\n \".endOffsetSecondsFromOpeningReference\"\n );\n if (!(end > start)) {\n throw new InvalidConfigurationError(\n path +\n \" exposure window end must be greater than start.\"\n );\n }\n return {\n bindingId:\n requireNonEmptyString(\n record.bindingId,\n path + \".bindingId\"\n ),\n timeReference:\n \"first-opening-boundary-phase\",\n start,\n end\n };\n}\n\n/**\n * Validates an untrusted additional stored-charge component (charge other than photo and dark\n * charge, declared explicitly as an addition) and returns it normalized. Unknown enum values,\n * missing required fields and inconsistent scientific data are rejected.\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 parseSensorAdditionalStoredChargeComponent(\n value: unknown\n): SensorAdditionalStoredChargeComponent {\n const record = requireRecord(\n value,\n \"sensorAdditionalStoredCharge\", [\"kind\", \"accountingMeaning\", \"site\", \"timeReference\", \"startOffsetSecondsFromOpeningReference\", \"endOffsetSecondsFromOpeningReference\", \"bindingId\", \"scientificStatus\", \"uncertainty\", \"componentId\", \"colorSamplingProfileId\", \"channelId\", \"expectedElectronCount\", \"evidence\"]\n );\n\n const kinds =\n new Set<SensorAdditionalStoredChargeKind>([\n \"hot-pixel-defect-excess\",\n \"leakage\",\n \"charge-injection\",\n \"clock-induced-charge\",\n \"other\"\n ]);\n if (\n typeof record.kind !== \"string\" ||\n !kinds.has(\n record.kind as\n SensorAdditionalStoredChargeKind\n )\n ) {\n throw new InvalidConfigurationError(\n \"sensorAdditionalStoredCharge.kind is invalid.\"\n );\n }\n if (\n record.accountingMeaning !==\n \"incremental-stored-electrons-beyond-photo-and-modeled-dark-current\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorAdditionalStoredCharge.accountingMeaning is invalid.\"\n );\n }\n\n const site = requireRecord(\n record.site,\n \"sensorAdditionalStoredCharge.site\", [\"x\", \"y\"]\n );\n const window =\n parseWindowIdentity(\n record,\n \"sensorAdditionalStoredCharge\"\n );\n const scientificStatus =\n parseStatus(\n record.scientificStatus,\n \"sensorAdditionalStoredCharge.scientificStatus\"\n );\n const uncertainty =\n parseUncertainty(\n record.uncertainty,\n \"sensorAdditionalStoredCharge.uncertainty\"\n );\n if (\n scientificStatus === \"calibrated\" &&\n uncertainty.kind ===\n \"not-quantified\"\n ) {\n throw new InvalidConfigurationError(\n \"A calibrated additional stored-charge component must declare quantified relative uncertainty.\"\n );\n }\n\n return {\n componentId:\n requireNonEmptyString(\n record.componentId,\n \"sensorAdditionalStoredCharge.componentId\"\n ),\n kind:\n record.kind as\n SensorAdditionalStoredChargeKind,\n colorSamplingProfileId:\n requireNonEmptyString(\n record.colorSamplingProfileId,\n \"sensorAdditionalStoredCharge.colorSamplingProfileId\"\n ),\n channelId:\n requireNonEmptyString(\n record.channelId,\n \"sensorAdditionalStoredCharge.channelId\"\n ),\n site: {\n x: requireSiteIndex(\n site.x,\n \"sensorAdditionalStoredCharge.site.x\"\n ),\n y: requireSiteIndex(\n site.y,\n \"sensorAdditionalStoredCharge.site.y\"\n )\n },\n bindingId: window.bindingId,\n timeReference:\n window.timeReference,\n startOffsetSecondsFromOpeningReference:\n window.start,\n endOffsetSecondsFromOpeningReference:\n window.end,\n accountingMeaning:\n \"incremental-stored-electrons-beyond-photo-and-modeled-dark-current\",\n expectedElectronCount:\n requireNonNegativeFinite(\n record.expectedElectronCount,\n \"sensorAdditionalStoredCharge.expectedElectronCount\"\n ),\n scientificStatus,\n uncertainty,\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorAdditionalStoredCharge.evidence\"\n )\n };\n}\n\n/**\n * Validates an untrusted completeness profile and returns it normalized. Unknown enum values,\n * missing required fields and inconsistent scientific data are rejected.\n *\n * Completeness is a declaration backed by evidence. An empty list of components does not\n * mean the charge accounting is complete.\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 parseSensorAccumulatedChargeCompletenessProfile(\n value: unknown\n): SensorAccumulatedChargeCompletenessProfile {\n const record = requireRecord(\n value,\n \"sensorAccumulatedChargeCompleteness\", [\"schemaVersion\", \"coverageMeaning\", \"site\", \"timeReference\", \"startOffsetSecondsFromOpeningReference\", \"endOffsetSecondsFromOpeningReference\", \"bindingId\", \"scientificStatus\", \"limitation\", \"includedAdditionalComponentIds\", \"profileId\", \"colorSamplingProfileId\", \"channelId\", \"darkCurrentProfileId\", \"evidence\"]\n );\n if (\n record.schemaVersion !== \"0.1.0\"\n ) {\n throw new InvalidConfigurationError(\n 'sensorAccumulatedChargeCompleteness.schemaVersion must be \"0.1.0\".'\n );\n }\n if (\n record.coverageMeaning !==\n \"all-material-stored-electron-contributors-accounted-for\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorAccumulatedChargeCompleteness.coverageMeaning is invalid.\"\n );\n }\n\n const site = requireRecord(\n record.site,\n \"sensorAccumulatedChargeCompleteness.site\", [\"x\", \"y\"]\n );\n const window =\n parseWindowIdentity(\n record,\n \"sensorAccumulatedChargeCompleteness\"\n );\n const scientificStatus =\n parseStatus(\n record.scientificStatus,\n \"sensorAccumulatedChargeCompleteness.scientificStatus\"\n );\n const limitation =\n record.limitation === undefined\n ? undefined\n : requireNonEmptyString(\n record.limitation,\n \"sensorAccumulatedChargeCompleteness.limitation\"\n );\n if (\n scientificStatus === \"approximation\" &&\n limitation === undefined\n ) {\n throw new InvalidConfigurationError(\n \"sensorAccumulatedChargeCompleteness.limitation is required for an approximation.\"\n );\n }\n if (\n !Array.isArray(\n record.includedAdditionalComponentIds\n )\n ) {\n throw new InvalidConfigurationError(\n \"sensorAccumulatedChargeCompleteness.includedAdditionalComponentIds must be an array.\"\n );\n }\n\n const ids =\n record.includedAdditionalComponentIds.map(\n (entry, index) =>\n requireNonEmptyString(\n entry,\n \"sensorAccumulatedChargeCompleteness.includedAdditionalComponentIds[\" +\n index +\n \"]\"\n )\n );\n if (\n new Set(ids).size !== ids.length\n ) {\n throw new InvalidConfigurationError(\n \"sensorAccumulatedChargeCompleteness.includedAdditionalComponentIds must not contain duplicates.\"\n );\n }\n\n return {\n schemaVersion: \"0.1.0\",\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"sensorAccumulatedChargeCompleteness.profileId\"\n ),\n colorSamplingProfileId:\n requireNonEmptyString(\n record.colorSamplingProfileId,\n \"sensorAccumulatedChargeCompleteness.colorSamplingProfileId\"\n ),\n channelId:\n requireNonEmptyString(\n record.channelId,\n \"sensorAccumulatedChargeCompleteness.channelId\"\n ),\n site: {\n x: requireSiteIndex(\n site.x,\n \"sensorAccumulatedChargeCompleteness.site.x\"\n ),\n y: requireSiteIndex(\n site.y,\n \"sensorAccumulatedChargeCompleteness.site.y\"\n )\n },\n bindingId: window.bindingId,\n timeReference:\n window.timeReference,\n startOffsetSecondsFromOpeningReference:\n window.start,\n endOffsetSecondsFromOpeningReference:\n window.end,\n darkCurrentProfileId:\n requireNonEmptyString(\n record.darkCurrentProfileId,\n \"sensorAccumulatedChargeCompleteness.darkCurrentProfileId\"\n ),\n includedAdditionalComponentIds:\n [...ids].sort(),\n coverageMeaning:\n \"all-material-stored-electron-contributors-accounted-for\",\n scientificStatus,\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorAccumulatedChargeCompleteness.evidence\"\n ),\n ...(limitation === undefined\n ? {}\n : { limitation })\n };\n}\n\nfunction sameSite(\n a: { x: number; y: number },\n b: { x: number; y: number }\n): boolean {\n return a.x === b.x && a.y === b.y;\n}\n\nfunction sameWindow(\n startA: number,\n endA: number,\n startB: number,\n endB: number\n): boolean {\n return (\n startA === startB &&\n endA === endB\n );\n}\n\nfunction validatePhoto(\n photo:\n SensorEqePhotoExposure\n): void {\n requireInputObject(photo, \"photo input\");\n if (photo.kind === \"eqe-temporal-photo-signal\") parseSensorEqeTemporalPhotoSignal(photo);\n if (\n (photo.kind !== \"eqe-expected-counts\" && photo.kind !== \"eqe-temporal-photo-signal\") ||\n photo.temporalIntegrationApplied !==\n true ||\n photo.accumulatedSignalCompleteness !==\n \"photo-signal-only\" ||\n photo.darkChargeIncluded !== false ||\n photo.otherChargeIncluded !== false ||\n photo\n .physicalFullWellAssessmentAuthorized !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"Charge composition requires an unmodified photo-signal-only EQE exposure result.\"\n );\n }\n if (\n !Number.isFinite(\n photo\n .expectedGeneratedElectronCount\n ) ||\n photo\n .expectedGeneratedElectronCount < 0\n ) {\n throw new InvalidScientificInputError(\n \"Photo expected electron count must be finite and nonnegative.\"\n );\n }\n}\n\nfunction validateDarkBinding(\n photo:\n SensorEqePhotoExposure,\n dark:\n SensorPhotoDarkCurrentCharge\n): void {\n if (\n dark.colorSamplingProfileId !==\n photo.colorSamplingProfileId ||\n dark.channelId !==\n photo.channelId ||\n !sameSite(dark.site, photo.site) ||\n dark.bindingId !==\n photo.bindingId ||\n (photo.kind === \"eqe-temporal-photo-signal\"\n ? dark.stationarityProfileId !== undefined || dark.temporalIntegrationId !== photo.temporalIntegrationId\n : dark.temporalIntegrationId !== undefined || dark.stationarityProfileId !== photo.stationarityProfileId) ||\n dark.timeReference !==\n photo.timeReference ||\n !sameWindow(\n dark.startOffsetSecondsFromOpeningReference,\n dark.endOffsetSecondsFromOpeningReference,\n photo.startOffsetSecondsFromOpeningReference,\n photo.endOffsetSecondsFromOpeningReference\n ) ||\n dark.localExposureDurationSeconds !==\n photo.localExposureDurationSeconds ||\n dark.photoSignalIncluded !== false ||\n dark.otherChargeIncluded !== false ||\n dark\n .physicalFullWellAssessmentAuthorized !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"Dark charge must bind to the exact same uncombined EQE local exposure event.\"\n );\n }\n if (\n !Number.isFinite(\n dark.expectedDarkElectronCount\n ) ||\n dark.expectedDarkElectronCount < 0\n ) {\n throw new InvalidScientificInputError(\n \"Dark expected electron count must be finite and nonnegative.\"\n );\n }\n}\n\nfunction validateComponentBinding(\n component:\n SensorAdditionalStoredChargeComponent,\n photo:\n SensorEqePhotoExposure\n): void {\n if (\n component.colorSamplingProfileId !==\n photo.colorSamplingProfileId ||\n component.channelId !==\n photo.channelId ||\n !sameSite(\n component.site,\n photo.site\n ) ||\n component.bindingId !==\n photo.bindingId ||\n component.timeReference !==\n photo.timeReference ||\n !sameWindow(\n component.startOffsetSecondsFromOpeningReference,\n component.endOffsetSecondsFromOpeningReference,\n photo.startOffsetSecondsFromOpeningReference,\n photo.endOffsetSecondsFromOpeningReference\n )\n ) {\n throw new InvalidScientificInputError(\n \"Additional stored-charge component must bind to the exact same local exposure event.\"\n );\n }\n}\n\n/**\n * Adds photo charge, separately computed dark charge and any explicitly declared extra charge for\n * one exact local exposure, and keeps the completeness assessment. Capacity and noise come later.\n *\n * All parts must belong to the exact same site, colour channel and local shutter event. The\n * result is the expected charge, separate from a random realization or the full-well clamp.\n * @param input - Charge input with a per-exposure photo signal (SensorEqeExposureIntegration) and\n * dark charge (SensorDarkCurrentCharge); see `ComposeSensorAccumulatedChargeInput`.\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 composeSensorAccumulatedCharge(\n input: Omit<ComposeSensorAccumulatedChargeInput, \"photoSignal\" | \"darkCharge\"> & {\n photoSignal: SensorEqeExposureIntegration; darkCharge: SensorDarkCurrentCharge;\n }\n): CalculationResult<SensorAccumulatedChargeComposition>;\n/**\n * Adds photo charge, separately computed dark charge and any explicitly declared extra charge for\n * one exact local exposure, and keeps the completeness assessment. Capacity and noise come later.\n *\n * All parts must belong to the exact same site, colour channel and local shutter event. The\n * result is the expected charge, separate from a random realization or the full-well clamp.\n * @param input - Charge input with a time-resolved photo signal (SensorEqeTemporalPhotoSignal) and\n * dark charge (SensorTemporalDarkCurrentCharge); see `ComposeSensorAccumulatedChargeInput`.\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 composeSensorAccumulatedCharge(\n input: Omit<ComposeSensorAccumulatedChargeInput, \"photoSignal\" | \"darkCharge\"> & {\n photoSignal: SensorEqeTemporalPhotoSignal; darkCharge: SensorTemporalDarkCurrentCharge;\n }\n): CalculationResult<SensorAccumulatedChargeComposition<SensorEqeTemporalPhotoSignal, SensorTemporalDarkCurrentCharge>>;\n/**\n * Adds photo charge, separately computed dark charge and any explicitly declared extra charge for\n * one exact local exposure, and keeps the completeness assessment. Capacity and noise come later.\n *\n * All parts must belong to the exact same site, colour channel and local shutter event. The\n * result is the expected charge, separate from a random realization or the full-well clamp.\n * @param input - The input; see `ComposeSensorPhotoAccumulatedChargeInput` 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 composeSensorAccumulatedCharge(\n input: ComposeSensorPhotoAccumulatedChargeInput\n): CalculationResult<SensorPhotoAccumulatedChargeComposition>;\n/**\n * Adds photo charge, separately computed dark charge and any explicitly declared extra charge for\n * one exact local exposure, and keeps the completeness assessment. Capacity and noise come later.\n *\n * All parts must belong to the exact same site, colour channel and local shutter event. The\n * result is the expected charge, separate from a random realization or the full-well clamp.\n * @param input - The input; see `ComposeSensorPhotoAccumulatedChargeInput` 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 composeSensorAccumulatedCharge(\n input: ComposeSensorPhotoAccumulatedChargeInput\n): CalculationResult<SensorPhotoAccumulatedChargeComposition> {\n requireInputKeys(input, \"composeSensorAccumulatedCharge\", [\"photoSignal\", \"darkCharge\", \"additionalChargeComponents\", \"completenessProfile\"]);\n validatePhoto(input.photoSignal);\n validateDarkBinding(\n input.photoSignal,\n input.darkCharge\n );\n const profile =\n parseSensorAccumulatedChargeCompletenessProfile(\n input.completenessProfile\n );\n const components =\n (\n input.additionalChargeComponents ??\n []\n ).map(\n parseSensorAdditionalStoredChargeComponent\n );\n\n if (\n profile.colorSamplingProfileId !==\n input.photoSignal\n .colorSamplingProfileId ||\n profile.channelId !==\n input.photoSignal.channelId ||\n !sameSite(\n profile.site,\n input.photoSignal.site\n ) ||\n profile.bindingId !==\n input.photoSignal.bindingId ||\n profile.timeReference !==\n input.photoSignal.timeReference ||\n !sameWindow(\n profile.startOffsetSecondsFromOpeningReference,\n profile.endOffsetSecondsFromOpeningReference,\n input.photoSignal.startOffsetSecondsFromOpeningReference,\n input.photoSignal.endOffsetSecondsFromOpeningReference\n ) ||\n profile.darkCurrentProfileId !==\n input.darkCharge\n .darkCurrentProfileId\n ) {\n throw new InvalidScientificInputError(\n \"Charge-completeness profile must bind to the exact photo/dark exposure pipeline.\"\n );\n }\n\n const seen =\n new Set<string>();\n for (const component of components) {\n validateComponentBinding(\n component,\n input.photoSignal\n );\n if (\n seen.has(component.componentId)\n ) {\n throw new InvalidScientificInputError(\n \"Additional stored-charge component IDs must be unique.\"\n );\n }\n seen.add(component.componentId);\n }\n\n const actualIds =\n [...seen].sort();\n if (\n JSON.stringify(actualIds) !==\n JSON.stringify(\n profile\n .includedAdditionalComponentIds\n )\n ) {\n throw new InvalidScientificInputError(\n \"Charge-completeness profile additional component IDs must exactly match the supplied components.\"\n );\n }\n\n const sum: CompensatedSum = {\n sum: 0,\n correction: 0\n };\n addCompensated(\n sum,\n input.photoSignal\n .expectedGeneratedElectronCount\n );\n addCompensated(\n sum,\n input.darkCharge\n .expectedDarkElectronCount\n );\n\n let additionalTotal = 0;\n for (const component of components) {\n additionalTotal +=\n component.expectedElectronCount;\n addCompensated(\n sum,\n component.expectedElectronCount\n );\n }\n\n if (\n !Number.isFinite(\n additionalTotal\n ) ||\n !Number.isFinite(sum.sum) ||\n sum.sum < 0\n ) {\n throw new InvalidScientificInputError(\n \"Accumulated stored-electron totals must remain finite and nonnegative.\"\n );\n }\n\n const result:\n SensorPhotoAccumulatedChargeComposition = {\n completenessProfileId:\n profile.profileId,\n colorSamplingProfileId:\n input.photoSignal\n .colorSamplingProfileId,\n channelId:\n input.photoSignal.channelId,\n site: {\n ...input.photoSignal.site\n },\n bindingId:\n input.photoSignal.bindingId,\n timeReference:\n input.photoSignal.timeReference,\n startOffsetSecondsFromOpeningReference:\n input.photoSignal\n .startOffsetSecondsFromOpeningReference,\n endOffsetSecondsFromOpeningReference:\n input.photoSignal\n .endOffsetSecondsFromOpeningReference,\n localExposureDurationSeconds:\n input.photoSignal\n .localExposureDurationSeconds,\n photoExpectedElectronCount:\n input.photoSignal\n .expectedGeneratedElectronCount,\n darkExpectedElectronCount:\n input.darkCharge\n .expectedDarkElectronCount,\n additionalExpectedElectronCount:\n additionalTotal,\n totalExpectedStoredElectronCount:\n sum.sum,\n additionalComponents:\n components.map(\n (component) => ({\n componentId:\n component.componentId,\n kind: component.kind,\n expectedElectronCount:\n component.expectedElectronCount,\n scientificStatus:\n component.scientificStatus,\n uncertainty:\n component.uncertainty,\n evidence:\n component.evidence\n })\n ),\n accumulatedChargeCompleteness:\n \"complete-for-physical-storage-capacity-assessment\",\n completenessScientificStatus:\n profile.scientificStatus,\n allCountsAreExpectationValues:\n true,\n integerChargeSampled: false,\n photoSignalIncluded: true,\n darkChargeIncluded: true,\n otherChargeIncluded:\n components.length > 0,\n darkShotNoiseApplied: false,\n photoShotNoiseApplied: false,\n readNoiseApplied: false,\n physicalFullWellAssessmentAuthorized:\n true,\n cameraSaturationAssessmentAuthorized:\n false,\n saturationAssessed: false,\n bloomingModeled: false,\n adcQuantizationApplied:\n false,\n rawCodeValueProduced: false,\n componentEvidence: {\n completeness:\n profile.evidence,\n photoSignal:\n input.photoSignal\n .componentEvidence,\n darkCurrent:\n input.darkCharge\n .componentEvidence\n }\n };\n\n const assumptions = [\n \"Stored-electron accounting is bound to one exact EQE local exposure event; equal duration alone is insufficient.\",\n \"Photo-signal expected electrons and thermally generated dark electrons are separate terms.\",\n \"Every additional component must declare itself incremental beyond both modeled photo signal and modeled dark current to avoid silent double counting.\",\n \"The completeness profile is an evidence-backed scientific assertion that all material stored-electron contributors for physical charge-capacity assessment are accounted for; it is not inferred from an empty additional-component list.\",\n \"All values remain expectation counts. Photo/dark shot noise, read noise and stochastic integer sampling are not applied.\",\n \"Physical full-well assessment becomes structurally authorized, but no saturation or blooming behavior is calculated by this composition.\",\n \"Camera/digital saturation remains a separate response-chain property and is not authorized by physical charge completeness.\",\n \"A/W current/charge results remain outside this stored-electron composition until a separate carrier/storage mapping exists.\"\n ];\n\n return profile.scientificStatus ===\n \"calibrated\"\n ? calculatedResult(\n result,\n \"sensor-accumulated-charge-composition\",\n input.photoSignal.kind === \"eqe-temporal-photo-signal\" ? \"1.1.0\" : \"1.0.0\",\n assumptions\n )\n : approximationResult(\n result,\n \"sensor-accumulated-charge-composition\",\n input.photoSignal.kind === \"eqe-temporal-photo-signal\" ? \"1.1.0\" : \"1.0.0\",\n [\n ...assumptions,\n profile.limitation!\n ]\n );\n}\n"]}
1
+ {"version":3,"file":"accumulated-charge.js","sourceRoot":"","sources":["../../src/sensor/accumulated-charge.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Accumulated charge: adds up the expected electrons stored in one sensor site during one exposure.\n *\n * The parts are photo charge (from light), dark charge (from dark current) and any other stored\n * charge declared explicitly as an addition. All parts must belong to the exact same site, colour\n * channel and local shutter event. Whether the list of parts is complete is a declaration backed by\n * evidence; an empty list does not mean \"complete\".\n *\n * The result is the expected (mean) charge. It is separate from a random (noise) realization and from\n * the non-linear full-well clamp, which later steps apply.\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 { parseSensorEqeTemporalPhotoSignal } from \"./temporal-photo-signal.js\";\nimport type { SensorEqeExposureIntegration } from \"./constant-rate-temporal-integration.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport type {\n SensorEqePhotoExposure, SensorEqeTemporalPhotoSignal\n} from \"./temporal-photo-signal.js\";\nimport type {\n SensorPhotoDarkCurrentCharge, SensorDarkCurrentCharge, SensorTemporalDarkCurrentCharge\n} from \"./dark-current.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, parseConfigScientificStatus as parseStatus, requireConfigNonNegativeSafeInteger as requireSiteIndex, requireConfigFiniteAtLeastZero as requireNonNegativeFinite } from \"../core/record-validation.js\";\nimport { addCompensated, sameSite } from \"../core/shared-helpers.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport type SensorAdditionalStoredChargeKind =\n | \"hot-pixel-defect-excess\"\n | \"leakage\"\n | \"charge-injection\"\n | \"clock-induced-charge\"\n | \"other\";\n\nexport interface SensorAdditionalStoredChargeComponent {\n componentId: string;\n kind:\n SensorAdditionalStoredChargeKind;\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n timeReference:\n \"first-opening-boundary-phase\";\n startOffsetSecondsFromOpeningReference:\n number;\n endOffsetSecondsFromOpeningReference:\n number;\n accountingMeaning:\n \"incremental-stored-electrons-beyond-photo-and-modeled-dark-current\";\n expectedElectronCount: number;\n scientificStatus:\n SensorSpectralResponseScientificStatus;\n uncertainty:\n SensorSpectralResponseUncertainty;\n evidence:\n readonly EvidenceProvenance[];\n}\n\nexport interface SensorAccumulatedChargeCompletenessProfile {\n schemaVersion: \"0.1.0\";\n profileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n timeReference:\n \"first-opening-boundary-phase\";\n startOffsetSecondsFromOpeningReference:\n number;\n endOffsetSecondsFromOpeningReference:\n number;\n darkCurrentProfileId: string;\n includedAdditionalComponentIds:\n readonly string[];\n coverageMeaning:\n \"all-material-stored-electron-contributors-accounted-for\";\n scientificStatus:\n SensorSpectralResponseScientificStatus;\n evidence:\n readonly EvidenceProvenance[];\n limitation?: string;\n}\n\nexport interface ComposeSensorAccumulatedChargeInput<\n Photo extends SensorEqePhotoExposure = SensorEqeExposureIntegration,\n Dark extends SensorPhotoDarkCurrentCharge = SensorDarkCurrentCharge\n> {\n photoSignal:\n Photo;\n darkCharge:\n Dark;\n additionalChargeComponents?:\n readonly SensorAdditionalStoredChargeComponent[];\n completenessProfile:\n SensorAccumulatedChargeCompletenessProfile;\n}\n\nexport type ComposeSensorPhotoAccumulatedChargeInput = ComposeSensorAccumulatedChargeInput<SensorEqePhotoExposure, SensorPhotoDarkCurrentCharge>;\n\nexport interface SensorAccumulatedChargeComposition<\n Photo extends SensorEqePhotoExposure = SensorEqeExposureIntegration,\n Dark extends SensorPhotoDarkCurrentCharge = SensorDarkCurrentCharge\n> {\n completenessProfileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n timeReference:\n \"first-opening-boundary-phase\";\n startOffsetSecondsFromOpeningReference:\n number;\n endOffsetSecondsFromOpeningReference:\n number;\n localExposureDurationSeconds:\n number;\n photoExpectedElectronCount:\n number;\n darkExpectedElectronCount:\n number;\n additionalExpectedElectronCount:\n number;\n totalExpectedStoredElectronCount:\n number;\n additionalComponents:\n readonly {\n componentId: string;\n kind:\n SensorAdditionalStoredChargeKind;\n expectedElectronCount: number;\n scientificStatus:\n SensorSpectralResponseScientificStatus;\n uncertainty:\n SensorSpectralResponseUncertainty;\n evidence:\n readonly EvidenceProvenance[];\n }[];\n accumulatedChargeCompleteness:\n \"complete-for-physical-storage-capacity-assessment\";\n completenessScientificStatus:\n SensorSpectralResponseScientificStatus;\n allCountsAreExpectationValues: true;\n integerChargeSampled: false;\n photoSignalIncluded: true;\n darkChargeIncluded: true;\n otherChargeIncluded: boolean;\n darkShotNoiseApplied: false;\n photoShotNoiseApplied: false;\n readNoiseApplied: false;\n physicalFullWellAssessmentAuthorized:\n true;\n cameraSaturationAssessmentAuthorized:\n false;\n saturationAssessed: false;\n bloomingModeled: false;\n adcQuantizationApplied: false;\n rawCodeValueProduced: false;\n componentEvidence: {\n completeness:\n readonly EvidenceProvenance[];\n photoSignal:\n Photo[\"componentEvidence\"];\n darkCurrent:\n Dark[\"componentEvidence\"];\n };\n}\n\nexport type SensorPhotoAccumulatedChargeComposition = SensorAccumulatedChargeComposition<SensorEqePhotoExposure, SensorPhotoDarkCurrentCharge>;\n\ninterface CompensatedSum {\n sum: number;\n correction: number;\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 return {\n kind: \"relative\",\n fraction,\n basis: requireNonEmptyString(\n record.basis,\n path + \".basis\"\n )\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 parseWindowIdentity(\n record: UnknownRecord,\n path: string\n): {\n bindingId: string;\n timeReference:\n \"first-opening-boundary-phase\";\n start:\n number;\n end:\n number;\n} {\n if (\n record.timeReference !==\n \"first-opening-boundary-phase\"\n ) {\n throw new InvalidConfigurationError(\n path +\n '.timeReference must be \"first-opening-boundary-phase\".'\n );\n }\n const start =\n requireFinite(\n record.startOffsetSecondsFromOpeningReference,\n path +\n \".startOffsetSecondsFromOpeningReference\"\n );\n const end =\n requireFinite(\n record.endOffsetSecondsFromOpeningReference,\n path +\n \".endOffsetSecondsFromOpeningReference\"\n );\n if (!(end > start)) {\n throw new InvalidConfigurationError(\n path +\n \" exposure window end must be greater than start.\"\n );\n }\n return {\n bindingId:\n requireNonEmptyString(\n record.bindingId,\n path + \".bindingId\"\n ),\n timeReference:\n \"first-opening-boundary-phase\",\n start,\n end\n };\n}\n\n/**\n * Validates an untrusted additional stored-charge component (charge other than photo and dark\n * charge, declared explicitly as an addition) and returns it normalized. Unknown enum values,\n * missing required fields and inconsistent scientific data are rejected.\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 parseSensorAdditionalStoredChargeComponent(\n value: unknown\n): SensorAdditionalStoredChargeComponent {\n const record = requireRecord(\n value,\n \"sensorAdditionalStoredCharge\", [\"kind\", \"accountingMeaning\", \"site\", \"timeReference\", \"startOffsetSecondsFromOpeningReference\", \"endOffsetSecondsFromOpeningReference\", \"bindingId\", \"scientificStatus\", \"uncertainty\", \"componentId\", \"colorSamplingProfileId\", \"channelId\", \"expectedElectronCount\", \"evidence\"]\n );\n\n const kinds =\n new Set<SensorAdditionalStoredChargeKind>([\n \"hot-pixel-defect-excess\",\n \"leakage\",\n \"charge-injection\",\n \"clock-induced-charge\",\n \"other\"\n ]);\n if (\n typeof record.kind !== \"string\" ||\n !kinds.has(\n record.kind as\n SensorAdditionalStoredChargeKind\n )\n ) {\n throw new InvalidConfigurationError(\n \"sensorAdditionalStoredCharge.kind is invalid.\"\n );\n }\n if (\n record.accountingMeaning !==\n \"incremental-stored-electrons-beyond-photo-and-modeled-dark-current\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorAdditionalStoredCharge.accountingMeaning is invalid.\"\n );\n }\n\n const site = requireRecord(\n record.site,\n \"sensorAdditionalStoredCharge.site\", [\"x\", \"y\"]\n );\n const window =\n parseWindowIdentity(\n record,\n \"sensorAdditionalStoredCharge\"\n );\n const scientificStatus =\n parseStatus(\n record.scientificStatus,\n \"sensorAdditionalStoredCharge.scientificStatus\"\n );\n const uncertainty =\n parseUncertainty(\n record.uncertainty,\n \"sensorAdditionalStoredCharge.uncertainty\"\n );\n if (\n scientificStatus === \"calibrated\" &&\n uncertainty.kind ===\n \"not-quantified\"\n ) {\n throw new InvalidConfigurationError(\n \"A calibrated additional stored-charge component must declare quantified relative uncertainty.\"\n );\n }\n\n return {\n componentId:\n requireNonEmptyString(\n record.componentId,\n \"sensorAdditionalStoredCharge.componentId\"\n ),\n kind:\n record.kind as\n SensorAdditionalStoredChargeKind,\n colorSamplingProfileId:\n requireNonEmptyString(\n record.colorSamplingProfileId,\n \"sensorAdditionalStoredCharge.colorSamplingProfileId\"\n ),\n channelId:\n requireNonEmptyString(\n record.channelId,\n \"sensorAdditionalStoredCharge.channelId\"\n ),\n site: {\n x: requireSiteIndex(\n site.x,\n \"sensorAdditionalStoredCharge.site.x\"\n ),\n y: requireSiteIndex(\n site.y,\n \"sensorAdditionalStoredCharge.site.y\"\n )\n },\n bindingId: window.bindingId,\n timeReference:\n window.timeReference,\n startOffsetSecondsFromOpeningReference:\n window.start,\n endOffsetSecondsFromOpeningReference:\n window.end,\n accountingMeaning:\n \"incremental-stored-electrons-beyond-photo-and-modeled-dark-current\",\n expectedElectronCount:\n requireNonNegativeFinite(\n record.expectedElectronCount,\n \"sensorAdditionalStoredCharge.expectedElectronCount\"\n ),\n scientificStatus,\n uncertainty,\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorAdditionalStoredCharge.evidence\"\n )\n };\n}\n\n/**\n * Validates an untrusted completeness profile and returns it normalized. Unknown enum values,\n * missing required fields and inconsistent scientific data are rejected.\n *\n * Completeness is a declaration backed by evidence. An empty list of components does not\n * mean the charge accounting is complete.\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 parseSensorAccumulatedChargeCompletenessProfile(\n value: unknown\n): SensorAccumulatedChargeCompletenessProfile {\n const record = requireRecord(\n value,\n \"sensorAccumulatedChargeCompleteness\", [\"schemaVersion\", \"coverageMeaning\", \"site\", \"timeReference\", \"startOffsetSecondsFromOpeningReference\", \"endOffsetSecondsFromOpeningReference\", \"bindingId\", \"scientificStatus\", \"limitation\", \"includedAdditionalComponentIds\", \"profileId\", \"colorSamplingProfileId\", \"channelId\", \"darkCurrentProfileId\", \"evidence\"]\n );\n if (\n record.schemaVersion !== \"0.1.0\"\n ) {\n throw new InvalidConfigurationError(\n 'sensorAccumulatedChargeCompleteness.schemaVersion must be \"0.1.0\".'\n );\n }\n if (\n record.coverageMeaning !==\n \"all-material-stored-electron-contributors-accounted-for\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorAccumulatedChargeCompleteness.coverageMeaning is invalid.\"\n );\n }\n\n const site = requireRecord(\n record.site,\n \"sensorAccumulatedChargeCompleteness.site\", [\"x\", \"y\"]\n );\n const window =\n parseWindowIdentity(\n record,\n \"sensorAccumulatedChargeCompleteness\"\n );\n const scientificStatus =\n parseStatus(\n record.scientificStatus,\n \"sensorAccumulatedChargeCompleteness.scientificStatus\"\n );\n const limitation =\n record.limitation === undefined\n ? undefined\n : requireNonEmptyString(\n record.limitation,\n \"sensorAccumulatedChargeCompleteness.limitation\"\n );\n if (\n scientificStatus === \"approximation\" &&\n limitation === undefined\n ) {\n throw new InvalidConfigurationError(\n \"sensorAccumulatedChargeCompleteness.limitation is required for an approximation.\"\n );\n }\n if (\n !Array.isArray(\n record.includedAdditionalComponentIds\n )\n ) {\n throw new InvalidConfigurationError(\n \"sensorAccumulatedChargeCompleteness.includedAdditionalComponentIds must be an array.\"\n );\n }\n\n const ids =\n record.includedAdditionalComponentIds.map(\n (entry, index) =>\n requireNonEmptyString(\n entry,\n \"sensorAccumulatedChargeCompleteness.includedAdditionalComponentIds[\" +\n index +\n \"]\"\n )\n );\n if (\n new Set(ids).size !== ids.length\n ) {\n throw new InvalidConfigurationError(\n \"sensorAccumulatedChargeCompleteness.includedAdditionalComponentIds must not contain duplicates.\"\n );\n }\n\n return {\n schemaVersion: \"0.1.0\",\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"sensorAccumulatedChargeCompleteness.profileId\"\n ),\n colorSamplingProfileId:\n requireNonEmptyString(\n record.colorSamplingProfileId,\n \"sensorAccumulatedChargeCompleteness.colorSamplingProfileId\"\n ),\n channelId:\n requireNonEmptyString(\n record.channelId,\n \"sensorAccumulatedChargeCompleteness.channelId\"\n ),\n site: {\n x: requireSiteIndex(\n site.x,\n \"sensorAccumulatedChargeCompleteness.site.x\"\n ),\n y: requireSiteIndex(\n site.y,\n \"sensorAccumulatedChargeCompleteness.site.y\"\n )\n },\n bindingId: window.bindingId,\n timeReference:\n window.timeReference,\n startOffsetSecondsFromOpeningReference:\n window.start,\n endOffsetSecondsFromOpeningReference:\n window.end,\n darkCurrentProfileId:\n requireNonEmptyString(\n record.darkCurrentProfileId,\n \"sensorAccumulatedChargeCompleteness.darkCurrentProfileId\"\n ),\n includedAdditionalComponentIds:\n [...ids].sort(),\n coverageMeaning:\n \"all-material-stored-electron-contributors-accounted-for\",\n scientificStatus,\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorAccumulatedChargeCompleteness.evidence\"\n ),\n ...(limitation === undefined\n ? {}\n : { limitation })\n };\n}\n\nfunction sameWindow(\n startA: number,\n endA: number,\n startB: number,\n endB: number\n): boolean {\n return (\n startA === startB &&\n endA === endB\n );\n}\n\nfunction validatePhoto(\n photo:\n SensorEqePhotoExposure\n): void {\n requireInputObject(photo, \"photo input\");\n if (photo.kind === \"eqe-temporal-photo-signal\") parseSensorEqeTemporalPhotoSignal(photo);\n if (\n (photo.kind !== \"eqe-expected-counts\" && photo.kind !== \"eqe-temporal-photo-signal\") ||\n photo.temporalIntegrationApplied !==\n true ||\n photo.accumulatedSignalCompleteness !==\n \"photo-signal-only\" ||\n photo.darkChargeIncluded !== false ||\n photo.otherChargeIncluded !== false ||\n photo\n .physicalFullWellAssessmentAuthorized !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"Charge composition requires an unmodified photo-signal-only EQE exposure result.\"\n );\n }\n if (\n !Number.isFinite(\n photo\n .expectedGeneratedElectronCount\n ) ||\n photo\n .expectedGeneratedElectronCount < 0\n ) {\n throw new InvalidScientificInputError(\n \"Photo expected electron count must be finite and nonnegative.\"\n );\n }\n}\n\nfunction validateDarkBinding(\n photo:\n SensorEqePhotoExposure,\n dark:\n SensorPhotoDarkCurrentCharge\n): void {\n if (\n dark.colorSamplingProfileId !==\n photo.colorSamplingProfileId ||\n dark.channelId !==\n photo.channelId ||\n !sameSite(dark.site, photo.site) ||\n dark.bindingId !==\n photo.bindingId ||\n (photo.kind === \"eqe-temporal-photo-signal\"\n ? dark.stationarityProfileId !== undefined || dark.temporalIntegrationId !== photo.temporalIntegrationId\n : dark.temporalIntegrationId !== undefined || dark.stationarityProfileId !== photo.stationarityProfileId) ||\n dark.timeReference !==\n photo.timeReference ||\n !sameWindow(\n dark.startOffsetSecondsFromOpeningReference,\n dark.endOffsetSecondsFromOpeningReference,\n photo.startOffsetSecondsFromOpeningReference,\n photo.endOffsetSecondsFromOpeningReference\n ) ||\n dark.localExposureDurationSeconds !==\n photo.localExposureDurationSeconds ||\n dark.photoSignalIncluded !== false ||\n dark.otherChargeIncluded !== false ||\n dark\n .physicalFullWellAssessmentAuthorized !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"Dark charge must bind to the exact same uncombined EQE local exposure event.\"\n );\n }\n if (\n !Number.isFinite(\n dark.expectedDarkElectronCount\n ) ||\n dark.expectedDarkElectronCount < 0\n ) {\n throw new InvalidScientificInputError(\n \"Dark expected electron count must be finite and nonnegative.\"\n );\n }\n}\n\nfunction validateComponentBinding(\n component:\n SensorAdditionalStoredChargeComponent,\n photo:\n SensorEqePhotoExposure\n): void {\n if (\n component.colorSamplingProfileId !==\n photo.colorSamplingProfileId ||\n component.channelId !==\n photo.channelId ||\n !sameSite(\n component.site,\n photo.site\n ) ||\n component.bindingId !==\n photo.bindingId ||\n component.timeReference !==\n photo.timeReference ||\n !sameWindow(\n component.startOffsetSecondsFromOpeningReference,\n component.endOffsetSecondsFromOpeningReference,\n photo.startOffsetSecondsFromOpeningReference,\n photo.endOffsetSecondsFromOpeningReference\n )\n ) {\n throw new InvalidScientificInputError(\n \"Additional stored-charge component must bind to the exact same local exposure event.\"\n );\n }\n}\n\n/**\n * Adds photo charge, separately computed dark charge and any explicitly declared extra charge for\n * one exact local exposure, and keeps the completeness assessment. Capacity and noise come later.\n *\n * All parts must belong to the exact same site, colour channel and local shutter event. The\n * result is the expected charge, separate from a random realization or the full-well clamp.\n * @param input - Charge input with a per-exposure photo signal (SensorEqeExposureIntegration) and\n * dark charge (SensorDarkCurrentCharge); see `ComposeSensorAccumulatedChargeInput`.\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 composeSensorAccumulatedCharge(\n input: Omit<ComposeSensorAccumulatedChargeInput, \"photoSignal\" | \"darkCharge\"> & {\n photoSignal: SensorEqeExposureIntegration; darkCharge: SensorDarkCurrentCharge;\n }\n): CalculationResult<SensorAccumulatedChargeComposition>;\n/**\n * Adds photo charge, separately computed dark charge and any explicitly declared extra charge for\n * one exact local exposure, and keeps the completeness assessment. Capacity and noise come later.\n *\n * All parts must belong to the exact same site, colour channel and local shutter event. The\n * result is the expected charge, separate from a random realization or the full-well clamp.\n * @param input - Charge input with a time-resolved photo signal (SensorEqeTemporalPhotoSignal) and\n * dark charge (SensorTemporalDarkCurrentCharge); see `ComposeSensorAccumulatedChargeInput`.\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 composeSensorAccumulatedCharge(\n input: Omit<ComposeSensorAccumulatedChargeInput, \"photoSignal\" | \"darkCharge\"> & {\n photoSignal: SensorEqeTemporalPhotoSignal; darkCharge: SensorTemporalDarkCurrentCharge;\n }\n): CalculationResult<SensorAccumulatedChargeComposition<SensorEqeTemporalPhotoSignal, SensorTemporalDarkCurrentCharge>>;\n/**\n * Adds photo charge, separately computed dark charge and any explicitly declared extra charge for\n * one exact local exposure, and keeps the completeness assessment. Capacity and noise come later.\n *\n * All parts must belong to the exact same site, colour channel and local shutter event. The\n * result is the expected charge, separate from a random realization or the full-well clamp.\n * @param input - The input; see `ComposeSensorPhotoAccumulatedChargeInput` 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 composeSensorAccumulatedCharge(\n input: ComposeSensorPhotoAccumulatedChargeInput\n): CalculationResult<SensorPhotoAccumulatedChargeComposition>;\n/**\n * Adds photo charge, separately computed dark charge and any explicitly declared extra charge for\n * one exact local exposure, and keeps the completeness assessment. Capacity and noise come later.\n *\n * All parts must belong to the exact same site, colour channel and local shutter event. The\n * result is the expected charge, separate from a random realization or the full-well clamp.\n * @param input - The input; see `ComposeSensorPhotoAccumulatedChargeInput` 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 composeSensorAccumulatedCharge(\n input: ComposeSensorPhotoAccumulatedChargeInput\n): CalculationResult<SensorPhotoAccumulatedChargeComposition> {\n requireInputKeys(input, \"composeSensorAccumulatedCharge\", [\"photoSignal\", \"darkCharge\", \"additionalChargeComponents\", \"completenessProfile\"]);\n validatePhoto(input.photoSignal);\n validateDarkBinding(\n input.photoSignal,\n input.darkCharge\n );\n const profile =\n parseSensorAccumulatedChargeCompletenessProfile(\n input.completenessProfile\n );\n const components =\n (\n input.additionalChargeComponents ??\n []\n ).map(\n parseSensorAdditionalStoredChargeComponent\n );\n\n if (\n profile.colorSamplingProfileId !==\n input.photoSignal\n .colorSamplingProfileId ||\n profile.channelId !==\n input.photoSignal.channelId ||\n !sameSite(\n profile.site,\n input.photoSignal.site\n ) ||\n profile.bindingId !==\n input.photoSignal.bindingId ||\n profile.timeReference !==\n input.photoSignal.timeReference ||\n !sameWindow(\n profile.startOffsetSecondsFromOpeningReference,\n profile.endOffsetSecondsFromOpeningReference,\n input.photoSignal.startOffsetSecondsFromOpeningReference,\n input.photoSignal.endOffsetSecondsFromOpeningReference\n ) ||\n profile.darkCurrentProfileId !==\n input.darkCharge\n .darkCurrentProfileId\n ) {\n throw new InvalidScientificInputError(\n \"Charge-completeness profile must bind to the exact photo/dark exposure pipeline.\"\n );\n }\n\n const seen =\n new Set<string>();\n for (const component of components) {\n validateComponentBinding(\n component,\n input.photoSignal\n );\n if (\n seen.has(component.componentId)\n ) {\n throw new InvalidScientificInputError(\n \"Additional stored-charge component IDs must be unique.\"\n );\n }\n seen.add(component.componentId);\n }\n\n const actualIds =\n [...seen].sort();\n if (\n JSON.stringify(actualIds) !==\n JSON.stringify(\n profile\n .includedAdditionalComponentIds\n )\n ) {\n throw new InvalidScientificInputError(\n \"Charge-completeness profile additional component IDs must exactly match the supplied components.\"\n );\n }\n\n const sum: CompensatedSum = {\n sum: 0,\n correction: 0\n };\n addCompensated(\n sum,\n input.photoSignal\n .expectedGeneratedElectronCount\n );\n addCompensated(\n sum,\n input.darkCharge\n .expectedDarkElectronCount\n );\n\n let additionalTotal = 0;\n for (const component of components) {\n additionalTotal +=\n component.expectedElectronCount;\n addCompensated(\n sum,\n component.expectedElectronCount\n );\n }\n\n if (\n !Number.isFinite(\n additionalTotal\n ) ||\n !Number.isFinite(sum.sum) ||\n sum.sum < 0\n ) {\n throw new InvalidScientificInputError(\n \"Accumulated stored-electron totals must remain finite and nonnegative.\"\n );\n }\n\n const result:\n SensorPhotoAccumulatedChargeComposition = {\n completenessProfileId:\n profile.profileId,\n colorSamplingProfileId:\n input.photoSignal\n .colorSamplingProfileId,\n channelId:\n input.photoSignal.channelId,\n site: {\n ...input.photoSignal.site\n },\n bindingId:\n input.photoSignal.bindingId,\n timeReference:\n input.photoSignal.timeReference,\n startOffsetSecondsFromOpeningReference:\n input.photoSignal\n .startOffsetSecondsFromOpeningReference,\n endOffsetSecondsFromOpeningReference:\n input.photoSignal\n .endOffsetSecondsFromOpeningReference,\n localExposureDurationSeconds:\n input.photoSignal\n .localExposureDurationSeconds,\n photoExpectedElectronCount:\n input.photoSignal\n .expectedGeneratedElectronCount,\n darkExpectedElectronCount:\n input.darkCharge\n .expectedDarkElectronCount,\n additionalExpectedElectronCount:\n additionalTotal,\n totalExpectedStoredElectronCount:\n sum.sum,\n additionalComponents:\n components.map(\n (component) => ({\n componentId:\n component.componentId,\n kind: component.kind,\n expectedElectronCount:\n component.expectedElectronCount,\n scientificStatus:\n component.scientificStatus,\n uncertainty:\n component.uncertainty,\n evidence:\n component.evidence\n })\n ),\n accumulatedChargeCompleteness:\n \"complete-for-physical-storage-capacity-assessment\",\n completenessScientificStatus:\n profile.scientificStatus,\n allCountsAreExpectationValues:\n true,\n integerChargeSampled: false,\n photoSignalIncluded: true,\n darkChargeIncluded: true,\n otherChargeIncluded:\n components.length > 0,\n darkShotNoiseApplied: false,\n photoShotNoiseApplied: false,\n readNoiseApplied: false,\n physicalFullWellAssessmentAuthorized:\n true,\n cameraSaturationAssessmentAuthorized:\n false,\n saturationAssessed: false,\n bloomingModeled: false,\n adcQuantizationApplied:\n false,\n rawCodeValueProduced: false,\n componentEvidence: {\n completeness:\n profile.evidence,\n photoSignal:\n input.photoSignal\n .componentEvidence,\n darkCurrent:\n input.darkCharge\n .componentEvidence\n }\n };\n\n const assumptions = [\n \"Stored-electron accounting is bound to one exact EQE local exposure event; equal duration alone is insufficient.\",\n \"Photo-signal expected electrons and thermally generated dark electrons are separate terms.\",\n \"Every additional component must declare itself incremental beyond both modeled photo signal and modeled dark current to avoid silent double counting.\",\n \"The completeness profile is an evidence-backed scientific assertion that all material stored-electron contributors for physical charge-capacity assessment are accounted for; it is not inferred from an empty additional-component list.\",\n \"All values remain expectation counts. Photo/dark shot noise, read noise and stochastic integer sampling are not applied.\",\n \"Physical full-well assessment becomes structurally authorized, but no saturation or blooming behavior is calculated by this composition.\",\n \"Camera/digital saturation remains a separate response-chain property and is not authorized by physical charge completeness.\",\n \"A/W current/charge results remain outside this stored-electron composition until a separate carrier/storage mapping exists.\"\n ];\n\n return profile.scientificStatus ===\n \"calibrated\"\n ? calculatedResult(\n result,\n \"sensor-accumulated-charge-composition\",\n input.photoSignal.kind === \"eqe-temporal-photo-signal\" ? \"1.1.0\" : \"1.0.0\",\n assumptions\n )\n : approximationResult(\n result,\n \"sensor-accumulated-charge-composition\",\n input.photoSignal.kind === \"eqe-temporal-photo-signal\" ? \"1.1.0\" : \"1.0.0\",\n [\n ...assumptions,\n profile.limitation!\n ]\n );\n}\n"]}
@@ -9,8 +9,8 @@
9
9
  * limits.
10
10
  */
11
11
  import { parseEvidenceList } from "../core/evidence-provenance.js";
12
- import { rejectUnknownFields } from "../core/record-validation.js";
13
12
  import { InvalidConfigurationError } from "../core/configuration-error.js";
13
+ import { requireConfigRecord as requireRecord, requireConfigRecordString as requireNonEmptyString } from "../core/record-validation.js";
14
14
  export const SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION = "0.3.0";
15
15
  const LEGACY_SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION = "0.2.0";
16
16
  const TECHNOLOGY_FAMILY_VALUES = new Set([
@@ -37,21 +37,6 @@ const COLOR_SAMPLING_VALUES = new Set([
37
37
  "custom-rgb-mosaic",
38
38
  "layered-color"
39
39
  ]);
40
- function requireRecord(value, path, allowedKeys) {
41
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
42
- throw new InvalidConfigurationError(`${path} must be an object.`);
43
- }
44
- if (allowedKeys !== null)
45
- rejectUnknownFields(value, allowedKeys, path);
46
- return value;
47
- }
48
- function requireNonEmptyString(record, key, path) {
49
- const value = record[key];
50
- if (typeof value !== "string" || value.trim().length === 0) {
51
- throw new InvalidConfigurationError(`${path}.${key} must be a non-empty string.`);
52
- }
53
- return value;
54
- }
55
40
  function parseScalarFact(value, path, allowed) {
56
41
  const record = requireRecord(value, path, ["value", "evidence"]);
57
42
  const factValue = requireNonEmptyString(record, "value", path);
@@ -1 +1 @@
1
- {"version":3,"file":"architecture.js","sourceRoot":"","sources":["../../src/sensor/architecture.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Sensor architecture: descriptive metadata about a sensor's hardware (such as CMOS or CCD, and its\n * readout capabilities), each fact backed by evidence.\n *\n * This metadata describes; it does not switch on any physics. The parser checks structure, allowed\n * values and per-field evidence. It does not check that a cited real-world claim is true.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport {\n parseEvidenceList,\n type EvidenceBackedFact,\n type EvidenceProvenance,\n type EvidenceReuseStatus,\n type EvidenceSourceOrigin\n} from \"../core/evidence-provenance.js\";\nimport { rejectUnknownFields } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport const SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION = \"0.3.0\" as const;\nconst LEGACY_SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION = \"0.2.0\" as const;\n\nexport type SensorTechnologyFamily = \"cmos\" | \"ccd\";\n\nexport type SensorIlluminationArchitecture = \"fsi\" | \"bsi\";\n\nexport type SensorIntegrationArchitecture =\n | \"monolithic\"\n | \"partially-stacked\"\n | \"stacked\";\n\nexport type SensorReadoutArchitecture = \"rolling\" | \"global\";\n\nexport type SensorColorSamplingFamily =\n | \"bayer\"\n | \"quad-bayer\"\n | \"monochrome\"\n | \"custom-rgb-mosaic\"\n | \"layered-color\";\n\nexport type SensorArchitectureSourceKind = EvidenceSourceOrigin;\nexport type SensorArchitectureReuseStatus = EvidenceReuseStatus;\nexport type SensorArchitectureFactProvenance = EvidenceProvenance;\nexport type SourcedSensorArchitectureFact<T> = EvidenceBackedFact<T>;\n\ninterface SensorArchitectureProfileBase {\n illumination?: SourcedSensorArchitectureFact<SensorIlluminationArchitecture>;\n integration?: SourcedSensorArchitectureFact<SensorIntegrationArchitecture>;\n /**\n * Readout modes the hardware supports, not the mode chosen for one capture.\n * Each capability has its own evidence, because different readout modes may\n * be documented by different sources.\n */\n readoutCapabilities?: readonly SourcedSensorArchitectureFact<SensorReadoutArchitecture>[];\n colorSamplingFamily?: SourcedSensorArchitectureFact<SensorColorSamplingFamily>;\n}\n\n/**\n * Legacy sensor-architecture schema.\n *\n * Schema 0.2.0 is still accepted and kept exactly as parsed. It has no\n * technologyFamily field; leaving it out means \"unknown, not stated\".\n */\nexport interface SensorArchitectureProfileV0_2\n extends SensorArchitectureProfileBase {\n schemaVersion: typeof LEGACY_SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION;\n technologyFamily?: never;\n}\n\n/**\n * Current descriptive metadata about the sensor hardware and its capabilities.\n *\n * An omitted property means \"unknown, not stated\". These fields do not switch\n * on scientific effects. No architecture value changes noise, dynamic range,\n * field of view, crop factor, exposure, readout timing or sampling unless a\n * separate downstream model explicitly uses that fact.\n */\nexport interface SensorArchitectureProfileV0_3\n extends SensorArchitectureProfileBase {\n schemaVersion: typeof SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION;\n /** Broad detector technology (for example CMOS or CCD) only. It never changes performance. */\n technologyFamily?: SourcedSensorArchitectureFact<SensorTechnologyFamily>;\n}\n\nexport type SensorArchitectureProfile =\n | SensorArchitectureProfileV0_2\n | SensorArchitectureProfileV0_3;\n\nconst TECHNOLOGY_FAMILY_VALUES = new Set<SensorTechnologyFamily>([\n \"cmos\",\n \"ccd\"\n]);\n\nconst ILLUMINATION_VALUES = new Set<SensorIlluminationArchitecture>([\n \"fsi\",\n \"bsi\"\n]);\n\nconst INTEGRATION_VALUES = new Set<SensorIntegrationArchitecture>([\n \"monolithic\",\n \"partially-stacked\",\n \"stacked\"\n]);\n\nconst READOUT_VALUES = new Set<SensorReadoutArchitecture>([\n \"rolling\",\n \"global\"\n]);\n\nconst COLOR_SAMPLING_VALUES = new Set<SensorColorSamplingFamily>([\n \"bayer\",\n \"quad-bayer\",\n \"monochrome\",\n \"custom-rgb-mosaic\",\n \"layered-color\"\n]);\n\nfunction requireRecord(\n value: unknown,\n path: string,\n allowedKeys: readonly string[] | null\n): UnknownRecord {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n throw new InvalidConfigurationError(`${path} must be an object.`);\n }\n if (allowedKeys !== null) rejectUnknownFields(value, allowedKeys, path);\n return value as UnknownRecord;\n}\n\nfunction requireNonEmptyString(\n record: UnknownRecord,\n key: string,\n path: string\n): string {\n const value = record[key];\n if (typeof value !== \"string\" || value.trim().length === 0) {\n throw new InvalidConfigurationError(\n `${path}.${key} must be a non-empty string.`\n );\n }\n return value;\n}\n\nfunction parseScalarFact<T extends string>(\n value: unknown,\n path: string,\n allowed: ReadonlySet<T>\n): SourcedSensorArchitectureFact<T> {\n const record = requireRecord(value, path, [\"value\", \"evidence\"]);\n const factValue = requireNonEmptyString(record, \"value\", path);\n if (!allowed.has(factValue as T)) {\n throw new InvalidConfigurationError(`${path}.value is invalid.`);\n }\n\n return {\n value: factValue as T,\n evidence: parseEvidenceList(record.evidence, `${path}.evidence`)\n };\n}\n\nfunction parseReadoutCapabilities(\n value: unknown,\n path: string\n): readonly SourcedSensorArchitectureFact<SensorReadoutArchitecture>[] {\n if (!Array.isArray(value) || value.length === 0) {\n throw new InvalidConfigurationError(\n `${path} must be a non-empty array.`\n );\n }\n\n const parsed = value.map((entry, index) =>\n parseScalarFact(\n entry,\n `${path}[${index}]`,\n READOUT_VALUES\n )\n );\n\n const values = parsed.map((fact) => fact.value);\n if (new Set(values).size !== values.length) {\n throw new InvalidConfigurationError(\n `${path} must not contain duplicate capability values.`\n );\n }\n\n return parsed;\n}\n\nfunction parseSharedArchitectureFields(\n profile: UnknownRecord\n): SensorArchitectureProfileBase {\n return {\n ...(profile.illumination === undefined\n ? {}\n : {\n illumination: parseScalarFact(\n profile.illumination,\n \"sensorArchitecture.illumination\",\n ILLUMINATION_VALUES\n )\n }),\n ...(profile.integration === undefined\n ? {}\n : {\n integration: parseScalarFact(\n profile.integration,\n \"sensorArchitecture.integration\",\n INTEGRATION_VALUES\n )\n }),\n ...(profile.readoutCapabilities === undefined\n ? {}\n : {\n readoutCapabilities: parseReadoutCapabilities(\n profile.readoutCapabilities,\n \"sensorArchitecture.readoutCapabilities\"\n )\n }),\n ...(profile.colorSamplingFamily === undefined\n ? {}\n : {\n colorSamplingFamily: parseScalarFact(\n profile.colorSamplingFamily,\n \"sensorArchitecture.colorSamplingFamily\",\n COLOR_SAMPLING_VALUES\n )\n })\n };\n}\n\n/**\n * Parses untrusted descriptive sensor-architecture metadata.\n *\n * Schema 0.2.0 is still accepted and returns a 0.2.0 profile with the same\n * meaning. Schema 0.3.0 adds an optional, evidence-backed technology family\n * (CMOS or CCD). Leave out facts you do not know rather than guessing them.\n *\n * The parser checks structure, allowed values and per-field evidence only. It\n * does not check that a cited real-world claim is true, and the technology\n * family does not switch on any hidden physics.\n *\n * @param value Unknown external data.\n * @returns Validated sensor architecture profile preserving input schema identity.\n */\nexport function parseSensorArchitectureProfile(\n value: unknown\n): SensorArchitectureProfile {\n const profile = requireRecord(value, \"sensorArchitecture\", [\"illumination\", \"integration\", \"readoutCapabilities\", \"colorSamplingFamily\", \"schemaVersion\", \"technologyFamily\"]);\n const shared = parseSharedArchitectureFields(profile);\n\n if (\n profile.schemaVersion ===\n LEGACY_SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION\n ) {\n if (profile.technologyFamily !== undefined) {\n throw new InvalidConfigurationError(\n \"sensorArchitecture schema 0.2.0 does not support technologyFamily; use schema 0.3.0.\"\n );\n }\n\n return {\n schemaVersion: LEGACY_SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION,\n ...shared\n };\n }\n\n if (profile.schemaVersion === SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION) {\n return {\n schemaVersion: SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION,\n ...shared,\n ...(profile.technologyFamily === undefined\n ? {}\n : {\n technologyFamily: parseScalarFact(\n profile.technologyFamily,\n \"sensorArchitecture.technologyFamily\",\n TECHNOLOGY_FAMILY_VALUES\n )\n })\n };\n }\n\n throw new InvalidConfigurationError(\n 'sensorArchitecture.schemaVersion must be \"0.2.0\" or \"0.3.0\".'\n );\n}\n"]}
1
+ {"version":3,"file":"architecture.js","sourceRoot":"","sources":["../../src/sensor/architecture.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Sensor architecture: descriptive metadata about a sensor's hardware (such as CMOS or CCD, and its\n * readout capabilities), each fact backed by evidence.\n *\n * This metadata describes; it does not switch on any physics. The parser checks structure, allowed\n * values and per-field evidence. It does not check that a cited real-world claim is true.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport {\n parseEvidenceList,\n type EvidenceBackedFact,\n type EvidenceProvenance,\n type EvidenceReuseStatus,\n type EvidenceSourceOrigin\n} from \"../core/evidence-provenance.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { requireConfigRecord as requireRecord, requireConfigRecordString as requireNonEmptyString } from \"../core/record-validation.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport const SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION = \"0.3.0\" as const;\nconst LEGACY_SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION = \"0.2.0\" as const;\n\nexport type SensorTechnologyFamily = \"cmos\" | \"ccd\";\n\nexport type SensorIlluminationArchitecture = \"fsi\" | \"bsi\";\n\nexport type SensorIntegrationArchitecture =\n | \"monolithic\"\n | \"partially-stacked\"\n | \"stacked\";\n\nexport type SensorReadoutArchitecture = \"rolling\" | \"global\";\n\nexport type SensorColorSamplingFamily =\n | \"bayer\"\n | \"quad-bayer\"\n | \"monochrome\"\n | \"custom-rgb-mosaic\"\n | \"layered-color\";\n\nexport type SensorArchitectureSourceKind = EvidenceSourceOrigin;\nexport type SensorArchitectureReuseStatus = EvidenceReuseStatus;\nexport type SensorArchitectureFactProvenance = EvidenceProvenance;\nexport type SourcedSensorArchitectureFact<T> = EvidenceBackedFact<T>;\n\ninterface SensorArchitectureProfileBase {\n illumination?: SourcedSensorArchitectureFact<SensorIlluminationArchitecture>;\n integration?: SourcedSensorArchitectureFact<SensorIntegrationArchitecture>;\n /**\n * Readout modes the hardware supports, not the mode chosen for one capture.\n * Each capability has its own evidence, because different readout modes may\n * be documented by different sources.\n */\n readoutCapabilities?: readonly SourcedSensorArchitectureFact<SensorReadoutArchitecture>[];\n colorSamplingFamily?: SourcedSensorArchitectureFact<SensorColorSamplingFamily>;\n}\n\n/**\n * Legacy sensor-architecture schema.\n *\n * Schema 0.2.0 is still accepted and kept exactly as parsed. It has no\n * technologyFamily field; leaving it out means \"unknown, not stated\".\n */\nexport interface SensorArchitectureProfileV0_2\n extends SensorArchitectureProfileBase {\n schemaVersion: typeof LEGACY_SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION;\n technologyFamily?: never;\n}\n\n/**\n * Current descriptive metadata about the sensor hardware and its capabilities.\n *\n * An omitted property means \"unknown, not stated\". These fields do not switch\n * on scientific effects. No architecture value changes noise, dynamic range,\n * field of view, crop factor, exposure, readout timing or sampling unless a\n * separate downstream model explicitly uses that fact.\n */\nexport interface SensorArchitectureProfileV0_3\n extends SensorArchitectureProfileBase {\n schemaVersion: typeof SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION;\n /** Broad detector technology (for example CMOS or CCD) only. It never changes performance. */\n technologyFamily?: SourcedSensorArchitectureFact<SensorTechnologyFamily>;\n}\n\nexport type SensorArchitectureProfile =\n | SensorArchitectureProfileV0_2\n | SensorArchitectureProfileV0_3;\n\nconst TECHNOLOGY_FAMILY_VALUES = new Set<SensorTechnologyFamily>([\n \"cmos\",\n \"ccd\"\n]);\n\nconst ILLUMINATION_VALUES = new Set<SensorIlluminationArchitecture>([\n \"fsi\",\n \"bsi\"\n]);\n\nconst INTEGRATION_VALUES = new Set<SensorIntegrationArchitecture>([\n \"monolithic\",\n \"partially-stacked\",\n \"stacked\"\n]);\n\nconst READOUT_VALUES = new Set<SensorReadoutArchitecture>([\n \"rolling\",\n \"global\"\n]);\n\nconst COLOR_SAMPLING_VALUES = new Set<SensorColorSamplingFamily>([\n \"bayer\",\n \"quad-bayer\",\n \"monochrome\",\n \"custom-rgb-mosaic\",\n \"layered-color\"\n]);\n\nfunction parseScalarFact<T extends string>(\n value: unknown,\n path: string,\n allowed: ReadonlySet<T>\n): SourcedSensorArchitectureFact<T> {\n const record = requireRecord(value, path, [\"value\", \"evidence\"]);\n const factValue = requireNonEmptyString(record, \"value\", path);\n if (!allowed.has(factValue as T)) {\n throw new InvalidConfigurationError(`${path}.value is invalid.`);\n }\n\n return {\n value: factValue as T,\n evidence: parseEvidenceList(record.evidence, `${path}.evidence`)\n };\n}\n\nfunction parseReadoutCapabilities(\n value: unknown,\n path: string\n): readonly SourcedSensorArchitectureFact<SensorReadoutArchitecture>[] {\n if (!Array.isArray(value) || value.length === 0) {\n throw new InvalidConfigurationError(\n `${path} must be a non-empty array.`\n );\n }\n\n const parsed = value.map((entry, index) =>\n parseScalarFact(\n entry,\n `${path}[${index}]`,\n READOUT_VALUES\n )\n );\n\n const values = parsed.map((fact) => fact.value);\n if (new Set(values).size !== values.length) {\n throw new InvalidConfigurationError(\n `${path} must not contain duplicate capability values.`\n );\n }\n\n return parsed;\n}\n\nfunction parseSharedArchitectureFields(\n profile: UnknownRecord\n): SensorArchitectureProfileBase {\n return {\n ...(profile.illumination === undefined\n ? {}\n : {\n illumination: parseScalarFact(\n profile.illumination,\n \"sensorArchitecture.illumination\",\n ILLUMINATION_VALUES\n )\n }),\n ...(profile.integration === undefined\n ? {}\n : {\n integration: parseScalarFact(\n profile.integration,\n \"sensorArchitecture.integration\",\n INTEGRATION_VALUES\n )\n }),\n ...(profile.readoutCapabilities === undefined\n ? {}\n : {\n readoutCapabilities: parseReadoutCapabilities(\n profile.readoutCapabilities,\n \"sensorArchitecture.readoutCapabilities\"\n )\n }),\n ...(profile.colorSamplingFamily === undefined\n ? {}\n : {\n colorSamplingFamily: parseScalarFact(\n profile.colorSamplingFamily,\n \"sensorArchitecture.colorSamplingFamily\",\n COLOR_SAMPLING_VALUES\n )\n })\n };\n}\n\n/**\n * Parses untrusted descriptive sensor-architecture metadata.\n *\n * Schema 0.2.0 is still accepted and returns a 0.2.0 profile with the same\n * meaning. Schema 0.3.0 adds an optional, evidence-backed technology family\n * (CMOS or CCD). Leave out facts you do not know rather than guessing them.\n *\n * The parser checks structure, allowed values and per-field evidence only. It\n * does not check that a cited real-world claim is true, and the technology\n * family does not switch on any hidden physics.\n *\n * @param value Unknown external data.\n * @returns Validated sensor architecture profile preserving input schema identity.\n */\nexport function parseSensorArchitectureProfile(\n value: unknown\n): SensorArchitectureProfile {\n const profile = requireRecord(value, \"sensorArchitecture\", [\"illumination\", \"integration\", \"readoutCapabilities\", \"colorSamplingFamily\", \"schemaVersion\", \"technologyFamily\"]);\n const shared = parseSharedArchitectureFields(profile);\n\n if (\n profile.schemaVersion ===\n LEGACY_SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION\n ) {\n if (profile.technologyFamily !== undefined) {\n throw new InvalidConfigurationError(\n \"sensorArchitecture schema 0.2.0 does not support technologyFamily; use schema 0.3.0.\"\n );\n }\n\n return {\n schemaVersion: LEGACY_SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION,\n ...shared\n };\n }\n\n if (profile.schemaVersion === SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION) {\n return {\n schemaVersion: SENSOR_ARCHITECTURE_PROFILE_SCHEMA_VERSION,\n ...shared,\n ...(profile.technologyFamily === undefined\n ? {}\n : {\n technologyFamily: parseScalarFact(\n profile.technologyFamily,\n \"sensorArchitecture.technologyFamily\",\n TECHNOLOGY_FAMILY_VALUES\n )\n })\n };\n }\n\n throw new InvalidConfigurationError(\n 'sensorArchitecture.schemaVersion must be \"0.2.0\" or \"0.3.0\".'\n );\n}\n"]}
@@ -105,6 +105,7 @@ export interface SensorCameraSaturationCapacityAssessment {
105
105
  chargeCompleteness: SensorAccumulatedChargeComposition["componentEvidence"]["completeness"];
106
106
  };
107
107
  }
108
+ export declare function parseUncertainty(value: unknown, path: string): SensorSpectralResponseUncertainty;
108
109
  /**
109
110
  * Validates untrusted input and returns it as a normalized, typed value. Unknown enum values,
110
111
  * missing required fields and inconsistent scientific data are rejected.
@@ -14,58 +14,9 @@ import { approximationResult } from "../core/calculation-result.js";
14
14
  import { InvalidConfigurationError } from "../core/configuration-error.js";
15
15
  import { parseEvidenceList } from "../core/evidence-provenance.js";
16
16
  import { InvalidScientificInputError } from "../core/validation.js";
17
- import { requireInputObject, requireInputKeys, rejectUnknownFields, rejectUnknownVariantFields, UNCERTAINTY_VARIANT_FIELDS } from "../core/record-validation.js";
18
- function requireRecord(value, path, allowedKeys) {
19
- if (typeof value !== "object" ||
20
- value === null ||
21
- Array.isArray(value)) {
22
- throw new InvalidConfigurationError(path + " must be an object.");
23
- }
24
- if (allowedKeys !== null)
25
- rejectUnknownFields(value, allowedKeys, path);
26
- return value;
27
- }
28
- function requireNonEmptyString(value, path) {
29
- if (typeof value !== "string" ||
30
- value.trim().length === 0) {
31
- throw new InvalidConfigurationError(path + " must be a non-empty string.");
32
- }
33
- return value;
34
- }
35
- function requireFinite(value, path) {
36
- if (typeof value !== "number" ||
37
- !Number.isFinite(value)) {
38
- throw new InvalidConfigurationError(path + " must be finite.");
39
- }
40
- return value;
41
- }
42
- function requirePositiveFinite(value, path) {
43
- const parsed = requireFinite(value, path);
44
- if (parsed <= 0) {
45
- throw new InvalidConfigurationError(path +
46
- " must be greater than zero.");
47
- }
48
- return parsed;
49
- }
50
- function requireNonNegativeFiniteInput(value, path) {
51
- if (typeof value !== "number" ||
52
- !Number.isFinite(value) ||
53
- value < 0) {
54
- throw new InvalidScientificInputError(path +
55
- " must be finite and greater than or equal to zero.");
56
- }
57
- return value;
58
- }
59
- function requireSiteIndex(value, path) {
60
- if (typeof value !== "number" ||
61
- !Number.isSafeInteger(value) ||
62
- value < 0) {
63
- throw new InvalidConfigurationError(path +
64
- " must be a non-negative safe integer.");
65
- }
66
- return value;
67
- }
68
- function parseUncertainty(value, path) {
17
+ import { requireInputObject, requireInputKeys, rejectUnknownVariantFields, UNCERTAINTY_VARIANT_FIELDS, requireConfigRecord as requireRecord, requireConfigFinite as requireFinite, requireConfigString as requireNonEmptyString, requireInputNonNegativeFinite as requireNonNegativeFiniteInput, requireConfigNonNegativeSafeInteger as requireSiteIndex, requireConfigFiniteAboveZero as requirePositiveFinite } from "../core/record-validation.js";
18
+ import { sameSite } from "../core/shared-helpers.js";
19
+ export function parseUncertainty(value, path) {
69
20
  const record = requireRecord(value, path, null);
70
21
  rejectUnknownVariantFields(record, "kind", UNCERTAINTY_VARIANT_FIELDS, path);
71
22
  if (record.kind === "relative") {
@@ -204,9 +155,6 @@ export function parseSensorCameraSaturationCapacityProfile(value) {
204
155
  temperatureApplicability
205
156
  };
206
157
  }
207
- function sameSite(a, b) {
208
- return a.x === b.x && a.y === b.y;
209
- }
210
158
  function validateCharge(charge) {
211
159
  requireInputObject(charge, "charge input");
212
160
  if (charge.accumulatedChargeCompleteness !==
@@ -1 +1 @@
1
- {"version":3,"file":"camera-saturation-capacity.js","sourceRoot":"","sources":["../../src/sensor/camera-saturation-capacity.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Camera saturation capacity: checks whether the expected photo signal would exceed the camera's\n * declared saturation limit.\n *\n * Camera saturation capacity is the largest photo signal, in electron equivalents and after dark\n * correction, that the whole camera response chain records before saturating. It is not the\n * physical charge a pixel can store, not the ADC maximum, and does not say which hardware stage\n * limits it. The check compares expected values only; it does not make up codes beyond saturation.\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 { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport type {\n SensorPhotoAccumulatedChargeComposition as SensorAccumulatedChargeComposition\n} from \"./accumulated-charge.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 SensorCameraSaturationSiteApplicability =\n | {\n kind: \"exact-site\";\n site: {\n x: number;\n y: number;\n };\n }\n | {\n kind:\n \"channel-population-mean-approximation\";\n limitation: string;\n evidence:\n readonly EvidenceProvenance[];\n };\n\nexport type SensorCameraSaturationTemperatureApplicability =\n | {\n kind:\n \"exact-reference-temperature\";\n temperatureC: number;\n }\n | {\n kind: \"not-modeled\";\n limitation: string;\n evidence:\n readonly EvidenceProvenance[];\n };\n\nexport interface SensorCameraSaturationCapacityProfile {\n schemaVersion: \"0.1.0\";\n profileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\";\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\";\n saturationCapacityElectrons: number;\n scientificStatus:\n SensorSpectralResponseScientificStatus;\n uncertainty:\n SensorSpectralResponseUncertainty;\n evidence:\n readonly EvidenceProvenance[];\n measurementMethodId: string;\n measurementMethodEvidence:\n readonly EvidenceProvenance[];\n siteApplicability:\n SensorCameraSaturationSiteApplicability;\n operatingState: {\n stateId: string;\n evidence:\n readonly EvidenceProvenance[];\n };\n temperatureApplicability:\n SensorCameraSaturationTemperatureApplicability;\n}\n\nexport interface AssessSensorCameraSaturationCapacityInput {\n accumulatedCharge:\n SensorAccumulatedChargeComposition;\n saturationProfile:\n SensorCameraSaturationCapacityProfile;\n operatingStateId: string;\n operatingTemperatureC: number;\n}\n\nexport interface SensorCameraSaturationCapacityAssessment {\n saturationProfileId: string;\n completenessProfileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n operatingStateId: string;\n operatingTemperatureC: number;\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\";\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\";\n cameraSignalSaturationCapacityElectrons:\n number;\n expectedPhotoSignalElectronCount:\n number;\n totalExpectedStoredElectronCountDiagnostic:\n number;\n totalStoredChargeUsedForCameraSaturationComparison:\n false;\n expectedSignalToCameraSaturationRatio:\n number;\n expectedSignalHeadroomElectrons:\n number;\n expectedCameraSignalCapacityStatus:\n | \"below-capacity\"\n | \"at-capacity\"\n | \"above-capacity\";\n expectedPhotoSignalExceedsCameraCapacity:\n boolean;\n cameraSaturationCapacityAssessmentPerformed:\n true;\n assessmentMeaning:\n \"expected-dark-corrected-photo-signal-vs-camera-saturation-capacity\";\n physicalChargeCapacityUsed:\n false;\n limitingSaturationMechanismResolved:\n false;\n analogClippingThresholdModeled:\n false;\n digitalClippingThresholdModeled:\n false;\n adcMaximumCodeUsed:\n false;\n actualStochasticSaturationStateKnown:\n false;\n stochasticSaturationProbabilityAssessed:\n false;\n outputSignalAfterSaturationCalculated:\n false;\n clampApplied: false;\n physicalStoredChargeModified:\n false;\n adcQuantizationApplied: false;\n rawCodeValueProduced: false;\n saturationCapacityUncertaintyPropagated:\n false;\n photoSignalUncertaintyPropagated:\n false;\n componentEvidence: {\n saturationCapacity:\n readonly EvidenceProvenance[];\n measurementMethod:\n readonly EvidenceProvenance[];\n operatingState:\n readonly EvidenceProvenance[];\n siteApproximation:\n readonly EvidenceProvenance[];\n temperatureApproximation:\n readonly EvidenceProvenance[];\n chargeCompleteness:\n SensorAccumulatedChargeComposition[\"componentEvidence\"][\"completeness\"];\n };\n}\n\nfunction requireRecord(\n value: unknown,\n path: string,\n allowedKeys: readonly string[] | null\n): UnknownRecord {\n if (\n typeof value !== \"object\" ||\n value === null ||\n Array.isArray(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be an object.\"\n );\n }\n if (allowedKeys !== null) rejectUnknownFields(value, allowedKeys, path);\n return value as UnknownRecord;\n}\n\nfunction requireNonEmptyString(\n value: unknown,\n path: string\n): string {\n if (\n typeof value !== \"string\" ||\n value.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path + \" must be a non-empty string.\"\n );\n }\n return value;\n}\n\nfunction requireFinite(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be finite.\"\n );\n }\n return value;\n}\n\nfunction requirePositiveFinite(\n value: unknown,\n path: string\n): number {\n const parsed = requireFinite(\n value,\n path\n );\n if (parsed <= 0) {\n throw new InvalidConfigurationError(\n path +\n \" must be greater than zero.\"\n );\n }\n return parsed;\n}\n\nfunction requireNonNegativeFiniteInput(\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 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 = requireRecord(\n value,\n path, null\n );\n rejectUnknownVariantFields(record, \"kind\", UNCERTAINTY_VARIANT_FIELDS, path);\n if (record.kind === \"relative\") {\n const fraction = requireFinite(\n record.fraction,\n path + \".fraction\"\n );\n if (fraction < 0) {\n throw new InvalidConfigurationError(\n path +\n \".fraction must be nonnegative.\"\n );\n }\n return {\n kind: \"relative\",\n fraction,\n basis: requireNonEmptyString(\n record.basis,\n path + \".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 throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parseSiteApplicability(\n value: unknown\n): SensorCameraSaturationSiteApplicability {\n const record = requireRecord(\n value,\n \"sensorCameraSaturation.siteApplicability\", null\n );\n rejectUnknownVariantFields(record, \"kind\", { \"exact-site\": [\"kind\", \"site\"], \"channel-population-mean-approximation\": [\"kind\", \"limitation\", \"evidence\"] }, \"sensorCameraSaturation.siteApplicability\");\n if (record.kind === \"exact-site\") {\n const site = requireRecord(\n record.site,\n \"sensorCameraSaturation.siteApplicability.site\", [\"x\", \"y\"]\n );\n return {\n kind: \"exact-site\",\n site: {\n x: requireSiteIndex(\n site.x,\n \"sensorCameraSaturation.siteApplicability.site.x\"\n ),\n y: requireSiteIndex(\n site.y,\n \"sensorCameraSaturation.siteApplicability.site.y\"\n )\n }\n };\n }\n if (\n record.kind ===\n \"channel-population-mean-approximation\"\n ) {\n return {\n kind:\n \"channel-population-mean-approximation\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n \"sensorCameraSaturation.siteApplicability.limitation\"\n ),\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorCameraSaturation.siteApplicability.evidence\"\n )\n };\n }\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.siteApplicability.kind is invalid.\"\n );\n}\n\nfunction parseTemperatureApplicability(\n value: unknown\n): SensorCameraSaturationTemperatureApplicability {\n const record = requireRecord(\n value,\n \"sensorCameraSaturation.temperatureApplicability\", null\n );\n rejectUnknownVariantFields(record, \"kind\", { \"exact-reference-temperature\": [\"kind\", \"temperatureC\"], \"not-modeled\": [\"kind\", \"limitation\", \"evidence\"] }, \"sensorCameraSaturation.temperatureApplicability\");\n if (\n record.kind ===\n \"exact-reference-temperature\"\n ) {\n return {\n kind:\n \"exact-reference-temperature\",\n temperatureC:\n requireFinite(\n record.temperatureC,\n \"sensorCameraSaturation.temperatureApplicability.temperatureC\"\n )\n };\n }\n if (record.kind === \"not-modeled\") {\n return {\n kind: \"not-modeled\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n \"sensorCameraSaturation.temperatureApplicability.limitation\"\n ),\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorCameraSaturation.temperatureApplicability.evidence\"\n )\n };\n }\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.temperatureApplicability.kind is invalid.\"\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 * Camera saturation capacity is a limit on the dark-corrected photo signal, in electron\n * equivalents. It is not the physical charge storage capacity or the ADC maximum, and does not\n * identify which hardware stage limits it.\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 parseSensorCameraSaturationCapacityProfile(\n value: unknown\n): SensorCameraSaturationCapacityProfile {\n const record = requireRecord(\n value,\n \"sensorCameraSaturation\", [\"schemaVersion\", \"capacityMeaning\", \"signalDomain\", \"scientificStatus\", \"uncertainty\", \"siteApplicability\", \"temperatureApplicability\", \"operatingState\", \"profileId\", \"colorSamplingProfileId\", \"channelId\", \"saturationCapacityElectrons\", \"evidence\", \"measurementMethodId\", \"measurementMethodEvidence\"]\n );\n\n if (\n record.schemaVersion !== \"0.1.0\"\n ) {\n throw new InvalidConfigurationError(\n 'sensorCameraSaturation.schemaVersion must be \"0.1.0\".'\n );\n }\n if (\n record.capacityMeaning !==\n \"camera-signal-saturation-capacity-electrons\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.capacityMeaning is invalid.\"\n );\n }\n if (\n record.signalDomain !==\n \"dark-corrected-photo-generated-electron-equivalent\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.signalDomain is invalid.\"\n );\n }\n if (\n record.scientificStatus !==\n \"calibrated\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.scientificStatus is invalid.\"\n );\n }\n\n const uncertainty =\n parseUncertainty(\n record.uncertainty,\n \"sensorCameraSaturation.uncertainty\"\n );\n if (\n record.scientificStatus ===\n \"calibrated\" &&\n uncertainty.kind ===\n \"not-quantified\"\n ) {\n throw new InvalidConfigurationError(\n \"A calibrated camera saturation-capacity profile must declare quantified relative uncertainty.\"\n );\n }\n\n const siteApplicability =\n parseSiteApplicability(\n record.siteApplicability\n );\n const temperatureApplicability =\n parseTemperatureApplicability(\n record.temperatureApplicability\n );\n\n if (\n siteApplicability.kind ===\n \"channel-population-mean-approximation\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"A channel-population mean camera saturation capacity must remain an approximation when applied to one site.\"\n );\n }\n if (\n temperatureApplicability.kind ===\n \"not-modeled\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"A camera saturation capacity with unmodeled temperature dependence must remain an approximation.\"\n );\n }\n\n const state = requireRecord(\n record.operatingState,\n \"sensorCameraSaturation.operatingState\", [\"stateId\", \"evidence\"]\n );\n\n return {\n schemaVersion: \"0.1.0\",\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"sensorCameraSaturation.profileId\"\n ),\n colorSamplingProfileId:\n requireNonEmptyString(\n record.colorSamplingProfileId,\n \"sensorCameraSaturation.colorSamplingProfileId\"\n ),\n channelId:\n requireNonEmptyString(\n record.channelId,\n \"sensorCameraSaturation.channelId\"\n ),\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\",\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\",\n saturationCapacityElectrons:\n requirePositiveFinite(\n record.saturationCapacityElectrons,\n \"sensorCameraSaturation.saturationCapacityElectrons\"\n ),\n scientificStatus:\n record.scientificStatus,\n uncertainty,\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorCameraSaturation.evidence\"\n ),\n measurementMethodId:\n requireNonEmptyString(\n record.measurementMethodId,\n \"sensorCameraSaturation.measurementMethodId\"\n ),\n measurementMethodEvidence:\n parseEvidenceList(\n record.measurementMethodEvidence,\n \"sensorCameraSaturation.measurementMethodEvidence\"\n ),\n siteApplicability,\n operatingState: {\n stateId:\n requireNonEmptyString(\n state.stateId,\n \"sensorCameraSaturation.operatingState.stateId\"\n ),\n evidence: parseEvidenceList(\n state.evidence,\n \"sensorCameraSaturation.operatingState.evidence\"\n )\n },\n temperatureApplicability\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 validateCharge(\n charge:\n SensorAccumulatedChargeComposition\n): {\n photo: number;\n total: number;\n} {\n requireInputObject(charge, \"charge input\");\n if (\n charge.accumulatedChargeCompleteness !==\n \"complete-for-physical-storage-capacity-assessment\" ||\n charge.saturationAssessed !==\n false ||\n charge.adcQuantizationApplied !==\n false ||\n charge.rawCodeValueProduced !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"Camera saturation assessment requires an unmodified pre-saturation accumulated-charge composition.\"\n );\n }\n\n return {\n photo: requireNonNegativeFiniteInput(\n charge.photoExpectedElectronCount,\n \"accumulatedCharge.photoExpectedElectronCount\"\n ),\n total: requireNonNegativeFiniteInput(\n charge.totalExpectedStoredElectronCount,\n \"accumulatedCharge.totalExpectedStoredElectronCount\"\n )\n };\n}\n\n/**\n * Compares the expected photo signal (dark-corrected) with the camera's declared saturation\n * capacity. Dark charge is not added, and no hardware clamp is identified.\n *\n * This compares expected values only. It does not make up codes beyond saturation.\n * @param input - The input; see `AssessSensorCameraSaturationCapacityInput` for its fields, units and coordinates.\n * @returns The assessment. Read its structured blockers before acting on an authorization.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function assessSensorCameraSaturationCapacity(\n input:\n AssessSensorCameraSaturationCapacityInput\n): CalculationResult<SensorCameraSaturationCapacityAssessment> {\n requireInputKeys(input, \"assessSensorCameraSaturationCapacity\", [\"accumulatedCharge\", \"saturationProfile\", \"operatingStateId\", \"operatingTemperatureC\"]);\n const charge =\n validateCharge(\n input.accumulatedCharge\n );\n const profile =\n parseSensorCameraSaturationCapacityProfile(\n input.saturationProfile\n );\n\n if (\n profile.colorSamplingProfileId !==\n input.accumulatedCharge\n .colorSamplingProfileId ||\n profile.channelId !==\n input.accumulatedCharge.channelId\n ) {\n throw new InvalidScientificInputError(\n \"Camera saturation-capacity profile color/channel identity must match the accumulated charge.\"\n );\n }\n if (\n profile.siteApplicability.kind ===\n \"exact-site\" &&\n !sameSite(\n profile.siteApplicability.site,\n input.accumulatedCharge.site\n )\n ) {\n throw new InvalidScientificInputError(\n \"Exact-site camera saturation capacity does not apply to the accumulated-charge site.\"\n );\n }\n if (\n typeof input.operatingStateId !==\n \"string\" ||\n input.operatingStateId.trim().length ===\n 0 ||\n input.operatingStateId !==\n profile.operatingState.stateId\n ) {\n throw new InvalidScientificInputError(\n \"operatingStateId must exactly match the camera saturation-capacity calibration state.\"\n );\n }\n if (\n typeof input.operatingTemperatureC !==\n \"number\" ||\n !Number.isFinite(\n input.operatingTemperatureC\n )\n ) {\n throw new InvalidScientificInputError(\n \"operatingTemperatureC must be finite.\"\n );\n }\n if (\n profile.temperatureApplicability.kind ===\n \"exact-reference-temperature\" &&\n input.operatingTemperatureC !==\n profile.temperatureApplicability\n .temperatureC\n ) {\n throw new InvalidScientificInputError(\n \"operatingTemperatureC must exactly match the camera saturation-capacity reference temperature.\"\n );\n }\n\n const capacity =\n profile.saturationCapacityElectrons;\n const ratio =\n charge.photo / capacity;\n const headroom =\n capacity - charge.photo;\n\n if (\n !Number.isFinite(ratio) ||\n !Number.isFinite(headroom)\n ) {\n throw new InvalidScientificInputError(\n \"Camera saturation-capacity comparison must remain finite.\"\n );\n }\n\n const status =\n charge.photo < capacity\n ? \"below-capacity\"\n : charge.photo === capacity\n ? \"at-capacity\"\n : \"above-capacity\";\n\n return approximationResult(\n {\n saturationProfileId:\n profile.profileId,\n completenessProfileId:\n input.accumulatedCharge\n .completenessProfileId,\n colorSamplingProfileId:\n input.accumulatedCharge\n .colorSamplingProfileId,\n channelId:\n input.accumulatedCharge.channelId,\n site: {\n ...input.accumulatedCharge.site\n },\n bindingId:\n input.accumulatedCharge.bindingId,\n operatingStateId:\n input.operatingStateId,\n operatingTemperatureC:\n input.operatingTemperatureC,\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\",\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\",\n cameraSignalSaturationCapacityElectrons:\n capacity,\n expectedPhotoSignalElectronCount:\n charge.photo,\n totalExpectedStoredElectronCountDiagnostic:\n charge.total,\n totalStoredChargeUsedForCameraSaturationComparison:\n false,\n expectedSignalToCameraSaturationRatio:\n ratio,\n expectedSignalHeadroomElectrons:\n headroom,\n expectedCameraSignalCapacityStatus:\n status,\n expectedPhotoSignalExceedsCameraCapacity:\n status === \"above-capacity\",\n cameraSaturationCapacityAssessmentPerformed:\n true,\n assessmentMeaning:\n \"expected-dark-corrected-photo-signal-vs-camera-saturation-capacity\",\n physicalChargeCapacityUsed: false,\n limitingSaturationMechanismResolved:\n false,\n analogClippingThresholdModeled:\n false,\n digitalClippingThresholdModeled:\n false,\n adcMaximumCodeUsed: false,\n actualStochasticSaturationStateKnown:\n false,\n stochasticSaturationProbabilityAssessed:\n false,\n outputSignalAfterSaturationCalculated:\n false,\n clampApplied: false,\n physicalStoredChargeModified:\n false,\n adcQuantizationApplied: false,\n rawCodeValueProduced: false,\n saturationCapacityUncertaintyPropagated:\n false,\n photoSignalUncertaintyPropagated:\n false,\n componentEvidence: {\n saturationCapacity:\n profile.evidence,\n measurementMethod:\n profile\n .measurementMethodEvidence,\n operatingState:\n profile.operatingState\n .evidence,\n siteApproximation:\n profile.siteApplicability.kind ===\n \"channel-population-mean-approximation\"\n ? profile\n .siteApplicability\n .evidence\n : [],\n temperatureApproximation:\n profile.temperatureApplicability.kind ===\n \"not-modeled\"\n ? profile\n .temperatureApplicability\n .evidence\n : [],\n chargeCompleteness:\n input.accumulatedCharge\n .componentEvidence\n .completeness\n }\n },\n \"sensor-camera-saturation-capacity-assessment\",\n \"1.0.0\",\n [\n \"The camera saturation capacity is a measured/calibrated dark-corrected photo-signal capacity in electron-equivalent signal units; total stored charge is deliberately not used for this comparison.\",\n \"Physical charge-storage capacity is a separate sensor property and is not used by this assessment.\",\n \"The camera saturation capacity may be limited by a downstream camera stage before physical pixel storage is full; this contract does not infer which analog or digital stage is limiting.\",\n \"The operating-state identifier is an exact calibration-state key covering the source-specific camera/readout configuration; Photivra does not infer equivalence from ISO, gain or bit-depth labels.\",\n \"A channel-population mean applied to one site or unmodeled temperature dependence remains an explicit approximation.\",\n \"Expected photo signal below the measured capacity does not establish the actual stochastic saturation state; no saturation probability is calculated.\",\n \"No signal clamp, analog clipping transfer, ADC maximum code, RAW code or post-saturation output is calculated.\",\n \"Dark and other stored charge remain diagnostics for physical storage accounting and are not silently added to a dark-corrected camera-signal capacity.\",\n \"Capacity and photo-signal uncertainties are not propagated into a saturation probability or confidence interval by this version.\"\n ]\n );\n}\n"]}
1
+ {"version":3,"file":"camera-saturation-capacity.js","sourceRoot":"","sources":["../../src/sensor/camera-saturation-capacity.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Camera saturation capacity: checks whether the expected photo signal would exceed the camera's\n * declared saturation limit.\n *\n * Camera saturation capacity is the largest photo signal, in electron equivalents and after dark\n * correction, that the whole camera response chain records before saturating. It is not the\n * physical charge a pixel can store, not the ADC maximum, and does not say which hardware stage\n * limits it. The check compares expected values only; it does not make up codes beyond saturation.\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 { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport type {\n SensorPhotoAccumulatedChargeComposition as SensorAccumulatedChargeComposition\n} from \"./accumulated-charge.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, requireInputNonNegativeFinite as requireNonNegativeFiniteInput, requireConfigNonNegativeSafeInteger as requireSiteIndex, requireConfigFiniteAboveZero as requirePositiveFinite } from \"../core/record-validation.js\";\nimport { sameSite } from \"../core/shared-helpers.js\";\n\nexport type SensorCameraSaturationSiteApplicability =\n | {\n kind: \"exact-site\";\n site: {\n x: number;\n y: number;\n };\n }\n | {\n kind:\n \"channel-population-mean-approximation\";\n limitation: string;\n evidence:\n readonly EvidenceProvenance[];\n };\n\nexport type SensorCameraSaturationTemperatureApplicability =\n | {\n kind:\n \"exact-reference-temperature\";\n temperatureC: number;\n }\n | {\n kind: \"not-modeled\";\n limitation: string;\n evidence:\n readonly EvidenceProvenance[];\n };\n\nexport interface SensorCameraSaturationCapacityProfile {\n schemaVersion: \"0.1.0\";\n profileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\";\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\";\n saturationCapacityElectrons: number;\n scientificStatus:\n SensorSpectralResponseScientificStatus;\n uncertainty:\n SensorSpectralResponseUncertainty;\n evidence:\n readonly EvidenceProvenance[];\n measurementMethodId: string;\n measurementMethodEvidence:\n readonly EvidenceProvenance[];\n siteApplicability:\n SensorCameraSaturationSiteApplicability;\n operatingState: {\n stateId: string;\n evidence:\n readonly EvidenceProvenance[];\n };\n temperatureApplicability:\n SensorCameraSaturationTemperatureApplicability;\n}\n\nexport interface AssessSensorCameraSaturationCapacityInput {\n accumulatedCharge:\n SensorAccumulatedChargeComposition;\n saturationProfile:\n SensorCameraSaturationCapacityProfile;\n operatingStateId: string;\n operatingTemperatureC: number;\n}\n\nexport interface SensorCameraSaturationCapacityAssessment {\n saturationProfileId: string;\n completenessProfileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n operatingStateId: string;\n operatingTemperatureC: number;\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\";\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\";\n cameraSignalSaturationCapacityElectrons:\n number;\n expectedPhotoSignalElectronCount:\n number;\n totalExpectedStoredElectronCountDiagnostic:\n number;\n totalStoredChargeUsedForCameraSaturationComparison:\n false;\n expectedSignalToCameraSaturationRatio:\n number;\n expectedSignalHeadroomElectrons:\n number;\n expectedCameraSignalCapacityStatus:\n | \"below-capacity\"\n | \"at-capacity\"\n | \"above-capacity\";\n expectedPhotoSignalExceedsCameraCapacity:\n boolean;\n cameraSaturationCapacityAssessmentPerformed:\n true;\n assessmentMeaning:\n \"expected-dark-corrected-photo-signal-vs-camera-saturation-capacity\";\n physicalChargeCapacityUsed:\n false;\n limitingSaturationMechanismResolved:\n false;\n analogClippingThresholdModeled:\n false;\n digitalClippingThresholdModeled:\n false;\n adcMaximumCodeUsed:\n false;\n actualStochasticSaturationStateKnown:\n false;\n stochasticSaturationProbabilityAssessed:\n false;\n outputSignalAfterSaturationCalculated:\n false;\n clampApplied: false;\n physicalStoredChargeModified:\n false;\n adcQuantizationApplied: false;\n rawCodeValueProduced: false;\n saturationCapacityUncertaintyPropagated:\n false;\n photoSignalUncertaintyPropagated:\n false;\n componentEvidence: {\n saturationCapacity:\n readonly EvidenceProvenance[];\n measurementMethod:\n readonly EvidenceProvenance[];\n operatingState:\n readonly EvidenceProvenance[];\n siteApproximation:\n readonly EvidenceProvenance[];\n temperatureApproximation:\n readonly EvidenceProvenance[];\n chargeCompleteness:\n SensorAccumulatedChargeComposition[\"componentEvidence\"][\"completeness\"];\n };\n}\n\nexport function parseUncertainty(\n value: unknown,\n path: string\n): SensorSpectralResponseUncertainty {\n const record = requireRecord(\n value,\n path, null\n );\n rejectUnknownVariantFields(record, \"kind\", UNCERTAINTY_VARIANT_FIELDS, path);\n if (record.kind === \"relative\") {\n const fraction = requireFinite(\n record.fraction,\n path + \".fraction\"\n );\n if (fraction < 0) {\n throw new InvalidConfigurationError(\n path +\n \".fraction must be nonnegative.\"\n );\n }\n return {\n kind: \"relative\",\n fraction,\n basis: requireNonEmptyString(\n record.basis,\n path + \".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 throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parseSiteApplicability(\n value: unknown\n): SensorCameraSaturationSiteApplicability {\n const record = requireRecord(\n value,\n \"sensorCameraSaturation.siteApplicability\", null\n );\n rejectUnknownVariantFields(record, \"kind\", { \"exact-site\": [\"kind\", \"site\"], \"channel-population-mean-approximation\": [\"kind\", \"limitation\", \"evidence\"] }, \"sensorCameraSaturation.siteApplicability\");\n if (record.kind === \"exact-site\") {\n const site = requireRecord(\n record.site,\n \"sensorCameraSaturation.siteApplicability.site\", [\"x\", \"y\"]\n );\n return {\n kind: \"exact-site\",\n site: {\n x: requireSiteIndex(\n site.x,\n \"sensorCameraSaturation.siteApplicability.site.x\"\n ),\n y: requireSiteIndex(\n site.y,\n \"sensorCameraSaturation.siteApplicability.site.y\"\n )\n }\n };\n }\n if (\n record.kind ===\n \"channel-population-mean-approximation\"\n ) {\n return {\n kind:\n \"channel-population-mean-approximation\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n \"sensorCameraSaturation.siteApplicability.limitation\"\n ),\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorCameraSaturation.siteApplicability.evidence\"\n )\n };\n }\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.siteApplicability.kind is invalid.\"\n );\n}\n\nfunction parseTemperatureApplicability(\n value: unknown\n): SensorCameraSaturationTemperatureApplicability {\n const record = requireRecord(\n value,\n \"sensorCameraSaturation.temperatureApplicability\", null\n );\n rejectUnknownVariantFields(record, \"kind\", { \"exact-reference-temperature\": [\"kind\", \"temperatureC\"], \"not-modeled\": [\"kind\", \"limitation\", \"evidence\"] }, \"sensorCameraSaturation.temperatureApplicability\");\n if (\n record.kind ===\n \"exact-reference-temperature\"\n ) {\n return {\n kind:\n \"exact-reference-temperature\",\n temperatureC:\n requireFinite(\n record.temperatureC,\n \"sensorCameraSaturation.temperatureApplicability.temperatureC\"\n )\n };\n }\n if (record.kind === \"not-modeled\") {\n return {\n kind: \"not-modeled\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n \"sensorCameraSaturation.temperatureApplicability.limitation\"\n ),\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorCameraSaturation.temperatureApplicability.evidence\"\n )\n };\n }\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.temperatureApplicability.kind is invalid.\"\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 * Camera saturation capacity is a limit on the dark-corrected photo signal, in electron\n * equivalents. It is not the physical charge storage capacity or the ADC maximum, and does not\n * identify which hardware stage limits it.\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 parseSensorCameraSaturationCapacityProfile(\n value: unknown\n): SensorCameraSaturationCapacityProfile {\n const record = requireRecord(\n value,\n \"sensorCameraSaturation\", [\"schemaVersion\", \"capacityMeaning\", \"signalDomain\", \"scientificStatus\", \"uncertainty\", \"siteApplicability\", \"temperatureApplicability\", \"operatingState\", \"profileId\", \"colorSamplingProfileId\", \"channelId\", \"saturationCapacityElectrons\", \"evidence\", \"measurementMethodId\", \"measurementMethodEvidence\"]\n );\n\n if (\n record.schemaVersion !== \"0.1.0\"\n ) {\n throw new InvalidConfigurationError(\n 'sensorCameraSaturation.schemaVersion must be \"0.1.0\".'\n );\n }\n if (\n record.capacityMeaning !==\n \"camera-signal-saturation-capacity-electrons\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.capacityMeaning is invalid.\"\n );\n }\n if (\n record.signalDomain !==\n \"dark-corrected-photo-generated-electron-equivalent\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.signalDomain is invalid.\"\n );\n }\n if (\n record.scientificStatus !==\n \"calibrated\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.scientificStatus is invalid.\"\n );\n }\n\n const uncertainty =\n parseUncertainty(\n record.uncertainty,\n \"sensorCameraSaturation.uncertainty\"\n );\n if (\n record.scientificStatus ===\n \"calibrated\" &&\n uncertainty.kind ===\n \"not-quantified\"\n ) {\n throw new InvalidConfigurationError(\n \"A calibrated camera saturation-capacity profile must declare quantified relative uncertainty.\"\n );\n }\n\n const siteApplicability =\n parseSiteApplicability(\n record.siteApplicability\n );\n const temperatureApplicability =\n parseTemperatureApplicability(\n record.temperatureApplicability\n );\n\n if (\n siteApplicability.kind ===\n \"channel-population-mean-approximation\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"A channel-population mean camera saturation capacity must remain an approximation when applied to one site.\"\n );\n }\n if (\n temperatureApplicability.kind ===\n \"not-modeled\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"A camera saturation capacity with unmodeled temperature dependence must remain an approximation.\"\n );\n }\n\n const state = requireRecord(\n record.operatingState,\n \"sensorCameraSaturation.operatingState\", [\"stateId\", \"evidence\"]\n );\n\n return {\n schemaVersion: \"0.1.0\",\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"sensorCameraSaturation.profileId\"\n ),\n colorSamplingProfileId:\n requireNonEmptyString(\n record.colorSamplingProfileId,\n \"sensorCameraSaturation.colorSamplingProfileId\"\n ),\n channelId:\n requireNonEmptyString(\n record.channelId,\n \"sensorCameraSaturation.channelId\"\n ),\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\",\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\",\n saturationCapacityElectrons:\n requirePositiveFinite(\n record.saturationCapacityElectrons,\n \"sensorCameraSaturation.saturationCapacityElectrons\"\n ),\n scientificStatus:\n record.scientificStatus,\n uncertainty,\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorCameraSaturation.evidence\"\n ),\n measurementMethodId:\n requireNonEmptyString(\n record.measurementMethodId,\n \"sensorCameraSaturation.measurementMethodId\"\n ),\n measurementMethodEvidence:\n parseEvidenceList(\n record.measurementMethodEvidence,\n \"sensorCameraSaturation.measurementMethodEvidence\"\n ),\n siteApplicability,\n operatingState: {\n stateId:\n requireNonEmptyString(\n state.stateId,\n \"sensorCameraSaturation.operatingState.stateId\"\n ),\n evidence: parseEvidenceList(\n state.evidence,\n \"sensorCameraSaturation.operatingState.evidence\"\n )\n },\n temperatureApplicability\n };\n}\n\nfunction validateCharge(\n charge:\n SensorAccumulatedChargeComposition\n): {\n photo: number;\n total: number;\n} {\n requireInputObject(charge, \"charge input\");\n if (\n charge.accumulatedChargeCompleteness !==\n \"complete-for-physical-storage-capacity-assessment\" ||\n charge.saturationAssessed !==\n false ||\n charge.adcQuantizationApplied !==\n false ||\n charge.rawCodeValueProduced !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"Camera saturation assessment requires an unmodified pre-saturation accumulated-charge composition.\"\n );\n }\n\n return {\n photo: requireNonNegativeFiniteInput(\n charge.photoExpectedElectronCount,\n \"accumulatedCharge.photoExpectedElectronCount\"\n ),\n total: requireNonNegativeFiniteInput(\n charge.totalExpectedStoredElectronCount,\n \"accumulatedCharge.totalExpectedStoredElectronCount\"\n )\n };\n}\n\n/**\n * Compares the expected photo signal (dark-corrected) with the camera's declared saturation\n * capacity. Dark charge is not added, and no hardware clamp is identified.\n *\n * This compares expected values only. It does not make up codes beyond saturation.\n * @param input - The input; see `AssessSensorCameraSaturationCapacityInput` for its fields, units and coordinates.\n * @returns The assessment. Read its structured blockers before acting on an authorization.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function assessSensorCameraSaturationCapacity(\n input:\n AssessSensorCameraSaturationCapacityInput\n): CalculationResult<SensorCameraSaturationCapacityAssessment> {\n requireInputKeys(input, \"assessSensorCameraSaturationCapacity\", [\"accumulatedCharge\", \"saturationProfile\", \"operatingStateId\", \"operatingTemperatureC\"]);\n const charge =\n validateCharge(\n input.accumulatedCharge\n );\n const profile =\n parseSensorCameraSaturationCapacityProfile(\n input.saturationProfile\n );\n\n if (\n profile.colorSamplingProfileId !==\n input.accumulatedCharge\n .colorSamplingProfileId ||\n profile.channelId !==\n input.accumulatedCharge.channelId\n ) {\n throw new InvalidScientificInputError(\n \"Camera saturation-capacity profile color/channel identity must match the accumulated charge.\"\n );\n }\n if (\n profile.siteApplicability.kind ===\n \"exact-site\" &&\n !sameSite(\n profile.siteApplicability.site,\n input.accumulatedCharge.site\n )\n ) {\n throw new InvalidScientificInputError(\n \"Exact-site camera saturation capacity does not apply to the accumulated-charge site.\"\n );\n }\n if (\n typeof input.operatingStateId !==\n \"string\" ||\n input.operatingStateId.trim().length ===\n 0 ||\n input.operatingStateId !==\n profile.operatingState.stateId\n ) {\n throw new InvalidScientificInputError(\n \"operatingStateId must exactly match the camera saturation-capacity calibration state.\"\n );\n }\n if (\n typeof input.operatingTemperatureC !==\n \"number\" ||\n !Number.isFinite(\n input.operatingTemperatureC\n )\n ) {\n throw new InvalidScientificInputError(\n \"operatingTemperatureC must be finite.\"\n );\n }\n if (\n profile.temperatureApplicability.kind ===\n \"exact-reference-temperature\" &&\n input.operatingTemperatureC !==\n profile.temperatureApplicability\n .temperatureC\n ) {\n throw new InvalidScientificInputError(\n \"operatingTemperatureC must exactly match the camera saturation-capacity reference temperature.\"\n );\n }\n\n const capacity =\n profile.saturationCapacityElectrons;\n const ratio =\n charge.photo / capacity;\n const headroom =\n capacity - charge.photo;\n\n if (\n !Number.isFinite(ratio) ||\n !Number.isFinite(headroom)\n ) {\n throw new InvalidScientificInputError(\n \"Camera saturation-capacity comparison must remain finite.\"\n );\n }\n\n const status =\n charge.photo < capacity\n ? \"below-capacity\"\n : charge.photo === capacity\n ? \"at-capacity\"\n : \"above-capacity\";\n\n return approximationResult(\n {\n saturationProfileId:\n profile.profileId,\n completenessProfileId:\n input.accumulatedCharge\n .completenessProfileId,\n colorSamplingProfileId:\n input.accumulatedCharge\n .colorSamplingProfileId,\n channelId:\n input.accumulatedCharge.channelId,\n site: {\n ...input.accumulatedCharge.site\n },\n bindingId:\n input.accumulatedCharge.bindingId,\n operatingStateId:\n input.operatingStateId,\n operatingTemperatureC:\n input.operatingTemperatureC,\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\",\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\",\n cameraSignalSaturationCapacityElectrons:\n capacity,\n expectedPhotoSignalElectronCount:\n charge.photo,\n totalExpectedStoredElectronCountDiagnostic:\n charge.total,\n totalStoredChargeUsedForCameraSaturationComparison:\n false,\n expectedSignalToCameraSaturationRatio:\n ratio,\n expectedSignalHeadroomElectrons:\n headroom,\n expectedCameraSignalCapacityStatus:\n status,\n expectedPhotoSignalExceedsCameraCapacity:\n status === \"above-capacity\",\n cameraSaturationCapacityAssessmentPerformed:\n true,\n assessmentMeaning:\n \"expected-dark-corrected-photo-signal-vs-camera-saturation-capacity\",\n physicalChargeCapacityUsed: false,\n limitingSaturationMechanismResolved:\n false,\n analogClippingThresholdModeled:\n false,\n digitalClippingThresholdModeled:\n false,\n adcMaximumCodeUsed: false,\n actualStochasticSaturationStateKnown:\n false,\n stochasticSaturationProbabilityAssessed:\n false,\n outputSignalAfterSaturationCalculated:\n false,\n clampApplied: false,\n physicalStoredChargeModified:\n false,\n adcQuantizationApplied: false,\n rawCodeValueProduced: false,\n saturationCapacityUncertaintyPropagated:\n false,\n photoSignalUncertaintyPropagated:\n false,\n componentEvidence: {\n saturationCapacity:\n profile.evidence,\n measurementMethod:\n profile\n .measurementMethodEvidence,\n operatingState:\n profile.operatingState\n .evidence,\n siteApproximation:\n profile.siteApplicability.kind ===\n \"channel-population-mean-approximation\"\n ? profile\n .siteApplicability\n .evidence\n : [],\n temperatureApproximation:\n profile.temperatureApplicability.kind ===\n \"not-modeled\"\n ? profile\n .temperatureApplicability\n .evidence\n : [],\n chargeCompleteness:\n input.accumulatedCharge\n .componentEvidence\n .completeness\n }\n },\n \"sensor-camera-saturation-capacity-assessment\",\n \"1.0.0\",\n [\n \"The camera saturation capacity is a measured/calibrated dark-corrected photo-signal capacity in electron-equivalent signal units; total stored charge is deliberately not used for this comparison.\",\n \"Physical charge-storage capacity is a separate sensor property and is not used by this assessment.\",\n \"The camera saturation capacity may be limited by a downstream camera stage before physical pixel storage is full; this contract does not infer which analog or digital stage is limiting.\",\n \"The operating-state identifier is an exact calibration-state key covering the source-specific camera/readout configuration; Photivra does not infer equivalence from ISO, gain or bit-depth labels.\",\n \"A channel-population mean applied to one site or unmodeled temperature dependence remains an explicit approximation.\",\n \"Expected photo signal below the measured capacity does not establish the actual stochastic saturation state; no saturation probability is calculated.\",\n \"No signal clamp, analog clipping transfer, ADC maximum code, RAW code or post-saturation output is calculated.\",\n \"Dark and other stored charge remain diagnostics for physical storage accounting and are not silently added to a dark-corrected camera-signal capacity.\",\n \"Capacity and photo-signal uncertainties are not propagated into a saturation probability or confidence interval by this version.\"\n ]\n );\n}\n"]}
@@ -14,32 +14,7 @@ import { parseEvidenceList } from "../core/evidence-provenance.js";
14
14
  import { InvalidScientificInputError, requirePositiveInteger } from "../core/validation.js";
15
15
  import { resolveCaptureMode } from "./capture-mode.js";
16
16
  import { parseSensorColorSamplingProfile } from "./color-sampling.js";
17
- import { requireInputObject, requireInputKeys, rejectUnknownFields } from "../core/record-validation.js";
18
- function requireRecord(value, path, allowedKeys) {
19
- if (typeof value !== "object" ||
20
- value === null ||
21
- Array.isArray(value)) {
22
- throw new InvalidConfigurationError(path + " must be an object.");
23
- }
24
- if (allowedKeys !== null)
25
- rejectUnknownFields(value, allowedKeys, path);
26
- return value;
27
- }
28
- function requireNonEmptyString(value, path) {
29
- if (typeof value !== "string" ||
30
- value.trim().length === 0) {
31
- throw new InvalidConfigurationError(path + " must be a non-empty string.");
32
- }
33
- return value;
34
- }
35
- function requirePositiveSafeInteger(value, path) {
36
- if (typeof value !== "number" ||
37
- !Number.isSafeInteger(value) ||
38
- value <= 0) {
39
- throw new InvalidConfigurationError(path + " must be a positive safe integer.");
40
- }
41
- return value;
42
- }
17
+ import { requireInputObject, requireInputKeys, requireConfigRecord as requireRecord, requireConfigString as requireNonEmptyString, requireConfigPositiveSafeInteger as requirePositiveSafeInteger } from "../core/record-validation.js";
43
18
  function parseBoundNativeRaster(value) {
44
19
  const nativeRaster = requireRecord(value, "colorSamplingBinding.nativeRaster", ["pixelWidth", "pixelHeight"]);
45
20
  const pixelWidth = requirePositiveSafeInteger(nativeRaster.pixelWidth, "colorSamplingBinding.nativeRaster.pixelWidth");