@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":"camera-saturation-capacity.js","sourceRoot":"","sources":["../../src/sensor/camera-saturation-capacity.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Camera saturation capacity: checks whether the expected photo signal would exceed the camera's\n * declared saturation limit.\n *\n * Camera saturation capacity is the largest photo signal, in electron equivalents and after dark\n * correction, that the whole camera response chain records before saturating. It is not the\n * physical charge a pixel can store, not the ADC maximum, and does not say which hardware stage\n * limits it. The check compares expected values only; it does not make up codes beyond saturation.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport type {\n SensorPhotoAccumulatedChargeComposition as SensorAccumulatedChargeComposition\n} from \"./accumulated-charge.js\";\nimport type {\n SensorSpectralResponseScientificStatus,\n SensorSpectralResponseUncertainty\n} from \"./spectral-response.js\";\nimport { requireInputObject, requireInputKeys, rejectUnknownFields, rejectUnknownVariantFields, UNCERTAINTY_VARIANT_FIELDS } from \"../core/record-validation.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport type SensorCameraSaturationSiteApplicability =\n | {\n kind: \"exact-site\";\n site: {\n x: number;\n y: number;\n };\n }\n | {\n kind:\n \"channel-population-mean-approximation\";\n limitation: string;\n evidence:\n readonly EvidenceProvenance[];\n };\n\nexport type SensorCameraSaturationTemperatureApplicability =\n | {\n kind:\n \"exact-reference-temperature\";\n temperatureC: number;\n }\n | {\n kind: \"not-modeled\";\n limitation: string;\n evidence:\n readonly EvidenceProvenance[];\n };\n\nexport interface SensorCameraSaturationCapacityProfile {\n schemaVersion: \"0.1.0\";\n profileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\";\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\";\n saturationCapacityElectrons: number;\n scientificStatus:\n SensorSpectralResponseScientificStatus;\n uncertainty:\n SensorSpectralResponseUncertainty;\n evidence:\n readonly EvidenceProvenance[];\n measurementMethodId: string;\n measurementMethodEvidence:\n readonly EvidenceProvenance[];\n siteApplicability:\n SensorCameraSaturationSiteApplicability;\n operatingState: {\n stateId: string;\n evidence:\n readonly EvidenceProvenance[];\n };\n temperatureApplicability:\n SensorCameraSaturationTemperatureApplicability;\n}\n\nexport interface AssessSensorCameraSaturationCapacityInput {\n accumulatedCharge:\n SensorAccumulatedChargeComposition;\n saturationProfile:\n SensorCameraSaturationCapacityProfile;\n operatingStateId: string;\n operatingTemperatureC: number;\n}\n\nexport interface SensorCameraSaturationCapacityAssessment {\n saturationProfileId: string;\n completenessProfileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n operatingStateId: string;\n operatingTemperatureC: number;\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\";\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\";\n cameraSignalSaturationCapacityElectrons:\n number;\n expectedPhotoSignalElectronCount:\n number;\n totalExpectedStoredElectronCountDiagnostic:\n number;\n totalStoredChargeUsedForCameraSaturationComparison:\n false;\n expectedSignalToCameraSaturationRatio:\n number;\n expectedSignalHeadroomElectrons:\n number;\n expectedCameraSignalCapacityStatus:\n | \"below-capacity\"\n | \"at-capacity\"\n | \"above-capacity\";\n expectedPhotoSignalExceedsCameraCapacity:\n boolean;\n cameraSaturationCapacityAssessmentPerformed:\n true;\n assessmentMeaning:\n \"expected-dark-corrected-photo-signal-vs-camera-saturation-capacity\";\n physicalChargeCapacityUsed:\n false;\n limitingSaturationMechanismResolved:\n false;\n analogClippingThresholdModeled:\n false;\n digitalClippingThresholdModeled:\n false;\n adcMaximumCodeUsed:\n false;\n actualStochasticSaturationStateKnown:\n false;\n stochasticSaturationProbabilityAssessed:\n false;\n outputSignalAfterSaturationCalculated:\n false;\n clampApplied: false;\n physicalStoredChargeModified:\n false;\n adcQuantizationApplied: false;\n rawCodeValueProduced: false;\n saturationCapacityUncertaintyPropagated:\n false;\n photoSignalUncertaintyPropagated:\n false;\n componentEvidence: {\n saturationCapacity:\n readonly EvidenceProvenance[];\n measurementMethod:\n readonly EvidenceProvenance[];\n operatingState:\n readonly EvidenceProvenance[];\n siteApproximation:\n readonly EvidenceProvenance[];\n temperatureApproximation:\n readonly EvidenceProvenance[];\n chargeCompleteness:\n SensorAccumulatedChargeComposition[\"componentEvidence\"][\"completeness\"];\n };\n}\n\nfunction requireRecord(\n value: unknown,\n path: string,\n allowedKeys: readonly string[] | null\n): UnknownRecord {\n if (\n typeof value !== \"object\" ||\n value === null ||\n Array.isArray(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be an object.\"\n );\n }\n if (allowedKeys !== null) rejectUnknownFields(value, allowedKeys, path);\n return value as UnknownRecord;\n}\n\nfunction requireNonEmptyString(\n value: unknown,\n path: string\n): string {\n if (\n typeof value !== \"string\" ||\n value.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path + \" must be a non-empty string.\"\n );\n }\n return value;\n}\n\nfunction requireFinite(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be finite.\"\n );\n }\n return value;\n}\n\nfunction requirePositiveFinite(\n value: unknown,\n path: string\n): number {\n const parsed = requireFinite(\n value,\n path\n );\n if (parsed <= 0) {\n throw new InvalidConfigurationError(\n path +\n \" must be greater than zero.\"\n );\n }\n return parsed;\n}\n\nfunction requireNonNegativeFiniteInput(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n value < 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be finite and greater than or equal to zero.\"\n );\n }\n return value;\n}\n\nfunction requireSiteIndex(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isSafeInteger(value) ||\n value < 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must be a non-negative safe integer.\"\n );\n }\n return value;\n}\n\nfunction parseUncertainty(\n value: unknown,\n path: string\n): SensorSpectralResponseUncertainty {\n const record = requireRecord(\n value,\n path, null\n );\n rejectUnknownVariantFields(record, \"kind\", UNCERTAINTY_VARIANT_FIELDS, path);\n if (record.kind === \"relative\") {\n const fraction = requireFinite(\n record.fraction,\n path + \".fraction\"\n );\n if (fraction < 0) {\n throw new InvalidConfigurationError(\n path +\n \".fraction must be nonnegative.\"\n );\n }\n return {\n kind: \"relative\",\n fraction,\n basis: requireNonEmptyString(\n record.basis,\n path + \".basis\"\n )\n };\n }\n if (\n record.kind === \"not-quantified\"\n ) {\n return {\n kind: \"not-quantified\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n path + \".limitation\"\n )\n };\n }\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parseSiteApplicability(\n value: unknown\n): SensorCameraSaturationSiteApplicability {\n const record = requireRecord(\n value,\n \"sensorCameraSaturation.siteApplicability\", null\n );\n rejectUnknownVariantFields(record, \"kind\", { \"exact-site\": [\"kind\", \"site\"], \"channel-population-mean-approximation\": [\"kind\", \"limitation\", \"evidence\"] }, \"sensorCameraSaturation.siteApplicability\");\n if (record.kind === \"exact-site\") {\n const site = requireRecord(\n record.site,\n \"sensorCameraSaturation.siteApplicability.site\", [\"x\", \"y\"]\n );\n return {\n kind: \"exact-site\",\n site: {\n x: requireSiteIndex(\n site.x,\n \"sensorCameraSaturation.siteApplicability.site.x\"\n ),\n y: requireSiteIndex(\n site.y,\n \"sensorCameraSaturation.siteApplicability.site.y\"\n )\n }\n };\n }\n if (\n record.kind ===\n \"channel-population-mean-approximation\"\n ) {\n return {\n kind:\n \"channel-population-mean-approximation\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n \"sensorCameraSaturation.siteApplicability.limitation\"\n ),\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorCameraSaturation.siteApplicability.evidence\"\n )\n };\n }\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.siteApplicability.kind is invalid.\"\n );\n}\n\nfunction parseTemperatureApplicability(\n value: unknown\n): SensorCameraSaturationTemperatureApplicability {\n const record = requireRecord(\n value,\n \"sensorCameraSaturation.temperatureApplicability\", null\n );\n rejectUnknownVariantFields(record, \"kind\", { \"exact-reference-temperature\": [\"kind\", \"temperatureC\"], \"not-modeled\": [\"kind\", \"limitation\", \"evidence\"] }, \"sensorCameraSaturation.temperatureApplicability\");\n if (\n record.kind ===\n \"exact-reference-temperature\"\n ) {\n return {\n kind:\n \"exact-reference-temperature\",\n temperatureC:\n requireFinite(\n record.temperatureC,\n \"sensorCameraSaturation.temperatureApplicability.temperatureC\"\n )\n };\n }\n if (record.kind === \"not-modeled\") {\n return {\n kind: \"not-modeled\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n \"sensorCameraSaturation.temperatureApplicability.limitation\"\n ),\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorCameraSaturation.temperatureApplicability.evidence\"\n )\n };\n }\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.temperatureApplicability.kind is invalid.\"\n );\n}\n\n/**\n * Validates untrusted input and returns it as a normalized, typed value. Unknown enum values,\n * missing required fields and inconsistent scientific data are rejected.\n *\n * Camera saturation capacity is a limit on the dark-corrected photo signal, in electron\n * equivalents. It is not the physical charge storage capacity or the ADC maximum, and does not\n * identify which hardware stage limits it.\n * @param value - Untrusted data. TypeScript types alone are not validation.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function parseSensorCameraSaturationCapacityProfile(\n value: unknown\n): SensorCameraSaturationCapacityProfile {\n const record = requireRecord(\n value,\n \"sensorCameraSaturation\", [\"schemaVersion\", \"capacityMeaning\", \"signalDomain\", \"scientificStatus\", \"uncertainty\", \"siteApplicability\", \"temperatureApplicability\", \"operatingState\", \"profileId\", \"colorSamplingProfileId\", \"channelId\", \"saturationCapacityElectrons\", \"evidence\", \"measurementMethodId\", \"measurementMethodEvidence\"]\n );\n\n if (\n record.schemaVersion !== \"0.1.0\"\n ) {\n throw new InvalidConfigurationError(\n 'sensorCameraSaturation.schemaVersion must be \"0.1.0\".'\n );\n }\n if (\n record.capacityMeaning !==\n \"camera-signal-saturation-capacity-electrons\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.capacityMeaning is invalid.\"\n );\n }\n if (\n record.signalDomain !==\n \"dark-corrected-photo-generated-electron-equivalent\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.signalDomain is invalid.\"\n );\n }\n if (\n record.scientificStatus !==\n \"calibrated\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.scientificStatus is invalid.\"\n );\n }\n\n const uncertainty =\n parseUncertainty(\n record.uncertainty,\n \"sensorCameraSaturation.uncertainty\"\n );\n if (\n record.scientificStatus ===\n \"calibrated\" &&\n uncertainty.kind ===\n \"not-quantified\"\n ) {\n throw new InvalidConfigurationError(\n \"A calibrated camera saturation-capacity profile must declare quantified relative uncertainty.\"\n );\n }\n\n const siteApplicability =\n parseSiteApplicability(\n record.siteApplicability\n );\n const temperatureApplicability =\n parseTemperatureApplicability(\n record.temperatureApplicability\n );\n\n if (\n siteApplicability.kind ===\n \"channel-population-mean-approximation\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"A channel-population mean camera saturation capacity must remain an approximation when applied to one site.\"\n );\n }\n if (\n temperatureApplicability.kind ===\n \"not-modeled\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"A camera saturation capacity with unmodeled temperature dependence must remain an approximation.\"\n );\n }\n\n const state = requireRecord(\n record.operatingState,\n \"sensorCameraSaturation.operatingState\", [\"stateId\", \"evidence\"]\n );\n\n return {\n schemaVersion: \"0.1.0\",\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"sensorCameraSaturation.profileId\"\n ),\n colorSamplingProfileId:\n requireNonEmptyString(\n record.colorSamplingProfileId,\n \"sensorCameraSaturation.colorSamplingProfileId\"\n ),\n channelId:\n requireNonEmptyString(\n record.channelId,\n \"sensorCameraSaturation.channelId\"\n ),\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\",\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\",\n saturationCapacityElectrons:\n requirePositiveFinite(\n record.saturationCapacityElectrons,\n \"sensorCameraSaturation.saturationCapacityElectrons\"\n ),\n scientificStatus:\n record.scientificStatus,\n uncertainty,\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorCameraSaturation.evidence\"\n ),\n measurementMethodId:\n requireNonEmptyString(\n record.measurementMethodId,\n \"sensorCameraSaturation.measurementMethodId\"\n ),\n measurementMethodEvidence:\n parseEvidenceList(\n record.measurementMethodEvidence,\n \"sensorCameraSaturation.measurementMethodEvidence\"\n ),\n siteApplicability,\n operatingState: {\n stateId:\n requireNonEmptyString(\n state.stateId,\n \"sensorCameraSaturation.operatingState.stateId\"\n ),\n evidence: parseEvidenceList(\n state.evidence,\n \"sensorCameraSaturation.operatingState.evidence\"\n )\n },\n temperatureApplicability\n };\n}\n\nfunction sameSite(\n a: { x: number; y: number },\n b: { x: number; y: number }\n): boolean {\n return a.x === b.x && a.y === b.y;\n}\n\nfunction validateCharge(\n charge:\n SensorAccumulatedChargeComposition\n): {\n photo: number;\n total: number;\n} {\n requireInputObject(charge, \"charge input\");\n if (\n charge.accumulatedChargeCompleteness !==\n \"complete-for-physical-storage-capacity-assessment\" ||\n charge.saturationAssessed !==\n false ||\n charge.adcQuantizationApplied !==\n false ||\n charge.rawCodeValueProduced !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"Camera saturation assessment requires an unmodified pre-saturation accumulated-charge composition.\"\n );\n }\n\n return {\n photo: requireNonNegativeFiniteInput(\n charge.photoExpectedElectronCount,\n \"accumulatedCharge.photoExpectedElectronCount\"\n ),\n total: requireNonNegativeFiniteInput(\n charge.totalExpectedStoredElectronCount,\n \"accumulatedCharge.totalExpectedStoredElectronCount\"\n )\n };\n}\n\n/**\n * Compares the expected photo signal (dark-corrected) with the camera's declared saturation\n * capacity. Dark charge is not added, and no hardware clamp is identified.\n *\n * This compares expected values only. It does not make up codes beyond saturation.\n * @param input - The input; see `AssessSensorCameraSaturationCapacityInput` for its fields, units and coordinates.\n * @returns The assessment. Read its structured blockers before acting on an authorization.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function assessSensorCameraSaturationCapacity(\n input:\n AssessSensorCameraSaturationCapacityInput\n): CalculationResult<SensorCameraSaturationCapacityAssessment> {\n requireInputKeys(input, \"assessSensorCameraSaturationCapacity\", [\"accumulatedCharge\", \"saturationProfile\", \"operatingStateId\", \"operatingTemperatureC\"]);\n const charge =\n validateCharge(\n input.accumulatedCharge\n );\n const profile =\n parseSensorCameraSaturationCapacityProfile(\n input.saturationProfile\n );\n\n if (\n profile.colorSamplingProfileId !==\n input.accumulatedCharge\n .colorSamplingProfileId ||\n profile.channelId !==\n input.accumulatedCharge.channelId\n ) {\n throw new InvalidScientificInputError(\n \"Camera saturation-capacity profile color/channel identity must match the accumulated charge.\"\n );\n }\n if (\n profile.siteApplicability.kind ===\n \"exact-site\" &&\n !sameSite(\n profile.siteApplicability.site,\n input.accumulatedCharge.site\n )\n ) {\n throw new InvalidScientificInputError(\n \"Exact-site camera saturation capacity does not apply to the accumulated-charge site.\"\n );\n }\n if (\n typeof input.operatingStateId !==\n \"string\" ||\n input.operatingStateId.trim().length ===\n 0 ||\n input.operatingStateId !==\n profile.operatingState.stateId\n ) {\n throw new InvalidScientificInputError(\n \"operatingStateId must exactly match the camera saturation-capacity calibration state.\"\n );\n }\n if (\n typeof input.operatingTemperatureC !==\n \"number\" ||\n !Number.isFinite(\n input.operatingTemperatureC\n )\n ) {\n throw new InvalidScientificInputError(\n \"operatingTemperatureC must be finite.\"\n );\n }\n if (\n profile.temperatureApplicability.kind ===\n \"exact-reference-temperature\" &&\n input.operatingTemperatureC !==\n profile.temperatureApplicability\n .temperatureC\n ) {\n throw new InvalidScientificInputError(\n \"operatingTemperatureC must exactly match the camera saturation-capacity reference temperature.\"\n );\n }\n\n const capacity =\n profile.saturationCapacityElectrons;\n const ratio =\n charge.photo / capacity;\n const headroom =\n capacity - charge.photo;\n\n if (\n !Number.isFinite(ratio) ||\n !Number.isFinite(headroom)\n ) {\n throw new InvalidScientificInputError(\n \"Camera saturation-capacity comparison must remain finite.\"\n );\n }\n\n const status =\n charge.photo < capacity\n ? \"below-capacity\"\n : charge.photo === capacity\n ? \"at-capacity\"\n : \"above-capacity\";\n\n return approximationResult(\n {\n saturationProfileId:\n profile.profileId,\n completenessProfileId:\n input.accumulatedCharge\n .completenessProfileId,\n colorSamplingProfileId:\n input.accumulatedCharge\n .colorSamplingProfileId,\n channelId:\n input.accumulatedCharge.channelId,\n site: {\n ...input.accumulatedCharge.site\n },\n bindingId:\n input.accumulatedCharge.bindingId,\n operatingStateId:\n input.operatingStateId,\n operatingTemperatureC:\n input.operatingTemperatureC,\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\",\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\",\n cameraSignalSaturationCapacityElectrons:\n capacity,\n expectedPhotoSignalElectronCount:\n charge.photo,\n totalExpectedStoredElectronCountDiagnostic:\n charge.total,\n totalStoredChargeUsedForCameraSaturationComparison:\n false,\n expectedSignalToCameraSaturationRatio:\n ratio,\n expectedSignalHeadroomElectrons:\n headroom,\n expectedCameraSignalCapacityStatus:\n status,\n expectedPhotoSignalExceedsCameraCapacity:\n status === \"above-capacity\",\n cameraSaturationCapacityAssessmentPerformed:\n true,\n assessmentMeaning:\n \"expected-dark-corrected-photo-signal-vs-camera-saturation-capacity\",\n physicalChargeCapacityUsed: false,\n limitingSaturationMechanismResolved:\n false,\n analogClippingThresholdModeled:\n false,\n digitalClippingThresholdModeled:\n false,\n adcMaximumCodeUsed: false,\n actualStochasticSaturationStateKnown:\n false,\n stochasticSaturationProbabilityAssessed:\n false,\n outputSignalAfterSaturationCalculated:\n false,\n clampApplied: false,\n physicalStoredChargeModified:\n false,\n adcQuantizationApplied: false,\n rawCodeValueProduced: false,\n saturationCapacityUncertaintyPropagated:\n false,\n photoSignalUncertaintyPropagated:\n false,\n componentEvidence: {\n saturationCapacity:\n profile.evidence,\n measurementMethod:\n profile\n .measurementMethodEvidence,\n operatingState:\n profile.operatingState\n .evidence,\n siteApproximation:\n profile.siteApplicability.kind ===\n \"channel-population-mean-approximation\"\n ? profile\n .siteApplicability\n .evidence\n : [],\n temperatureApproximation:\n profile.temperatureApplicability.kind ===\n \"not-modeled\"\n ? profile\n .temperatureApplicability\n .evidence\n : [],\n chargeCompleteness:\n input.accumulatedCharge\n .componentEvidence\n .completeness\n }\n },\n \"sensor-camera-saturation-capacity-assessment\",\n \"1.0.0\",\n [\n \"The camera saturation capacity is a measured/calibrated dark-corrected photo-signal capacity in electron-equivalent signal units; total stored charge is deliberately not used for this comparison.\",\n \"Physical charge-storage capacity is a separate sensor property and is not used by this assessment.\",\n \"The camera saturation capacity may be limited by a downstream camera stage before physical pixel storage is full; this contract does not infer which analog or digital stage is limiting.\",\n \"The operating-state identifier is an exact calibration-state key covering the source-specific camera/readout configuration; Photivra does not infer equivalence from ISO, gain or bit-depth labels.\",\n \"A channel-population mean applied to one site or unmodeled temperature dependence remains an explicit approximation.\",\n \"Expected photo signal below the measured capacity does not establish the actual stochastic saturation state; no saturation probability is calculated.\",\n \"No signal clamp, analog clipping transfer, ADC maximum code, RAW code or post-saturation output is calculated.\",\n \"Dark and other stored charge remain diagnostics for physical storage accounting and are not silently added to a dark-corrected camera-signal capacity.\",\n \"Capacity and photo-signal uncertainties are not propagated into a saturation probability or confidence interval by this version.\"\n ]\n );\n}\n"]}
1
+ {"version":3,"file":"camera-saturation-capacity.js","sourceRoot":"","sources":["../../src/sensor/camera-saturation-capacity.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Camera saturation capacity: checks whether the expected photo signal would exceed the camera's\n * declared saturation limit.\n *\n * Camera saturation capacity is the largest photo signal, in electron equivalents and after dark\n * correction, that the whole camera response chain records before saturating. It is not the\n * physical charge a pixel can store, not the ADC maximum, and does not say which hardware stage\n * limits it. The check compares expected values only; it does not make up codes beyond saturation.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport {\n approximationResult,\n type CalculationResult\n} from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport type {\n SensorPhotoAccumulatedChargeComposition as SensorAccumulatedChargeComposition\n} from \"./accumulated-charge.js\";\nimport type {\n SensorSpectralResponseScientificStatus,\n SensorSpectralResponseUncertainty\n} from \"./spectral-response.js\";\nimport { requireInputObject, requireInputKeys, rejectUnknownVariantFields, UNCERTAINTY_VARIANT_FIELDS, requireConfigRecord as requireRecord, requireConfigFinite as requireFinite, requireConfigString as requireNonEmptyString, requireInputNonNegativeFinite as requireNonNegativeFiniteInput, requireConfigNonNegativeSafeInteger as requireSiteIndex, requireConfigFiniteAboveZero as requirePositiveFinite } from \"../core/record-validation.js\";\nimport { sameSite } from \"../core/shared-helpers.js\";\n\nexport type SensorCameraSaturationSiteApplicability =\n | {\n kind: \"exact-site\";\n site: {\n x: number;\n y: number;\n };\n }\n | {\n kind:\n \"channel-population-mean-approximation\";\n limitation: string;\n evidence:\n readonly EvidenceProvenance[];\n };\n\nexport type SensorCameraSaturationTemperatureApplicability =\n | {\n kind:\n \"exact-reference-temperature\";\n temperatureC: number;\n }\n | {\n kind: \"not-modeled\";\n limitation: string;\n evidence:\n readonly EvidenceProvenance[];\n };\n\nexport interface SensorCameraSaturationCapacityProfile {\n schemaVersion: \"0.1.0\";\n profileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\";\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\";\n saturationCapacityElectrons: number;\n scientificStatus:\n SensorSpectralResponseScientificStatus;\n uncertainty:\n SensorSpectralResponseUncertainty;\n evidence:\n readonly EvidenceProvenance[];\n measurementMethodId: string;\n measurementMethodEvidence:\n readonly EvidenceProvenance[];\n siteApplicability:\n SensorCameraSaturationSiteApplicability;\n operatingState: {\n stateId: string;\n evidence:\n readonly EvidenceProvenance[];\n };\n temperatureApplicability:\n SensorCameraSaturationTemperatureApplicability;\n}\n\nexport interface AssessSensorCameraSaturationCapacityInput {\n accumulatedCharge:\n SensorAccumulatedChargeComposition;\n saturationProfile:\n SensorCameraSaturationCapacityProfile;\n operatingStateId: string;\n operatingTemperatureC: number;\n}\n\nexport interface SensorCameraSaturationCapacityAssessment {\n saturationProfileId: string;\n completenessProfileId: string;\n colorSamplingProfileId: string;\n channelId: string;\n site: {\n x: number;\n y: number;\n };\n bindingId: string;\n operatingStateId: string;\n operatingTemperatureC: number;\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\";\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\";\n cameraSignalSaturationCapacityElectrons:\n number;\n expectedPhotoSignalElectronCount:\n number;\n totalExpectedStoredElectronCountDiagnostic:\n number;\n totalStoredChargeUsedForCameraSaturationComparison:\n false;\n expectedSignalToCameraSaturationRatio:\n number;\n expectedSignalHeadroomElectrons:\n number;\n expectedCameraSignalCapacityStatus:\n | \"below-capacity\"\n | \"at-capacity\"\n | \"above-capacity\";\n expectedPhotoSignalExceedsCameraCapacity:\n boolean;\n cameraSaturationCapacityAssessmentPerformed:\n true;\n assessmentMeaning:\n \"expected-dark-corrected-photo-signal-vs-camera-saturation-capacity\";\n physicalChargeCapacityUsed:\n false;\n limitingSaturationMechanismResolved:\n false;\n analogClippingThresholdModeled:\n false;\n digitalClippingThresholdModeled:\n false;\n adcMaximumCodeUsed:\n false;\n actualStochasticSaturationStateKnown:\n false;\n stochasticSaturationProbabilityAssessed:\n false;\n outputSignalAfterSaturationCalculated:\n false;\n clampApplied: false;\n physicalStoredChargeModified:\n false;\n adcQuantizationApplied: false;\n rawCodeValueProduced: false;\n saturationCapacityUncertaintyPropagated:\n false;\n photoSignalUncertaintyPropagated:\n false;\n componentEvidence: {\n saturationCapacity:\n readonly EvidenceProvenance[];\n measurementMethod:\n readonly EvidenceProvenance[];\n operatingState:\n readonly EvidenceProvenance[];\n siteApproximation:\n readonly EvidenceProvenance[];\n temperatureApproximation:\n readonly EvidenceProvenance[];\n chargeCompleteness:\n SensorAccumulatedChargeComposition[\"componentEvidence\"][\"completeness\"];\n };\n}\n\nexport function parseUncertainty(\n value: unknown,\n path: string\n): SensorSpectralResponseUncertainty {\n const record = requireRecord(\n value,\n path, null\n );\n rejectUnknownVariantFields(record, \"kind\", UNCERTAINTY_VARIANT_FIELDS, path);\n if (record.kind === \"relative\") {\n const fraction = requireFinite(\n record.fraction,\n path + \".fraction\"\n );\n if (fraction < 0) {\n throw new InvalidConfigurationError(\n path +\n \".fraction must be nonnegative.\"\n );\n }\n return {\n kind: \"relative\",\n fraction,\n basis: requireNonEmptyString(\n record.basis,\n path + \".basis\"\n )\n };\n }\n if (\n record.kind === \"not-quantified\"\n ) {\n return {\n kind: \"not-quantified\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n path + \".limitation\"\n )\n };\n }\n throw new InvalidConfigurationError(\n path + \".kind is invalid.\"\n );\n}\n\nfunction parseSiteApplicability(\n value: unknown\n): SensorCameraSaturationSiteApplicability {\n const record = requireRecord(\n value,\n \"sensorCameraSaturation.siteApplicability\", null\n );\n rejectUnknownVariantFields(record, \"kind\", { \"exact-site\": [\"kind\", \"site\"], \"channel-population-mean-approximation\": [\"kind\", \"limitation\", \"evidence\"] }, \"sensorCameraSaturation.siteApplicability\");\n if (record.kind === \"exact-site\") {\n const site = requireRecord(\n record.site,\n \"sensorCameraSaturation.siteApplicability.site\", [\"x\", \"y\"]\n );\n return {\n kind: \"exact-site\",\n site: {\n x: requireSiteIndex(\n site.x,\n \"sensorCameraSaturation.siteApplicability.site.x\"\n ),\n y: requireSiteIndex(\n site.y,\n \"sensorCameraSaturation.siteApplicability.site.y\"\n )\n }\n };\n }\n if (\n record.kind ===\n \"channel-population-mean-approximation\"\n ) {\n return {\n kind:\n \"channel-population-mean-approximation\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n \"sensorCameraSaturation.siteApplicability.limitation\"\n ),\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorCameraSaturation.siteApplicability.evidence\"\n )\n };\n }\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.siteApplicability.kind is invalid.\"\n );\n}\n\nfunction parseTemperatureApplicability(\n value: unknown\n): SensorCameraSaturationTemperatureApplicability {\n const record = requireRecord(\n value,\n \"sensorCameraSaturation.temperatureApplicability\", null\n );\n rejectUnknownVariantFields(record, \"kind\", { \"exact-reference-temperature\": [\"kind\", \"temperatureC\"], \"not-modeled\": [\"kind\", \"limitation\", \"evidence\"] }, \"sensorCameraSaturation.temperatureApplicability\");\n if (\n record.kind ===\n \"exact-reference-temperature\"\n ) {\n return {\n kind:\n \"exact-reference-temperature\",\n temperatureC:\n requireFinite(\n record.temperatureC,\n \"sensorCameraSaturation.temperatureApplicability.temperatureC\"\n )\n };\n }\n if (record.kind === \"not-modeled\") {\n return {\n kind: \"not-modeled\",\n limitation:\n requireNonEmptyString(\n record.limitation,\n \"sensorCameraSaturation.temperatureApplicability.limitation\"\n ),\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorCameraSaturation.temperatureApplicability.evidence\"\n )\n };\n }\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.temperatureApplicability.kind is invalid.\"\n );\n}\n\n/**\n * Validates untrusted input and returns it as a normalized, typed value. Unknown enum values,\n * missing required fields and inconsistent scientific data are rejected.\n *\n * Camera saturation capacity is a limit on the dark-corrected photo signal, in electron\n * equivalents. It is not the physical charge storage capacity or the ADC maximum, and does not\n * identify which hardware stage limits it.\n * @param value - Untrusted data. TypeScript types alone are not validation.\n * @returns The result. Success does not imply calibration.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function parseSensorCameraSaturationCapacityProfile(\n value: unknown\n): SensorCameraSaturationCapacityProfile {\n const record = requireRecord(\n value,\n \"sensorCameraSaturation\", [\"schemaVersion\", \"capacityMeaning\", \"signalDomain\", \"scientificStatus\", \"uncertainty\", \"siteApplicability\", \"temperatureApplicability\", \"operatingState\", \"profileId\", \"colorSamplingProfileId\", \"channelId\", \"saturationCapacityElectrons\", \"evidence\", \"measurementMethodId\", \"measurementMethodEvidence\"]\n );\n\n if (\n record.schemaVersion !== \"0.1.0\"\n ) {\n throw new InvalidConfigurationError(\n 'sensorCameraSaturation.schemaVersion must be \"0.1.0\".'\n );\n }\n if (\n record.capacityMeaning !==\n \"camera-signal-saturation-capacity-electrons\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.capacityMeaning is invalid.\"\n );\n }\n if (\n record.signalDomain !==\n \"dark-corrected-photo-generated-electron-equivalent\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.signalDomain is invalid.\"\n );\n }\n if (\n record.scientificStatus !==\n \"calibrated\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"sensorCameraSaturation.scientificStatus is invalid.\"\n );\n }\n\n const uncertainty =\n parseUncertainty(\n record.uncertainty,\n \"sensorCameraSaturation.uncertainty\"\n );\n if (\n record.scientificStatus ===\n \"calibrated\" &&\n uncertainty.kind ===\n \"not-quantified\"\n ) {\n throw new InvalidConfigurationError(\n \"A calibrated camera saturation-capacity profile must declare quantified relative uncertainty.\"\n );\n }\n\n const siteApplicability =\n parseSiteApplicability(\n record.siteApplicability\n );\n const temperatureApplicability =\n parseTemperatureApplicability(\n record.temperatureApplicability\n );\n\n if (\n siteApplicability.kind ===\n \"channel-population-mean-approximation\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"A channel-population mean camera saturation capacity must remain an approximation when applied to one site.\"\n );\n }\n if (\n temperatureApplicability.kind ===\n \"not-modeled\" &&\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n \"A camera saturation capacity with unmodeled temperature dependence must remain an approximation.\"\n );\n }\n\n const state = requireRecord(\n record.operatingState,\n \"sensorCameraSaturation.operatingState\", [\"stateId\", \"evidence\"]\n );\n\n return {\n schemaVersion: \"0.1.0\",\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"sensorCameraSaturation.profileId\"\n ),\n colorSamplingProfileId:\n requireNonEmptyString(\n record.colorSamplingProfileId,\n \"sensorCameraSaturation.colorSamplingProfileId\"\n ),\n channelId:\n requireNonEmptyString(\n record.channelId,\n \"sensorCameraSaturation.channelId\"\n ),\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\",\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\",\n saturationCapacityElectrons:\n requirePositiveFinite(\n record.saturationCapacityElectrons,\n \"sensorCameraSaturation.saturationCapacityElectrons\"\n ),\n scientificStatus:\n record.scientificStatus,\n uncertainty,\n evidence: parseEvidenceList(\n record.evidence,\n \"sensorCameraSaturation.evidence\"\n ),\n measurementMethodId:\n requireNonEmptyString(\n record.measurementMethodId,\n \"sensorCameraSaturation.measurementMethodId\"\n ),\n measurementMethodEvidence:\n parseEvidenceList(\n record.measurementMethodEvidence,\n \"sensorCameraSaturation.measurementMethodEvidence\"\n ),\n siteApplicability,\n operatingState: {\n stateId:\n requireNonEmptyString(\n state.stateId,\n \"sensorCameraSaturation.operatingState.stateId\"\n ),\n evidence: parseEvidenceList(\n state.evidence,\n \"sensorCameraSaturation.operatingState.evidence\"\n )\n },\n temperatureApplicability\n };\n}\n\nfunction validateCharge(\n charge:\n SensorAccumulatedChargeComposition\n): {\n photo: number;\n total: number;\n} {\n requireInputObject(charge, \"charge input\");\n if (\n charge.accumulatedChargeCompleteness !==\n \"complete-for-physical-storage-capacity-assessment\" ||\n charge.saturationAssessed !==\n false ||\n charge.adcQuantizationApplied !==\n false ||\n charge.rawCodeValueProduced !==\n false\n ) {\n throw new InvalidScientificInputError(\n \"Camera saturation assessment requires an unmodified pre-saturation accumulated-charge composition.\"\n );\n }\n\n return {\n photo: requireNonNegativeFiniteInput(\n charge.photoExpectedElectronCount,\n \"accumulatedCharge.photoExpectedElectronCount\"\n ),\n total: requireNonNegativeFiniteInput(\n charge.totalExpectedStoredElectronCount,\n \"accumulatedCharge.totalExpectedStoredElectronCount\"\n )\n };\n}\n\n/**\n * Compares the expected photo signal (dark-corrected) with the camera's declared saturation\n * capacity. Dark charge is not added, and no hardware clamp is identified.\n *\n * This compares expected values only. It does not make up codes beyond saturation.\n * @param input - The input; see `AssessSensorCameraSaturationCapacityInput` for its fields, units and coordinates.\n * @returns The assessment. Read its structured blockers before acting on an authorization.\n *\n * @see docs/API_REFERENCE.md for the package export and its full type definitions.\n */\nexport function assessSensorCameraSaturationCapacity(\n input:\n AssessSensorCameraSaturationCapacityInput\n): CalculationResult<SensorCameraSaturationCapacityAssessment> {\n requireInputKeys(input, \"assessSensorCameraSaturationCapacity\", [\"accumulatedCharge\", \"saturationProfile\", \"operatingStateId\", \"operatingTemperatureC\"]);\n const charge =\n validateCharge(\n input.accumulatedCharge\n );\n const profile =\n parseSensorCameraSaturationCapacityProfile(\n input.saturationProfile\n );\n\n if (\n profile.colorSamplingProfileId !==\n input.accumulatedCharge\n .colorSamplingProfileId ||\n profile.channelId !==\n input.accumulatedCharge.channelId\n ) {\n throw new InvalidScientificInputError(\n \"Camera saturation-capacity profile color/channel identity must match the accumulated charge.\"\n );\n }\n if (\n profile.siteApplicability.kind ===\n \"exact-site\" &&\n !sameSite(\n profile.siteApplicability.site,\n input.accumulatedCharge.site\n )\n ) {\n throw new InvalidScientificInputError(\n \"Exact-site camera saturation capacity does not apply to the accumulated-charge site.\"\n );\n }\n if (\n typeof input.operatingStateId !==\n \"string\" ||\n input.operatingStateId.trim().length ===\n 0 ||\n input.operatingStateId !==\n profile.operatingState.stateId\n ) {\n throw new InvalidScientificInputError(\n \"operatingStateId must exactly match the camera saturation-capacity calibration state.\"\n );\n }\n if (\n typeof input.operatingTemperatureC !==\n \"number\" ||\n !Number.isFinite(\n input.operatingTemperatureC\n )\n ) {\n throw new InvalidScientificInputError(\n \"operatingTemperatureC must be finite.\"\n );\n }\n if (\n profile.temperatureApplicability.kind ===\n \"exact-reference-temperature\" &&\n input.operatingTemperatureC !==\n profile.temperatureApplicability\n .temperatureC\n ) {\n throw new InvalidScientificInputError(\n \"operatingTemperatureC must exactly match the camera saturation-capacity reference temperature.\"\n );\n }\n\n const capacity =\n profile.saturationCapacityElectrons;\n const ratio =\n charge.photo / capacity;\n const headroom =\n capacity - charge.photo;\n\n if (\n !Number.isFinite(ratio) ||\n !Number.isFinite(headroom)\n ) {\n throw new InvalidScientificInputError(\n \"Camera saturation-capacity comparison must remain finite.\"\n );\n }\n\n const status =\n charge.photo < capacity\n ? \"below-capacity\"\n : charge.photo === capacity\n ? \"at-capacity\"\n : \"above-capacity\";\n\n return approximationResult(\n {\n saturationProfileId:\n profile.profileId,\n completenessProfileId:\n input.accumulatedCharge\n .completenessProfileId,\n colorSamplingProfileId:\n input.accumulatedCharge\n .colorSamplingProfileId,\n channelId:\n input.accumulatedCharge.channelId,\n site: {\n ...input.accumulatedCharge.site\n },\n bindingId:\n input.accumulatedCharge.bindingId,\n operatingStateId:\n input.operatingStateId,\n operatingTemperatureC:\n input.operatingTemperatureC,\n capacityMeaning:\n \"camera-signal-saturation-capacity-electrons\",\n signalDomain:\n \"dark-corrected-photo-generated-electron-equivalent\",\n cameraSignalSaturationCapacityElectrons:\n capacity,\n expectedPhotoSignalElectronCount:\n charge.photo,\n totalExpectedStoredElectronCountDiagnostic:\n charge.total,\n totalStoredChargeUsedForCameraSaturationComparison:\n false,\n expectedSignalToCameraSaturationRatio:\n ratio,\n expectedSignalHeadroomElectrons:\n headroom,\n expectedCameraSignalCapacityStatus:\n status,\n expectedPhotoSignalExceedsCameraCapacity:\n status === \"above-capacity\",\n cameraSaturationCapacityAssessmentPerformed:\n true,\n assessmentMeaning:\n \"expected-dark-corrected-photo-signal-vs-camera-saturation-capacity\",\n physicalChargeCapacityUsed: false,\n limitingSaturationMechanismResolved:\n false,\n analogClippingThresholdModeled:\n false,\n digitalClippingThresholdModeled:\n false,\n adcMaximumCodeUsed: false,\n actualStochasticSaturationStateKnown:\n false,\n stochasticSaturationProbabilityAssessed:\n false,\n outputSignalAfterSaturationCalculated:\n false,\n clampApplied: false,\n physicalStoredChargeModified:\n false,\n adcQuantizationApplied: false,\n rawCodeValueProduced: false,\n saturationCapacityUncertaintyPropagated:\n false,\n photoSignalUncertaintyPropagated:\n false,\n componentEvidence: {\n saturationCapacity:\n profile.evidence,\n measurementMethod:\n profile\n .measurementMethodEvidence,\n operatingState:\n profile.operatingState\n .evidence,\n siteApproximation:\n profile.siteApplicability.kind ===\n \"channel-population-mean-approximation\"\n ? profile\n .siteApplicability\n .evidence\n : [],\n temperatureApproximation:\n profile.temperatureApplicability.kind ===\n \"not-modeled\"\n ? profile\n .temperatureApplicability\n .evidence\n : [],\n chargeCompleteness:\n input.accumulatedCharge\n .componentEvidence\n .completeness\n }\n },\n \"sensor-camera-saturation-capacity-assessment\",\n \"1.0.0\",\n [\n \"The camera saturation capacity is a measured/calibrated dark-corrected photo-signal capacity in electron-equivalent signal units; total stored charge is deliberately not used for this comparison.\",\n \"Physical charge-storage capacity is a separate sensor property and is not used by this assessment.\",\n \"The camera saturation capacity may be limited by a downstream camera stage before physical pixel storage is full; this contract does not infer which analog or digital stage is limiting.\",\n \"The operating-state identifier is an exact calibration-state key covering the source-specific camera/readout configuration; Photivra does not infer equivalence from ISO, gain or bit-depth labels.\",\n \"A channel-population mean applied to one site or unmodeled temperature dependence remains an explicit approximation.\",\n \"Expected photo signal below the measured capacity does not establish the actual stochastic saturation state; no saturation probability is calculated.\",\n \"No signal clamp, analog clipping transfer, ADC maximum code, RAW code or post-saturation output is calculated.\",\n \"Dark and other stored charge remain diagnostics for physical storage accounting and are not silently added to a dark-corrected camera-signal capacity.\",\n \"Capacity and photo-signal uncertainties are not propagated into a saturation probability or confidence interval by this version.\"\n ]\n );\n}\n"]}
@@ -14,32 +14,7 @@ import { parseEvidenceList } from "../core/evidence-provenance.js";
14
14
  import { InvalidScientificInputError, requirePositiveInteger } from "../core/validation.js";
