1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//! Shared per-bit soft-decision math for the self-identifying decoders.
//!
//! The ChArUco board matcher
//! (`calib-targets-charuco`) and the PuzzleBoard edge-code decoder
//! (`calib-targets-puzzleboard`) both score observed bits against an
//! expected pattern with a numerically-stable `log(sigmoid(·))` of a
//! per-bit logit. That base transfer function used to be copy-pasted into
//! both crates; it lives here so the two decoders share one definition and
//! cannot drift.
//!
//! Only the transfer function itself is shared. Each decoder keeps its own
//! logit construction and per-bit flooring policy, because those differ:
//! the PuzzleBoard decoder floors a `kappa * confidence` logit symmetrically
//! (see its `ll_pair`), while the ChArUco matcher floors an intensity-margin
//! logit and, in its diagnostic path, does not floor at all.
/// Numerically stable `log(sigmoid(x))`.
///
/// Evaluates `ln(1 / (1 + e^-x))` without overflow for large-magnitude `x`
/// by branching on the sign:
///
/// - `x ≥ 0`: `-ln(1 + e^-x)` (the `e^-x` term is in `(0, 1]`).
/// - `x < 0`: `x - ln(1 + e^x)` (the `e^x` term is in `(0, 1)`).
///
/// Both branches are pure `f32` arithmetic, so the result is deterministic
/// and bit-for-bit identical across the decoders that call it.