@photivra/engine 2.18.0 → 2.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) hide show
  1. package/CHANGELOG.md +179 -1
  2. package/CITATION.cff +1 -1
  3. package/NODE_SUPPORT.md +7 -5
  4. package/README.md +6 -6
  5. package/SECURITY.md +1 -1
  6. package/THIRD_PARTY.md +14 -10
  7. package/dist/capture/dng-lens-correction-opcodes.d.ts +32 -0
  8. package/dist/capture/dng-lens-correction-opcodes.js +119 -0
  9. package/dist/capture/dng-lens-correction-opcodes.js.map +1 -0
  10. package/dist/capture/focus-control.js +2 -75
  11. package/dist/capture/focus-control.js.map +1 -1
  12. package/dist/capture/native-environment-lean.d.ts +1 -0
  13. package/dist/capture/native-environment-lean.js +2 -6
  14. package/dist/capture/native-environment-lean.js.map +1 -1
  15. package/dist/capture/native-environment-optical-stage.d.ts +1 -0
  16. package/dist/capture/native-environment-optical-stage.js +2 -5
  17. package/dist/capture/native-environment-optical-stage.js.map +1 -1
  18. package/dist/capture/native-environment-raw.js +1 -5
  19. package/dist/capture/native-environment-raw.js.map +1 -1
  20. package/dist/capture/native-raw-dng.d.ts +6 -0
  21. package/dist/capture/native-raw-dng.js +16 -2
  22. package/dist/capture/native-raw-dng.js.map +1 -1
  23. package/dist/capture/release-sequence.js +2 -47
  24. package/dist/capture/release-sequence.js.map +1 -1
  25. package/dist/capture/sensor-raw-producer.js +3 -5
  26. package/dist/capture/sensor-raw-producer.js.map +1 -1
  27. package/dist/color/white-balance.d.ts +2 -0
  28. package/dist/color/white-balance.js +3 -56
  29. package/dist/color/white-balance.js.map +1 -1
  30. package/dist/composition/image-formation-plan.d.ts +1 -0
  31. package/dist/composition/image-formation-plan.js +3 -49
  32. package/dist/composition/image-formation-plan.js.map +1 -1
  33. package/dist/core/evidence-provenance.js +1 -16
  34. package/dist/core/evidence-provenance.js.map +1 -1
  35. package/dist/core/record-validation.d.ts +54 -0
  36. package/dist/core/record-validation.js +176 -0
  37. package/dist/core/record-validation.js.map +1 -1
  38. package/dist/core/scientific-assurance.js +1 -18
  39. package/dist/core/scientific-assurance.js.map +1 -1
  40. package/dist/core/shared-helpers.d.ts +35 -0
  41. package/dist/core/shared-helpers.js +38 -0
  42. package/dist/core/shared-helpers.js.map +1 -0
  43. package/dist/core/spectral-composition.js +1 -36
  44. package/dist/core/spectral-composition.js.map +1 -1
  45. package/dist/core/spectral.js +1 -19
  46. package/dist/core/spectral.js.map +1 -1
  47. package/dist/core/version.d.ts +1 -1
  48. package/dist/core/version.js +1 -1
  49. package/dist/core/version.js.map +1 -1
  50. package/dist/equipment/exposure-capabilities.d.ts +1 -0
  51. package/dist/equipment/exposure-capabilities.js +2 -36
  52. package/dist/equipment/exposure-capabilities.js.map +1 -1
  53. package/dist/equipment/generic-lens-catalogue.d.ts +8 -2
  54. package/dist/equipment/generic-lens-catalogue.js +31 -2
  55. package/dist/equipment/generic-lens-catalogue.js.map +1 -1
  56. package/dist/equipment/generic-tier-assets.js +2 -3
  57. package/dist/equipment/generic-tier-assets.js.map +1 -1
  58. package/dist/equipment/generic-tier-presets.js +2 -3
  59. package/dist/equipment/generic-tier-presets.js.map +1 -1
  60. package/dist/equipment/iso-capabilities.js +2 -49
  61. package/dist/equipment/iso-capabilities.js.map +1 -1
  62. package/dist/equipment/metering-capabilities.js +1 -26
  63. package/dist/equipment/metering-capabilities.js.map +1 -1
  64. package/dist/equipment/release-capabilities.js +1 -53
  65. package/dist/equipment/release-capabilities.js.map +1 -1
  66. package/dist/exposure/auto-iso-slowest-shutter.js +1 -7
  67. package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
  68. package/dist/exposure/exposure-duration-control.js +1 -16
  69. package/dist/exposure/exposure-duration-control.js.map +1 -1
  70. package/dist/exposure/exposure-mode-resolver.js +1 -17
  71. package/dist/exposure/exposure-mode-resolver.js.map +1 -1
  72. package/dist/exposure/filtered-exposure-time.d.ts +93 -0
  73. package/dist/exposure/filtered-exposure-time.js +129 -0
  74. package/dist/exposure/filtered-exposure-time.js.map +1 -0
  75. package/dist/exposure/flash-ttl.d.ts +1 -0
  76. package/dist/exposure/flash-ttl.js +1 -1
  77. package/dist/exposure/flash-ttl.js.map +1 -1
  78. package/dist/exposure/flash.js +1 -49
  79. package/dist/exposure/flash.js.map +1 -1
  80. package/dist/exposure/metering-scene-radiance.js +1 -25
  81. package/dist/exposure/metering-scene-radiance.js.map +1 -1
  82. package/dist/exposure/metering-target.js +1 -30
  83. package/dist/exposure/metering-target.js.map +1 -1
  84. package/dist/exposure/metering-temporal.js +1 -8
  85. package/dist/exposure/metering-temporal.js.map +1 -1
  86. package/dist/exposure/metering.js +1 -72
  87. package/dist/exposure/metering.js.map +1 -1
  88. package/dist/exposure/program-line.js +1 -45
  89. package/dist/exposure/program-line.js.map +1 -1
  90. package/dist/facade/develop-joined-parts.d.ts +29 -0
  91. package/dist/facade/develop-joined-parts.js +33 -0
  92. package/dist/facade/develop-joined-parts.js.map +1 -0
  93. package/dist/facade/display-view-gpu.d.ts +7 -4
  94. package/dist/facade/display-view-gpu.js +4 -4
  95. package/dist/facade/display-view-gpu.js.map +1 -1
  96. package/dist/facade/display-view-lens-signal.d.ts +1 -1
  97. package/dist/facade/display-view-lens-signal.js +5 -3
  98. package/dist/facade/display-view-lens-signal.js.map +1 -1
  99. package/dist/facade/display-view.d.ts +3 -1
  100. package/dist/facade/display-view.js +1 -1
  101. package/dist/facade/display-view.js.map +1 -1
  102. package/dist/facade/fast-methods.d.ts +50 -0
  103. package/dist/facade/fast-methods.js +50 -0
  104. package/dist/facade/fast-methods.js.map +1 -0
  105. package/dist/facade/fast-resolution.d.ts +12 -0
  106. package/dist/facade/fast-resolution.js +18 -0
  107. package/dist/facade/fast-resolution.js.map +1 -0
  108. package/dist/facade/generic-lens-capture.d.ts +6 -3
  109. package/dist/facade/generic-lens-capture.js +6 -3
  110. package/dist/facade/generic-lens-capture.js.map +1 -1
  111. package/dist/facade/simulate-capture.d.ts +88 -16
  112. package/dist/facade/simulate-capture.js +279 -153
  113. package/dist/facade/simulate-capture.js.map +1 -1
  114. package/dist/facade/simulate-display-view.d.ts +3 -2
  115. package/dist/facade/simulate-display-view.js +22 -12
  116. package/dist/facade/simulate-display-view.js.map +1 -1
  117. package/dist/gpu/chart-composite-gpu.js +1 -3
  118. package/dist/gpu/chart-composite-gpu.js.map +1 -1
  119. package/dist/gpu/chart-radiance-gpu.js +1 -5
  120. package/dist/gpu/chart-radiance-gpu.js.map +1 -1
  121. package/dist/gpu/display-view-quality-gpu.d.ts +13 -1
  122. package/dist/gpu/display-view-quality-gpu.js +4 -8
  123. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  124. package/dist/gpu/display-view-speed-gpu.js +3 -25
  125. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  126. package/dist/gpu/sensor-stage-gpu.js +1 -5
  127. package/dist/gpu/sensor-stage-gpu.js.map +1 -1
  128. package/dist/gpu/webgpu-runtime.d.ts +2 -0
  129. package/dist/gpu/webgpu-runtime.js +3 -0
  130. package/dist/gpu/webgpu-runtime.js.map +1 -1
  131. package/dist/index.d.ts +11 -1
  132. package/dist/index.js +8 -0
  133. package/dist/index.js.map +1 -1
  134. package/dist/motion/camera-rotation.js +1 -6
  135. package/dist/motion/camera-rotation.js.map +1 -1
  136. package/dist/motion/capture-rotation-inverse-mapping.d.ts +12 -0
  137. package/dist/motion/capture-rotation-inverse-mapping.js +2 -2
  138. package/dist/motion/capture-rotation-inverse-mapping.js.map +1 -1
  139. package/dist/motion/capture-rotation-temporal-quadrature.d.ts +1 -0
  140. package/dist/motion/capture-rotation-temporal-quadrature.js +1 -1
  141. package/dist/motion/capture-rotation-temporal-quadrature.js.map +1 -1
  142. package/dist/motion/capture-rotation-trajectory.js +2 -17
  143. package/dist/motion/capture-rotation-trajectory.js.map +1 -1
  144. package/dist/motion/capture-translation-parallax-temporal-quadrature.d.ts +1 -0
  145. package/dist/motion/capture-translation-parallax-temporal-quadrature.js +3 -14
  146. package/dist/motion/capture-translation-parallax-temporal-quadrature.js.map +1 -1
  147. package/dist/motion/extended-object-projection.js +5 -40
  148. package/dist/motion/extended-object-projection.js.map +1 -1
  149. package/dist/optics/aperture-diameter.d.ts +47 -0
  150. package/dist/optics/aperture-diameter.js +50 -0
  151. package/dist/optics/aperture-diameter.js.map +1 -0
  152. package/dist/optics/atmospheric-haze.js +1 -7
  153. package/dist/optics/atmospheric-haze.js.map +1 -1
  154. package/dist/optics/brown-conrady-distortion.js +1 -7
  155. package/dist/optics/brown-conrady-distortion.js.map +1 -1
  156. package/dist/optics/complex-pupil-psf.d.ts +1 -0
  157. package/dist/optics/complex-pupil-psf.js +2 -84
  158. package/dist/optics/complex-pupil-psf.js.map +1 -1
  159. package/dist/optics/detail-limit.d.ts +182 -0
  160. package/dist/optics/detail-limit.js +336 -0
  161. package/dist/optics/detail-limit.js.map +1 -0
  162. package/dist/optics/focus-state.js +1 -11
  163. package/dist/optics/focus-state.js.map +1 -1
  164. package/dist/optics/front-of-lens-filter.d.ts +5 -0
  165. package/dist/optics/front-of-lens-filter.js +3 -65
  166. package/dist/optics/front-of-lens-filter.js.map +1 -1
  167. package/dist/optics/generic-lens-psf.d.ts +18 -2
  168. package/dist/optics/generic-lens-psf.js +25 -2
  169. package/dist/optics/generic-lens-psf.js.map +1 -1
  170. package/dist/optics/generic-nd-filter-presets.d.ts +12 -0
  171. package/dist/optics/generic-nd-filter-presets.js +48 -0
  172. package/dist/optics/generic-nd-filter-presets.js.map +1 -0
  173. package/dist/optics/illumination-vignetting.js +1 -6
  174. package/dist/optics/illumination-vignetting.js.map +1 -1
  175. package/dist/optics/lens-psf-profile.d.ts +4 -1
  176. package/dist/optics/lens-psf-profile.js +12 -111
  177. package/dist/optics/lens-psf-profile.js.map +1 -1
  178. package/dist/optics/longitudinal-chromatic-aberration.js +1 -7
  179. package/dist/optics/longitudinal-chromatic-aberration.js.map +1 -1
  180. package/dist/optics/pupil-vignetting.js +1 -7
  181. package/dist/optics/pupil-vignetting.js.map +1 -1
  182. package/dist/optics/radial-distortion.js +1 -6
  183. package/dist/optics/radial-distortion.js.map +1 -1
  184. package/dist/optics/scene-to-sensor-irradiance.d.ts +13 -0
  185. package/dist/optics/scene-to-sensor-irradiance.js +11 -96
  186. package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
  187. package/dist/optics/stray-light.d.ts +1 -0
  188. package/dist/optics/stray-light.js +1 -1
  189. package/dist/optics/stray-light.js.map +1 -1
  190. package/dist/optics/zernike-wavefront.js +1 -7
  191. package/dist/optics/zernike-wavefront.js.map +1 -1
  192. package/dist/output/capture-geometry.js +1 -8
  193. package/dist/output/capture-geometry.js.map +1 -1
  194. package/dist/output/crop-to-match-focal-length.d.ts +62 -0
  195. package/dist/output/crop-to-match-focal-length.js +63 -0
  196. package/dist/output/crop-to-match-focal-length.js.map +1 -0
  197. package/dist/output/developed-lens-corrections.d.ts +62 -0
  198. package/dist/output/developed-lens-corrections.js +135 -0
  199. package/dist/output/developed-lens-corrections.js.map +1 -0
  200. package/dist/output/generic-lens-corrections.d.ts +5 -7
  201. package/dist/output/generic-lens-corrections.js +11 -10
  202. package/dist/output/generic-lens-corrections.js.map +1 -1
  203. package/dist/output/geometric-transforms.js +1 -4
  204. package/dist/output/geometric-transforms.js.map +1 -1
  205. package/dist/output/lens-corrections.d.ts +12 -2
  206. package/dist/output/lens-corrections.js +22 -7
  207. package/dist/output/lens-corrections.js.map +1 -1
  208. package/dist/output/native-capture-sdr.d.ts +2 -2
  209. package/dist/output/native-capture-sdr.js +3 -2
  210. package/dist/output/native-capture-sdr.js.map +1 -1
  211. package/dist/output/print-contrast-reference.js +1 -1
  212. package/dist/output/print-contrast-reference.js.map +1 -1
  213. package/dist/output/print-size-blur-noise.d.ts +110 -0
  214. package/dist/output/print-size-blur-noise.js +138 -0
  215. package/dist/output/print-size-blur-noise.js.map +1 -0
  216. package/dist/processing/development-detail.d.ts +29 -0
  217. package/dist/processing/development-detail.js +128 -107
  218. package/dist/processing/development-detail.js.map +1 -1
  219. package/dist/schema/illumination-temporal.js +3 -72
  220. package/dist/schema/illumination-temporal.js.map +1 -1
  221. package/dist/schema/illumination.d.ts +1 -0
  222. package/dist/schema/illumination.js +2 -65
  223. package/dist/schema/illumination.js.map +1 -1
  224. package/dist/schema/scene-radiance-temporal.js +2 -14
  225. package/dist/schema/scene-radiance-temporal.js.map +1 -1
  226. package/dist/schema/scene-radiance.js +2 -60
  227. package/dist/schema/scene-radiance.js.map +1 -1
  228. package/dist/schema/validation.d.ts +2 -0
  229. package/dist/schema/validation.js +2 -17
  230. package/dist/schema/validation.js.map +1 -1
  231. package/dist/sensor/accumulated-charge.js +2 -59
  232. package/dist/sensor/accumulated-charge.js.map +1 -1
  233. package/dist/sensor/architecture.js +1 -16
  234. package/dist/sensor/architecture.js.map +1 -1
  235. package/dist/sensor/camera-saturation-capacity.d.ts +1 -0
  236. package/dist/sensor/camera-saturation-capacity.js +3 -55
  237. package/dist/sensor/camera-saturation-capacity.js.map +1 -1
  238. package/dist/sensor/capture-color-sampling-binding.js +1 -26
  239. package/dist/sensor/capture-color-sampling-binding.js.map +1 -1
  240. package/dist/sensor/capture-mode-timing.js +1 -39
  241. package/dist/sensor/capture-mode-timing.js.map +1 -1
  242. package/dist/sensor/capture-mode.js +1 -23
  243. package/dist/sensor/capture-mode.js.map +1 -1
  244. package/dist/sensor/color-sampling.js +1 -26
  245. package/dist/sensor/color-sampling.js.map +1 -1
  246. package/dist/sensor/constant-rate-temporal-integration.js +2 -46
  247. package/dist/sensor/constant-rate-temporal-integration.js.map +1 -1
  248. package/dist/sensor/dark-current.js +2 -45
  249. package/dist/sensor/dark-current.js.map +1 -1
  250. package/dist/sensor/eqe-electron-rate.js +2 -41
  251. package/dist/sensor/eqe-electron-rate.js.map +1 -1
  252. package/dist/sensor/exposure-window.d.ts +7 -0
  253. package/dist/sensor/exposure-window.js +9 -24
  254. package/dist/sensor/exposure-window.js.map +1 -1
  255. package/dist/sensor/iso-gain.js +1 -7
  256. package/dist/sensor/iso-gain.js.map +1 -1
  257. package/dist/sensor/iso-signal-chain.js +1 -43
  258. package/dist/sensor/iso-signal-chain.js.map +1 -1
  259. package/dist/sensor/optical-stack.js +1 -25
  260. package/dist/sensor/optical-stack.js.map +1 -1
  261. package/dist/sensor/photon-energy.js +1 -26
  262. package/dist/sensor/photon-energy.js.map +1 -1
  263. package/dist/sensor/physical-charge-capacity.js +3 -77
  264. package/dist/sensor/physical-charge-capacity.js.map +1 -1
  265. package/dist/sensor/radiometry-readiness.js +2 -23
  266. package/dist/sensor/radiometry-readiness.js.map +1 -1
  267. package/dist/sensor/raw-readout.d.ts +3 -0
  268. package/dist/sensor/raw-readout.js +4 -66
  269. package/dist/sensor/raw-readout.js.map +1 -1
  270. package/dist/sensor/raw-reconstruction.js +2 -64
  271. package/dist/sensor/raw-reconstruction.js.map +1 -1
  272. package/dist/sensor/readout-exposure-linkage.js +1 -9
  273. package/dist/sensor/readout-exposure-linkage.js.map +1 -1
  274. package/dist/sensor/readout-timing.js +4 -79
  275. package/dist/sensor/readout-timing.js.map +1 -1
  276. package/dist/sensor/response-application-compatibility.d.ts +2 -0
  277. package/dist/sensor/response-application-compatibility.js +4 -35
  278. package/dist/sensor/response-application-compatibility.js.map +1 -1
  279. package/dist/sensor/response-operating-range.d.ts +1 -0
  280. package/dist/sensor/response-operating-range.js +3 -79
  281. package/dist/sensor/response-operating-range.js.map +1 -1
  282. package/dist/sensor/responsivity-photocurrent.js +4 -114
  283. package/dist/sensor/responsivity-photocurrent.js.map +1 -1
  284. package/dist/sensor/sampling-aperture.js +1 -25
  285. package/dist/sensor/sampling-aperture.js.map +1 -1
  286. package/dist/sensor/sensor-characteristics.d.ts +137 -0
  287. package/dist/sensor/sensor-characteristics.js +115 -0
  288. package/dist/sensor/sensor-characteristics.js.map +1 -0
  289. package/dist/sensor/spatial-sample-reduction.js +3 -24
  290. package/dist/sensor/spatial-sample-reduction.js.map +1 -1
  291. package/dist/sensor/spatial-sampling-quadrature.js +1 -3
  292. package/dist/sensor/spatial-sampling-quadrature.js.map +1 -1
  293. package/dist/sensor/spatio-spectral-reduction.js +1 -18
  294. package/dist/sensor/spatio-spectral-reduction.js.map +1 -1
  295. package/dist/sensor/spectral-coverage.d.ts +10 -1
  296. package/dist/sensor/spectral-coverage.js +1 -1
  297. package/dist/sensor/spectral-coverage.js.map +1 -1
  298. package/dist/sensor/spectral-quadrature.js +3 -41
  299. package/dist/sensor/spectral-quadrature.js.map +1 -1
  300. package/dist/sensor/spectral-response.js +1 -47
  301. package/dist/sensor/spectral-response.js.map +1 -1
  302. package/dist/sensor/temporal-photo-signal.d.ts +3 -0
  303. package/dist/sensor/temporal-photo-signal.js +5 -2
  304. package/dist/sensor/temporal-photo-signal.js.map +1 -1
  305. package/dist/stabilization/camera-shake.js +1 -6
  306. package/dist/stabilization/camera-shake.js.map +1 -1
  307. package/dist/stabilization/system.d.ts +9 -0
  308. package/dist/stabilization/system.js +7 -86
  309. package/dist/stabilization/system.js.map +1 -1
  310. package/docs/AGENT_GUIDE.md +5 -0
  311. package/docs/API_REFERENCE.md +116 -3
  312. package/docs/QUICK_START.md +178 -14
  313. package/docs/USAGE.md +111 -3
  314. package/docs/api/capture-native-raw-dng.md +6 -0
  315. package/docs/api/core-version.md +1 -1
  316. package/docs/api/equipment-generic-lens-catalogue.md +7 -2
  317. package/docs/api/exports.json +203 -3
  318. package/docs/api/exposure-filtered-exposure-time.md +105 -0
  319. package/docs/api/facade-develop-joined-parts.md +32 -0
  320. package/docs/api/facade-display-view-gpu.md +5 -2
  321. package/docs/api/facade-fast-methods.md +91 -0
  322. package/docs/api/facade-simulate-capture.md +72 -16
  323. package/docs/api/facade-simulate-display-view.md +3 -2
  324. package/docs/api/optics-aperture-diameter.md +51 -0
  325. package/docs/api/optics-detail-limit.md +203 -0
  326. package/docs/api/optics-generic-nd-filter-presets.md +34 -0
  327. package/docs/api/optics-lens-psf-profile.md +3 -1
  328. package/docs/api/output-crop-to-match-focal-length.md +67 -0
  329. package/docs/api/output-developed-lens-corrections.md +12 -0
  330. package/docs/api/output-generic-lens-corrections.md +3 -10
  331. package/docs/api/output-lens-corrections.md +11 -1
  332. package/docs/api/output-print-size-blur-noise.md +115 -0
  333. package/docs/api/sensor-sensor-characteristics.md +105 -0
  334. package/llms.txt +5 -0
  335. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/schema/validation.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Runtime parsers for untrusted camera configurations and scene definitions.\n * @see docs/SCENE_RADIANCE_AND_ILLUMINATION.md for equations, coordinate/unit conventions, blockers\n * and support limits.\n */\n\nimport { rejectUnknownFields, rejectUnknownVariantFields } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nexport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\nimport type {\n CameraConfiguration,\n CameraSupport\n} from \"./camera.js\";\nimport type {\n SceneCapability,\n SceneDefinition\n} from \"./scene.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nconst CAMERA_SUPPORTS = new Set<CameraSupport>([\n \"handheld\",\n \"braced\",\n \"monopod\",\n \"tripod\"\n]);\n\nconst SCENE_CAPABILITIES = new Set<SceneCapability>([\n \"scene-linear-rgb\",\n \"metric-depth\",\n \"segmentation\",\n \"occlusion-layers\",\n \"motion-vectors\",\n \"focus-targets\",\n \"multi-resolution-layers\",\n \"absolute-radiometry\"\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 requireString(\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 requireBoolean(\n record: UnknownRecord,\n key: string,\n path: string\n): boolean {\n const value = record[key];\n if (typeof value !== \"boolean\") {\n throw new InvalidConfigurationError(\n `${path}.${key} must be a boolean.`\n );\n }\n return value;\n}\n\nfunction requirePositiveNumber(\n record: UnknownRecord,\n key: string,\n path: string\n): number {\n const value = record[key];\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new InvalidConfigurationError(\n `${path}.${key} must be a finite number greater than zero.`\n );\n }\n return value;\n}\n\nfunction requirePositiveIntegerValue(\n record: UnknownRecord,\n key: string,\n path: string\n): number {\n const value = record[key];\n if (!Number.isSafeInteger(value) || (value as number) <= 0) {\n throw new InvalidConfigurationError(\n `${path}.${key} must be a positive safe integer.`\n );\n }\n return value as number;\n}\n\nfunction assertVector3(value: unknown, path: string): void {\n const vector = requireRecord(value, path, [\"x\", \"y\", \"z\"]);\n for (const component of [\"x\", \"y\", \"z\"] as const) {\n const coordinate = vector[component];\n if (typeof coordinate !== \"number\" || !Number.isFinite(coordinate)) {\n throw new InvalidConfigurationError(\n `${path}.${component} must be a finite number.`\n );\n }\n }\n}\n\nfunction assertStringArray(value: unknown, path: string): readonly string[] {\n if (\n !Array.isArray(value) ||\n value.some(\n (entry) => typeof entry !== \"string\" || entry.trim().length === 0\n )\n ) {\n throw new InvalidConfigurationError(\n `${path} must be an array of non-empty strings.`\n );\n }\n return value as readonly string[];\n}\n\nfunction assertUniqueStrings(values: readonly string[], path: string): void {\n const normalized = values.map((value) => value.trim());\n if (new Set(normalized).size !== normalized.length) {\n throw new InvalidConfigurationError(`${path} must not contain duplicates.`);\n }\n}\n\n/**\n * Parses and validates an untrusted camera configuration.\n *\n * @param value Unknown external data.\n * @returns The original value narrowed to CameraConfiguration.\n */\nexport function parseCameraConfiguration(\n value: unknown\n): CameraConfiguration {\n const camera = requireRecord(value, \"camera\", [\"sensor\", \"lens\", \"exposure\", \"focus\", \"stabilization\"]);\n\n const sensor = requireRecord(camera.sensor, \"camera.sensor\", [\"widthMm\", \"heightMm\", \"pixelWidth\", \"pixelHeight\"]);\n requirePositiveNumber(sensor, \"widthMm\", \"camera.sensor\");\n requirePositiveNumber(sensor, \"heightMm\", \"camera.sensor\");\n requirePositiveIntegerValue(sensor, \"pixelWidth\", \"camera.sensor\");\n requirePositiveIntegerValue(sensor, \"pixelHeight\", \"camera.sensor\");\n\n const lens = requireRecord(camera.lens, \"camera.lens\", [\"focalLengthMm\", \"aperture\"]);\n requirePositiveNumber(lens, \"focalLengthMm\", \"camera.lens\");\n requirePositiveNumber(lens, \"aperture\", \"camera.lens\");\n\n const exposure = requireRecord(camera.exposure, \"camera.exposure\", [\"shutterSeconds\", \"iso\"]);\n requirePositiveNumber(exposure, \"shutterSeconds\", \"camera.exposure\");\n requirePositiveNumber(exposure, \"iso\", \"camera.exposure\");\n\n const focus = requireRecord(camera.focus, \"camera.focus\", [\"focusDistanceM\"]);\n requirePositiveNumber(focus, \"focusDistanceM\", \"camera.focus\");\n\n if (camera.stabilization !== undefined) {\n const stabilization = requireRecord(\n camera.stabilization,\n \"camera.stabilization\", [\"bodyEnabled\", \"lensEnabled\", \"support\"]\n );\n requireBoolean(stabilization, \"bodyEnabled\", \"camera.stabilization\");\n requireBoolean(stabilization, \"lensEnabled\", \"camera.stabilization\");\n const support = requireString(\n stabilization,\n \"support\",\n \"camera.stabilization\"\n );\n if (!CAMERA_SUPPORTS.has(support as CameraSupport)) {\n throw new InvalidConfigurationError(\n \"camera.stabilization.support is invalid.\"\n );\n }\n }\n\n return value as CameraConfiguration;\n}\n\n/**\n * Parses and validates an untrusted renderer-independent scene definition.\n *\n * @param value Unknown external data.\n * @returns The original value narrowed to SceneDefinition.\n */\nexport function parseSceneDefinition(value: unknown): SceneDefinition {\n const scene = requireRecord(value, \"scene\", [\"schemaVersion\", \"id\", \"version\", \"radiometry\", \"capabilities\", \"objects\", \"focusTargetIds\"]);\n requireString(scene, \"schemaVersion\", \"scene\");\n requireString(scene, \"id\", \"scene\");\n requireString(scene, \"version\", \"scene\");\n\n const radiometry = requireRecord(scene.radiometry, \"scene.radiometry\", null);\n rejectUnknownVariantFields(radiometry, \"kind\", { \"relative-linear\": [\"kind\", \"referenceValue\"], \"absolute-luminance\": [\"kind\", \"referenceValue\", \"referenceLuminanceCdM2\"] }, \"scene.radiometry\");\n const radiometryKind = requireString(\n radiometry,\n \"kind\",\n \"scene.radiometry\"\n );\n requirePositiveNumber(radiometry, \"referenceValue\", \"scene.radiometry\");\n\n if (radiometryKind === \"absolute-luminance\") {\n requirePositiveNumber(\n radiometry,\n \"referenceLuminanceCdM2\",\n \"scene.radiometry\"\n );\n } else if (radiometryKind !== \"relative-linear\") {\n throw new InvalidConfigurationError(\"scene.radiometry.kind is invalid.\");\n }\n\n const capabilities = assertStringArray(\n scene.capabilities,\n \"scene.capabilities\"\n );\n assertUniqueStrings(capabilities, \"scene.capabilities\");\n for (const capability of capabilities) {\n if (!SCENE_CAPABILITIES.has(capability as SceneCapability)) {\n throw new InvalidConfigurationError(\n `Unknown scene capability: ${capability}`\n );\n }\n }\n\n if (!Array.isArray(scene.objects)) {\n throw new InvalidConfigurationError(\"scene.objects must be an array.\");\n }\n\n const objectIds: string[] = [];\n scene.objects.forEach((objectValue, index) => {\n const path = `scene.objects[${index}]`;\n const object = requireRecord(objectValue, path, [\"id\", \"distanceM\", \"label\", \"tags\", \"motion\"]);\n objectIds.push(requireString(object, \"id\", path));\n requirePositiveNumber(object, \"distanceM\", path);\n\n if (object.label !== undefined && typeof object.label !== \"string\") {\n throw new InvalidConfigurationError(`${path}.label must be a string.`);\n }\n if (object.tags !== undefined) {\n assertStringArray(object.tags, `${path}.tags`);\n }\n if (object.motion !== undefined) {\n const motion = requireRecord(object.motion, `${path}.motion`, [\"linearVelocityMps\", \"angularVelocityRadPerSec\"]);\n assertVector3(\n motion.linearVelocityMps,\n `${path}.motion.linearVelocityMps`\n );\n if (motion.angularVelocityRadPerSec !== undefined) {\n assertVector3(\n motion.angularVelocityRadPerSec,\n `${path}.motion.angularVelocityRadPerSec`\n );\n }\n }\n });\n assertUniqueStrings(objectIds, \"scene.objects[].id\");\n\n if (scene.focusTargetIds !== undefined) {\n const focusTargetIds = assertStringArray(\n scene.focusTargetIds,\n \"scene.focusTargetIds\"\n );\n assertUniqueStrings(focusTargetIds, \"scene.focusTargetIds\");\n const knownIds = new Set(objectIds);\n for (const focusTargetId of focusTargetIds) {\n if (!knownIds.has(focusTargetId)) {\n throw new InvalidConfigurationError(\n `Unknown scene focus target: ${focusTargetId}`\n );\n }\n }\n }\n\n return value as SceneDefinition;\n}\n"]}
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/schema/validation.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Runtime parsers for untrusted camera configurations and scene definitions.\n * @see docs/SCENE_RADIANCE_AND_ILLUMINATION.md for equations, coordinate/unit conventions, blockers\n * and support limits.\n */\n\nimport { rejectUnknownVariantFields, requireConfigRecord as requireRecord, requireConfigRecordString as requireString } from \"../core/record-validation.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nexport { InvalidConfigurationError } from \"../core/configuration-error.js\";\n\nimport type {\n CameraConfiguration,\n CameraSupport\n} from \"./camera.js\";\nimport type {\n SceneCapability,\n SceneDefinition\n} from \"./scene.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nconst CAMERA_SUPPORTS = new Set<CameraSupport>([\n \"handheld\",\n \"braced\",\n \"monopod\",\n \"tripod\"\n]);\n\nconst SCENE_CAPABILITIES = new Set<SceneCapability>([\n \"scene-linear-rgb\",\n \"metric-depth\",\n \"segmentation\",\n \"occlusion-layers\",\n \"motion-vectors\",\n \"focus-targets\",\n \"multi-resolution-layers\",\n \"absolute-radiometry\"\n]);\n\nfunction requireBoolean(\n record: UnknownRecord,\n key: string,\n path: string\n): boolean {\n const value = record[key];\n if (typeof value !== \"boolean\") {\n throw new InvalidConfigurationError(\n `${path}.${key} must be a boolean.`\n );\n }\n return value;\n}\n\nexport function requirePositiveNumber(\n record: UnknownRecord,\n key: string,\n path: string\n): number {\n const value = record[key];\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new InvalidConfigurationError(\n `${path}.${key} must be a finite number greater than zero.`\n );\n }\n return value;\n}\n\nfunction requirePositiveIntegerValue(\n record: UnknownRecord,\n key: string,\n path: string\n): number {\n const value = record[key];\n if (!Number.isSafeInteger(value) || (value as number) <= 0) {\n throw new InvalidConfigurationError(\n `${path}.${key} must be a positive safe integer.`\n );\n }\n return value as number;\n}\n\nfunction assertVector3(value: unknown, path: string): void {\n const vector = requireRecord(value, path, [\"x\", \"y\", \"z\"]);\n for (const component of [\"x\", \"y\", \"z\"] as const) {\n const coordinate = vector[component];\n if (typeof coordinate !== \"number\" || !Number.isFinite(coordinate)) {\n throw new InvalidConfigurationError(\n `${path}.${component} must be a finite number.`\n );\n }\n }\n}\n\nfunction assertStringArray(value: unknown, path: string): readonly string[] {\n if (\n !Array.isArray(value) ||\n value.some(\n (entry) => typeof entry !== \"string\" || entry.trim().length === 0\n )\n ) {\n throw new InvalidConfigurationError(\n `${path} must be an array of non-empty strings.`\n );\n }\n return value as readonly string[];\n}\n\nfunction assertUniqueStrings(values: readonly string[], path: string): void {\n const normalized = values.map((value) => value.trim());\n if (new Set(normalized).size !== normalized.length) {\n throw new InvalidConfigurationError(`${path} must not contain duplicates.`);\n }\n}\n\n/**\n * Parses and validates an untrusted camera configuration.\n *\n * @param value Unknown external data.\n * @returns The original value narrowed to CameraConfiguration.\n */\nexport function parseCameraConfiguration(\n value: unknown\n): CameraConfiguration {\n const camera = requireRecord(value, \"camera\", [\"sensor\", \"lens\", \"exposure\", \"focus\", \"stabilization\"]);\n\n const sensor = requireRecord(camera.sensor, \"camera.sensor\", [\"widthMm\", \"heightMm\", \"pixelWidth\", \"pixelHeight\"]);\n requirePositiveNumber(sensor, \"widthMm\", \"camera.sensor\");\n requirePositiveNumber(sensor, \"heightMm\", \"camera.sensor\");\n requirePositiveIntegerValue(sensor, \"pixelWidth\", \"camera.sensor\");\n requirePositiveIntegerValue(sensor, \"pixelHeight\", \"camera.sensor\");\n\n const lens = requireRecord(camera.lens, \"camera.lens\", [\"focalLengthMm\", \"aperture\"]);\n requirePositiveNumber(lens, \"focalLengthMm\", \"camera.lens\");\n requirePositiveNumber(lens, \"aperture\", \"camera.lens\");\n\n const exposure = requireRecord(camera.exposure, \"camera.exposure\", [\"shutterSeconds\", \"iso\"]);\n requirePositiveNumber(exposure, \"shutterSeconds\", \"camera.exposure\");\n requirePositiveNumber(exposure, \"iso\", \"camera.exposure\");\n\n const focus = requireRecord(camera.focus, \"camera.focus\", [\"focusDistanceM\"]);\n requirePositiveNumber(focus, \"focusDistanceM\", \"camera.focus\");\n\n if (camera.stabilization !== undefined) {\n const stabilization = requireRecord(\n camera.stabilization,\n \"camera.stabilization\", [\"bodyEnabled\", \"lensEnabled\", \"support\"]\n );\n requireBoolean(stabilization, \"bodyEnabled\", \"camera.stabilization\");\n requireBoolean(stabilization, \"lensEnabled\", \"camera.stabilization\");\n const support = requireString(\n stabilization,\n \"support\",\n \"camera.stabilization\"\n );\n if (!CAMERA_SUPPORTS.has(support as CameraSupport)) {\n throw new InvalidConfigurationError(\n \"camera.stabilization.support is invalid.\"\n );\n }\n }\n\n return value as CameraConfiguration;\n}\n\n/**\n * Parses and validates an untrusted renderer-independent scene definition.\n *\n * @param value Unknown external data.\n * @returns The original value narrowed to SceneDefinition.\n */\nexport function parseSceneDefinition(value: unknown): SceneDefinition {\n const scene = requireRecord(value, \"scene\", [\"schemaVersion\", \"id\", \"version\", \"radiometry\", \"capabilities\", \"objects\", \"focusTargetIds\"]);\n requireString(scene, \"schemaVersion\", \"scene\");\n requireString(scene, \"id\", \"scene\");\n requireString(scene, \"version\", \"scene\");\n\n const radiometry = requireRecord(scene.radiometry, \"scene.radiometry\", null);\n rejectUnknownVariantFields(radiometry, \"kind\", { \"relative-linear\": [\"kind\", \"referenceValue\"], \"absolute-luminance\": [\"kind\", \"referenceValue\", \"referenceLuminanceCdM2\"] }, \"scene.radiometry\");\n const radiometryKind = requireString(\n radiometry,\n \"kind\",\n \"scene.radiometry\"\n );\n requirePositiveNumber(radiometry, \"referenceValue\", \"scene.radiometry\");\n\n if (radiometryKind === \"absolute-luminance\") {\n requirePositiveNumber(\n radiometry,\n \"referenceLuminanceCdM2\",\n \"scene.radiometry\"\n );\n } else if (radiometryKind !== \"relative-linear\") {\n throw new InvalidConfigurationError(\"scene.radiometry.kind is invalid.\");\n }\n\n const capabilities = assertStringArray(\n scene.capabilities,\n \"scene.capabilities\"\n );\n assertUniqueStrings(capabilities, \"scene.capabilities\");\n for (const capability of capabilities) {\n if (!SCENE_CAPABILITIES.has(capability as SceneCapability)) {\n throw new InvalidConfigurationError(\n `Unknown scene capability: ${capability}`\n );\n }\n }\n\n if (!Array.isArray(scene.objects)) {\n throw new InvalidConfigurationError(\"scene.objects must be an array.\");\n }\n\n const objectIds: string[] = [];\n scene.objects.forEach((objectValue, index) => {\n const path = `scene.objects[${index}]`;\n const object = requireRecord(objectValue, path, [\"id\", \"distanceM\", \"label\", \"tags\", \"motion\"]);\n objectIds.push(requireString(object, \"id\", path));\n requirePositiveNumber(object, \"distanceM\", path);\n\n if (object.label !== undefined && typeof object.label !== \"string\") {\n throw new InvalidConfigurationError(`${path}.label must be a string.`);\n }\n if (object.tags !== undefined) {\n assertStringArray(object.tags, `${path}.tags`);\n }\n if (object.motion !== undefined) {\n const motion = requireRecord(object.motion, `${path}.motion`, [\"linearVelocityMps\", \"angularVelocityRadPerSec\"]);\n assertVector3(\n motion.linearVelocityMps,\n `${path}.motion.linearVelocityMps`\n );\n if (motion.angularVelocityRadPerSec !== undefined) {\n assertVector3(\n motion.angularVelocityRadPerSec,\n `${path}.motion.angularVelocityRadPerSec`\n );\n }\n }\n });\n assertUniqueStrings(objectIds, \"scene.objects[].id\");\n\n if (scene.focusTargetIds !== undefined) {\n const focusTargetIds = assertStringArray(\n scene.focusTargetIds,\n \"scene.focusTargetIds\"\n );\n assertUniqueStrings(focusTargetIds, \"scene.focusTargetIds\");\n const knownIds = new Set(objectIds);\n for (const focusTargetId of focusTargetIds) {\n if (!knownIds.has(focusTargetId)) {\n throw new InvalidConfigurationError(\n `Unknown scene focus target: ${focusTargetId}`\n );\n }\n }\n }\n\n return value as SceneDefinition;\n}\n"]}
@@ -17,55 +17,8 @@ import { InvalidConfigurationError } from "../core/configuration-error.js";
17
17
  import { parseEvidenceList } from "../core/evidence-provenance.js";
