feat: bridge calibration workflow to autofocus study

This commit is contained in:
fy59 2026-09-03 14:35:46 +02:00
parent 524f08d1b3
commit 1a96bcce1f
9 changed files with 517 additions and 3 deletions

View file

@ -147,3 +147,44 @@ This v1 boundary does not:
The future physical study supplies the evidence needed to decide whether the The future physical study supplies the evidence needed to decide whether the
A6000 + E PZ 16-50 supports one domain, discrete domains, or exact focus only. A6000 + E PZ 16-50 supports one domain, discrete domains, or exact focus only.
## Materialized Workflow bridge v1
**Status: PASS / FROZEN.**
`CALIBRATION_AF_STUDY_WORKFLOW_BRIDGE_V1` is the additive conversion boundary
between the already-FROZEN Calibration Workflow materializer and AF-study
samples.
It accepts a `Lardon3DCalibrationWorkflowExternalEvidence` that has already
passed the existing immutable-file, solver-bundle and provenance checks. It
does not parse `solve.json` or any other solver file again.
The bridge:
```text
materialized Calibration Workflow evidence
+ caller-supplied exact focus token
+ caller-supplied FIT/HOLDOUT role
-> Lardon3DCalibrationAfStudySample
```
The sample publishes `repeated_parameters[0]` only after checking that all three
retained full solves remain exactly equal.
Its `calibration_evidence_sha256` is independent of the focus token and
FIT/HOLDOUT role and binds:
- target identity;
- optical-state identity;
- solver executable/configuration identity;
- initialization evidence;
- validation evidence;
- exact oriented dimensions;
- exact published binary64 intrinsics.
Therefore relabelling one calibration result cannot manufacture independent
calibration evidence.
The bridge performs no DB access, metadata interpretation, physical AF decision,
thresholding, interpolation or extrapolation.

View file

@ -950,5 +950,7 @@ CURRENT_NEXT PHYSICAL_AUTOFOCUS_OPTICAL_APPLICABILITY_VAL
Implementation proceeds only through explicitly human-authorized tranches under `prompt.md` and the Implementation proceeds only through explicitly human-authorized tranches under `prompt.md` and the
numbered `prompt/` execution contract. The current dependency is physical autofocus/optical numbered `prompt/` execution contract. The current dependency is physical autofocus/optical
applicability validation for real equipment, followed by a dedicated calibrated real campaign. applicability validation for real equipment, followed by a dedicated calibrated real campaign.
Generic autofocus applicability machinery is available, but the real Generic autofocus applicability machinery is available. AF-study evidence can now be produced from
A6000 + E PZ 16-50 autofocus applicability remains blocked until physical evidence validates it. already-materialized Calibration Workflow results without a second solver parser, while focus tokens
remain explicit study annotations. The real A6000 + E PZ 16-50 autofocus applicability remains
blocked until physical evidence validates it.

View file

@ -0,0 +1,51 @@
#ifndef LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_H
#define LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_H
#include <lardon3d/calibration_af_study.h>
#include <lardon3d/calibration_workflow.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_OK = 0,
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_ARGUMENT,
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_EVIDENCE,
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_CRYPTO_ERROR,
} Lardon3DCalibrationAfStudyWorkflowResult;
/* Convert one already validated/materialized Calibration Workflow result into
* one AF-study sample without re-parsing any solver file.
*
* `focus_token` and FIT/HOLDOUT role are study annotations. They are
* deliberately excluded from calibration_evidence_sha256, so relabelling the
* same calibration result cannot manufacture a new calibration-evidence
* identity.
*
* calibration_evidence_sha256 is domain-separated and binds:
* - target identity;
* - optical-state identity;
* - solver executable/configuration identity;
* - initialization and validation evidence;
* - exact oriented dimensions;
* - exact published binary64 fx,fy,cx,cy,k1,k2,p1,p2.
*
* The bridge checks the materialization invariants it consumes, including
* deterministic validation-evidence binding and equality of all three repeated
* full solves. It publishes repeated_parameters[0].
*
* No Project DB access, metadata interpretation, physical AF applicability
* decision, interpolation, extrapolation, solver execution or thresholding
* occurs here. */
Lardon3DCalibrationAfStudyWorkflowResult
lardon3d_calibration_af_study_sample_from_materialized_evidence(
const Lardon3DCalibrationWorkflowExternalEvidence *external,
Lardon3DCalibrationAfStudySampleRole role, const char *focus_token,
Lardon3DCalibrationAfStudySample *output);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -184,6 +184,7 @@ lardon3d_app = executable(
'src/calibration_tooling.c', 'src/calibration_tooling.c',
'src/calibration_tooling_v2.c', 'src/calibration_tooling_v2.c',
'src/calibration_af_study.c', 'src/calibration_af_study.c',
'src/calibration_af_study_workflow.c',
'src/calibration_workflow.cpp', 'src/calibration_workflow.cpp',
'src/calibration_workflow_materialize.cpp', 'src/calibration_workflow_materialize.cpp',
'src/calibration_workflow_bind.cpp', 'src/calibration_workflow_bind.cpp',
@ -930,6 +931,22 @@ calibration_af_study_test = executable(
test('calibration-af-study', calibration_af_study_test, timeout: 30) test('calibration-af-study', calibration_af_study_test, timeout: 30)
calibration_af_study_workflow_test = executable(
'test-calibration-af-study-workflow',
sources: [
'tests/test_calibration_af_study_workflow.c',
'src/calibration_af_study_workflow.c',
],
include_directories: include_directories('include'),
dependencies: [openssl, cc.find_library('m')],
)
test(
'calibration-af-study-workflow',
calibration_af_study_workflow_test,
timeout: 30,
)
calibration_workflow_v2_test = executable( calibration_workflow_v2_test = executable(
'test-calibration-workflow-v2', 'test-calibration-workflow-v2',
sources: [ sources: [

View file

@ -42,6 +42,7 @@ Calibration Workflow v2 PASS/FROZEN
Adaptive capture settings semantics PASS/FROZEN Adaptive capture settings semantics PASS/FROZEN
Autofocus v2 foundation PASS/FROZEN Autofocus v2 foundation PASS/FROZEN
Calibration AF study evidence v1 PASS/FROZEN Calibration AF study evidence v1 PASS/FROZEN
Calibration AF study Workflow bridge v1 PASS/FROZEN
Calibration Tooling planarity alignment PASS/FROZEN Calibration Tooling planarity alignment PASS/FROZEN
``` ```

View file

@ -24,6 +24,7 @@ CALIBRATION_V2_WORKFLOW_READY=PASS/FROZEN
ADAPTIVE_CAPTURE_SETTINGS_CONTRACT=PASS/FROZEN ADAPTIVE_CAPTURE_SETTINGS_CONTRACT=PASS/FROZEN
AUTOFOCUS_V2_FOUNDATION=PASS/FROZEN AUTOFOCUS_V2_FOUNDATION=PASS/FROZEN
CALIBRATION_AF_STUDY_EVIDENCE_V1=PASS/FROZEN CALIBRATION_AF_STUDY_EVIDENCE_V1=PASS/FROZEN
CALIBRATION_AF_STUDY_WORKFLOW_BRIDGE_V1=PASS/FROZEN
CURRENT_CALIBRATION_NEXT=PHYSICAL_AUTOFOCUS_OPTICAL_APPLICABILITY_VALIDATION CURRENT_CALIBRATION_NEXT=PHYSICAL_AUTOFOCUS_OPTICAL_APPLICABILITY_VALIDATION
``` ```

View file

@ -26,7 +26,7 @@ Default dependency order:
4. Heterogeneous calibration publication / Tooling / Bootstrap evolution — PASS/FROZEN; 4. Heterogeneous calibration publication / Tooling / Bootstrap evolution — PASS/FROZEN;
5. Heterogeneous Workflow v2 truthful READY proof — PASS/FROZEN; 5. Heterogeneous Workflow v2 truthful READY proof — PASS/FROZEN;
6. Adaptive capture settings / generic autofocus foundation — PASS/FROZEN; 6. Adaptive capture settings / generic autofocus foundation — PASS/FROZEN;
7. physical autofocus/optical applicability validation and dedicated calibrated real campaign — CURRENT; `CALIBRATION_AF_STUDY_EVIDENCE_V1=PASS/FROZEN` supplies deterministic measurement evidence but makes no physical applicability decision; 7. physical autofocus/optical applicability validation and dedicated calibrated real campaign — CURRENT; `CALIBRATION_AF_STUDY_EVIDENCE_V1=PASS/FROZEN` supplies deterministic measurement evidence and `CALIBRATION_AF_STUDY_WORKFLOW_BRIDGE_V1=PASS/FROZEN` converts already-materialized Calibration Workflow evidence into AF-study samples without reparsing solver artifacts; neither makes a physical applicability decision;
8. real Sparse SfM proof; 8. real Sparse SfM proof;
9. durable Dense/OpenMVS orchestration; 9. durable Dense/OpenMVS orchestration;
10. mesh / refinement / texturing / export; 10. mesh / refinement / texturing / export;

View file

@ -0,0 +1,211 @@
#include <lardon3d/calibration_af_study_workflow.h>
#include <math.h>
#include <openssl/evp.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
static bool nonzero_digest(const unsigned char value[32]) {
unsigned char any = 0;
for (size_t index = 0; index < 32; ++index) any |= value[index];
return any != 0;
}
static bool token_copy(
const char *input,
char output[LARDON3D_CALIBRATION_AF_STUDY_FOCUS_TOKEN_CAPACITY]) {
if (!input) return false;
size_t length = 0;
while (length < LARDON3D_CALIBRATION_AF_STUDY_FOCUS_TOKEN_CAPACITY &&
input[length] != '\0')
++length;
if (length == 0 ||
length >= LARDON3D_CALIBRATION_AF_STUDY_FOCUS_TOKEN_CAPACITY)
return false;
memset(output, 0, LARDON3D_CALIBRATION_AF_STUDY_FOCUS_TOKEN_CAPACITY);
memcpy(output, input, length);
return true;
}
static bool digest_begin(EVP_MD_CTX **ctx, const char *domain) {
*ctx = EVP_MD_CTX_new();
return *ctx &&
EVP_DigestInit_ex(*ctx, EVP_sha256(), NULL) == 1 &&
EVP_DigestUpdate(*ctx, domain, strlen(domain)) == 1;
}
static bool digest_add(EVP_MD_CTX *ctx, const void *bytes, size_t size) {
return EVP_DigestUpdate(ctx, bytes, size) == 1;
}
static bool digest_finish(EVP_MD_CTX *ctx, unsigned char output[32]) {
unsigned int output_size = 0;
const bool ok =
EVP_DigestFinal_ex(ctx, output, &output_size) == 1 && output_size == 32;
EVP_MD_CTX_free(ctx);
return ok;
}
static void encode_u32_le(uint32_t value, unsigned char output[4]) {
for (size_t index = 0; index < 4; ++index)
output[index] = (unsigned char)(value >> (8u * index));
}
static void encode_f64_le(double value, unsigned char output[8]) {
uint64_t bits = 0;
if (value == 0.0) value = 0.0;
memcpy(&bits, &value, sizeof(bits));
for (size_t index = 0; index < 8; ++index)
output[index] = (unsigned char)(bits >> (8u * index));
}
static bool validation_binding(
const Lardon3DCalibrationWorkflowInputBoundary *boundary,
unsigned char output[32]) {
static const char domain[] = "L3DCAL_WORKFLOW_VALIDATION_V1\n";
EVP_MD_CTX *ctx = NULL;
if (!digest_begin(&ctx, domain)) {
if (ctx) EVP_MD_CTX_free(ctx);
return false;
}
const bool ok =
digest_add(ctx, boundary->detection_sha256, 32) &&
digest_add(ctx, boundary->solve_sha256, 32) &&
digest_add(ctx, boundary->evidence_sha256, 32) &&
digest_add(ctx, boundary->producer_sha256, 32);
if (!ok) {
EVP_MD_CTX_free(ctx);
return false;
}
return digest_finish(ctx, output);
}
static bool sample_identity(
const Lardon3DCalibrationWorkflowExternalEvidence *external,
unsigned char output[32]) {
static const char domain[] = "L3DAF_CALIBRATION_SAMPLE_V1\n";
EVP_MD_CTX *ctx = NULL;
if (!digest_begin(&ctx, domain)) {
if (ctx) EVP_MD_CTX_free(ctx);
return false;
}
bool ok =
digest_add(ctx, external->target_sha256, 32) &&
digest_add(ctx, external->optical_state_sha256, 32) &&
digest_add(ctx, external->solver_executable_sha256, 32) &&
digest_add(ctx, external->solver_configuration_sha256, 32) &&
digest_add(ctx, external->initialization_evidence_sha256, 32) &&
digest_add(ctx, external->validation_evidence_sha256, 32);
unsigned char encoded_u32[4];
encode_u32_le(external->oriented_width, encoded_u32);
ok = ok && digest_add(ctx, encoded_u32, sizeof(encoded_u32));
encode_u32_le(external->oriented_height, encoded_u32);
ok = ok && digest_add(ctx, encoded_u32, sizeof(encoded_u32));
for (size_t parameter = 0; parameter < 8 && ok; ++parameter) {
unsigned char encoded_f64[8];
encode_f64_le(external->repeated_parameters[0][parameter], encoded_f64);
ok = digest_add(ctx, encoded_f64, sizeof(encoded_f64));
}
if (!ok) {
EVP_MD_CTX_free(ctx);
return false;
}
return digest_finish(ctx, output);
}
static bool boundary_provenance_valid(
const Lardon3DCalibrationWorkflowExternalEvidence *external) {
const Lardon3DCalibrationWorkflowInputBoundary *boundary =
&external->boundary;
if (!nonzero_digest(boundary->session_sha256) ||
!nonzero_digest(boundary->detection_sha256) ||
!nonzero_digest(boundary->solve_sha256) ||
!nonzero_digest(boundary->evidence_sha256) ||
!nonzero_digest(boundary->producer_sha256) ||
!nonzero_digest(boundary->optical_state_sha256) ||
!nonzero_digest(boundary->solver_executable_sha256) ||
!nonzero_digest(boundary->solver_configuration_sha256))
return false;
return memcmp(external->optical_state_sha256,
boundary->optical_state_sha256, 32) == 0 &&
memcmp(external->solver_executable_sha256,
boundary->solver_executable_sha256, 32) == 0 &&
memcmp(external->solver_configuration_sha256,
boundary->solver_configuration_sha256, 32) == 0 &&
memcmp(external->initialization_evidence_sha256,
boundary->session_sha256, 32) == 0;
}
static bool published_parameters_valid(
const Lardon3DCalibrationWorkflowExternalEvidence *external) {
const double *published = external->repeated_parameters[0];
for (size_t parameter = 0; parameter < 8; ++parameter) {
if (!isfinite(published[parameter])) return false;
if (external->repeated_parameters[1][parameter] != published[parameter] ||
external->repeated_parameters[2][parameter] != published[parameter])
return false;
}
return published[0] > 0.0 && published[1] > 0.0 &&
published[2] >= 0.0 && published[3] >= 0.0 &&
published[2] < (double)external->oriented_width &&
published[3] < (double)external->oriented_height;
}
Lardon3DCalibrationAfStudyWorkflowResult
lardon3d_calibration_af_study_sample_from_materialized_evidence(
const Lardon3DCalibrationWorkflowExternalEvidence *external,
Lardon3DCalibrationAfStudySampleRole role, const char *focus_token,
Lardon3DCalibrationAfStudySample *output) {
if (output) memset(output, 0, sizeof(*output));
if (!external || !focus_token || !output ||
(role != LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT &&
role != LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_HOLDOUT))
return LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_ARGUMENT;
Lardon3DCalibrationAfStudySample sample = {0};
if (!token_copy(focus_token, sample.focus_token))
return LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_ARGUMENT;
if (external->oriented_width == 0 || external->oriented_height == 0 ||
!nonzero_digest(external->target_sha256) ||
!nonzero_digest(external->optical_state_sha256) ||
!nonzero_digest(external->solver_executable_sha256) ||
!nonzero_digest(external->solver_configuration_sha256) ||
!nonzero_digest(external->initialization_evidence_sha256) ||
!nonzero_digest(external->validation_evidence_sha256) ||
!boundary_provenance_valid(external) ||
!published_parameters_valid(external))
return LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_EVIDENCE;
unsigned char expected_validation[32] = {0};
if (!validation_binding(&external->boundary, expected_validation))
return LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_CRYPTO_ERROR;
if (memcmp(external->validation_evidence_sha256,
expected_validation, 32) != 0)
return LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_EVIDENCE;
sample.role = role;
if (!sample_identity(external, sample.calibration_evidence_sha256))
return LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_CRYPTO_ERROR;
sample.fx = external->repeated_parameters[0][0];
sample.fy = external->repeated_parameters[0][1];
sample.cx = external->repeated_parameters[0][2];
sample.cy = external->repeated_parameters[0][3];
sample.k1 = external->repeated_parameters[0][4];
sample.k2 = external->repeated_parameters[0][5];
sample.p1 = external->repeated_parameters[0][6];
sample.p2 = external->repeated_parameters[0][7];
*output = sample;
return LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_OK;
}

View file

@ -0,0 +1,190 @@
#include <lardon3d/calibration_af_study_workflow.h>
#include <math.h>
#include <openssl/evp.h>
#include <stdio.h>
#include <string.h>
#define CHECK(expression) \
do { \
if (!(expression)) { \
fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, #expression); \
return 1; \
} \
} while (0)
static void fill_digest(unsigned char output[32], unsigned char value) {
memset(output, value, 32);
}
static int sha256_validation(
const Lardon3DCalibrationWorkflowInputBoundary *boundary,
unsigned char output[32]) {
static const char domain[] = "L3DCAL_WORKFLOW_VALIDATION_V1\n";
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
if (!ctx) return 0;
int ok = EVP_DigestInit_ex(ctx, EVP_sha256(), NULL) == 1 &&
EVP_DigestUpdate(ctx, domain, sizeof(domain) - 1) == 1 &&
EVP_DigestUpdate(ctx, boundary->detection_sha256, 32) == 1 &&
EVP_DigestUpdate(ctx, boundary->solve_sha256, 32) == 1 &&
EVP_DigestUpdate(ctx, boundary->evidence_sha256, 32) == 1 &&
EVP_DigestUpdate(ctx, boundary->producer_sha256, 32) == 1;
unsigned int length = 0;
ok = ok && EVP_DigestFinal_ex(ctx, output, &length) == 1 && length == 32;
EVP_MD_CTX_free(ctx);
return ok;
}
static Lardon3DCalibrationWorkflowExternalEvidence fixture(void) {
Lardon3DCalibrationWorkflowExternalEvidence value = {0};
fill_digest(value.boundary.session_sha256, 0x11);
fill_digest(value.boundary.detection_sha256, 0x22);
fill_digest(value.boundary.solve_sha256, 0x33);
fill_digest(value.boundary.evidence_sha256, 0x44);
fill_digest(value.boundary.producer_sha256, 0x55);
fill_digest(value.boundary.campaign_state_sha256, 0x66);
fill_digest(value.boundary.optical_state_sha256, 0x77);
fill_digest(value.boundary.solver_executable_sha256, 0x88);
fill_digest(value.boundary.solver_configuration_sha256, 0x99);
fill_digest(value.target_sha256, 0xA1);
memcpy(value.optical_state_sha256, value.boundary.optical_state_sha256, 32);
memcpy(value.solver_executable_sha256,
value.boundary.solver_executable_sha256, 32);
memcpy(value.solver_configuration_sha256,
value.boundary.solver_configuration_sha256, 32);
memcpy(value.initialization_evidence_sha256,
value.boundary.session_sha256, 32);
(void)sha256_validation(&value.boundary, value.validation_evidence_sha256);
value.oriented_width = 6000;
value.oriented_height = 4000;
const double params[8] = {
4000.0, 4002.0, 3000.0, 2000.0, -0.1, 0.01, 0.001, -0.001,
};
for (size_t run = 0; run < 3; ++run)
memcpy(value.repeated_parameters[run], params, sizeof(params));
return value;
}
static int test_happy_path_and_identity_stability(void) {
Lardon3DCalibrationWorkflowExternalEvidence external = fixture();
Lardon3DCalibrationAfStudySample fit = {0};
Lardon3DCalibrationAfStudySample holdout = {0};
CHECK(lardon3d_calibration_af_study_sample_from_materialized_evidence(
&external, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT,
"sony-focus:137", &fit) ==
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_OK);
CHECK(fit.role == LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT);
CHECK(strcmp(fit.focus_token, "sony-focus:137") == 0);
CHECK(fit.fx == 4000.0 && fit.fy == 4002.0);
CHECK(fit.cx == 3000.0 && fit.cy == 2000.0);
CHECK(lardon3d_calibration_af_study_sample_from_materialized_evidence(
&external, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_HOLDOUT,
"different-focus-label", &holdout) ==
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_OK);
CHECK(memcmp(fit.calibration_evidence_sha256,
holdout.calibration_evidence_sha256, 32) == 0);
unsigned char zero[32] = {0};
CHECK(memcmp(fit.calibration_evidence_sha256, zero, 32) != 0);
return 0;
}
static int test_identity_binds_provenance_and_parameters(void) {
Lardon3DCalibrationWorkflowExternalEvidence a = fixture();
Lardon3DCalibrationWorkflowExternalEvidence b = fixture();
Lardon3DCalibrationAfStudySample sample_a = {0};
Lardon3DCalibrationAfStudySample sample_b = {0};
CHECK(lardon3d_calibration_af_study_sample_from_materialized_evidence(
&a, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT, "focus:a",
&sample_a) == LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_OK);
b.target_sha256[0] ^= 1;
CHECK(lardon3d_calibration_af_study_sample_from_materialized_evidence(
&b, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT, "focus:a",
&sample_b) == LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_OK);
CHECK(memcmp(sample_a.calibration_evidence_sha256,
sample_b.calibration_evidence_sha256, 32) != 0);
b = fixture();
for (size_t run = 0; run < 3; ++run)
b.repeated_parameters[run][0] += 1.0;
CHECK(lardon3d_calibration_af_study_sample_from_materialized_evidence(
&b, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT, "focus:a",
&sample_b) == LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_OK);
CHECK(memcmp(sample_a.calibration_evidence_sha256,
sample_b.calibration_evidence_sha256, 32) != 0);
return 0;
}
static int test_invalid_materialization_rejected(void) {
Lardon3DCalibrationWorkflowExternalEvidence external = fixture();
Lardon3DCalibrationAfStudySample sample;
memset(&sample, 0xA5, sizeof(sample));
external.validation_evidence_sha256[0] ^= 1;
CHECK(lardon3d_calibration_af_study_sample_from_materialized_evidence(
&external, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT, "focus:a",
&sample) ==
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_EVIDENCE);
Lardon3DCalibrationAfStudySample zero = {0};
CHECK(memcmp(&sample, &zero, sizeof(sample)) == 0);
external = fixture();
external.repeated_parameters[1][0] += 1.0;
CHECK(lardon3d_calibration_af_study_sample_from_materialized_evidence(
&external, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT, "focus:a",
&sample) ==
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_EVIDENCE);
external = fixture();
external.repeated_parameters[0][0] = NAN;
external.repeated_parameters[1][0] = NAN;
external.repeated_parameters[2][0] = NAN;
CHECK(lardon3d_calibration_af_study_sample_from_materialized_evidence(
&external, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT, "focus:a",
&sample) ==
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_EVIDENCE);
external = fixture();
external.optical_state_sha256[0] ^= 1;
CHECK(lardon3d_calibration_af_study_sample_from_materialized_evidence(
&external, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT, "focus:a",
&sample) ==
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_EVIDENCE);
return 0;
}
static int test_invalid_arguments(void) {
Lardon3DCalibrationWorkflowExternalEvidence external = fixture();
Lardon3DCalibrationAfStudySample sample = {0};
CHECK(lardon3d_calibration_af_study_sample_from_materialized_evidence(
NULL, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT, "focus:a",
&sample) ==
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_ARGUMENT);
CHECK(lardon3d_calibration_af_study_sample_from_materialized_evidence(
&external, 0, "focus:a", &sample) ==
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_ARGUMENT);
CHECK(lardon3d_calibration_af_study_sample_from_materialized_evidence(
&external, LARDON3D_CALIBRATION_AF_STUDY_SAMPLE_FIT, "",
&sample) ==
LARDON3D_CALIBRATION_AF_STUDY_WORKFLOW_INVALID_ARGUMENT);
return 0;
}
int main(void) {
CHECK(test_happy_path_and_identity_stability() == 0);
CHECK(test_identity_binds_provenance_and_parameters() == 0);
CHECK(test_invalid_materialization_rejected() == 0);
CHECK(test_invalid_arguments() == 0);
puts("CALIBRATION_AF_STUDY_WORKFLOW_BRIDGE_V1=PASS");
return 0;
}