diff --git a/docs/architecture/calibration_tooling.md b/docs/architecture/calibration_tooling.md index 9a5b771..7ffaf86 100644 --- a/docs/architecture/calibration_tooling.md +++ b/docs/architecture/calibration_tooling.md @@ -59,6 +59,7 @@ Tooling continues to validate the frozen Science v1 requirements, including: - exact ChArUco 9 x 7 / DICT_5X5_100 target identity; - 30.000 mm squares and 21.000 mm markers; - ten physical square measurements and instrument resolution; +- measured free white border of at least 30 mm, supplied explicitly by the session; - immutable target, optical-state, solver and evidence digests; - accepted and rejected view evidence; - field-region, distance and angle diversity; diff --git a/docs/roadmap/roadmap.md b/docs/roadmap/roadmap.md index b8b701a..633d456 100644 --- a/docs/roadmap/roadmap.md +++ b/docs/roadmap/roadmap.md @@ -519,6 +519,8 @@ Calibration Evidence Solver v1 implements the external OpenCV 5.x evidence path Calibration Solver Preflight v1. It remains outside the Lardon3D runtime and Project DB and its deterministic synthetic CPU1 self-test is validated. +The solver session contract now also requires explicit measured `white_border >= 30 mm` evidence. This closes the previous gap between the physical target requirement and the future coordinator input; no default border width is inferred. + A bounded Tooling correction aligned planarity handling with Calibration Science v1: Science v1 defines a categorical physical planarity attestation, not a numeric flatness threshold. Tooling therefore rejects invented finite `target_flatness_mm` values. `L3DCALB1` v1 and Calibration diff --git a/prompt/02_CURRENT_FROZEN_STATE.md b/prompt/02_CURRENT_FROZEN_STATE.md index 2b4acf4..643f61d 100644 --- a/prompt/02_CURRENT_FROZEN_STATE.md +++ b/prompt/02_CURRENT_FROZEN_STATE.md @@ -27,6 +27,7 @@ User-facing TUI/control language PASS External SSD controller VALIDATED Calibration Solver Preflight v1 PASS Calibration Evidence Solver v1 IMPLEMENTED/VALIDATED +Calibration solver white-border evidence PASS/FROZEN Calibration Tooling planarity alignment PASS/FROZEN ``` diff --git a/prompt/12_CALIBRATION.md b/prompt/12_CALIBRATION.md index bca3e05..adaa302 100644 --- a/prompt/12_CALIBRATION.md +++ b/prompt/12_CALIBRATION.md @@ -8,6 +8,7 @@ CALIBRATION_TOOLING_V1=PASS/FROZEN CALIBRATION_TOOLING_PLANARITY_ALIGNMENT=PASS/FROZEN CALIBRATION_BOOTSTRAP_V1=PASS/FROZEN CALIBRATION_EVIDENCE_SOLVER_V1=IMPLEMENTED/VALIDATED +CALIBRATION_SOLVER_WHITE_BORDER_V1=PASS/FROZEN CALIBRATION_WORKFLOW=IN_PROGRESS CURRENT_CALIBRATION_NEXT=WORKFLOW_COORDINATOR ``` @@ -22,6 +23,8 @@ A bounded corrective review established that Calibration Science v1 defines targ The external `tools/calibration_evidence_solver/` implementation is present and validated by its deterministic synthetic CPU1 self-test. It remains external to the Lardon3D runtime and Project DB. +Its session v1 now requires an explicit measured `white_border` of at least 30 mm. No default border width may be invented by the solver or the future coordinator. + ## CURRENT Historical S21 and A6000 Engine Bay campaigns: diff --git a/tools/calibration_evidence_solver/README.md b/tools/calibration_evidence_solver/README.md index c0646f8..e01e68d 100644 --- a/tools/calibration_evidence_solver/README.md +++ b/tools/calibration_evidence_solver/README.md @@ -22,6 +22,7 @@ Un fichier texte ASCII, sans commentaire ni espace dans les chemins, contient : L3DCAL_SESSION_V1 target DICT_5X5_100 9 7 30 21 measurement <10-real-square-mm-values> +white_border planarity PASS decoder optical_state @@ -37,9 +38,10 @@ Il y a entre 1 et 4096 lignes `image`, chaque source SHA est unique, et chaque image a exactement une preuve pré-solve, clipping et coordinate. Les dix mesures réelles doivent être à 0.30 mm de 30.000 mm et leur étendue ne doit pas dépasser 0.20 mm; l'instrument doit annoncer une résolution de 0.1 mm ou -meilleure. `planarity` est une attestation PASS hachée : Science v1 impose une -planche rigide plane mais ne fixe pas de seuil numérique que l'outil pourrait -inventer. `optical_state` est un jeton atomique complet du manifeste d'état +meilleure. `white_border` archive la bordure blanche physique libre mesurée et +doit être d'au moins 30 mm; aucune valeur par défaut n'est inventée. +`planarity` est une attestation PASS hachée : Science v1 impose une planche +rigide plane mais ne fixe pas de seuil numérique que l'outil pourrait inventer. `optical_state` est un jeton atomique complet du manifeste d'état optique; aucune valeur inconnue n'est acceptée par convention. Les `coordinate_point` consomment exactement les `N` comparaisons annoncées. diff --git a/tools/calibration_evidence_solver/calibration_evidence_solver b/tools/calibration_evidence_solver/calibration_evidence_solver index ecd3006..9f53744 100755 Binary files a/tools/calibration_evidence_solver/calibration_evidence_solver and b/tools/calibration_evidence_solver/calibration_evidence_solver differ diff --git a/tools/calibration_evidence_solver/calibration_evidence_solver.cpp b/tools/calibration_evidence_solver/calibration_evidence_solver.cpp index e5a5f33..5afeb67 100644 --- a/tools/calibration_evidence_solver/calibration_evidence_solver.cpp +++ b/tools/calibration_evidence_solver/calibration_evidence_solver.cpp @@ -50,10 +50,10 @@ constexpr int kSquaresX = 9, kSquaresY = 7; struct SessionEvidence { std::string target_id, instrument, decoder, decoder_version; std::array generator_hash{}, planarity_hash{}; - double resolution_mm = -1, planarity_pass = 0; + double resolution_mm = -1, planarity_pass = 0, white_border_mm = -1; std::array measurements{}; bool target = false, measurement = false, planarity = false; - bool decoder_set = false, optical_state = false; + bool white_border = false, decoder_set = false, optical_state = false; std::string optical_state_fields; }; @@ -140,6 +140,7 @@ bool read_session(const std::string& path,std::vector* views,std::array>m)||!finite(m)){*why="measurement values";return false;} } evidence->measurement=true; + } else if(tag=="white_border") { if(evidence->white_border || !(s>>evidence->white_border_mm) || !finite(evidence->white_border_mm) || evidence->white_border_mm < 30.0) {*why="white border evidence";return false;} evidence->white_border=true; } else if(tag=="planarity") { std::string result,h; if(evidence->planarity || !(s>>result>>h) || !hex(h,&evidence->planarity_hash) || (result!="PASS"&&result!="FAIL")) {*why="planarity evidence";return false;} evidence->planarity=true; evidence->planarity_pass=result=="PASS"; } else if(tag=="decoder") { if(evidence->decoder_set || !(s>>evidence->decoder>>evidence->decoder_version) || evidence->decoder.size()>128 || evidence->decoder_version.size()>128 || !json_token(evidence->decoder) || !json_token(evidence->decoder_version)) {*why="decoder identity/version";return false;} evidence->decoder_set=true; } else if(tag=="optical_state") { std::string h; if(evidence->optical_state || !(s>>h>>evidence->optical_state_fields) || !hex(h,optical) || evidence->optical_state_fields.size()>1024 || !json_token(evidence->optical_state_fields) || evidence->optical_state_fields=="UNKNOWN") {*why="optical-state evidence";return false;} evidence->optical_state=true; have=true; @@ -150,7 +151,7 @@ bool read_session(const std::string& path,std::vector* views,std::arraya{};double meters=0;int band=-1;if(!(s>>h>>meters>>band)||!hex(h,&a)||!finite(meters)||meters<=0||band<0||band>2){*why="distance evidence";return false;}View*v=find_view(views,a);if(!v||v->measured_distance>0){*why="distance source";return false;}v->measured_distance=meters;v->declared_distance_band=band; } else {*why="unknown session record";return false;} } - if(!have||!evidence->target||!evidence->measurement||!evidence->planarity||!evidence->planarity_pass||!evidence->decoder_set||!evidence->optical_state||views->empty()||views->size()>kMaxViews){*why="required session evidence";return false;} + if(!have||!evidence->target||!evidence->measurement||!evidence->white_border||!evidence->planarity||!evidence->planarity_pass||!evidence->decoder_set||!evidence->optical_state||views->empty()||views->size()>kMaxViews){*why="required session evidence";return false;} double lo=std::numeric_limits::infinity(),hi=-lo;for(double m:evidence->measurements){lo=std::min(lo,m);hi=std::max(hi,m);if(std::abs(m-30.0)>.30){*why="target measurement tolerance";return false;}}if(hi-lo>.20){*why="target measurement range";return false;} std::sort(views->begin(),views->end(),[](const View&a,const View&b){return a.hashsize();++i) { @@ -335,7 +336,7 @@ bool synthetic_test() { double delta=0;for(auto ray:std::array{{{0,0},{-.7,-.7},{.7,-.7},{-.7,.7},{.7,.7}}}){auto eval=[](const Params&p,cv::Point2d q){double r2=q.x*q.x+q.y*q.y,rad=1+p.k1*r2+p.k2*r2*r2;return cv::Point2d(p.fx*(q.x*rad+2*p.p1*q.x*q.y+p.p2*(r2+2*q.x*q.x))+p.cx,p.fy*(q.y*rad+p.p1*(r2+2*q.y*q.y)+2*p.p2*q.x*q.y)+p.cy);};delta=std::max(delta,cv::norm(eval(a.p,ray)-eval(f.p,ray)));} double iq=0,oq=0;for(const auto&x:v){iq=std::max(iq,x.image_conversion_max);oq=std::max(oq,x.object_conversion_max);} double rr=0,rm=0;size_t rh=0,rc=0;if(!residuals(&v,all,a,&rr,&rm,&rh,&rc,&why))return false; - SessionEvidence evidence; evidence.target=true;evidence.measurement=true;evidence.planarity=true;evidence.planarity_pass=1;evidence.decoder_set=true;evidence.optical_state=true;evidence.target_id="synthetic";evidence.instrument="synthetic";evidence.decoder="synthetic_decoder";evidence.decoder_version="1";evidence.optical_state_fields="synthetic_only";evidence.resolution_mm=.1;evidence.measurements.fill(30.0); + SessionEvidence evidence; evidence.target=true;evidence.measurement=true;evidence.white_border=true;evidence.planarity=true;evidence.planarity_pass=1;evidence.decoder_set=true;evidence.optical_state=true;evidence.target_id="synthetic";evidence.instrument="synthetic";evidence.decoder="synthetic_decoder";evidence.decoder_version="1";evidence.optical_state_fields="synthetic_only";evidence.resolution_mm=.1;evidence.white_border_mm=30.0;evidence.measurements.fill(30.0); Solve runs[3]={a,b,c};std::array optical{};std::string bundle_why; const std::filesystem::path left=std::filesystem::temp_directory_path()/"l3dcal_byte_identity_left"; const std::filesystem::path right=std::filesystem::temp_directory_path()/"l3dcal_byte_identity_right";std::error_code ec;std::filesystem::remove_all(left.string()+".bundle",ec);std::filesystem::remove_all(right.string()+".bundle",ec); @@ -355,6 +356,7 @@ bool manifest_negative_tests() { x<<"L3DCAL_SESSION_V1\n" <<"target board "<