15
15
  import { resolveCaptureMode } from "./capture-mode.js";
16
16
  import { parseSensorColorSamplingProfile } from "./color-sampling.js";
17
- import { requireInputObject, requireInputKeys, rejectUnknownFields } from "../core/record-validation.js";
18
- function requireRecord(value, path, allowedKeys) {
19
- if (typeof value !== "object" ||
20
- value === null ||
21
- Array.isArray(value)) {
22
- throw new InvalidConfigurationError(path + " must be an object.");
23
- }
24
- if (allowedKeys !== null)
25
- rejectUnknownFields(value, allowedKeys, path);
26
- return value;
27
- }
28
- function requireNonEmptyString(value, path) {
29
- if (typeof value !== "string" ||
30
- value.trim().length === 0) {
31
- throw new InvalidConfigurationError(path + " must be a non-empty string.");
32
- }
33
- return value;
34
- }
35
- function requirePositiveSafeInteger(value, path) {
36
- if (typeof value !== "number" ||
37
- !Number.isSafeInteger(value) ||
38
- value <= 0) {
39
- throw new InvalidConfigurationError(path + " must be a positive safe integer.");
40
- }
41
- return value;
42
- }
17
+ import { requireInputObject, requireInputKeys, requireConfigRecord as requireRecord, requireConfigString as requireNonEmptyString, requireConfigPositiveSafeInteger as requirePositiveSafeInteger } from "../core/record-validation.js";
43
18
  function parseBoundNativeRaster(value) {
44
19
  const nativeRaster = requireRecord(value, "colorSamplingBinding.nativeRaster", ["pixelWidth", "pixelHeight"]);
45
20
  const pixelWidth = requirePositiveSafeInteger(nativeRaster.pixelWidth, "colorSamplingBinding.nativeRaster.pixelWidth");
@@ -1 +1 @@
1
- {"version":3,"file":"capture-color-sampling-binding.js","sourceRoot":"","sources":["../../src/sensor/capture-color-sampling-binding.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Links the sensor's native image pixel grid to its grid of colour-sampling sites (for example the\n * cells of a CFA, colour filter array), and finds which sites feed each capture-mode sample.\n *\n * The link exists only because the caller supplies an evidence-backed binding. Matching grid sizes\n * are never taken as proof of a one-to-one link. This first version supports only regular\n * rectangular blocks of sites that share the native top-left corner.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport {\n InvalidScientificInputError,\n requirePositiveInteger\n} from \"../core/validation.js\";\nimport {\n resolveCaptureMode,\n type CaptureModeProfile,\n type CaptureModeSampleCombinationDomain,\n type CaptureModeSensorOffsetNativeSamples,\n type SourcedCaptureModeFact\n} from \"./capture-mode.js\";\nimport {\n parseSensorColorSamplingProfile,\n type SensorColorSamplingProfile\n} from \"./color-sampling.js\";\nimport type { NativeImageRaster } from \"./sensor-geometry.js\";\nimport { requireInputObject, requireInputKeys, rejectUnknownFields } from \"../core/record-validation.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport interface NativeEffectiveRasterColorSamplingBindingProfile {\n schemaVersion: \"0.1.0\";\n bindingId: string;\n colorSamplingProfileId: string;\n /**\n * The exact native image grid this binding has evidence for. Do not reuse\n * the binding with another grid just because another sensor or mode has\n * similar dimensions or the same aspect ratio.\n */\n nativeRaster: NativeImageRaster;\n /**\n * Evidence for the link between the native image grid and the colour-site\n * grid itself.\n *\n * Capture-mode and colour-layout facts keep their own separate evidence.\n */\n evidence: readonly EvidenceProvenance[];\n relationship: {\n kind: \"regular-native-effective-sample-blocks\";\n /**\n * Number of colour-sampling sites one native image sample spans\n * horizontally.\n */\n sitesPerNativeSampleX: number;\n /**\n * Number of colour-sampling sites one native image sample spans\n * vertically.\n */\n sitesPerNativeSampleY: number;\n /**\n * Both grids start at the same native sensor top-left corner.\n *\n * This keeps the CFA phase absolute through later active-area crops and\n * orientation changes.\n */\n anchor: \"shared-native-top-left\";\n };\n}\n\nexport interface ColorSamplingSiteGridDimensions {\n widthSites: number;\n heightSites: number;\n}\n\nexport interface ResolvedNativeEffectiveRasterColorSamplingBinding {\n bindingId: string;\n colorSamplingProfileId: string;\n nativeRaster: NativeImageRaster;\n relationshipMeaning:\n | \"one-native-effective-sample-to-one-color-site\"\n | \"one-native-effective-sample-to-rectangular-color-site-block\";\n relationship: {\n kind: \"regular-native-effective-sample-blocks\";\n sitesPerNativeSampleX: number;\n sitesPerNativeSampleY: number;\n anchor: \"shared-native-top-left\";\n };\n colorSamplingSiteGrid: ColorSamplingSiteGridDimensions;\n totalColorSamplingSites: number;\n nativeImageRasterBindingEstablished: true;\n physicalPhotodiodeBindingEstablished: false;\n physicalPhotodiodeCountInference: \"not-permitted\";\n spectralResponseEstablished: false;\n componentEvidence: {\n binding: readonly EvidenceProvenance[];\n colorSamplingProfile: readonly EvidenceProvenance[];\n };\n}\n\nexport interface NativeEffectiveSampleRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface ColorSamplingSiteRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface CaptureModeFullFrameSampleIndex {\n /**\n * Zero-based index in the selected mode's full-frame sample grid (per frame).\n * Convert crop-relative coordinates to absolute full-frame indices before\n * calling this API.\n */\n x: number;\n y: number;\n}\n\nexport interface ColorSamplingChannelSiteCount {\n channelId: string;\n siteCount: number;\n}\n\nexport type ColorSamplingChannelComposition =\n | {\n kind: \"single-channel\";\n channelId: string;\n }\n | {\n kind: \"mixed-channels\";\n channelIds: readonly string[];\n };\n\nexport interface GroupedCaptureModeSamplingAnchorDeclaration {\n anchor: \"native-effective-raster-top-left\";\n evidence: readonly EvidenceProvenance[];\n}\n\nexport interface ResolveCaptureModeColorSamplingContributorsInput {\n nativeRaster: NativeImageRaster;\n captureModeProfile: CaptureModeProfile;\n modeId: string;\n colorSamplingProfile: SensorColorSamplingProfile;\n bindingProfile: NativeEffectiveRasterColorSamplingBindingProfile;\n modeSampleIndexFullFrame: CaptureModeFullFrameSampleIndex;\n /**\n * Required for modes that group native samples, because the capture-mode\n * profile declares grouping factors but does not prove where the groups start.\n */\n groupedSamplingAnchor?: GroupedCaptureModeSamplingAnchorDeclaration;\n}\n\nexport interface ResolvedCaptureModeColorSamplingContributors {\n modeId: string;\n colorSamplingProfileId: string;\n modeSampleCoordinateSystem:\n \"capture-mode-full-frame-effective-sample-index\";\n modeSampleIndexFullFrame: CaptureModeFullFrameSampleIndex;\n modeSampleRaster: {\n pixelWidth: number;\n pixelHeight: number;\n };\n modeSamplingKind:\n | \"native-effective-raster\"\n | \"grouped-native-samples\";\n nativeEffectiveSampleRect: NativeEffectiveSampleRect;\n colorSamplingSiteRect: ColorSamplingSiteRect;\n colorSamplingSiteGrid: ColorSamplingSiteGridDimensions;\n totalContributorSites: number;\n channelSiteCounts: readonly ColorSamplingChannelSiteCount[];\n channelComposition: ColorSamplingChannelComposition;\n /**\n * Present only when the selected capture mode groups several native samples\n * into one sample per frame.\n */\n grouping:\n | {\n groupWidthSamples: SourcedCaptureModeFact<number>;\n groupHeightSamples: SourcedCaptureModeFact<number>;\n combinationDomain?: SourcedCaptureModeFact<CaptureModeSampleCombinationDomain>;\n anchor: \"native-effective-raster-top-left\";\n anchorEvidence: readonly EvidenceProvenance[];\n }\n | null;\n /**\n * The sensor-shift sequence stays attached to the sensor and capture mode.\n *\n * The sensor and its CFA move together, so these offsets must not change\n * which colour channel a site belongs to.\n */\n interFrameSensorOffsetsNativeSamples?: SourcedCaptureModeFact<\n readonly CaptureModeSensorOffsetNativeSamples[]\n >;\n sensorShiftChangesColorSiteAssignment: false;\n sensorShiftOpticalRegistration:\n \"outside-this-contract\";\n structuralSourceStage: \"pre-reconstruction\";\n completeDownstreamPixelDependencyEstablished: false;\n signalCombinationWeightingEstablished: false;\n spectralResponseEstablished: false;\n physicalPhotodiodeBindingEstablished: false;\n physicalPhotodiodeCountInference: \"not-permitted\";\n componentEvidence: {\n binding: readonly EvidenceProvenance[];\n colorSamplingProfile: readonly EvidenceProvenance[];\n captureMode: readonly EvidenceProvenance[];\n };\n}\n\nfunction requireRecord(\n value: unknown,\n path: string,\n allowedKeys: readonly string[] | null\n): UnknownRecord {\n if (\n typeof value !== \"object\" ||\n value === null ||\n Array.isArray(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be an object.\"\n );\n }\n if (allowedKeys !== null) rejectUnknownFields(value, allowedKeys, path);\n return value as UnknownRecord;\n}\n\nfunction requireNonEmptyString(\n value: unknown,\n path: string\n): string {\n if (\n typeof value !== \"string\" ||\n value.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path + \" must be a non-empty string.\"\n );\n }\n return value;\n}\n\nfunction requirePositiveSafeInteger(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isSafeInteger(value) ||\n value <= 0\n ) {\n throw new InvalidConfigurationError(\n path + \" must be a positive safe integer.\"\n );\n }\n return value;\n}\n\nfunction parseBoundNativeRaster(\n value: unknown\n): NativeImageRaster {\n const nativeRaster = requireRecord(\n value,\n \"colorSamplingBinding.nativeRaster\", [\"pixelWidth\", \"pixelHeight\"]\n );\n const pixelWidth =\n requirePositiveSafeInteger(\n nativeRaster.pixelWidth,\n \"colorSamplingBinding.nativeRaster.pixelWidth\"\n );\n const pixelHeight =\n requirePositiveSafeInteger(\n nativeRaster.pixelHeight,\n \"colorSamplingBinding.nativeRaster.pixelHeight\"\n );\n\n if (\n !Number.isSafeInteger(\n pixelWidth * pixelHeight\n )\n ) {\n throw new InvalidConfigurationError(\n \"colorSamplingBinding.nativeRaster total image-sample count must be a safe integer.\"\n );\n }\n\n return {\n pixelWidth,\n pixelHeight\n };\n}\n\nfunction safeProduct(\n values: readonly number[],\n path: string\n): number {\n let result = 1;\n for (const value of values) {\n result *= value;\n if (!Number.isSafeInteger(result)) {\n throw new InvalidScientificInputError(\n path + \" must remain a safe integer.\"\n );\n }\n }\n return result;\n}\n\nfunction validateNativeRaster(\n raster: NativeImageRaster\n): void {\n requireInputObject(raster, \"raster input\");\n requirePositiveInteger(\n \"nativeRaster.pixelWidth\",\n raster.pixelWidth\n );\n requirePositiveInteger(\n \"nativeRaster.pixelHeight\",\n raster.pixelHeight\n );\n safeProduct(\n [raster.pixelWidth, raster.pixelHeight],\n \"nativeRaster total image-sample count\"\n );\n}\n\nfunction requireFullFrameSampleIndex(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isSafeInteger(value) ||\n value < 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be a non-negative safe integer full-frame mode sample index.\"\n );\n }\n return value;\n}\n\n/**\n * Parses the explicit link between the native image pixel grid and the separate\n * grid of colour-sampling sites.\n *\n * This first version supports only regular rectangular blocks of sites that\n * share the native top-left corner. Irregular mappings, sparse exceptions and\n * mode-specific declared grids would need new, explicit contracts.\n */\nexport function parseNativeEffectiveRasterColorSamplingBindingProfile(\n value: unknown\n): NativeEffectiveRasterColorSamplingBindingProfile {\n const profile = requireRecord(\n value,\n \"colorSamplingBinding\", [\"schemaVersion\", \"relationship\", \"bindingId\", \"colorSamplingProfileId\", \"nativeRaster\", \"evidence\"]\n );\n\n if (profile.schemaVersion !== \"0.1.0\") {\n throw new InvalidConfigurationError(\n 'colorSamplingBinding.schemaVersion must be \"0.1.0\".'\n );\n }\n\n const relationship = requireRecord(\n profile.relationship,\n \"colorSamplingBinding.relationship\", [\"kind\", \"anchor\", \"sitesPerNativeSampleX\", \"sitesPerNativeSampleY\"]\n );\n\n if (\n relationship.kind !==\n \"regular-native-effective-sample-blocks\"\n ) {\n throw new InvalidConfigurationError(\n \"colorSamplingBinding.relationship.kind is invalid.\"\n );\n }\n\n if (\n relationship.anchor !==\n \"shared-native-top-left\"\n ) {\n throw new InvalidConfigurationError(\n 'colorSamplingBinding.relationship.anchor must be \"shared-native-top-left\".'\n );\n }\n\n return {\n schemaVersion: \"0.1.0\",\n bindingId: requireNonEmptyString(\n profile.bindingId,\n \"colorSamplingBinding.bindingId\"\n ),\n colorSamplingProfileId:\n requireNonEmptyString(\n profile.colorSamplingProfileId,\n \"colorSamplingBinding.colorSamplingProfileId\"\n ),\n nativeRaster: parseBoundNativeRaster(\n profile.nativeRaster\n ),\n evidence: parseEvidenceList(\n profile.evidence,\n \"colorSamplingBinding.evidence\"\n ),\n relationship: {\n kind:\n \"regular-native-effective-sample-blocks\",\n sitesPerNativeSampleX:\n requirePositiveSafeInteger(\n relationship.sitesPerNativeSampleX,\n \"colorSamplingBinding.relationship.sitesPerNativeSampleX\"\n ),\n sitesPerNativeSampleY:\n requirePositiveSafeInteger(\n relationship.sitesPerNativeSampleY,\n \"colorSamplingBinding.relationship.sitesPerNativeSampleY\"\n ),\n anchor: \"shared-native-top-left\"\n }\n };\n}\n\n/**\n * Validates and resolves the link between NativeImageRaster and an exact colour\n * sampling layout (monochrome or a repeating pattern such as a CFA).\n *\n * Matching dimensions are never taken as proof of a one-to-one link. The link\n * exists only because the caller supplied this evidence-backed binding profile.\n */\nexport function resolveNativeEffectiveRasterColorSamplingBinding(\n input: {\n nativeRaster: NativeImageRaster;\n colorSamplingProfile: SensorColorSamplingProfile;\n bindingProfile: NativeEffectiveRasterColorSamplingBindingProfile;\n }\n): ResolvedNativeEffectiveRasterColorSamplingBinding {\n requireInputKeys(input, \"resolveNativeEffectiveRasterColorSamplingBinding\", [\"nativeRaster\", \"colorSamplingProfile\", \"bindingProfile\"]);\n validateNativeRaster(input.nativeRaster);\n\n const colorSamplingProfile =\n parseSensorColorSamplingProfile(\n input.colorSamplingProfile\n );\n const bindingProfile =\n parseNativeEffectiveRasterColorSamplingBindingProfile(\n input.bindingProfile\n );\n\n if (\n bindingProfile.colorSamplingProfileId !==\n colorSamplingProfile.profileId\n ) {\n throw new InvalidScientificInputError(\n \"Binding colorSamplingProfileId must match colorSamplingProfile.profileId.\"\n );\n }\n\n if (\n bindingProfile.nativeRaster.pixelWidth !==\n input.nativeRaster.pixelWidth ||\n bindingProfile.nativeRaster.pixelHeight !==\n input.nativeRaster.pixelHeight\n ) {\n throw new InvalidScientificInputError(\n \"Binding nativeRaster must exactly match the supplied canonical native effective raster.\"\n );\n }\n\n if (colorSamplingProfile.layout.kind === \"layered\") {\n throw new InvalidScientificInputError(\n \"Layered color sampling cannot be bound to NativeImageRaster until its per-layer spatial sampling relationship is explicitly modeled.\"\n );\n }\n\n if (\n colorSamplingProfile.coordinateSystem !==\n \"native-sensor-color-sampling-site-index\"\n ) {\n throw new InvalidScientificInputError(\n \"Resolvable color sampling binding requires the native sensor color-sampling-site coordinate system.\"\n );\n }\n\n const widthSites = safeProduct(\n [\n input.nativeRaster.pixelWidth,\n bindingProfile.relationship\n .sitesPerNativeSampleX\n ],\n \"color-sampling site-grid width\"\n );\n const heightSites = safeProduct(\n [\n input.nativeRaster.pixelHeight,\n bindingProfile.relationship\n .sitesPerNativeSampleY\n ],\n \"color-sampling site-grid height\"\n );\n const totalColorSamplingSites =\n safeProduct(\n [widthSites, heightSites],\n \"color-sampling site-grid total site count\"\n );\n\n const oneToOne =\n bindingProfile.relationship\n .sitesPerNativeSampleX === 1 &&\n bindingProfile.relationship\n .sitesPerNativeSampleY === 1;\n\n return {\n bindingId: bindingProfile.bindingId,\n colorSamplingProfileId:\n bindingProfile.colorSamplingProfileId,\n nativeRaster: {\n ...input.nativeRaster\n },\n relationshipMeaning: oneToOne\n ? \"one-native-effective-sample-to-one-color-site\"\n : \"one-native-effective-sample-to-rectangular-color-site-block\",\n relationship: {\n ...bindingProfile.relationship\n },\n colorSamplingSiteGrid: {\n widthSites,\n heightSites\n },\n totalColorSamplingSites,\n nativeImageRasterBindingEstablished: true,\n physicalPhotodiodeBindingEstablished: false,\n physicalPhotodiodeCountInference:\n \"not-permitted\",\n spectralResponseEstablished: false,\n componentEvidence: {\n binding: bindingProfile.evidence,\n colorSamplingProfile:\n colorSamplingProfile.evidence\n }\n };\n}\n\nfunction countResidueInRange(\n start: number,\n length: number,\n period: number,\n residue: number\n): number {\n const startResidue = start % period;\n const delta =\n (residue - startResidue + period) %\n period;\n\n if (delta >= length) {\n return 0;\n }\n\n return (\n 1 +\n Math.floor(\n (length - 1 - delta) / period\n )\n );\n}\n\nfunction channelCountsForSiteRect(\n profile: SensorColorSamplingProfile,\n rect: ColorSamplingSiteRect\n): readonly ColorSamplingChannelSiteCount[] {\n const totalSites = safeProduct(\n [rect.width, rect.height],\n \"color-sampling contributor site count\"\n );\n\n if (profile.layout.kind === \"layered\") {\n throw new InvalidScientificInputError(\n \"Layered color sampling does not have resolvable site contributors in schema 0.1.0.\"\n );\n }\n\n if (profile.layout.kind === \"monochrome\") {\n return [\n {\n channelId: profile.layout.channelId,\n siteCount: totalSites\n }\n ];\n }\n\n const counts =\n new Map<string, number>();\n\n for (\n let tileY = 0;\n tileY < profile.layout.repeatHeightSites;\n tileY += 1\n ) {\n const countY = countResidueInRange(\n rect.y,\n rect.height,\n profile.layout.repeatHeightSites,\n tileY\n );\n if (countY === 0) {\n continue;\n }\n\n for (\n let tileX = 0;\n tileX < profile.layout.repeatWidthSites;\n tileX += 1\n ) {\n const countX = countResidueInRange(\n rect.x,\n rect.width,\n profile.layout.repeatWidthSites,\n tileX\n );\n if (countX === 0) {\n continue;\n }\n\n const tileIndex =\n tileY *\n profile.layout.repeatWidthSites +\n tileX;\n const channelId =\n profile.layout.siteChannelIds[\n tileIndex\n ];\n\n if (channelId === undefined) {\n throw new InvalidScientificInputError(\n \"Periodic color-sampling tile resolution failed while calculating contributor counts.\"\n );\n }\n\n const siteCount = safeProduct(\n [countX, countY],\n \"per-channel contributor site count\"\n );\n const prior = counts.get(channelId) ?? 0;\n const next = prior + siteCount;\n if (!Number.isSafeInteger(next)) {\n throw new InvalidScientificInputError(\n \"Per-channel contributor site count must remain a safe integer.\"\n );\n }\n counts.set(channelId, next);\n }\n }\n\n const result = Array.from(\n counts,\n ([channelId, siteCount]) => ({\n channelId,\n siteCount\n })\n );\n const countedTotal = result.reduce(\n (sum, entry) => sum + entry.siteCount,\n 0\n );\n\n if (countedTotal !== totalSites) {\n throw new InvalidScientificInputError(\n \"Color-sampling contributor channel counts do not cover the complete contributor site rectangle.\"\n );\n }\n\n return result;\n}\n\n/**\n * Finds which colour-sampling sites lie under one capture-mode sample, before any\n * reconstruction (demosaicing).\n *\n * The sample index is always absolute in the selected mode's full-frame grid. Crop-relative\n * indices are not accepted, so an active-area crop can never silently reset the CFA phase.\n *\n * For modes that group native samples, the returned rectangle covers every native sample in\n * the declared regular group. This only describes which sites lie under the sample. The engine\n * reports how many sites of each channel there are, but does not claim this is every dependency\n * of the final digital pixel. It never makes up combination weights, sum or average rules,\n * spectral response, or photon or electron values.\n *\n * Modes with a declared sample grid are rejected, because the capture-mode profile says their\n * relationship to the native grid cannot safely be written as simple integer grouping.\n *\n * Sensor shifts between frames are kept as capture-mode metadata only. The sensor and CFA move\n * together, so the shifts do not change which channel a site belongs to. Their effect on optical\n * registration belongs to a later spatial-sampling or image-formation model.\n */\nexport function resolveCaptureModeColorSamplingContributors(\n input: ResolveCaptureModeColorSamplingContributorsInput\n): ResolvedCaptureModeColorSamplingContributors {\n requireInputKeys(input, \"resolveCaptureModeColorSamplingContributors\", [\"nativeRaster\", \"captureModeProfile\", \"modeId\", \"colorSamplingProfile\", \"bindingProfile\", \"modeSampleIndexFullFrame\", \"groupedSamplingAnchor\"]);\n const colorSamplingProfile =\n parseSensorColorSamplingProfile(\n input.colorSamplingProfile\n );\n const binding =\n resolveNativeEffectiveRasterColorSamplingBinding(\n {\n nativeRaster: input.nativeRaster,\n colorSamplingProfile,\n bindingProfile: input.bindingProfile\n }\n );\n const captureMode = resolveCaptureMode({\n nativeRaster: input.nativeRaster,\n profile: input.captureModeProfile,\n modeId: input.modeId\n });\n\n if (\n !captureMode.dependencies.includes(\n \"color-sampling-model\"\n )\n ) {\n throw new InvalidScientificInputError(\n \"Selected capture mode must declare the color-sampling-model dependency before color-site contributors can be resolved.\"\n );\n }\n\n const modeX = requireFullFrameSampleIndex(\n input.modeSampleIndexFullFrame.x,\n \"modeSampleIndexFullFrame.x\"\n );\n const modeY = requireFullFrameSampleIndex(\n input.modeSampleIndexFullFrame.y,\n \"modeSampleIndexFullFrame.y\"\n );\n\n if (\n captureMode.perFrameSampling.kind ===\n \"declared-effective-raster\"\n ) {\n throw new InvalidScientificInputError(\n \"declared-effective-raster capture modes require a separate mode-specific color-site binding; no relationship may be inferred from raster dimensions alone.\"\n );\n }\n\n const modeSampleRaster =\n captureMode.perFrameSampling.raster;\n\n if (\n modeX >= modeSampleRaster.pixelWidth ||\n modeY >= modeSampleRaster.pixelHeight\n ) {\n throw new InvalidScientificInputError(\n \"modeSampleIndexFullFrame must lie within the selected mode's full-frame per-frame sampling raster.\"\n );\n }\n\n let nativeEffectiveSampleRect:\n NativeEffectiveSampleRect;\n let grouping:\n ResolvedCaptureModeColorSamplingContributors[\"grouping\"];\n\n if (\n captureMode.perFrameSampling.kind ===\n \"native-effective-raster\"\n ) {\n if (input.groupedSamplingAnchor !== undefined) {\n throw new InvalidScientificInputError(\n \"groupedSamplingAnchor must be omitted for native-effective-raster capture modes.\"\n );\n }\n\n nativeEffectiveSampleRect = {\n x: modeX,\n y: modeY,\n width: 1,\n height: 1\n };\n grouping = null;\n } else {\n if (\n input.groupedSamplingAnchor === undefined ||\n input.groupedSamplingAnchor.anchor !==\n \"native-effective-raster-top-left\"\n ) {\n throw new InvalidScientificInputError(\n \"grouped-native-samples capture modes require an explicit native-effective-raster-top-left grouping anchor declaration.\"\n );\n }\n const groupingAnchorEvidence =\n parseEvidenceList(\n input.groupedSamplingAnchor.evidence,\n \"groupedSamplingAnchor.evidence\"\n );\n\n const groupWidth =\n captureMode.perFrameSampling\n .groupWidthSamples.value;\n const groupHeight =\n captureMode.perFrameSampling\n .groupHeightSamples.value;\n\n nativeEffectiveSampleRect = {\n x: safeProduct(\n [modeX, groupWidth],\n \"native effective sample rectangle x\"\n ),\n y: safeProduct(\n [modeY, groupHeight],\n \"native effective sample rectangle y\"\n ),\n width: groupWidth,\n height: groupHeight\n };\n grouping = {\n groupWidthSamples:\n captureMode.perFrameSampling\n .groupWidthSamples,\n groupHeightSamples:\n captureMode.perFrameSampling\n .groupHeightSamples,\n ...(captureMode.perFrameSampling\n .combinationDomain === undefined\n ? {}\n : {\n combinationDomain:\n captureMode.perFrameSampling\n .combinationDomain\n }),\n anchor:\n \"native-effective-raster-top-left\",\n anchorEvidence:\n groupingAnchorEvidence\n };\n }\n\n const sitesPerNativeX =\n binding.relationship\n .sitesPerNativeSampleX;\n const sitesPerNativeY =\n binding.relationship\n .sitesPerNativeSampleY;\n\n const colorSamplingSiteRect:\n ColorSamplingSiteRect = {\n x: safeProduct(\n [\n nativeEffectiveSampleRect.x,\n sitesPerNativeX\n ],\n \"color-sampling contributor rectangle x\"\n ),\n y: safeProduct(\n [\n nativeEffectiveSampleRect.y,\n sitesPerNativeY\n ],\n \"color-sampling contributor rectangle y\"\n ),\n width: safeProduct(\n [\n nativeEffectiveSampleRect.width,\n sitesPerNativeX\n ],\n \"color-sampling contributor rectangle width\"\n ),\n height: safeProduct(\n [\n nativeEffectiveSampleRect.height,\n sitesPerNativeY\n ],\n \"color-sampling contributor rectangle height\"\n )\n };\n\n if (\n colorSamplingSiteRect.x +\n colorSamplingSiteRect.width >\n binding.colorSamplingSiteGrid.widthSites ||\n colorSamplingSiteRect.y +\n colorSamplingSiteRect.height >\n binding.colorSamplingSiteGrid.heightSites\n ) {\n throw new InvalidScientificInputError(\n \"Resolved color-sampling contributor rectangle exceeds the bound color-site grid.\"\n );\n }\n\n const totalContributorSites =\n safeProduct(\n [\n colorSamplingSiteRect.width,\n colorSamplingSiteRect.height\n ],\n \"total contributor site count\"\n );\n const channelSiteCounts =\n channelCountsForSiteRect(\n colorSamplingProfile,\n colorSamplingSiteRect\n );\n\n const firstChannel = channelSiteCounts[0];\n if (firstChannel === undefined) {\n throw new InvalidScientificInputError(\n \"Resolved structural source region must contain at least one color-sampling channel.\"\n );\n }\n\n const channelComposition:\n ColorSamplingChannelComposition =\n channelSiteCounts.length === 1\n ? {\n kind: \"single-channel\",\n channelId: firstChannel.channelId\n }\n : {\n kind: \"mixed-channels\",\n channelIds:\n channelSiteCounts.map(\n (entry) => entry.channelId\n )\n };\n\n return {\n modeId: captureMode.modeId,\n colorSamplingProfileId:\n colorSamplingProfile.profileId,\n modeSampleCoordinateSystem:\n \"capture-mode-full-frame-effective-sample-index\",\n modeSampleIndexFullFrame: {\n x: modeX,\n y: modeY\n },\n modeSampleRaster: {\n ...modeSampleRaster\n },\n modeSamplingKind:\n captureMode.perFrameSampling.kind,\n nativeEffectiveSampleRect,\n colorSamplingSiteRect,\n colorSamplingSiteGrid: {\n ...binding.colorSamplingSiteGrid\n },\n totalContributorSites,\n channelSiteCounts,\n channelComposition,\n grouping,\n ...(captureMode\n .interFrameSensorOffsetsNativeSamples ===\n undefined\n ? {}\n : {\n interFrameSensorOffsetsNativeSamples:\n captureMode\n .interFrameSensorOffsetsNativeSamples\n }),\n sensorShiftChangesColorSiteAssignment:\n false,\n sensorShiftOpticalRegistration:\n \"outside-this-contract\",\n structuralSourceStage:\n \"pre-reconstruction\",\n completeDownstreamPixelDependencyEstablished:\n false,\n signalCombinationWeightingEstablished:\n false,\n spectralResponseEstablished: false,\n physicalPhotodiodeBindingEstablished:\n false,\n physicalPhotodiodeCountInference:\n \"not-permitted\",\n componentEvidence: {\n binding:\n binding.componentEvidence.binding,\n colorSamplingProfile:\n binding.componentEvidence\n .colorSamplingProfile,\n captureMode: captureMode.modeEvidence\n }\n };\n}\n"]}
1
+ {"version":3,"file":"capture-color-sampling-binding.js","sourceRoot":"","sources":["../../src/sensor/capture-color-sampling-binding.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Links the sensor's native image pixel grid to its grid of colour-sampling sites (for example the\n * cells of a CFA, colour filter array), and finds which sites feed each capture-mode sample.\n *\n * The link exists only because the caller supplies an evidence-backed binding. Matching grid sizes\n * are never taken as proof of a one-to-one link. This first version supports only regular\n * rectangular blocks of sites that share the native top-left corner.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport {\n InvalidScientificInputError,\n requirePositiveInteger\n} from \"../core/validation.js\";\nimport {\n resolveCaptureMode,\n type CaptureModeProfile,\n type CaptureModeSampleCombinationDomain,\n type CaptureModeSensorOffsetNativeSamples,\n type SourcedCaptureModeFact\n} from \"./capture-mode.js\";\nimport {\n parseSensorColorSamplingProfile,\n type SensorColorSamplingProfile\n} from \"./color-sampling.js\";\nimport type { NativeImageRaster } from \"./sensor-geometry.js\";\nimport { requireInputObject, requireInputKeys, requireConfigRecord as requireRecord, requireConfigString as requireNonEmptyString, requireConfigPositiveSafeInteger as requirePositiveSafeInteger } from \"../core/record-validation.js\";\n\n\nexport interface NativeEffectiveRasterColorSamplingBindingProfile {\n schemaVersion: \"0.1.0\";\n bindingId: string;\n colorSamplingProfileId: string;\n /**\n * The exact native image grid this binding has evidence for. Do not reuse\n * the binding with another grid just because another sensor or mode has\n * similar dimensions or the same aspect ratio.\n */\n nativeRaster: NativeImageRaster;\n /**\n * Evidence for the link between the native image grid and the colour-site\n * grid itself.\n *\n * Capture-mode and colour-layout facts keep their own separate evidence.\n */\n evidence: readonly EvidenceProvenance[];\n relationship: {\n kind: \"regular-native-effective-sample-blocks\";\n /**\n * Number of colour-sampling sites one native image sample spans\n * horizontally.\n */\n sitesPerNativeSampleX: number;\n /**\n * Number of colour-sampling sites one native image sample spans\n * vertically.\n */\n sitesPerNativeSampleY: number;\n /**\n * Both grids start at the same native sensor top-left corner.\n *\n * This keeps the CFA phase absolute through later active-area crops and\n * orientation changes.\n */\n anchor: \"shared-native-top-left\";\n };\n}\n\nexport interface ColorSamplingSiteGridDimensions {\n widthSites: number;\n heightSites: number;\n}\n\nexport interface ResolvedNativeEffectiveRasterColorSamplingBinding {\n bindingId: string;\n colorSamplingProfileId: string;\n nativeRaster: NativeImageRaster;\n relationshipMeaning:\n | \"one-native-effective-sample-to-one-color-site\"\n | \"one-native-effective-sample-to-rectangular-color-site-block\";\n relationship: {\n kind: \"regular-native-effective-sample-blocks\";\n sitesPerNativeSampleX: number;\n sitesPerNativeSampleY: number;\n anchor: \"shared-native-top-left\";\n };\n colorSamplingSiteGrid: ColorSamplingSiteGridDimensions;\n totalColorSamplingSites: number;\n nativeImageRasterBindingEstablished: true;\n physicalPhotodiodeBindingEstablished: false;\n physicalPhotodiodeCountInference: \"not-permitted\";\n spectralResponseEstablished: false;\n componentEvidence: {\n binding: readonly EvidenceProvenance[];\n colorSamplingProfile: readonly EvidenceProvenance[];\n };\n}\n\nexport interface NativeEffectiveSampleRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface ColorSamplingSiteRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface CaptureModeFullFrameSampleIndex {\n /**\n * Zero-based index in the selected mode's full-frame sample grid (per frame).\n * Convert crop-relative coordinates to absolute full-frame indices before\n * calling this API.\n */\n x: number;\n y: number;\n}\n\nexport interface ColorSamplingChannelSiteCount {\n channelId: string;\n siteCount: number;\n}\n\nexport type ColorSamplingChannelComposition =\n | {\n kind: \"single-channel\";\n channelId: string;\n }\n | {\n kind: \"mixed-channels\";\n channelIds: readonly string[];\n };\n\nexport interface GroupedCaptureModeSamplingAnchorDeclaration {\n anchor: \"native-effective-raster-top-left\";\n evidence: readonly EvidenceProvenance[];\n}\n\nexport interface ResolveCaptureModeColorSamplingContributorsInput {\n nativeRaster: NativeImageRaster;\n captureModeProfile: CaptureModeProfile;\n modeId: string;\n colorSamplingProfile: SensorColorSamplingProfile;\n bindingProfile: NativeEffectiveRasterColorSamplingBindingProfile;\n modeSampleIndexFullFrame: CaptureModeFullFrameSampleIndex;\n /**\n * Required for modes that group native samples, because the capture-mode\n * profile declares grouping factors but does not prove where the groups start.\n */\n groupedSamplingAnchor?: GroupedCaptureModeSamplingAnchorDeclaration;\n}\n\nexport interface ResolvedCaptureModeColorSamplingContributors {\n modeId: string;\n colorSamplingProfileId: string;\n modeSampleCoordinateSystem:\n \"capture-mode-full-frame-effective-sample-index\";\n modeSampleIndexFullFrame: CaptureModeFullFrameSampleIndex;\n modeSampleRaster: {\n pixelWidth: number;\n pixelHeight: number;\n };\n modeSamplingKind:\n | \"native-effective-raster\"\n | \"grouped-native-samples\";\n nativeEffectiveSampleRect: NativeEffectiveSampleRect;\n colorSamplingSiteRect: ColorSamplingSiteRect;\n colorSamplingSiteGrid: ColorSamplingSiteGridDimensions;\n totalContributorSites: number;\n channelSiteCounts: readonly ColorSamplingChannelSiteCount[];\n channelComposition: ColorSamplingChannelComposition;\n /**\n * Present only when the selected capture mode groups several native samples\n * into one sample per frame.\n */\n grouping:\n | {\n groupWidthSamples: SourcedCaptureModeFact<number>;\n groupHeightSamples: SourcedCaptureModeFact<number>;\n combinationDomain?: SourcedCaptureModeFact<CaptureModeSampleCombinationDomain>;\n anchor: \"native-effective-raster-top-left\";\n anchorEvidence: readonly EvidenceProvenance[];\n }\n | null;\n /**\n * The sensor-shift sequence stays attached to the sensor and capture mode.\n *\n * The sensor and its CFA move together, so these offsets must not change\n * which colour channel a site belongs to.\n */\n interFrameSensorOffsetsNativeSamples?: SourcedCaptureModeFact<\n readonly CaptureModeSensorOffsetNativeSamples[]\n >;\n sensorShiftChangesColorSiteAssignment: false;\n sensorShiftOpticalRegistration:\n \"outside-this-contract\";\n structuralSourceStage: \"pre-reconstruction\";\n completeDownstreamPixelDependencyEstablished: false;\n signalCombinationWeightingEstablished: false;\n spectralResponseEstablished: false;\n physicalPhotodiodeBindingEstablished: false;\n physicalPhotodiodeCountInference: \"not-permitted\";\n componentEvidence: {\n binding: readonly EvidenceProvenance[];\n colorSamplingProfile: readonly EvidenceProvenance[];\n captureMode: readonly EvidenceProvenance[];\n };\n}\n\nfunction parseBoundNativeRaster(\n value: unknown\n): NativeImageRaster {\n const nativeRaster = requireRecord(\n value,\n \"colorSamplingBinding.nativeRaster\", [\"pixelWidth\", \"pixelHeight\"]\n );\n const pixelWidth =\n requirePositiveSafeInteger(\n nativeRaster.pixelWidth,\n \"colorSamplingBinding.nativeRaster.pixelWidth\"\n );\n const pixelHeight =\n requirePositiveSafeInteger(\n nativeRaster.pixelHeight,\n \"colorSamplingBinding.nativeRaster.pixelHeight\"\n );\n\n if (\n !Number.isSafeInteger(\n pixelWidth * pixelHeight\n )\n ) {\n throw new InvalidConfigurationError(\n \"colorSamplingBinding.nativeRaster total image-sample count must be a safe integer.\"\n );\n }\n\n return {\n pixelWidth,\n pixelHeight\n };\n}\n\nfunction safeProduct(\n values: readonly number[],\n path: string\n): number {\n let result = 1;\n for (const value of values) {\n result *= value;\n if (!Number.isSafeInteger(result)) {\n throw new InvalidScientificInputError(\n path + \" must remain a safe integer.\"\n );\n }\n }\n return result;\n}\n\nfunction validateNativeRaster(\n raster: NativeImageRaster\n): void {\n requireInputObject(raster, \"raster input\");\n requirePositiveInteger(\n \"nativeRaster.pixelWidth\",\n raster.pixelWidth\n );\n requirePositiveInteger(\n \"nativeRaster.pixelHeight\",\n raster.pixelHeight\n );\n safeProduct(\n [raster.pixelWidth, raster.pixelHeight],\n \"nativeRaster total image-sample count\"\n );\n}\n\nfunction requireFullFrameSampleIndex(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isSafeInteger(value) ||\n value < 0\n ) {\n throw new InvalidScientificInputError(\n path +\n \" must be a non-negative safe integer full-frame mode sample index.\"\n );\n }\n return value;\n}\n\n/**\n * Parses the explicit link between the native image pixel grid and the separate\n * grid of colour-sampling sites.\n *\n * This first version supports only regular rectangular blocks of sites that\n * share the native top-left corner. Irregular mappings, sparse exceptions and\n * mode-specific declared grids would need new, explicit contracts.\n */\nexport function parseNativeEffectiveRasterColorSamplingBindingProfile(\n value: unknown\n): NativeEffectiveRasterColorSamplingBindingProfile {\n const profile = requireRecord(\n value,\n \"colorSamplingBinding\", [\"schemaVersion\", \"relationship\", \"bindingId\", \"colorSamplingProfileId\", \"nativeRaster\", \"evidence\"]\n );\n\n if (profile.schemaVersion !== \"0.1.0\") {\n throw new InvalidConfigurationError(\n 'colorSamplingBinding.schemaVersion must be \"0.1.0\".'\n );\n }\n\n const relationship = requireRecord(\n profile.relationship,\n \"colorSamplingBinding.relationship\", [\"kind\", \"anchor\", \"sitesPerNativeSampleX\", \"sitesPerNativeSampleY\"]\n );\n\n if (\n relationship.kind !==\n \"regular-native-effective-sample-blocks\"\n ) {\n throw new InvalidConfigurationError(\n \"colorSamplingBinding.relationship.kind is invalid.\"\n );\n }\n\n if (\n relationship.anchor !==\n \"shared-native-top-left\"\n ) {\n throw new InvalidConfigurationError(\n 'colorSamplingBinding.relationship.anchor must be \"shared-native-top-left\".'\n );\n }\n\n return {\n schemaVersion: \"0.1.0\",\n bindingId: requireNonEmptyString(\n profile.bindingId,\n \"colorSamplingBinding.bindingId\"\n ),\n colorSamplingProfileId:\n requireNonEmptyString(\n profile.colorSamplingProfileId,\n \"colorSamplingBinding.colorSamplingProfileId\"\n ),\n nativeRaster: parseBoundNativeRaster(\n profile.nativeRaster\n ),\n evidence: parseEvidenceList(\n profile.evidence,\n \"colorSamplingBinding.evidence\"\n ),\n relationship: {\n kind:\n \"regular-native-effective-sample-blocks\",\n sitesPerNativeSampleX:\n requirePositiveSafeInteger(\n relationship.sitesPerNativeSampleX,\n \"colorSamplingBinding.relationship.sitesPerNativeSampleX\"\n ),\n sitesPerNativeSampleY:\n requirePositiveSafeInteger(\n relationship.sitesPerNativeSampleY,\n \"colorSamplingBinding.relationship.sitesPerNativeSampleY\"\n ),\n anchor: \"shared-native-top-left\"\n }\n };\n}\n\n/**\n * Validates and resolves the link between NativeImageRaster and an exact colour\n * sampling layout (monochrome or a repeating pattern such as a CFA).\n *\n * Matching dimensions are never taken as proof of a one-to-one link. The link\n * exists only because the caller supplied this evidence-backed binding profile.\n */\nexport function resolveNativeEffectiveRasterColorSamplingBinding(\n input: {\n nativeRaster: NativeImageRaster;\n colorSamplingProfile: SensorColorSamplingProfile;\n bindingProfile: NativeEffectiveRasterColorSamplingBindingProfile;\n }\n): ResolvedNativeEffectiveRasterColorSamplingBinding {\n requireInputKeys(input, \"resolveNativeEffectiveRasterColorSamplingBinding\", [\"nativeRaster\", \"colorSamplingProfile\", \"bindingProfile\"]);\n validateNativeRaster(input.nativeRaster);\n\n const colorSamplingProfile =\n parseSensorColorSamplingProfile(\n input.colorSamplingProfile\n );\n const bindingProfile =\n parseNativeEffectiveRasterColorSamplingBindingProfile(\n input.bindingProfile\n );\n\n if (\n bindingProfile.colorSamplingProfileId !==\n colorSamplingProfile.profileId\n ) {\n throw new InvalidScientificInputError(\n \"Binding colorSamplingProfileId must match colorSamplingProfile.profileId.\"\n );\n }\n\n if (\n bindingProfile.nativeRaster.pixelWidth !==\n input.nativeRaster.pixelWidth ||\n bindingProfile.nativeRaster.pixelHeight !==\n input.nativeRaster.pixelHeight\n ) {\n throw new InvalidScientificInputError(\n \"Binding nativeRaster must exactly match the supplied canonical native effective raster.\"\n );\n }\n\n if (colorSamplingProfile.layout.kind === \"layered\") {\n throw new InvalidScientificInputError(\n \"Layered color sampling cannot be bound to NativeImageRaster until its per-layer spatial sampling relationship is explicitly modeled.\"\n );\n }\n\n if (\n colorSamplingProfile.coordinateSystem !==\n \"native-sensor-color-sampling-site-index\"\n ) {\n throw new InvalidScientificInputError(\n \"Resolvable color sampling binding requires the native sensor color-sampling-site coordinate system.\"\n );\n }\n\n const widthSites = safeProduct(\n [\n input.nativeRaster.pixelWidth,\n bindingProfile.relationship\n .sitesPerNativeSampleX\n ],\n \"color-sampling site-grid width\"\n );\n const heightSites = safeProduct(\n [\n input.nativeRaster.pixelHeight,\n bindingProfile.relationship\n .sitesPerNativeSampleY\n ],\n \"color-sampling site-grid height\"\n );\n const totalColorSamplingSites =\n safeProduct(\n [widthSites, heightSites],\n \"color-sampling site-grid total site count\"\n );\n\n const oneToOne =\n bindingProfile.relationship\n .sitesPerNativeSampleX === 1 &&\n bindingProfile.relationship\n .sitesPerNativeSampleY === 1;\n\n return {\n bindingId: bindingProfile.bindingId,\n colorSamplingProfileId:\n bindingProfile.colorSamplingProfileId,\n nativeRaster: {\n ...input.nativeRaster\n },\n relationshipMeaning: oneToOne\n ? \"one-native-effective-sample-to-one-color-site\"\n : \"one-native-effective-sample-to-rectangular-color-site-block\",\n relationship: {\n ...bindingProfile.relationship\n },\n colorSamplingSiteGrid: {\n widthSites,\n heightSites\n },\n totalColorSamplingSites,\n nativeImageRasterBindingEstablished: true,\n physicalPhotodiodeBindingEstablished: false,\n physicalPhotodiodeCountInference:\n \"not-permitted\",\n spectralResponseEstablished: false,\n componentEvidence: {\n binding: bindingProfile.evidence,\n colorSamplingProfile:\n colorSamplingProfile.evidence\n }\n };\n}\n\nfunction countResidueInRange(\n start: number,\n length: number,\n period: number,\n residue: number\n): number {\n const startResidue = start % period;\n const delta =\n (residue - startResidue + period) %\n period;\n\n if (delta >= length) {\n return 0;\n }\n\n return (\n 1 +\n Math.floor(\n (length - 1 - delta) / period\n )\n );\n}\n\nfunction channelCountsForSiteRect(\n profile: SensorColorSamplingProfile,\n rect: ColorSamplingSiteRect\n): readonly ColorSamplingChannelSiteCount[] {\n const totalSites = safeProduct(\n [rect.width, rect.height],\n \"color-sampling contributor site count\"\n );\n\n if (profile.layout.kind === \"layered\") {\n throw new InvalidScientificInputError(\n \"Layered color sampling does not have resolvable site contributors in schema 0.1.0.\"\n );\n }\n\n if (profile.layout.kind === \"monochrome\") {\n return [\n {\n channelId: profile.layout.channelId,\n siteCount: totalSites\n }\n ];\n }\n\n const counts =\n new Map<string, number>();\n\n for (\n let tileY = 0;\n tileY < profile.layout.repeatHeightSites;\n tileY += 1\n ) {\n const countY = countResidueInRange(\n rect.y,\n rect.height,\n profile.layout.repeatHeightSites,\n tileY\n );\n if (countY === 0) {\n continue;\n }\n\n for (\n let tileX = 0;\n tileX < profile.layout.repeatWidthSites;\n tileX += 1\n ) {\n const countX = countResidueInRange(\n rect.x,\n rect.width,\n profile.layout.repeatWidthSites,\n tileX\n );\n if (countX === 0) {\n continue;\n }\n\n const tileIndex =\n tileY *\n profile.layout.repeatWidthSites +\n tileX;\n const channelId =\n profile.layout.siteChannelIds[\n tileIndex\n ];\n\n if (channelId === undefined) {\n throw new InvalidScientificInputError(\n \"Periodic color-sampling tile resolution failed while calculating contributor counts.\"\n );\n }\n\n const siteCount = safeProduct(\n [countX, countY],\n \"per-channel contributor site count\"\n );\n const prior = counts.get(channelId) ?? 0;\n const next = prior + siteCount;\n if (!Number.isSafeInteger(next)) {\n throw new InvalidScientificInputError(\n \"Per-channel contributor site count must remain a safe integer.\"\n );\n }\n counts.set(channelId, next);\n }\n }\n\n const result = Array.from(\n counts,\n ([channelId, siteCount]) => ({\n channelId,\n siteCount\n })\n );\n const countedTotal = result.reduce(\n (sum, entry) => sum + entry.siteCount,\n 0\n );\n\n if (countedTotal !== totalSites) {\n throw new InvalidScientificInputError(\n \"Color-sampling contributor channel counts do not cover the complete contributor site rectangle.\"\n );\n }\n\n return result;\n}\n\n/**\n * Finds which colour-sampling sites lie under one capture-mode sample, before any\n * reconstruction (demosaicing).\n *\n * The sample index is always absolute in the selected mode's full-frame grid. Crop-relative\n * indices are not accepted, so an active-area crop can never silently reset the CFA phase.\n *\n * For modes that group native samples, the returned rectangle covers every native sample in\n * the declared regular group. This only describes which sites lie under the sample. The engine\n * reports how many sites of each channel there are, but does not claim this is every dependency\n * of the final digital pixel. It never makes up combination weights, sum or average rules,\n * spectral response, or photon or electron values.\n *\n * Modes with a declared sample grid are rejected, because the capture-mode profile says their\n * relationship to the native grid cannot safely be written as simple integer grouping.\n *\n * Sensor shifts between frames are kept as capture-mode metadata only. The sensor and CFA move\n * together, so the shifts do not change which channel a site belongs to. Their effect on optical\n * registration belongs to a later spatial-sampling or image-formation model.\n */\nexport function resolveCaptureModeColorSamplingContributors(\n input: ResolveCaptureModeColorSamplingContributorsInput\n): ResolvedCaptureModeColorSamplingContributors {\n requireInputKeys(input, \"resolveCaptureModeColorSamplingContributors\", [\"nativeRaster\", \"captureModeProfile\", \"modeId\", \"colorSamplingProfile\", \"bindingProfile\", \"modeSampleIndexFullFrame\", \"groupedSamplingAnchor\"]);\n const colorSamplingProfile =\n parseSensorColorSamplingProfile(\n input.colorSamplingProfile\n );\n const binding =\n resolveNativeEffectiveRasterColorSamplingBinding(\n {\n nativeRaster: input.nativeRaster,\n colorSamplingProfile,\n bindingProfile: input.bindingProfile\n }\n );\n const captureMode = resolveCaptureMode({\n nativeRaster: input.nativeRaster,\n profile: input.captureModeProfile,\n modeId: input.modeId\n });\n\n if (\n !captureMode.dependencies.includes(\n \"color-sampling-model\"\n )\n ) {\n throw new InvalidScientificInputError(\n \"Selected capture mode must declare the color-sampling-model dependency before color-site contributors can be resolved.\"\n );\n }\n\n const modeX = requireFullFrameSampleIndex(\n input.modeSampleIndexFullFrame.x,\n \"modeSampleIndexFullFrame.x\"\n );\n const modeY = requireFullFrameSampleIndex(\n input.modeSampleIndexFullFrame.y,\n \"modeSampleIndexFullFrame.y\"\n );\n\n if (\n captureMode.perFrameSampling.kind ===\n \"declared-effective-raster\"\n ) {\n throw new InvalidScientificInputError(\n \"declared-effective-raster capture modes require a separate mode-specific color-site binding; no relationship may be inferred from raster dimensions alone.\"\n );\n }\n\n const modeSampleRaster =\n captureMode.perFrameSampling.raster;\n\n if (\n modeX >= modeSampleRaster.pixelWidth ||\n modeY >= modeSampleRaster.pixelHeight\n ) {\n throw new InvalidScientificInputError(\n \"modeSampleIndexFullFrame must lie within the selected mode's full-frame per-frame sampling raster.\"\n );\n }\n\n let nativeEffectiveSampleRect:\n NativeEffectiveSampleRect;\n let grouping:\n ResolvedCaptureModeColorSamplingContributors[\"grouping\"];\n\n if (\n captureMode.perFrameSampling.kind ===\n \"native-effective-raster\"\n ) {\n if (input.groupedSamplingAnchor !== undefined) {\n throw new InvalidScientificInputError(\n \"groupedSamplingAnchor must be omitted for native-effective-raster capture modes.\"\n );\n }\n\n nativeEffectiveSampleRect = {\n x: modeX,\n y: modeY,\n width: 1,\n height: 1\n };\n grouping = null;\n } else {\n if (\n input.groupedSamplingAnchor === undefined ||\n input.groupedSamplingAnchor.anchor !==\n \"native-effective-raster-top-left\"\n ) {\n throw new InvalidScientificInputError(\n \"grouped-native-samples capture modes require an explicit native-effective-raster-top-left grouping anchor declaration.\"\n );\n }\n const groupingAnchorEvidence =\n parseEvidenceList(\n input.groupedSamplingAnchor.evidence,\n \"groupedSamplingAnchor.evidence\"\n );\n\n const groupWidth =\n captureMode.perFrameSampling\n .groupWidthSamples.value;\n const groupHeight =\n captureMode.perFrameSampling\n .groupHeightSamples.value;\n\n nativeEffectiveSampleRect = {\n x: safeProduct(\n [modeX, groupWidth],\n \"native effective sample rectangle x\"\n ),\n y: safeProduct(\n [modeY, groupHeight],\n \"native effective sample rectangle y\"\n ),\n width: groupWidth,\n height: groupHeight\n };\n grouping = {\n groupWidthSamples:\n captureMode.perFrameSampling\n .groupWidthSamples,\n groupHeightSamples:\n captureMode.perFrameSampling\n .groupHeightSamples,\n ...(captureMode.perFrameSampling\n .combinationDomain === undefined\n ? {}\n : {\n combinationDomain:\n captureMode.perFrameSampling\n .combinationDomain\n }),\n anchor:\n \"native-effective-raster-top-left\",\n anchorEvidence:\n groupingAnchorEvidence\n };\n }\n\n const sitesPerNativeX =\n binding.relationship\n .sitesPerNativeSampleX;\n const sitesPerNativeY =\n binding.relationship\n .sitesPerNativeSampleY;\n\n const colorSamplingSiteRect:\n ColorSamplingSiteRect = {\n x: safeProduct(\n [\n nativeEffectiveSampleRect.x,\n sitesPerNativeX\n ],\n \"color-sampling contributor rectangle x\"\n ),\n y: safeProduct(\n [\n nativeEffectiveSampleRect.y,\n sitesPerNativeY\n ],\n \"color-sampling contributor rectangle y\"\n ),\n width: safeProduct(\n [\n nativeEffectiveSampleRect.width,\n sitesPerNativeX\n ],\n \"color-sampling contributor rectangle width\"\n ),\n height: safeProduct(\n [\n nativeEffectiveSampleRect.height,\n sitesPerNativeY\n ],\n \"color-sampling contributor rectangle height\"\n )\n };\n\n if (\n colorSamplingSiteRect.x +\n colorSamplingSiteRect.width >\n binding.colorSamplingSiteGrid.widthSites ||\n colorSamplingSiteRect.y +\n colorSamplingSiteRect.height >\n binding.colorSamplingSiteGrid.heightSites\n ) {\n throw new InvalidScientificInputError(\n \"Resolved color-sampling contributor rectangle exceeds the bound color-site grid.\"\n );\n }\n\n const totalContributorSites =\n safeProduct(\n [\n colorSamplingSiteRect.width,\n colorSamplingSiteRect.height\n ],\n \"total contributor site count\"\n );\n const channelSiteCounts =\n channelCountsForSiteRect(\n colorSamplingProfile,\n colorSamplingSiteRect\n );\n\n const firstChannel = channelSiteCounts[0];\n if (firstChannel === undefined) {\n throw new InvalidScientificInputError(\n \"Resolved structural source region must contain at least one color-sampling channel.\"\n );\n }\n\n const channelComposition:\n ColorSamplingChannelComposition =\n channelSiteCounts.length === 1\n ? {\n kind: \"single-channel\",\n channelId: firstChannel.channelId\n }\n : {\n kind: \"mixed-channels\",\n channelIds:\n channelSiteCounts.map(\n (entry) => entry.channelId\n )\n };\n\n return {\n modeId: captureMode.modeId,\n colorSamplingProfileId:\n colorSamplingProfile.profileId,\n modeSampleCoordinateSystem:\n \"capture-mode-full-frame-effective-sample-index\",\n modeSampleIndexFullFrame: {\n x: modeX,\n y: modeY\n },\n modeSampleRaster: {\n ...modeSampleRaster\n },\n modeSamplingKind:\n captureMode.perFrameSampling.kind,\n nativeEffectiveSampleRect,\n colorSamplingSiteRect,\n colorSamplingSiteGrid: {\n ...binding.colorSamplingSiteGrid\n },\n totalContributorSites,\n channelSiteCounts,\n channelComposition,\n grouping,\n ...(captureMode\n .interFrameSensorOffsetsNativeSamples ===\n undefined\n ? {}\n : {\n interFrameSensorOffsetsNativeSamples:\n captureMode\n .interFrameSensorOffsetsNativeSamples\n }),\n sensorShiftChangesColorSiteAssignment:\n false,\n sensorShiftOpticalRegistration:\n \"outside-this-contract\",\n structuralSourceStage:\n \"pre-reconstruction\",\n completeDownstreamPixelDependencyEstablished:\n false,\n signalCombinationWeightingEstablished:\n false,\n spectralResponseEstablished: false,\n physicalPhotodiodeBindingEstablished:\n false,\n physicalPhotodiodeCountInference:\n \"not-permitted\",\n componentEvidence: {\n binding:\n binding.componentEvidence.binding,\n colorSamplingProfile:\n binding.componentEvidence\n .colorSamplingProfile,\n captureMode: captureMode.modeEvidence\n }\n };\n}\n"]}
@@ -14,7 +14,7 @@ import { parseEvidenceList } from "../core/evidence-provenance.js";
14
14
  import { InvalidScientificInputError } from "../core/validation.js";
15
15
  import { calculateCaptureExposureWindows } from "./exposure-window.js";
16
16
  import { calculateSensorReadoutTiming } from "./readout-timing.js";
17
- import { requireInputKeys, rejectUnknownFields } from "../core/record-validation.js";
17
+ import { requireInputKeys, requireConfigRecord as requireRecord, requireTrimmedConfigString as requireNonEmptyString, requireConfigNonNegativeFinite as requireNonNegativeFinite, parseConfigLimitations as parseLimitations } from "../core/record-validation.js";
18
18
  export const CAPTURE_MODE_TIMING_PROFILE_SCHEMA_VERSION = "0.1.0";
19
19
  const SHUTTER_MECHANISMS = new Set([
20
20
  "mechanical",
@@ -27,44 +27,6 @@ const SCAN_DIRECTIONS = new Set([
27
27
  "left-to-right",
28
28
  "right-to-left"
29
29
  ]);
30
- function requireRecord(value, path, allowedKeys) {
31
- if (typeof value !== "object" ||
32
- value === null ||
33
- Array.isArray(value)) {
34
- throw new InvalidConfigurationError(path + " must be an object.");
35
- }
36
- if (allowedKeys !== null)
37
- rejectUnknownFields(value, allowedKeys, path);
38
- return value;
39
- }
40
- function requireNonEmptyString(value, path) {
41
- if (typeof value !== "string" ||
42
- value.trim().length === 0) {
43
- throw new InvalidConfigurationError(path + " must be a non-empty string.");
44
- }
45
- return value.trim();
46
- }
47
- function requireNonNegativeFinite(value, path) {
48
- if (typeof value !== "number" ||
49
- !Number.isFinite(value) ||
50
- value < 0) {
51
- throw new InvalidConfigurationError(path +
52
- " must be finite and greater than or equal to zero.");
53
- }
54
- return value;
55
- }
56
- function parseLimitations(value, path) {
57
- if (!Array.isArray(value)) {
58
- throw new InvalidConfigurationError(path + " must be an array.");
59
- }
60
- const parsed = value.map((entry, index) => requireNonEmptyString(entry, path + "[" + index + "]"));
61
- if (new Set(parsed).size !==
62
- parsed.length) {
63
- throw new InvalidConfigurationError(path +
64
- " must not contain duplicates.");
65
- }
66
- return parsed;
67
- }
68
30
  function parseSecondsFact(value, path) {
69
31
  const record = requireRecord(value, path, ["unit", "value", "evidence"]);
70
32
  if (record.unit !== "s") {
@@ -1 +1 @@
1
- {"version":3,"file":"capture-mode-timing.js","sourceRoot":"","sources":["../../src/sensor/capture-mode-timing.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Capture-mode timing: exposure and readout timing facts tied to one exact capture mode.\n *\n * Only the schedules the readout-timing and exposure-window models already support are accepted:\n * global (all rows at once) or a uniform linear scan (rolling, row by row). Non-uniform or segmented\n * schedules are rejected, not approximated. Readout timing is kept separate from the exposure\n * opening and closing times, and no absolute sync between them is assumed.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport type {\n RasterPoint,\n RasterRect\n} from \"../output/capture-geometry.js\";\nimport {\n calculateCaptureExposureWindows,\n type CaptureExposureWindows,\n type ExposureBoundarySchedule,\n type SourcedCaptureBoundaryDirection,\n type SourcedCaptureTimingSeconds\n} from \"./exposure-window.js\";\nimport type {\n ResolvedCaptureMode\n} from \"./capture-mode.js\";\nimport {\n calculateSensorReadoutTiming,\n type CaptureShutterMechanism,\n type NativeSensorReadoutScanDirection,\n type SensorReadoutTiming,\n type SensorReadoutTimingDeclaration,\n type SourcedSensorReadoutFact,\n type SourcedSensorTimingSeconds\n} from \"./readout-timing.js\";\nimport type {\n NativeImageRaster\n} from \"./sensor-geometry.js\";\nimport { requireInputKeys, rejectUnknownFields } from \"../core/record-validation.js\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport const CAPTURE_MODE_TIMING_PROFILE_SCHEMA_VERSION =\n \"0.1.0\" as const;\n\nexport interface CaptureModeTimingProfile {\n schemaVersion:\n typeof CAPTURE_MODE_TIMING_PROFILE_SCHEMA_VERSION;\n profileId: string;\n profileVersion: string;\n captureModeId: string;\n scientificStatus: \"approximation\";\n scheduleFamily:\n \"global-or-uniform-linear-native-scan\";\n shutterMechanism:\n CaptureShutterMechanism;\n opening:\n ExposureBoundarySchedule;\n closing:\n ExposureBoundarySchedule;\n readout:\n SensorReadoutTimingDeclaration;\n nonUniformScheduleModeled: false;\n evidence: readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\nexport interface ResolveCaptureModeTimingInput {\n captureMode:\n ResolvedCaptureMode;\n timingProfile:\n CaptureModeTimingProfile;\n nominalExposureDurationSeconds:\n SourcedCaptureTimingSeconds;\n activeCaptureRect?: RasterRect;\n samplePointsNative?:\n readonly RasterPoint[];\n}\n\nexport interface ResolvedCaptureModeTiming {\n schemaVersion:\n typeof CAPTURE_MODE_TIMING_PROFILE_SCHEMA_VERSION;\n timingProfileId: string;\n timingProfileVersion: string;\n captureModeId: string;\n captureModeTimingIdentity:\n \"exact-mode-and-profile\";\n nativeRaster: NativeImageRaster;\n modeDeclaresTimingDependency:\n boolean;\n scheduleFamily:\n \"global-or-uniform-linear-native-scan\";\n exposureWindows:\n CaptureExposureWindows;\n readoutTiming:\n SensorReadoutTiming;\n timeReference:\n \"first-opening-boundary-phase\";\n readoutExposureSynchronization:\n \"not-assumed\";\n nonUniformScheduleModeled: false;\n evidence: readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\nconst SHUTTER_MECHANISMS =\n new Set<CaptureShutterMechanism>([\n \"mechanical\",\n \"electronic-first-curtain\",\n \"electronic\"\n ]);\n\nconst SCAN_DIRECTIONS =\n new Set<NativeSensorReadoutScanDirection>([\n \"top-to-bottom\",\n \"bottom-to-top\",\n \"left-to-right\",\n \"right-to-left\"\n ]);\n\nfunction requireRecord(\n value: unknown,\n path: string,\n allowedKeys: readonly string[] | null\n): UnknownRecord {\n if (\n typeof value !== \"object\" ||\n value === null ||\n Array.isArray(value)\n ) {\n throw new InvalidConfigurationError(\n path + \" must be an object.\"\n );\n }\n if (allowedKeys !== null) rejectUnknownFields(value, allowedKeys, path);\n return value as UnknownRecord;\n}\n\nfunction requireNonEmptyString(\n value: unknown,\n path: string\n): string {\n if (\n typeof value !== \"string\" ||\n value.trim().length === 0\n ) {\n throw new InvalidConfigurationError(\n path + \" must be a non-empty string.\"\n );\n }\n return value.trim();\n}\n\nfunction requireNonNegativeFinite(\n value: unknown,\n path: string\n): number {\n if (\n typeof value !== \"number\" ||\n !Number.isFinite(value) ||\n value < 0\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must be finite and greater than or equal to zero.\"\n );\n }\n return value;\n}\n\nfunction parseLimitations(\n value: unknown,\n path: string\n): readonly string[] {\n if (!Array.isArray(value)) {\n throw new InvalidConfigurationError(\n path + \" must be an array.\"\n );\n }\n const parsed =\n value.map((entry, index) =>\n requireNonEmptyString(\n entry,\n path + \"[\" + index + \"]\"\n )\n );\n if (\n new Set(parsed).size !==\n parsed.length\n ) {\n throw new InvalidConfigurationError(\n path +\n \" must not contain duplicates.\"\n );\n }\n return parsed;\n}\n\nfunction parseSecondsFact(\n value: unknown,\n path: string\n): SourcedSensorTimingSeconds {\n const record =\n requireRecord(value, path, [\"unit\", \"value\", \"evidence\"]);\n if (record.unit !== \"s\") {\n throw new InvalidConfigurationError(\n path + '.unit must be \"s\".'\n );\n }\n return {\n value:\n requireNonNegativeFinite(\n record.value,\n path + \".value\"\n ),\n unit: \"s\",\n evidence:\n parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n )\n };\n}\n\nfunction parseDirectionFact(\n value: unknown,\n path: string\n): SourcedSensorReadoutFact<NativeSensorReadoutScanDirection> {\n const record =\n requireRecord(value, path, [\"value\", \"evidence\"]);\n if (\n typeof record.value !== \"string\" ||\n !SCAN_DIRECTIONS.has(\n record.value as NativeSensorReadoutScanDirection\n )\n ) {\n throw new InvalidConfigurationError(\n path + \".value is invalid.\"\n );\n }\n return {\n value:\n record.value as NativeSensorReadoutScanDirection,\n evidence:\n parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n )\n };\n}\n\nfunction parseBoundarySchedule(\n value: unknown,\n path: string\n): ExposureBoundarySchedule {\n const record =\n requireRecord(value, path, [\"kind\", \"directionNative\", \"traversalDurationSeconds\"]);\n\n if (record.kind === \"simultaneous\") {\n if (\n record.directionNative !==\n undefined ||\n record.traversalDurationSeconds !==\n undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" simultaneous schedule must not include scan-only fields.\"\n );\n }\n return {\n kind: \"simultaneous\"\n };\n }\n\n if (\n record.kind ===\n \"uniform-linear-native-scan\"\n ) {\n return {\n kind:\n \"uniform-linear-native-scan\",\n directionNative:\n parseDirectionFact(\n record.directionNative,\n path + \".directionNative\"\n ) as SourcedCaptureBoundaryDirection,\n traversalDurationSeconds:\n parseSecondsFact(\n record.traversalDurationSeconds,\n path +\n \".traversalDurationSeconds\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n path +\n '.kind must be \"simultaneous\" or \"uniform-linear-native-scan\"; non-uniform schedules are not modeled.'\n );\n}\n\nfunction parseReadout(\n value: unknown,\n path: string\n): SensorReadoutTimingDeclaration {\n const record =\n requireRecord(value, path, [\"captureReadoutDurationSeconds\", \"readoutMode\", \"scanDirectionNative\", \"spatialSamplingSkewSeconds\"]);\n const duration =\n parseSecondsFact(\n record.captureReadoutDurationSeconds,\n path +\n \".captureReadoutDurationSeconds\"\n );\n\n if (record.readoutMode === \"global\") {\n if (\n record.scanDirectionNative !==\n undefined ||\n record.spatialSamplingSkewSeconds !==\n undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" global readout must not include rolling-scan fields.\"\n );\n }\n return {\n readoutMode: \"global\",\n captureReadoutDurationSeconds:\n duration\n };\n }\n\n if (record.readoutMode === \"rolling\") {\n return {\n readoutMode: \"rolling\",\n captureReadoutDurationSeconds:\n duration,\n scanDirectionNative:\n parseDirectionFact(\n record.scanDirectionNative,\n path +\n \".scanDirectionNative\"\n ),\n spatialSamplingSkewSeconds:\n parseSecondsFact(\n record.spatialSamplingSkewSeconds,\n path +\n \".spatialSamplingSkewSeconds\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n path +\n '.readoutMode must be \"global\" or \"rolling\".'\n );\n}\n\n/**\n * Parses timing facts tied to one exact capture mode.\n *\n * Schema 0.1.0 accepts only the schedules the readout-timing and exposure-window\n * models already support: global (simultaneous) or uniform linear native scans.\n * Non-uniform or segmented schedules are rejected rather than approximated.\n */\nexport function parseCaptureModeTimingProfile(\n value: unknown\n): CaptureModeTimingProfile {\n const record =\n requireRecord(\n value,\n \"captureModeTimingProfile\", [\"schemaVersion\", \"scientificStatus\", \"scheduleFamily\", \"shutterMechanism\", \"nonUniformScheduleModeled\", \"profileId\", \"profileVersion\", \"captureModeId\", \"opening\", \"closing\", \"readout\", \"evidence\", \"limitations\"]\n );\n\n if (\n record.schemaVersion !==\n CAPTURE_MODE_TIMING_PROFILE_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'captureModeTimingProfile.schemaVersion must be \"' +\n CAPTURE_MODE_TIMING_PROFILE_SCHEMA_VERSION +\n '\".'\n );\n }\n if (\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n 'captureModeTimingProfile.scientificStatus must be \"approximation\".'\n );\n }\n if (\n record.scheduleFamily !==\n \"global-or-uniform-linear-native-scan\"\n ) {\n throw new InvalidConfigurationError(\n 'captureModeTimingProfile.scheduleFamily must be \"global-or-uniform-linear-native-scan\"; unsupported schedule families fail closed.'\n );\n }\n if (\n typeof record.shutterMechanism !==\n \"string\" ||\n !SHUTTER_MECHANISMS.has(\n record.shutterMechanism as CaptureShutterMechanism\n )\n ) {\n throw new InvalidConfigurationError(\n \"captureModeTimingProfile.shutterMechanism is invalid.\"\n );\n }\n if (\n record.nonUniformScheduleModeled !==\n false\n ) {\n throw new InvalidConfigurationError(\n \"captureModeTimingProfile.nonUniformScheduleModeled must be false in schema 0.1.0.\"\n );\n }\n\n return {\n schemaVersion:\n CAPTURE_MODE_TIMING_PROFILE_SCHEMA_VERSION,\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"captureModeTimingProfile.profileId\"\n ),\n profileVersion:\n requireNonEmptyString(\n record.profileVersion,\n \"captureModeTimingProfile.profileVersion\"\n ),\n captureModeId:\n requireNonEmptyString(\n record.captureModeId,\n \"captureModeTimingProfile.captureModeId\"\n ),\n scientificStatus:\n \"approximation\",\n scheduleFamily:\n \"global-or-uniform-linear-native-scan\",\n shutterMechanism:\n record.shutterMechanism as CaptureShutterMechanism,\n opening:\n parseBoundarySchedule(\n record.opening,\n \"captureModeTimingProfile.opening\"\n ),\n closing:\n parseBoundarySchedule(\n record.closing,\n \"captureModeTimingProfile.closing\"\n ),\n readout:\n parseReadout(\n record.readout,\n \"captureModeTimingProfile.readout\"\n ),\n nonUniformScheduleModeled:\n false,\n evidence:\n parseEvidenceList(\n record.evidence,\n \"captureModeTimingProfile.evidence\"\n ),\n limitations:\n parseLimitations(\n record.limitations,\n \"captureModeTimingProfile.limitations\"\n )\n };\n}\n\n/**\n * Resolves the exposure and readout timing for one exact capture mode and\n * timing profile.\n *\n * Readout timing stays separate from the exposure opening and closing times.\n * No absolute sync between readout and exposure is assumed.\n */\nexport function resolveCaptureModeTiming(\n input:\n ResolveCaptureModeTimingInput\n): ResolvedCaptureModeTiming {\n requireInputKeys(input, \"resolveCaptureModeTiming\", [\"captureMode\", \"timingProfile\", \"nominalExposureDurationSeconds\", \"activeCaptureRect\", \"samplePointsNative\"]);\n const profile =\n parseCaptureModeTimingProfile(\n input.timingProfile\n );\n\n if (\n profile.captureModeId !==\n input.captureMode.modeId\n ) {\n throw new InvalidScientificInputError(\n \"captureModeTimingProfile.captureModeId must exactly match the resolved capture mode.\"\n );\n }\n const exposureWindows =\n calculateCaptureExposureWindows({\n nativeRaster:\n input.captureMode.nativeRaster,\n ...(input.activeCaptureRect ===\n undefined\n ? {}\n : {\n activeCaptureRect:\n input.activeCaptureRect\n }),\n shutterMechanism:\n profile.shutterMechanism,\n nominalExposureDurationSeconds:\n input\n .nominalExposureDurationSeconds,\n opening: profile.opening,\n closing: profile.closing,\n ...(input.samplePointsNative ===\n undefined\n ? {}\n : {\n samplePointsNative:\n input.samplePointsNative\n })\n }).value;\n\n const readoutTiming =\n calculateSensorReadoutTiming({\n nativeRaster:\n input.captureMode.nativeRaster,\n ...(input.activeCaptureRect ===\n undefined\n ? {}\n : {\n activeCaptureRect:\n input.activeCaptureRect\n }),\n shutterMechanism:\n profile.shutterMechanism,\n readout: profile.readout,\n ...(input.samplePointsNative ===\n undefined\n ? {}\n : {\n samplePointsNative:\n input.samplePointsNative\n })\n }).value;\n\n return {\n schemaVersion:\n CAPTURE_MODE_TIMING_PROFILE_SCHEMA_VERSION,\n timingProfileId:\n profile.profileId,\n timingProfileVersion:\n profile.profileVersion,\n captureModeId:\n input.captureMode.modeId,\n captureModeTimingIdentity:\n \"exact-mode-and-profile\",\n nativeRaster: {\n ...input.captureMode.nativeRaster\n },\n modeDeclaresTimingDependency:\n input.captureMode\n .dependencies.includes(\n \"mode-specific-readout-timing\"\n ),\n scheduleFamily:\n profile.scheduleFamily,\n exposureWindows,\n readoutTiming,\n timeReference:\n \"first-opening-boundary-phase\",\n readoutExposureSynchronization:\n \"not-assumed\",\n nonUniformScheduleModeled:\n false,\n evidence: [\n ...input.captureMode.modeEvidence,\n ...profile.evidence\n ],\n limitations: [\n ...profile.limitations,\n \"Sensor data readout timing and exposure-boundary timing remain independent unless a separate linkage/synchronization contract establishes a relationship.\",\n \"Capture-mode identity does not infer timing from CMOS/CCD, marketing labels, output raster size, or generic readout-speed names.\"\n ]\n };\n}\n"]}
1
+ {"version":3,"file":"capture-mode-timing.js","sourceRoot":"","sources":["../../src/sensor/capture-mode-timing.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Capture-mode timing: exposure and readout timing facts tied to one exact capture mode.\n *\n * Only the schedules the readout-timing and exposure-window models already support are accepted:\n * global (all rows at once) or a uniform linear scan (rolling, row by row). Non-uniform or segmented\n * schedules are rejected, not approximated. Readout timing is kept separate from the exposure\n * opening and closing times, and no absolute sync between them is assumed.\n * @see docs/MOTION_AND_SIGNAL.md for equations, coordinate/unit conventions, blockers and support\n * limits.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport {\n parseEvidenceList,\n type EvidenceProvenance\n} from \"../core/evidence-provenance.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport type {\n RasterPoint,\n RasterRect\n} from \"../output/capture-geometry.js\";\nimport {\n calculateCaptureExposureWindows,\n type CaptureExposureWindows,\n type ExposureBoundarySchedule,\n type SourcedCaptureBoundaryDirection,\n type SourcedCaptureTimingSeconds\n} from \"./exposure-window.js\";\nimport type {\n ResolvedCaptureMode\n} from \"./capture-mode.js\";\nimport {\n calculateSensorReadoutTiming,\n type CaptureShutterMechanism,\n type NativeSensorReadoutScanDirection,\n type SensorReadoutTiming,\n type SensorReadoutTimingDeclaration,\n type SourcedSensorReadoutFact,\n type SourcedSensorTimingSeconds\n} from \"./readout-timing.js\";\nimport type {\n NativeImageRaster\n} from \"./sensor-geometry.js\";\nimport { requireInputKeys, requireConfigRecord as requireRecord, requireTrimmedConfigString as requireNonEmptyString, requireConfigNonNegativeFinite as requireNonNegativeFinite, parseConfigLimitations as parseLimitations } from \"../core/record-validation.js\";\n\nexport const CAPTURE_MODE_TIMING_PROFILE_SCHEMA_VERSION =\n \"0.1.0\" as const;\n\nexport interface CaptureModeTimingProfile {\n schemaVersion:\n typeof CAPTURE_MODE_TIMING_PROFILE_SCHEMA_VERSION;\n profileId: string;\n profileVersion: string;\n captureModeId: string;\n scientificStatus: \"approximation\";\n scheduleFamily:\n \"global-or-uniform-linear-native-scan\";\n shutterMechanism:\n CaptureShutterMechanism;\n opening:\n ExposureBoundarySchedule;\n closing:\n ExposureBoundarySchedule;\n readout:\n SensorReadoutTimingDeclaration;\n nonUniformScheduleModeled: false;\n evidence: readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\nexport interface ResolveCaptureModeTimingInput {\n captureMode:\n ResolvedCaptureMode;\n timingProfile:\n CaptureModeTimingProfile;\n nominalExposureDurationSeconds:\n SourcedCaptureTimingSeconds;\n activeCaptureRect?: RasterRect;\n samplePointsNative?:\n readonly RasterPoint[];\n}\n\nexport interface ResolvedCaptureModeTiming {\n schemaVersion:\n typeof CAPTURE_MODE_TIMING_PROFILE_SCHEMA_VERSION;\n timingProfileId: string;\n timingProfileVersion: string;\n captureModeId: string;\n captureModeTimingIdentity:\n \"exact-mode-and-profile\";\n nativeRaster: NativeImageRaster;\n modeDeclaresTimingDependency:\n boolean;\n scheduleFamily:\n \"global-or-uniform-linear-native-scan\";\n exposureWindows:\n CaptureExposureWindows;\n readoutTiming:\n SensorReadoutTiming;\n timeReference:\n \"first-opening-boundary-phase\";\n readoutExposureSynchronization:\n \"not-assumed\";\n nonUniformScheduleModeled: false;\n evidence: readonly EvidenceProvenance[];\n limitations: readonly string[];\n}\n\nconst SHUTTER_MECHANISMS =\n new Set<CaptureShutterMechanism>([\n \"mechanical\",\n \"electronic-first-curtain\",\n \"electronic\"\n ]);\n\nconst SCAN_DIRECTIONS =\n new Set<NativeSensorReadoutScanDirection>([\n \"top-to-bottom\",\n \"bottom-to-top\",\n \"left-to-right\",\n \"right-to-left\"\n ]);\n\nfunction parseSecondsFact(\n value: unknown,\n path: string\n): SourcedSensorTimingSeconds {\n const record =\n requireRecord(value, path, [\"unit\", \"value\", \"evidence\"]);\n if (record.unit !== \"s\") {\n throw new InvalidConfigurationError(\n path + '.unit must be \"s\".'\n );\n }\n return {\n value:\n requireNonNegativeFinite(\n record.value,\n path + \".value\"\n ),\n unit: \"s\",\n evidence:\n parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n )\n };\n}\n\nfunction parseDirectionFact(\n value: unknown,\n path: string\n): SourcedSensorReadoutFact<NativeSensorReadoutScanDirection> {\n const record =\n requireRecord(value, path, [\"value\", \"evidence\"]);\n if (\n typeof record.value !== \"string\" ||\n !SCAN_DIRECTIONS.has(\n record.value as NativeSensorReadoutScanDirection\n )\n ) {\n throw new InvalidConfigurationError(\n path + \".value is invalid.\"\n );\n }\n return {\n value:\n record.value as NativeSensorReadoutScanDirection,\n evidence:\n parseEvidenceList(\n record.evidence,\n path + \".evidence\"\n )\n };\n}\n\nfunction parseBoundarySchedule(\n value: unknown,\n path: string\n): ExposureBoundarySchedule {\n const record =\n requireRecord(value, path, [\"kind\", \"directionNative\", \"traversalDurationSeconds\"]);\n\n if (record.kind === \"simultaneous\") {\n if (\n record.directionNative !==\n undefined ||\n record.traversalDurationSeconds !==\n undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" simultaneous schedule must not include scan-only fields.\"\n );\n }\n return {\n kind: \"simultaneous\"\n };\n }\n\n if (\n record.kind ===\n \"uniform-linear-native-scan\"\n ) {\n return {\n kind:\n \"uniform-linear-native-scan\",\n directionNative:\n parseDirectionFact(\n record.directionNative,\n path + \".directionNative\"\n ) as SourcedCaptureBoundaryDirection,\n traversalDurationSeconds:\n parseSecondsFact(\n record.traversalDurationSeconds,\n path +\n \".traversalDurationSeconds\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n path +\n '.kind must be \"simultaneous\" or \"uniform-linear-native-scan\"; non-uniform schedules are not modeled.'\n );\n}\n\nfunction parseReadout(\n value: unknown,\n path: string\n): SensorReadoutTimingDeclaration {\n const record =\n requireRecord(value, path, [\"captureReadoutDurationSeconds\", \"readoutMode\", \"scanDirectionNative\", \"spatialSamplingSkewSeconds\"]);\n const duration =\n parseSecondsFact(\n record.captureReadoutDurationSeconds,\n path +\n \".captureReadoutDurationSeconds\"\n );\n\n if (record.readoutMode === \"global\") {\n if (\n record.scanDirectionNative !==\n undefined ||\n record.spatialSamplingSkewSeconds !==\n undefined\n ) {\n throw new InvalidConfigurationError(\n path +\n \" global readout must not include rolling-scan fields.\"\n );\n }\n return {\n readoutMode: \"global\",\n captureReadoutDurationSeconds:\n duration\n };\n }\n\n if (record.readoutMode === \"rolling\") {\n return {\n readoutMode: \"rolling\",\n captureReadoutDurationSeconds:\n duration,\n scanDirectionNative:\n parseDirectionFact(\n record.scanDirectionNative,\n path +\n \".scanDirectionNative\"\n ),\n spatialSamplingSkewSeconds:\n parseSecondsFact(\n record.spatialSamplingSkewSeconds,\n path +\n \".spatialSamplingSkewSeconds\"\n )\n };\n }\n\n throw new InvalidConfigurationError(\n path +\n '.readoutMode must be \"global\" or \"rolling\".'\n );\n}\n\n/**\n * Parses timing facts tied to one exact capture mode.\n *\n * Schema 0.1.0 accepts only the schedules the readout-timing and exposure-window\n * models already support: global (simultaneous) or uniform linear native scans.\n * Non-uniform or segmented schedules are rejected rather than approximated.\n */\nexport function parseCaptureModeTimingProfile(\n value: unknown\n): CaptureModeTimingProfile {\n const record =\n requireRecord(\n value,\n \"captureModeTimingProfile\", [\"schemaVersion\", \"scientificStatus\", \"scheduleFamily\", \"shutterMechanism\", \"nonUniformScheduleModeled\", \"profileId\", \"profileVersion\", \"captureModeId\", \"opening\", \"closing\", \"readout\", \"evidence\", \"limitations\"]\n );\n\n if (\n record.schemaVersion !==\n CAPTURE_MODE_TIMING_PROFILE_SCHEMA_VERSION\n ) {\n throw new InvalidConfigurationError(\n 'captureModeTimingProfile.schemaVersion must be \"' +\n CAPTURE_MODE_TIMING_PROFILE_SCHEMA_VERSION +\n '\".'\n );\n }\n if (\n record.scientificStatus !==\n \"approximation\"\n ) {\n throw new InvalidConfigurationError(\n 'captureModeTimingProfile.scientificStatus must be \"approximation\".'\n );\n }\n if (\n record.scheduleFamily !==\n \"global-or-uniform-linear-native-scan\"\n ) {\n throw new InvalidConfigurationError(\n 'captureModeTimingProfile.scheduleFamily must be \"global-or-uniform-linear-native-scan\"; unsupported schedule families fail closed.'\n );\n }\n if (\n typeof record.shutterMechanism !==\n \"string\" ||\n !SHUTTER_MECHANISMS.has(\n record.shutterMechanism as CaptureShutterMechanism\n )\n ) {\n throw new InvalidConfigurationError(\n \"captureModeTimingProfile.shutterMechanism is invalid.\"\n );\n }\n if (\n record.nonUniformScheduleModeled !==\n false\n ) {\n throw new InvalidConfigurationError(\n \"captureModeTimingProfile.nonUniformScheduleModeled must be false in schema 0.1.0.\"\n );\n }\n\n return {\n schemaVersion:\n CAPTURE_MODE_TIMING_PROFILE_SCHEMA_VERSION,\n profileId:\n requireNonEmptyString(\n record.profileId,\n \"captureModeTimingProfile.profileId\"\n ),\n profileVersion:\n requireNonEmptyString(\n record.profileVersion,\n \"captureModeTimingProfile.profileVersion\"\n ),\n captureModeId:\n requireNonEmptyString(\n record.captureModeId,\n \"captureModeTimingProfile.captureModeId\"\n ),\n scientificStatus:\n \"approximation\",\n scheduleFamily:\n \"global-or-uniform-linear-native-scan\",\n shutterMechanism:\n record.shutterMechanism as CaptureShutterMechanism,\n opening:\n parseBoundarySchedule(\n record.opening,\n \"captureModeTimingProfile.opening\"\n ),\n closing:\n parseBoundarySchedule(\n record.closing,\n \"captureModeTimingProfile.closing\"\n ),\n readout:\n parseReadout(\n record.readout,\n \"captureModeTimingProfile.readout\"\n ),\n nonUniformScheduleModeled:\n false,\n evidence:\n parseEvidenceList(\n record.evidence,\n \"captureModeTimingProfile.evidence\"\n ),\n limitations:\n parseLimitations(\n record.limitations,\n \"captureModeTimingProfile.limitations\"\n )\n };\n}\n\n/**\n * Resolves the exposure and readout timing for one exact capture mode and\n * timing profile.\n *\n * Readout timing stays separate from the exposure opening and closing times.\n * No absolute sync between readout and exposure is assumed.\n */\nexport function resolveCaptureModeTiming(\n input:\n ResolveCaptureModeTimingInput\n): ResolvedCaptureModeTiming {\n requireInputKeys(input, \"resolveCaptureModeTiming\", [\"captureMode\", \"timingProfile\", \"nominalExposureDurationSeconds\", \"activeCaptureRect\", \"samplePointsNative\"]);\n const profile =\n parseCaptureModeTimingProfile(\n input.timingProfile\n );\n\n if (\n profile.captureModeId !==\n input.captureMode.modeId\n ) {\n throw new InvalidScientificInputError(\n \"captureModeTimingProfile.captureModeId must exactly match the resolved capture mode.\"\n );\n }\n const exposureWindows =\n calculateCaptureExposureWindows({\n nativeRaster:\n input.captureMode.nativeRaster,\n ...(input.activeCaptureRect ===\n undefined\n ? {}\n : {\n activeCaptureRect:\n input.activeCaptureRect\n }),\n shutterMechanism:\n profile.shutterMechanism,\n nominalExposureDurationSeconds:\n input\n .nominalExposureDurationSeconds,\n opening: profile.opening,\n closing: profile.closing,\n ...(input.samplePointsNative ===\n undefined\n ? {}\n : {\n samplePointsNative:\n input.samplePointsNative\n })\n }).value;\n\n const readoutTiming =\n calculateSensorReadoutTiming({\n nativeRaster:\n input.captureMode.nativeRaster,\n ...(input.activeCaptureRect ===\n undefined\n ? {}\n : {\n activeCaptureRect:\n input.activeCaptureRect\n }),\n shutterMechanism:\n profile.shutterMechanism,\n readout: profile.readout,\n ...(input.samplePointsNative ===\n undefined\n ? {}\n : {\n samplePointsNative:\n input.samplePointsNative\n })\n }).value;\n\n return {\n schemaVersion:\n CAPTURE_MODE_TIMING_PROFILE_SCHEMA_VERSION,\n timingProfileId:\n profile.profileId,\n timingProfileVersion:\n profile.profileVersion,\n captureModeId:\n input.captureMode.modeId,\n captureModeTimingIdentity:\n \"exact-mode-and-profile\",\n nativeRaster: {\n ...input.captureMode.nativeRaster\n },\n modeDeclaresTimingDependency:\n input.captureMode\n .dependencies.includes(\n \"mode-specific-readout-timing\"\n ),\n scheduleFamily:\n profile.scheduleFamily,\n exposureWindows,\n readoutTiming,\n timeReference:\n \"first-opening-boundary-phase\",\n readoutExposureSynchronization:\n \"not-assumed\",\n nonUniformScheduleModeled:\n false,\n evidence: [\n ...input.captureMode.modeEvidence,\n ...profile.evidence\n ],\n limitations: [\n ...profile.limitations,\n \"Sensor data readout timing and exposure-boundary timing remain independent unless a separate linkage/synchronization contract establishes a relationship.\",\n \"Capture-mode identity does not infer timing from CMOS/CCD, marketing labels, output raster size, or generic readout-speed names.\"\n ]\n };\n}\n"]}
@@ -13,7 +13,7 @@
13
13
  import { InvalidConfigurationError } from "../core/configuration-error.js";
14
14
  import { parseEvidenceList } from "../core/evidence-provenance.js";
15
15
  import { InvalidScientificInputError, requirePositiveInteger } from "../core/validation.js";
16
- import { requireInputObject, requireInputKeys, rejectUnknownFields } from "../core/record-validation.js";
16
+ import { requireInputObject, requireInputKeys, requireConfigRecord as requireRecord, requireConfigString as requireNonEmptyString, requireConfigPositiveSafeInteger as requirePositiveSafeIntegerValue } from "../core/record-validation.js";
17
17
  const COMBINATION_DOMAINS = new Set([
18
18
  "charge-domain",
19
19
  "pre-conversion-analog",
@@ -30,28 +30,6 @@ const DEPENDENCIES = new Set([
30
30
  "radiometric-calibration",
31
31
  "inter-frame-registration"
32
32
  ]);
33
- function requireRecord(value, path, allowedKeys) {
34
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
35
- throw new InvalidConfigurationError(path + " must be an object.");
36
- }
37
- if (allowedKeys !== null)
38
- rejectUnknownFields(value, allowedKeys, path);
39
- return value;
40
- }
41
- function requireNonEmptyString(value, path) {
42
- if (typeof value !== "string" || value.trim().length === 0) {
43
- throw new InvalidConfigurationError(path + " must be a non-empty string.");
44
- }
45
- return value;
46
- }
47
- function requirePositiveSafeIntegerValue(value, path) {
48
- if (typeof value !== "number" ||
49
- !Number.isSafeInteger(value) ||
50
- value <= 0) {
51
- throw new InvalidConfigurationError(path + " must be a positive safe integer.");
52
- }
53
- return value;
54
- }
55
33
  function parseIntegerFact(value, path) {
56
34
  const record = requireRecord(value, path, ["value", "evidence"]);
57
35
  return {