@photivra/engine 0.2.0 → 0.4.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 (89) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/CITATION.cff +1 -1
  3. package/README.md +58 -10
  4. package/dist/camera/equivalent-focal-length.d.ts +34 -0
  5. package/dist/camera/equivalent-focal-length.d.ts.map +1 -0
  6. package/dist/camera/equivalent-focal-length.js +33 -0
  7. package/dist/camera/equivalent-focal-length.js.map +1 -0
  8. package/dist/camera/field-of-view.d.ts +40 -3
  9. package/dist/camera/field-of-view.d.ts.map +1 -1
  10. package/dist/camera/field-of-view.js +66 -24
  11. package/dist/camera/field-of-view.js.map +1 -1
  12. package/dist/core/calculation-result.d.ts.map +1 -1
  13. package/dist/core/calculation-result.js +14 -2
  14. package/dist/core/calculation-result.js.map +1 -1
  15. package/dist/core/configuration-error.d.ts +9 -0
  16. package/dist/core/configuration-error.d.ts.map +1 -0
  17. package/dist/core/configuration-error.js +13 -0
  18. package/dist/core/configuration-error.js.map +1 -0
  19. package/dist/core/evidence-provenance.d.ts +40 -0
  20. package/dist/core/evidence-provenance.d.ts.map +1 -0
  21. package/dist/core/evidence-provenance.js +79 -0
  22. package/dist/core/evidence-provenance.js.map +1 -0
  23. package/dist/core/image-formation.d.ts +70 -0
  24. package/dist/core/image-formation.d.ts.map +1 -0
  25. package/dist/core/image-formation.js +335 -0
  26. package/dist/core/image-formation.js.map +1 -0
  27. package/dist/index.d.ts +15 -3
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +15 -3
  30. package/dist/index.js.map +1 -1
  31. package/dist/motion/camera-rotation.d.ts +91 -0
  32. package/dist/motion/camera-rotation.d.ts.map +1 -0
  33. package/dist/motion/camera-rotation.js +157 -0
  34. package/dist/motion/camera-rotation.js.map +1 -0
  35. package/dist/optics/focus-breathing.d.ts +73 -0
  36. package/dist/optics/focus-breathing.d.ts.map +1 -0
  37. package/dist/optics/focus-breathing.js +77 -0
  38. package/dist/optics/focus-breathing.js.map +1 -0
  39. package/dist/optics/illumination-vignetting.d.ts +59 -0
  40. package/dist/optics/illumination-vignetting.d.ts.map +1 -0
  41. package/dist/optics/illumination-vignetting.js +107 -0
  42. package/dist/optics/illumination-vignetting.js.map +1 -0
  43. package/dist/optics/lateral-chromatic-aberration.d.ts +93 -0
  44. package/dist/optics/lateral-chromatic-aberration.d.ts.map +1 -0
  45. package/dist/optics/lateral-chromatic-aberration.js +174 -0
  46. package/dist/optics/lateral-chromatic-aberration.js.map +1 -0
  47. package/dist/optics/psf-foundation.d.ts +102 -0
  48. package/dist/optics/psf-foundation.d.ts.map +1 -0
  49. package/dist/optics/psf-foundation.js +164 -0
  50. package/dist/optics/psf-foundation.js.map +1 -0
  51. package/dist/optics/radial-distortion.d.ts +91 -0
  52. package/dist/optics/radial-distortion.d.ts.map +1 -0
  53. package/dist/optics/radial-distortion.js +198 -0
  54. package/dist/optics/radial-distortion.js.map +1 -0
  55. package/dist/output/capture-geometry.d.ts +225 -0
  56. package/dist/output/capture-geometry.d.ts.map +1 -0
  57. package/dist/output/capture-geometry.js +478 -0
  58. package/dist/output/capture-geometry.js.map +1 -0
  59. package/dist/schema/validation.d.ts +1 -8
  60. package/dist/schema/validation.d.ts.map +1 -1
  61. package/dist/schema/validation.js +2 -11
  62. package/dist/schema/validation.js.map +1 -1
  63. package/dist/sensor/architecture.d.ts +40 -0
  64. package/dist/sensor/architecture.d.ts.map +1 -0
  65. package/dist/sensor/architecture.js +98 -0
  66. package/dist/sensor/architecture.js.map +1 -0
  67. package/dist/sensor/radiometry-readiness.d.ts +111 -0
  68. package/dist/sensor/radiometry-readiness.d.ts.map +1 -0
  69. package/dist/sensor/radiometry-readiness.js +294 -0
  70. package/dist/sensor/radiometry-readiness.js.map +1 -0
  71. package/dist/sensor/sensor-geometry.d.ts +99 -0
  72. package/dist/sensor/sensor-geometry.d.ts.map +1 -0
  73. package/dist/sensor/sensor-geometry.js +71 -0
  74. package/dist/sensor/sensor-geometry.js.map +1 -0
  75. package/dist/simulation/poc-simulation.d.ts +93 -1
  76. package/dist/simulation/poc-simulation.d.ts.map +1 -1
  77. package/dist/simulation/poc-simulation.js +308 -24
  78. package/dist/simulation/poc-simulation.js.map +1 -1
  79. package/docs/API_STYLE.md +35 -2
  80. package/docs/ARCHITECTURE.md +179 -1
  81. package/docs/IMAGE_FORMATION.md +142 -0
  82. package/docs/MOTION_AND_SIGNAL.md +52 -1
  83. package/docs/PHYSICS_FOUNDATION.md +190 -3
  84. package/docs/POC_API.md +73 -7
  85. package/docs/PROVENANCE.md +24 -0
  86. package/docs/PSF_FOUNDATION.md +133 -0
  87. package/docs/STABILIZATION.md +17 -4
  88. package/docs/USAGE.md +657 -3
  89. package/package.json +1 -1
