feat: bridge calibration workflow to autofocus study
This commit is contained in:
parent
524f08d1b3
commit
1a96bcce1f
9 changed files with 517 additions and 3 deletions
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
51
include/lardon3d/calibration_af_study_workflow.h
Normal file
51
include/lardon3d/calibration_af_study_workflow.h
Normal 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
|
||||||
17
meson.build
17
meson.build
|
|
@ -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: [
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
211
src/calibration_af_study_workflow.c
Normal file
211
src/calibration_af_study_workflow.c
Normal 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;
|
||||||
|
}
|
||||||
190
tests/test_calibration_af_study_workflow.c
Normal file
190
tests/test_calibration_af_study_workflow.c
Normal 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;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue