diff --git a/rugnux/Rugnux.cpp b/rugnux/Rugnux.cpp index c864392e7..decacb39c 100644 --- a/rugnux/Rugnux.cpp +++ b/rugnux/Rugnux.cpp @@ -3071,6 +3071,26 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b beam_center_searched_ = true; const float beam_x = experiment_.GetBeamX_pxl(), beam_y = experiment_.GetBeamY_pxl(); const int reach = config_.beam_center_search_pxl; + // The background centre is measured off the whole projection rather than off a lattice, so + // where it is significant it BOUNDS where the beam can be, and a ladder that reaches + // `reach` px cannot arrive at a centre further away than that: every rung it can try is one + // the measurement has already excluded. The check above indexes the measured centre once + // and is right not to gate on significance for that - one pass is cheap. This ladder is + // 4*reach passes, so the same evidence is worth acting on here. Measured on a deposition + // whose header centre lands on a masked pixel: 170 px away at 0.34 px sigma, 48 rungs, and + // nothing adopted. + const bool centre_past_reach = + measured_beam_center_.has_value() + && std::hypot(measured_beam_center_->beam_x_pxl - beam_x, + measured_beam_center_->beam_y_pxl - beam_y) + > std::max(3.0f * measured_beam_center_->sigma_pxl, static_cast(reach)); + if (centre_past_reach) + logger.Info("Beam-centre hypothesis: the background places the centre {:.2f} px away " + "(+- {:.2f} px), past the {} px this ladder reaches - no centre it can try " + "is the beam, so it is not walked", + std::hypot(measured_beam_center_->beam_x_pxl - beam_x, + measured_beam_center_->beam_y_pxl - beam_y), + measured_beam_center_->sigma_pxl, reach); logger.Info("Beam-centre hypothesis: {}/{} validation frames at ({:.2f},{:.2f}); trying " "the centre +-{} px along each detector axis, a pixel at a time", best.score, static_cast(validation.size()), beam_x, beam_y, reach); @@ -3081,7 +3101,13 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b const int adopt_bar = static_cast(validation.size()) / 2; bool adopted = false; float won_x = beam_x, won_y = beam_y; - for (int k = 1; k <= reach && !cancelled_ && !adopted; k++) { + // The count is the only thing the ladder steers on, so a ladder that has not seen a THIRD + // of its own bar anywhere in two complete rings has nothing to steer by and is walking the + // noise floor. Measured over the beam-centre walks on the open battery: after two rings the + // two runs that went on to adopt stood at 17/60 and 59/60, and the three that adopted + // nothing stood at 4/60 or below - the two populations do not overlap. + int seen = best.score; + for (int k = 1; k <= reach && !cancelled_ && !adopted && !centre_past_reach; k++) { for (const auto &[dx, dy]: {std::pair{-k, 0}, {k, 0}, {0, -k}, {0, k}}) { const float trial_x = beam_x + static_cast(dx); const float trial_y = beam_y + static_cast(dy); @@ -3089,6 +3115,7 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b FirstPass alt = pick_best(*indexer_pool, *indexer); logger.Info("Beam-centre hypothesis ({:.2f},{:.2f}): {}/{} frames", trial_x, trial_y, alt.score, static_cast(validation.size())); + seen = std::max(seen, alt.score); if (alt.result.has_value() && alt.score > adopt_bar && alt.score > best.score) { best = alt; won_x = trial_x; @@ -3097,6 +3124,14 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b break; } } + if (k == 2 && !adopted && seen < adopt_bar / 3) { + logger.Info("Beam-centre hypothesis: the best of two rings is {}/{} against the " + "{}/{} a centre has to index to be adopted - the ladder is at the " + "count's noise floor and is stopped here", seen, + static_cast(validation.size()), adopt_bar + 1, + static_cast(validation.size())); + break; + } } experiment_.BeamX_pxl(won_x).BeamY_pxl(won_y); if (adopted)