@@ -0,0 +1,98 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { parseEvidenceList } from "../core/evidence-provenance.js";
3
+ import { InvalidConfigurationError } from "../core/configuration-error.js";
4
+ const ILLUMINATION_VALUES = new Set([
5
+ "fsi",
6
+ "bsi"
7
+ ]);
8
+ const INTEGRATION_VALUES = new Set([
9
+ "monolithic",
10
+ "partially-stacked",
11
+ "stacked"
12
+ ]);
13
+ const READOUT_VALUES = new Set([
14
+ "rolling",
15
+ "global"
16
+ ]);
17
+ const COLOR_SAMPLING_VALUES = new Set([
18
+ "bayer",
19
+ "quad-bayer",
20
+ "monochrome",
21
+ "custom-rgb-mosaic",
22
+ "layered-color"
23
+ ]);
24
+ function requireRecord(value, path) {
25
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
26
+ throw new InvalidConfigurationError(`${path} must be an object.`);
27
+ }
28
+ return value;
29
+ }
30
+ function requireNonEmptyString(record, key, path) {
31
+ const value = record[key];
32
+ if (typeof value !== "string" || value.trim().length === 0) {
33
+ throw new InvalidConfigurationError(`${path}.${key} must be a non-empty string.`);
34
+ }
35
+ return value;
36
+ }
37
+ function parseScalarFact(value, path, allowed) {
38
+ const record = requireRecord(value, path);
39
+ const factValue = requireNonEmptyString(record, "value", path);
40
+ if (!allowed.has(factValue)) {
41
+ throw new InvalidConfigurationError(`${path}.value is invalid.`);
42
+ }
43
+ return {
44
+ value: factValue,
45
+ evidence: parseEvidenceList(record.evidence, `${path}.evidence`)
46
+ };
47
+ }
48
+ function parseReadoutCapabilities(value, path) {
49
+ if (!Array.isArray(value) || value.length === 0) {
50
+ throw new InvalidConfigurationError(`${path} must be a non-empty array.`);
51
+ }
52
+ const parsed = value.map((entry, index) => parseScalarFact(entry, `${path}[${index}]`, READOUT_VALUES));
53
+ const values = parsed.map((fact) => fact.value);
54
+ if (new Set(values).size !== values.length) {
55
+ throw new InvalidConfigurationError(`${path} must not contain duplicate capability values.`);
56
+ }
57
+ return parsed;
58
+ }
59
+ /**
60
+ * Parses untrusted descriptive sensor-architecture metadata.
61
+ *
62
+ * Unknown facts should be omitted instead of inferred. The parser validates
63
+ * structure, supported vocabulary, and field-level evidence only; it does not
64
+ * verify that a cited real-world claim is factually true.
65
+ *
66
+ * @param value Unknown external data.
67
+ * @returns Validated sensor architecture profile.
68
+ */
69
+ export function parseSensorArchitectureProfile(value) {
70
+ const profile = requireRecord(value, "sensorArchitecture");
71
+ if (profile.schemaVersion !== "0.2.0") {
72
+ throw new InvalidConfigurationError('sensorArchitecture.schemaVersion must be "0.2.0".');
73
+ }
74
+ return {
75
+ schemaVersion: "0.2.0",
76
+ ...(profile.illumination === undefined
77
+ ? {}
78
+ : {
79
+ illumination: parseScalarFact(profile.illumination, "sensorArchitecture.illumination", ILLUMINATION_VALUES)
80
+ }),
81
+ ...(profile.integration === undefined
82
+ ? {}
83
+ : {
84
+ integration: parseScalarFact(profile.integration, "sensorArchitecture.integration", INTEGRATION_VALUES)
85
+ }),
86
+ ...(profile.readoutCapabilities === undefined
87
+ ? {}
88
+ : {
89
+ readoutCapabilities: parseReadoutCapabilities(profile.readoutCapabilities, "sensorArchitecture.readoutCapabilities")
90
+ }),
91
+ ...(profile.colorSamplingFamily === undefined
92
+ ? {}
93
+ : {
94
+ colorSamplingFamily: parseScalarFact(profile.colorSamplingFamily, "sensorArchitecture.colorSamplingFamily", COLOR_SAMPLING_VALUES)
95
+ })
96
+ };
97
+ }
98
+ //# sourceMappingURL=architecture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"architecture.js","sourceRoot":"","sources":["../../src/sensor/architecture.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,111 @@
1
+ import { type EvidenceProvenance } from "../core/evidence-provenance.js";
2
+ export type RadiometryRequirementId = "scene-spectral-radiance" | "optical-transmission" | "pupil-vignetting" | "photosite-collection-area" | "exposure-integration" | "sensor-response";
3
+ export type RadiometryScientificStatus = "calibrated" | "approximation";
4
+ export type RadiometryUncertaintyDeclaration = {
5
+ kind: "relative";
6
+ fraction: number;
7
+ basis: string;
8
+ } | {
9
+ kind: "absolute";
10
+ plusMinus: number;
11
+ unit: string;
12
+ basis: string;
13
+ } | {
14
+ kind: "not-quantified";
15
+ limitation: string;
16
+ };
17
+ export interface CalibrationArtifactReference {
18
+ id: string;
19
+ checksumSha256: string;
20
+ }
21
+ interface RadiometryRequirementBase {
22
+ requirement: RadiometryRequirementId;
23
+ scientificStatus: RadiometryScientificStatus;
24
+ modelId: string;
25
+ modelVersion: string;
26
+ evidence: readonly EvidenceProvenance[];
27
+ uncertainty: RadiometryUncertaintyDeclaration;
28
+ }
29
+ export interface SceneSpectralRadianceRequirement extends RadiometryRequirementBase {
30
+ requirement: "scene-spectral-radiance";
31
+ representation: "spectral-data" | "documented-spectral-approximation";
32
+ dataArtifact: CalibrationArtifactReference;
33
+ }
34
+ export type OpticalTransmissionRequirement = (RadiometryRequirementBase & {
35
+ requirement: "optical-transmission";
36
+ representation: "spectral-data";
37
+ dataArtifact: CalibrationArtifactReference;
38
+ }) | (RadiometryRequirementBase & {
39
+ requirement: "optical-transmission";
40
+ representation: "t-stop-approximation";
41
+ tStop: number;
42
+ });
43
+ export type PupilVignettingRequirement = (RadiometryRequirementBase & {
44
+ requirement: "pupil-vignetting";
45
+ representation: "spatial-data";
46
+ dataArtifact: CalibrationArtifactReference;
47
+ }) | (RadiometryRequirementBase & {
48
+ requirement: "pupil-vignetting";
49
+ representation: "documented-approximation";
50
+ });
51
+ export type PhotositeCollectionAreaRequirement = (RadiometryRequirementBase & {
52
+ requirement: "photosite-collection-area";
53
+ areaModel: "effective-collection-area";
54
+ effectiveCollectionAreaSquareMicrometers: number;
55
+ }) | (RadiometryRequirementBase & {
56
+ requirement: "photosite-collection-area";
57
+ areaModel: "geometric-area-times-fill-factor";
58
+ geometricCellAreaSquareMicrometers: number;
59
+ fillFactor: number;
60
+ });
61
+ export type ExposureIntegrationRequirement = (RadiometryRequirementBase & {
62
+ requirement: "exposure-integration";
63
+ integrationModel: "uniform-boxcar";
64
+ }) | (RadiometryRequirementBase & {
65
+ requirement: "exposure-integration";
66
+ integrationModel: "documented-shutter-function";
67
+ dataArtifact: CalibrationArtifactReference;
68
+ });
69
+ export type SensorResponseRequirement = (RadiometryRequirementBase & {
70
+ requirement: "sensor-response";
71
+ responseRepresentation: "spectral-quantum-efficiency";
72
+ dataArtifact: CalibrationArtifactReference;
73
+ }) | (RadiometryRequirementBase & {
74
+ requirement: "sensor-response";
75
+ responseRepresentation: "effective-qe-approximation";
76
+ effectiveQuantumEfficiency: number;
77
+ });
78
+ export type RadiometryRequirement = SceneSpectralRadianceRequirement | OpticalTransmissionRequirement | PupilVignettingRequirement | PhotositeCollectionAreaRequirement | ExposureIntegrationRequirement | SensorResponseRequirement;
79
+ export interface RadiometryReadinessProfile {
80
+ schemaVersion: "0.1.0";
81
+ components: readonly RadiometryRequirement[];
82
+ }
83
+ export interface RadiometryReadinessAssessment {
84
+ status: "not-ready" | "approximate-only" | "calibrated-ready";
85
+ nominalPhotonEstimateReady: boolean;
86
+ calibratedPhotonClaimReady: boolean;
87
+ missingRequirements: readonly RadiometryRequirementId[];
88
+ approximateRequirements: readonly RadiometryRequirementId[];
89
+ unquantifiedUncertaintyRequirements: readonly RadiometryRequirementId[];
90
+ /**
91
+ * This assessment never enables composed photon/noise output by itself.
92
+ * Integration remains an explicit later product/engine decision.
93
+ */
94
+ composedPhotonOutputEnabled: false;
95
+ }
96
+ /**
97
+ * Parses untrusted radiometry-prerequisite metadata.
98
+ *
99
+ * This validates structure and provenance; it cannot prove that a cited
100
+ * calibration/evidence claim is scientifically true.
101
+ */
102
+ export declare function parseRadiometryReadinessProfile(value: unknown): RadiometryReadinessProfile;
103
+ /**
104
+ * Assesses whether the declared prerequisites are sufficient for a nominal
105
+ * photon estimate and, separately, for a calibrated photon claim.
106
+ *
107
+ * This assessment does not enable composed photon/noise output.
108
+ */
109
+ export declare function assessRadiometryReadiness(profile: RadiometryReadinessProfile): RadiometryReadinessAssessment;
110
+ export {};
111
+ //# sourceMappingURL=radiometry-readiness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radiometry-readiness.d.ts","sourceRoot":"","sources":["../../src/sensor/radiometry-readiness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,294 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { parseEvidenceList } from "../core/evidence-provenance.js";
3
+ import { InvalidConfigurationError } from "../core/configuration-error.js";
4
+ const REQUIRED_REQUIREMENTS = [
5
+ "scene-spectral-radiance",
6
+ "optical-transmission",
7
+ "pupil-vignetting",
8
+ "photosite-collection-area",
9
+ "exposure-integration",
10
+ "sensor-response"
11
+ ];
12
+ const REQUIREMENT_IDS = new Set(REQUIRED_REQUIREMENTS);
13
+ const SCIENTIFIC_STATUSES = new Set([
14
+ "calibrated",
15
+ "approximation"
16
+ ]);
17
+ function requireRecord(value, path) {
18
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
19
+ throw new InvalidConfigurationError(`${path} must be an object.`);
20
+ }
21
+ return value;
22
+ }
23
+ function requireString(record, key, path) {
24
+ const value = record[key];
25
+ if (typeof value !== "string" || value.trim().length === 0) {
26
+ throw new InvalidConfigurationError(`${path}.${key} must be a non-empty string.`);
27
+ }
28
+ return value;
29
+ }
30
+ function requirePositiveNumber(record, key, path) {
31
+ const value = record[key];
32
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
33
+ throw new InvalidConfigurationError(`${path}.${key} must be a finite number greater than zero.`);
34
+ }
35
+ return value;
36
+ }
37
+ function requireFraction(record, key, path, allowZero) {
38
+ const value = record[key];
39
+ if (typeof value !== "number" ||
40
+ !Number.isFinite(value) ||
41
+ (allowZero ? value < 0 : value <= 0) ||
42
+ value > 1) {
43
+ throw new InvalidConfigurationError(`${path}.${key} must be a finite fraction ${allowZero ? "from 0 through 1" : "greater than 0 and at most 1"}.`);
44
+ }
45
+ return value;
46
+ }
47
+ function parseArtifact(value, path) {
48
+ const record = requireRecord(value, path);
49
+ const id = requireString(record, "id", path);
50
+ const checksumSha256 = requireString(record, "checksumSha256", path);
51
+ if (!/^[a-f0-9]{64}$/iu.test(checksumSha256)) {
52
+ throw new InvalidConfigurationError(`${path}.checksumSha256 must be a 64-character SHA-256 hex digest.`);
53
+ }
54
+ return { id, checksumSha256: checksumSha256.toLowerCase() };
55
+ }
56
+ function parseUncertainty(value, path) {
57
+ const record = requireRecord(value, path);
58
+ const kind = requireString(record, "kind", path);
59
+ if (kind === "relative") {
60
+ const fraction = record.fraction;
61
+ if (typeof fraction !== "number" ||
62
+ !Number.isFinite(fraction) ||
63
+ fraction < 0) {
64
+ throw new InvalidConfigurationError(`${path}.fraction must be finite and greater than or equal to zero.`);
65
+ }
66
+ return {
67
+ kind,
68
+ fraction,
69
+ basis: requireString(record, "basis", path)
70
+ };
71
+ }
72
+ if (kind === "absolute") {
73
+ const plusMinus = record.plusMinus;
74
+ if (typeof plusMinus !== "number" ||
75
+ !Number.isFinite(plusMinus) ||
76
+ plusMinus < 0) {
77
+ throw new InvalidConfigurationError(`${path}.plusMinus must be finite and greater than or equal to zero.`);
78
+ }
79
+ return {
80
+ kind,
81
+ plusMinus,
82
+ unit: requireString(record, "unit", path),
83
+ basis: requireString(record, "basis", path)
84
+ };
85
+ }
86
+ if (kind === "not-quantified") {
87
+ return {
88
+ kind,
89
+ limitation: requireString(record, "limitation", path)
90
+ };
91
+ }
92
+ throw new InvalidConfigurationError(`${path}.kind is invalid.`);
93
+ }
94
+ function parseBase(record, path) {
95
+ const scientificStatus = requireString(record, "scientificStatus", path);
96
+ if (!SCIENTIFIC_STATUSES.has(scientificStatus)) {
97
+ throw new InvalidConfigurationError(`${path}.scientificStatus is invalid.`);
98
+ }
99
+ return {
100
+ scientificStatus: scientificStatus,
101
+ modelId: requireString(record, "modelId", path),
102
+ modelVersion: requireString(record, "modelVersion", path),
103
+ evidence: parseEvidenceList(record.evidence, `${path}.evidence`),
104
+ uncertainty: parseUncertainty(record.uncertainty, `${path}.uncertainty`)
105
+ };
106
+ }
107
+ function requireApproximationStatus(base, path, representation) {
108
+ if (base.scientificStatus !== "approximation") {
109
+ throw new InvalidConfigurationError(`${path} uses inherently approximate representation "${representation}" and must declare scientificStatus "approximation".`);
110
+ }
111
+ }
112
+ function parseRequirement(value, path) {
113
+ const record = requireRecord(value, path);
114
+ const requirement = requireString(record, "requirement", path);
115
+ if (!REQUIREMENT_IDS.has(requirement)) {
116
+ throw new InvalidConfigurationError(`${path}.requirement is invalid.`);
117
+ }
118
+ const base = parseBase(record, path);
119
+ if (requirement === "scene-spectral-radiance") {
120
+ const representation = requireString(record, "representation", path);
121
+ if (representation !== "spectral-data" &&
122
+ representation !== "documented-spectral-approximation") {
123
+ throw new InvalidConfigurationError(`${path}.representation is invalid.`);
124
+ }
125
+ if (representation === "documented-spectral-approximation") {
126
+ requireApproximationStatus(base, path, representation);
127
+ }
128
+ return {
129
+ requirement,
130
+ ...base,
131
+ representation,
132
+ dataArtifact: parseArtifact(record.dataArtifact, `${path}.dataArtifact`)
133
+ };
134
+ }
135
+ if (requirement === "optical-transmission") {
136
+ const representation = requireString(record, "representation", path);
137
+ if (representation === "spectral-data") {
138
+ return {
139
+ requirement,
140
+ ...base,
141
+ representation,
142
+ dataArtifact: parseArtifact(record.dataArtifact, `${path}.dataArtifact`)
143
+ };
144
+ }
145
+ if (representation === "t-stop-approximation") {
146
+ requireApproximationStatus(base, path, representation);
147
+ return {
148
+ requirement,
149
+ ...base,
150
+ representation,
151
+ tStop: requirePositiveNumber(record, "tStop", path)
152
+ };
153
+ }
154
+ throw new InvalidConfigurationError(`${path}.representation is invalid.`);
155
+ }
156
+ if (requirement === "pupil-vignetting") {
157
+ const representation = requireString(record, "representation", path);
158
+ if (representation === "spatial-data") {
159
+ return {
160
+ requirement,
161
+ ...base,
162
+ representation,
163
+ dataArtifact: parseArtifact(record.dataArtifact, `${path}.dataArtifact`)
164
+ };
165
+ }
166
+ if (representation === "documented-approximation") {
167
+ requireApproximationStatus(base, path, representation);
168
+ return { requirement, ...base, representation };
169
+ }
170
+ throw new InvalidConfigurationError(`${path}.representation is invalid.`);
171
+ }
172
+ if (requirement === "photosite-collection-area") {
173
+ if (record.areaModel === undefined &&
174
+ (record.geometricPitchXMicrometers !== undefined ||
175
+ record.geometricPitchYMicrometers !== undefined)) {
176
+ throw new InvalidConfigurationError(`${path}.areaModel is required. Geometric sample pitch alone is not a photosite collection-area model.`);
177
+ }
178
+ const areaModel = requireString(record, "areaModel", path);
179
+ if (areaModel === "effective-collection-area") {
180
+ return {
181
+ requirement,
182
+ ...base,
183
+ areaModel,
184
+ effectiveCollectionAreaSquareMicrometers: requirePositiveNumber(record, "effectiveCollectionAreaSquareMicrometers", path)
185
+ };
186
+ }
187
+ if (areaModel === "geometric-area-times-fill-factor") {
188
+ return {
189
+ requirement,
190
+ ...base,
191
+ areaModel,
192
+ geometricCellAreaSquareMicrometers: requirePositiveNumber(record, "geometricCellAreaSquareMicrometers", path),
193
+ fillFactor: requireFraction(record, "fillFactor", path, false)
194
+ };
195
+ }
196
+ throw new InvalidConfigurationError(`${path}.areaModel is invalid. Geometric sample pitch alone is not a photosite collection-area model.`);
197
+ }
198
+ if (requirement === "exposure-integration") {
199
+ const integrationModel = requireString(record, "integrationModel", path);
200
+ if (integrationModel === "uniform-boxcar") {
201
+ return { requirement, ...base, integrationModel };
202
+ }
203
+ if (integrationModel === "documented-shutter-function") {
204
+ return {
205
+ requirement,
206
+ ...base,
207
+ integrationModel,
208
+ dataArtifact: parseArtifact(record.dataArtifact, `${path}.dataArtifact`)
209
+ };
210
+ }
211
+ throw new InvalidConfigurationError(`${path}.integrationModel is invalid.`);
212
+ }
213
+ const responseRepresentation = requireString(record, "responseRepresentation", path);
214
+ if (responseRepresentation === "spectral-quantum-efficiency") {
215
+ return {
216
+ requirement: "sensor-response",
217
+ ...base,
218
+ responseRepresentation,
219
+ dataArtifact: parseArtifact(record.dataArtifact, `${path}.dataArtifact`)
220
+ };
221
+ }
222
+ if (responseRepresentation === "effective-qe-approximation") {
223
+ requireApproximationStatus(base, path, responseRepresentation);
224
+ return {
225
+ requirement: "sensor-response",
226
+ ...base,
227
+ responseRepresentation,
228
+ effectiveQuantumEfficiency: requireFraction(record, "effectiveQuantumEfficiency", path, true)
229
+ };
230
+ }
231
+ throw new InvalidConfigurationError(`${path}.responseRepresentation is invalid.`);
232
+ }
233
+ /**
234
+ * Parses untrusted radiometry-prerequisite metadata.
235
+ *
236
+ * This validates structure and provenance; it cannot prove that a cited
237
+ * calibration/evidence claim is scientifically true.
238
+ */
239
+ export function parseRadiometryReadinessProfile(value) {
240
+ const profile = requireRecord(value, "radiometryReadiness");
241
+ if (profile.schemaVersion !== "0.1.0") {
242
+ throw new InvalidConfigurationError('radiometryReadiness.schemaVersion must be "0.1.0".');
243
+ }
244
+ if (!Array.isArray(profile.components)) {
245
+ throw new InvalidConfigurationError("radiometryReadiness.components must be an array.");
246
+ }
247
+ const components = profile.components.map((component, index) => parseRequirement(component, `radiometryReadiness.components[${index}]`));
248
+ const ids = components.map((component) => component.requirement);
249
+ if (new Set(ids).size !== ids.length) {
250
+ throw new InvalidConfigurationError("radiometryReadiness.components must not contain duplicate requirement IDs.");
251
+ }
252
+ return {
253
+ schemaVersion: "0.1.0",
254
+ components
255
+ };
256
+ }
257
+ /**
258
+ * Assesses whether the declared prerequisites are sufficient for a nominal
259
+ * photon estimate and, separately, for a calibrated photon claim.
260
+ *
261
+ * This assessment does not enable composed photon/noise output.
262
+ */
263
+ export function assessRadiometryReadiness(profile) {
264
+ const validatedProfile = parseRadiometryReadinessProfile(profile);
265
+ const byId = new Map(validatedProfile.components.map((component) => [
266
+ component.requirement,
267
+ component
268
+ ]));
269
+ const missingRequirements = REQUIRED_REQUIREMENTS.filter((requirement) => !byId.has(requirement));
270
+ const approximateRequirements = validatedProfile.components
271
+ .filter((component) => component.scientificStatus === "approximation")
272
+ .map((component) => component.requirement);
273
+ const unquantifiedUncertaintyRequirements = validatedProfile.components
274
+ .filter((component) => component.uncertainty.kind === "not-quantified")
275
+ .map((component) => component.requirement);
276
+ const nominalPhotonEstimateReady = missingRequirements.length === 0;
277
+ const calibratedPhotonClaimReady = nominalPhotonEstimateReady &&
278
+ approximateRequirements.length === 0 &&
279
+ unquantifiedUncertaintyRequirements.length === 0;
280
+ return {
281
+ status: !nominalPhotonEstimateReady
282
+ ? "not-ready"
283
+ : calibratedPhotonClaimReady
284
+ ? "calibrated-ready"
285
+ : "approximate-only",
286
+ nominalPhotonEstimateReady,
287
+ calibratedPhotonClaimReady,
288
+ missingRequirements,
289
+ approximateRequirements,
290
+ unquantifiedUncertaintyRequirements,
291
+ composedPhotonOutputEnabled: false
292
+ };
293
+ }
294
+ //# sourceMappingURL=radiometry-readiness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"radiometry-readiness.js","sourceRoot":"","sources":["../../src/sensor/radiometry-readiness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ import { type CalculationResult } from "../core/calculation-result.js";
2
+ /**
3
+ * Physical photosensitive imaging area used for image formation.
4
+ *
5
+ * This is deliberately not a sensor package/die dimension.
6
+ */
7
+ export interface SensorImagingArea {
8
+ /** Physical active imaging width in millimetres. */
9
+ widthMm: number;
10
+ /** Physical active imaging height in millimetres. */
11
+ heightMm: number;
12
+ }
13
+ /**
14
+ * Native effective image-sampling raster associated with an imaging area.
15
+ *
16
+ * The raster describes effective image samples. It does not imply that each
17
+ * sample corresponds one-to-one with a physical photodiode/photosite.
18
+ */
19
+ export interface RasterDimensions {
20
+ /** Horizontal image-sample/pixel count. */
21
+ pixelWidth: number;
22
+ /** Vertical image-sample/pixel count. */
23
+ pixelHeight: number;
24
+ }
25
+ /**
26
+ * Native effective image-sampling raster associated with an imaging area.
27
+ *
28
+ * This alias identifies semantic role; generic active/output rasters should
29
+ * use RasterDimensions instead.
30
+ */
31
+ export type NativeImageRaster = RasterDimensions;
32
+ export interface ImagingAreaMetrics {
33
+ /** Physical imaging-area diagonal in millimetres. */
34
+ diagonalMm: number;
35
+ /** Physical width divided by physical height. */
36
+ aspectRatio: number;
37
+ /**
38
+ * Diagonal crop factor relative to a 36 × 24 mm reference frame.
39
+ *
40
+ * This is a physical imaging-area quantity and does not include later
41
+ * digital/output cropping.
42
+ */
43
+ cropFactor35Mm: number;
44
+ }
45
+ export interface CalculateSensorGeometryMetricsInput {
46
+ imagingArea: SensorImagingArea;
47
+ nativeRaster: NativeImageRaster;
48
+ }
49
+ export interface SensorGeometryMetrics {
50
+ imagingArea: ImagingAreaMetrics;
51
+ nativeRaster: {
52
+ /** Exact product of native raster width and height. */
53
+ totalImageSamples: number;
54
+ /** Derived megapixels from the exact native raster. */
55
+ megapixels: number;
56
+ /** Native raster width divided by native raster height. */
57
+ aspectRatio: number;
58
+ };
59
+ sampling: {
60
+ /**
61
+ * Horizontal geometric sampling pitch in micrometres.
62
+ *
63
+ * This is image-sample spacing, not physical photodiode active area.
64
+ */
65
+ pitchXMicrometers: number;
66
+ /**
67
+ * Vertical geometric sampling pitch in micrometres.
68
+ *
69
+ * This is image-sample spacing, not physical photodiode active area.
70
+ */
71
+ pitchYMicrometers: number;
72
+ /** Horizontal sampling pitch divided by vertical sampling pitch. */
73
+ pitchAspectRatio: number;
74
+ };
75
+ }
76
+ /**
77
+ * Calculates physical imaging-area metrics independently from native raster
78
+ * density.
79
+ *
80
+ * Crop factor uses the diagonal ratio to a 36 × 24 mm reference frame.
81
+ * Digital/output crops are not part of this physical imaging-area quantity.
82
+ *
83
+ * @param imagingArea Physical photosensitive imaging area.
84
+ * @returns Physical diagonal, aspect ratio, and diagonal 35 mm crop factor.
85
+ */
86
+ export declare function calculateImagingAreaMetrics(imagingArea: SensorImagingArea): CalculationResult<ImagingAreaMetrics>;
87
+ /**
88
+ * Calculates physical imaging-area, native-raster, and geometric sampling
89
+ * metrics without coupling sensor size to resolution.
90
+ *
91
+ * The supplied raster is assumed to span the supplied imaging area. Derived
92
+ * sampling pitch is geometric sample spacing only and must not be interpreted
93
+ * as photosite fill factor or photon-collection area.
94
+ *
95
+ * @param input Physical imaging area and native effective image raster.
96
+ * @returns Derived imaging-area, raster, and sampling metrics.
97
+ */
98
+ export declare function calculateSensorGeometryMetrics(input: CalculateSensorGeometryMetricsInput): CalculationResult<SensorGeometryMetrics>;
99
+ //# sourceMappingURL=sensor-geometry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sensor-geometry.d.ts","sourceRoot":"","sources":["../../src/sensor/sensor-geometry.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;AAcvC;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,CAAC;AAEjD,MAAM,WAAW,kBAAkB;IACjC,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,mCAAmC;IAClD,WAAW,EAAE,iBAAiB,CAAC;IAC/B,YAAY,EAAE,iBAAiB,CAAC;CACjC;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,kBAAkB,CAAC;IAChC,YAAY,EAAE;QACZ,uDAAuD;QACvD,iBAAiB,EAAE,MAAM,CAAC;QAC1B,uDAAuD;QACvD,UAAU,EAAE,MAAM,CAAC;QACnB,2DAA2D;QAC3D,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,QAAQ,EAAE;QACR;;;;WAIG;QACH,iBAAiB,EAAE,MAAM,CAAC;QAC1B;;;;WAIG;QACH,iBAAiB,EAAE,MAAM,CAAC;QAC1B,oEAAoE;QACpE,gBAAgB,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH;AAED;;;;;;;;;GASG;AACH,wBAAgB,2BAA2B,CACzC,WAAW,EAAE,iBAAiB,GAC7B,iBAAiB,CAAC,kBAAkB,CAAC,CAmBvC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,mCAAmC,GACzC,iBAAiB,CAAC,qBAAqB,CAAC,CAmD1C"}