18
18
  import { parseSensorEqeTemporalPhotoSignal } from "./temporal-photo-signal.js";
19
19
  import { InvalidScientificInputError } from "../core/validation.js";
20
- import { requireInputObject, requireInputKeys, rejectUnknownFields, rejectUnknownVariantFields, UNCERTAINTY_VARIANT_FIELDS } from "../core/record-validation.js";
21
- function addCompensated(state, value) {
22
- const adjusted = value - state.correction;
23
- const next = state.sum + adjusted;
24
- state.correction =
25
- (next - state.sum) - adjusted;
26
- state.sum = next;
27
- }
28
- function requireRecord(value, path, allowedKeys) {
29
- if (typeof value !== "object" ||
30
- value === null ||
31
- Array.isArray(value)) {
32
- throw new InvalidConfigurationError(path + " must be an object.");
33
- }
34
- if (allowedKeys !== null)
35
- rejectUnknownFields(value, allowedKeys, path);
36
- return value;
37
- }
38
- function requireNonEmptyString(value, path) {
39
- if (typeof value !== "string" ||
40
- value.trim().length === 0) {
41
- throw new InvalidConfigurationError(path + " must be a non-empty string.");
42
- }
43
- return value;
44
- }
45
- function requireFinite(value, path) {
46
- if (typeof value !== "number" ||
47
- !Number.isFinite(value)) {
48
- throw new InvalidConfigurationError(path + " must be finite.");
49
- }
50
- return value;
51
- }
52
- function requireNonNegativeFinite(value, path) {
53
- const parsed = requireFinite(value, path);
54
- if (parsed < 0) {
55
- throw new InvalidConfigurationError(path +
56
- " must be greater than or equal to zero.");
57
- }
58
- return parsed;
59
- }
60
- function requireSiteIndex(value, path) {
61
- if (typeof value !== "number" ||
62
- !Number.isSafeInteger(value) ||
63
- value < 0) {
64
- throw new InvalidConfigurationError(path +
65
- " must be a non-negative safe integer.");
66
- }
67
- return value;
68
- }
20
+ import { 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";
21
+ import { addCompensated, sameSite } from "../core/shared-helpers.js";
69
22
  function parseUncertainty(value, path) {
70
23
  const record = requireRecord(value, path, null);
71
24
  rejectUnknownVariantFields(record, "kind", UNCERTAINTY_VARIANT_FIELDS, path);
@@ -85,13 +38,6 @@ function parseUncertainty(value, path) {
85
38
  }
86
39
  throw new InvalidConfigurationError(path + ".kind is invalid.");
87
40
  }
88
- function parseStatus(value, path) {
89
- if (value !== "calibrated" &&
90
- value !== "approximation") {
91
- throw new InvalidConfigurationError(path + " is invalid.");
92
- }
93
- return value;
94
- }
95
41
  function parseWindowIdentity(record, path) {
96
42
  if (record.timeReference !==
97
43
  "first-opening-boundary-phase") {
@@ -230,9 +176,6 @@ export function parseSensorAccumulatedChargeCompletenessProfile(value) {
230
176
  : { limitation })
231
177
  };
232
178
  }
233
- function sameSite(a, b) {
234
- return a.x === b.x && a.y === b.y;
235
- }
236
179
  function sameWindow(startA, endA, startB, endB) {
237
180
  return (startA === startB &&
238
181
  endA === endB);
@@ -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 !==