Virtual anchorincidence point
Interactive note · multipath geometry for radio SLAM

One multipath measurement, two geometries: the virtual anchor and the incidence point

A reflected radio path hands you four numbers: a delay (a path length), an arrival angle at the UE, a departure angle at the BS, and a pathloss. Read the delay and angle one way — walk the full measured length along the arrival direction — and you get a virtual anchor: a fictitious, wall-agnostic transmitter that BP-SLAM treats as a static feature. Read them the other way — stop where the arrival ray crosses the delay ellipse whose foci are the BS and the UE — and you get the incidence point: the physical spot on the wall where the ray actually bounced. Same data, two maps.

Context. The inputs mirror the per-path estimates (delay, AoA/AoD, amplitude) produced by the SAGE estimator in the Gaussian_Splatting_Test pipeline. The virtual-anchor reading is the feature model of E. Leitinger et al., “A Belief Propagation Algorithm for Multipath-Based SLAM,” IEEE Trans. Wireless Commun. 18(12), 2019; the anchor-mirroring idea goes back to Channel-SLAM (Gentner et al., IEEE TWC 15(9), 2016). Scene and numbers here are illustrative (scale: 1 px ≙ 0.1 m).

01Measurement

For one multipath component the channel estimator (SAGE, ESPRIT, …) returns a tuple (τ, φ, ψ, PL). Each element has its own geometric meaning:

delay τ → a length L = cτ
angle φ (AoA at the UE)
angle ψ (AoD at the BS)
pathloss → a weight
Animated power-delay profile: estimated multipath components as stems of path gain versus delay, evolving as the receiver moves
A random power-delay profile

What are the factors, in addition to the distance, that affect the pathloss?

  1. Interaction loss at each bounce (dominant). Paths with the same travel distance span ~20 dB of excess loss. What differs is what they hit — which surface, how many times, specular vs. diffuse.
  2. Material and incidence angle, evidenced by polarization. On matched reflected paths, half of all matched pairs differ by more than 3 dB between polarizations.
  3. Small-scale / rough-surface effects.
  4. Blockage / shadowing. Some clusters vanish for whole stretches of frames in the delay–frame waterfall while the geometry barely changes.

02Filter view with known UE position

2.1Single bounce: draw everything from (τ, φ, ψ)

the data (drives the drawing)

layers

drag the green UE dot — sliders re-sync to its specular measurement
Read it. d₁ + d₂ = L puts P on the orange ellipse; the mirror makes ‖P→VA‖ = d₂, so the same ray, extended to the full measured length L, ends exactly at VA. The dark wall segment is drawn purely from (τ, φ): perpendicular bisector of BS↔VA, through P. The dashed orange rings mark the two foci (the pins) of the ellipse. Delay + angle cannot tell the two readings apart — you choose the representation. And already here the wall can be made dispersive: slide σ up and the single specular point diffuses into a weighted cloud of reflection points hugging the true wall (dot size/opacity ∝ pathloss weight), while the crisp VA smears into a cloud of phantom anchors — the tension §4 resolves.

2.2Double bounce: two virtual anchors, two incidence points

the data — path 1 (single bounce)

the data — path 2 (double bounce)

not an input — and not fixed by path 2’s (τ, φ, ψ) alone: locating P₂ needs VA¹, bootstrapped here from path 1. Given it, the AoA pins P₂ and L₁ = L₂ − ‖UE→P₂‖ follows.

construction — step

drag the green UE dot · σ = 0 recovers pure specular
Legend. ■ BS · ● UE (draggable) · ◆ purple = virtual anchors: VA¹ bootstrapped live from path 1, VA² (hollow = ground-truth reference, filled = from the data) · orange = path 1, the single-bounce path whose full-length walk supplies VA¹ · thin purple curve = the one-parameter family of VA¹ candidates that path 2’s data alone allows — the bootstrapped VA¹ must land on it, and does exactly when both paths’ data are clean · ● teal = incidence points P₁, P₂ estimated from the data, with the diffuse clouds on the true walls · dot size/opacity ∝ pathloss weight of that scattered sub-path · red ○ + dashed red segments = the single-bounce hypothesis drawn in full: its bounce point must absorb the whole measured length (the two red distances sum to L₂), forcing an implied wall beyond wall B that matches no wall in the map · green ellipse = the bounce-1 segment: leftover string L₂ − ‖UE→P₂‖ pinned at BS & P₂, passing through P₁ · dashed rings = the two foci (pins) of the same-colored ellipse: red at BS & UE, teal at VA¹ & UE, green at BS & P₂.

2.3Triple bounce: the recursion run in full

the data — path 1 (single bounce)

the data — path 2 (double bounce)

the data — path 3 (triple bounce)

not inputs — each peel subtracts one leg; the splits exist only once VA¹ and VA² do.

construction — step

drag the green UE dot · σ = 0 recovers pure specular
Legend. ■ BS · ● UE (draggable) · ◆ purple = the anchor ladder: VA¹ bootstrapped from path 1, VA² from path 2, VA³ (hollow = ground-truth reference, filled = the full-L₃ walk of the data) · orange = path 1 · gold = path 2 · red dashed = the two rejected hypotheses: the 1-bounce ellipse (string at BS & UE) with its split points P(φ) ≠ P(ψ), and the VA¹-anchored 2-bounce reading whose implied first-bounce wall fails to mirror BS onto VA¹ · teal ellipse = bounce 3, string L₃ at VA² & UE → P₃ · green ellipse = bounce 2, leftover string at VA¹ & P₃, its point picked not by data but by aiming from P₃ at VA² · blue ellipse = bounce 1, leftover string at BS & P₂, picked by the measured AoD → P₁ · dashed rings mark each ellipse’s foci (its pins) · ● teal dots = incidence points, with diffuse clouds on all three true walls in the last step · dot size/opacity ∝ pathloss weight.

2.4Double bounce, corridor edition: two parallel walls

the data — path 1 (single bounce)

the data — path 2 (double bounce)

construction — step

drag the green UE dot · σ = 0 recovers pure specular
Legend. Same as the corner demos above — dashed rings mark each ellipse's foci · red = rejected 1-bounce hypothesis with its implied wall · note the AoD arrow at the BS is exactly anti-parallel to the AoA arrow at the UE, and stays so while you drag: that lock is the parallel-wall signature.

2.5Triple bounce, corridor edition: parity, and a hypothesis that angles cannot kill

the data — path 1 (single bounce)

the data — path 2 (double bounce)

the data — path 3 (triple bounce)

construction — step

drag the green UE dot · σ = 0 recovers pure specular
Legend. Same conventions as the corner triple — orange = path 1, gold = path 2, teal / green / blue ellipses = bounces 3 / 2 / 1 with dashed rings at their foci, ◆ purple = the anchor ladder VA¹, VA², VA³ · the ghost dashed vertical = the image of wall L in mirror R: the 1-bounce hypothesis point sits exactly on it, at the mirror image of the true P₂, and the red ✗ where the BS ray pierces wall R (the true P₁) is the occlusion that rejects it · note the parity locks in the statline: odd paths mirror (ψ = −φ), even paths anti-parallel (ψ = φ ± 180°).

2.6Estimating the map: motion collapses the family

the unknown split

layers

drag pose a (green) · pose b is a second, fixed snapshot of the same static map · data at both poses is perfectly specular
Read it. Each purple curve is everything one pose can say about VA¹ from its own (τ, φ, ψ): a one-parameter family of coherent double-bounce explanations. The static-map constraint is that all poses’ curves pass through the same VA¹ — their crossing. Arrowheads give the direction of the family parameter — the last leg growing, L₁ shrinking — and the labels at pose a’s endpoints give the L₁ range. Orange = the bootstrap: the single-bounce path off wall A; extending its AoA ray to its own full measured length lands exactly on VA¹, no curves needed. The faint walls are ground truth, drawn only for reference; nothing in the construction uses them.

motion, step by step

each pose contributes one curve of VA¹ candidates; the second already collapses the family to a point — later poses just keep confirming it
Motion, quantified. The UE walks t₁ → t₄. One pose constrains VA¹ to a curve; the second pose’s curve intersects it in a point (teal ring, found numerically — not read off the ground truth); every further pose must pass through the same point. That over-consistency across motion is what makes VA¹ a reliable static feature to hang SLAM on.

03Optimization view with unknown UE position

One quiet assumption still runs through §3.1–3.5: delays are true ranges (synchronized clocks). The other classic assumption — that array orientations are known, so ψ and φ can be drawn as global directions — is only half fair. The BS earns it: infrastructure knows which way it faces. The UE does not: its heading is one more unknown per pose, so every demo below carries a heading-hypothesis slider δ that rotates all arrival rays by a common guess. Watch what it does. In the corner scenes every construction stays perfectly coherent for every δ — the one-parameter slide becomes a two-parameter family (P⁽¹⁾, δ), and §3.1’s candidate line fattens into the whole delay disk. In the corridor scenes a wrong δ wedges the recovered walls out of parallel, yet the wedge still closes at the sliding UE₁ — heading error is indistinguishable from wall tilt. Angles measured without a compass buy geometry only relative to that compass. §3.6 draws the consequence: it never uses arrival angles to build geometry at all — wall directions come from motion, offsets from the one link that crosses the families, and each pose’s heading is read off last, from the recovered geometry.

3.1Single bounce: draw everything from (τ, φ, ψ)

the data (drives the drawing)

construction — step

the wall and the heading are the unknowns: P and δ are both hypotheses — the green UE(P) is the estimated UE, not the truth
Read it. E = BS + L·d̂ is the full measured length walked along the AoD — the mirror image of the UE across the unknown wall, the exact dual of §2.1's virtual-anchor walk (there: UE-side walk lands on the mirrored BS; here: BS-side walk lands on the mirrored UE). The foci change with it: the delay string, un-pinnable at the unknown UE, is re-pinned at P and E — a bounce hypothesis P on the AoD ray keeps ‖P→E‖ = L − ‖BS→P‖, so every point of the orange circle is at the correct leftover distance, and the candidate UE(P) is where the reverse-AoA ray out of P crosses it. The implied wall runs along the middle between P→UE(P) and P→E — the bisector at P, equivalently the perpendicular bisector of E↔UE(P) — and stays parallel to the true wall for every P. Slide P: finding P is really finding the wall — each P fixes the wall (the bisector through it) and the wall fixes the UE, so every position is a fully coherent (wall, UE) explanation, and the candidates sweep a straight line perpendicular to the true wall (step ⑥). But that line was drawn with a borrowed compass: the AoA is measured in the UE's own frame, and promoting it to a global direction spends a heading the UE does not know. Slide δ — the heading hypothesis — and the whole line pivots, every point still coherent, the implied wall tilting by δ/2 as it goes: heading error and wall tilt are indistinguishable from one path. With both P and δ free the candidates fill the entire delay disk ‖x − BS‖ ≤ L: one path leaves a two-parameter family, and the AoA, on its own, localizes nothing. What breaks it is a second path off a differently-oriented wall — the next subsections.

3.2Double bounce: two virtual anchors, two incidence points

the data — path 1 (single bounce)

the data — path 2 (double bounce)

construction — step

both walls and the heading are unknowns: P⁽¹⁾ is the wall-A hypothesis, P₂ the wall-B hypothesis, δ the heading hypothesis — the green dots are estimated UEs, never the truth
Read it. Path 1 runs §3.1 verbatim: E⁽¹⁾ = BS + L⁽¹⁾·d̂⁽¹⁾ is the mirrored UE, P⁽¹⁾ hypothesizes the bounce, UE₁ sits the leftover distance down the reverse-AoA ray, and the candidates sweep line 1 ⊥ wall A — finding P⁽¹⁾ is finding wall A. That hypothesis is what path 2 was waiting for: its AoD ray crosses the hypothesized wall A at P₁, reflects there, and the changed focus climbs the ladder — the string re-pins at P₁, the full leftover walk lands on E₂ (the sub-problem's mirrored UE), P₂ hypothesizes the second bounce, and the UE₂ candidates sweep line 2 ⊥ wall B. The UE must lie on both lines, so the crossing solves P₂ and wall B follows. But slide P⁽¹⁾ and watch: the crossing lands exactly on UE₁ for every hypothesis — the whole explanation (wall A, wall B, UE) slides as one rigid family. Slide δ and watch again: both arrival rays rotate together (one UE, one heading), and the crossing still tracks UE₁ exactly — the family gains a second parameter instead of losing its first. Two paths that share wall A leave a two-parameter ambiguity (P⁽¹⁾, δ): the double bounce buys wall B, never the fix and never the heading. What breaks it is an independent last wall — a single-bounce path off wall B, a LOS path, or a known anchor — the subject of the corridor subsections.

3.3Triple bounce: the recursion run in full

the data — path 3 (triple bounce)

construction — step

paths 1 and 2 enter at their specular values; all three walls and the heading are hypotheses driven by the two sliders — the green dots are estimated UEs, never the truth
Read it. The ladder of §3.1–§3.2, run one rung further. Path 1 hypothesizes wall A (P⁽¹⁾, line 1 ⊥ A); path 2, stripped at wall A, is solved by its crossing with line 1 — wall B follows. Path 3 then strips twice: its AoD ray reflects at the hypothesized wall A, again at the hypothesized wall B, and the leftover walk lands on E₃, the sub-sub-problem's mirrored UE; its candidates sweep line 3 ⊥ wall C, and the crossing solves the last bounce — wall C follows. But the statline tells the honest ending: every crossing lands exactly on UE₁ for every P⁽¹⁾ — and for every heading hypothesis δ. Order after order, the recursion buys the next wall and never the fix — the family survives the full ladder with two free parameters (P⁽¹⁾, δ), because every path in this scene starts on wall A. Only an independent last wall (or LOS) breaks it.

3.4Double bounce, corridor edition: two parallel walls

the data — path 2 (double bounce)

construction — step

path 1 enters at its specular values; the corridor's two walls and the heading are the unknowns — the green dots are estimated UEs, never the truth
Read it. The corner scene at least produced a crossing; the corridor cannot. Path 1 hypothesizes wall R exactly as in §3.1 — candidates sweep line 1 ⊥ wall R, which in a corridor means across the corridor. Path 2, stripped at that hypothesis, produces its own candidate line ⊥ wall L — but wall L is parallel to wall R, so line 2 is parallel to line 1, and with clean data it coincides with it exactly: the statline prints the angle between them and the offset, both zero for every P⁽¹⁾. There is no crossing to solve; wall L is fixed only by declaring UE₂ = UE₁ on the shared line. The heading slider adds the corridor's own twist: a wrong δ tilts both wall hypotheses out of parallel, the lines wedge apart by δ/2 and now cross — but exactly at the sliding UE₁, so the "crossing" certifies nothing: heading error just re-dresses the corridor as a slightly wedged one. One slider still slides the whole explanation — both walls and the UE together — and δ slides a second one. In a corridor the double bounce adds no UE information whatsoever: the cross-corridor offset stays free, and no bounce order will change that (next demo).

3.5Triple bounce, corridor edition: parity, and a hypothesis that angles cannot kill

the data — path 3 (triple bounce)

construction — step

paths 1 and 2 enter at their specular values; every wall and the heading are hypotheses driven by the two sliders
Read it. The full corridor ladder: wall R from path 1 (§3.1), wall L by the forced declaration UE₂ = UE₁ (§3.4), and now path 3 stripped twice — its AoD ray reflecting at hypothesized wall R, then at hypothesized wall L, the leftover walk landing on E₃. Its candidates sweep line 3 ⊥ wall R again — and the statline shows the parity ending: line 3 coincides with line 1 exactly, for every P⁽¹⁾. Odd or even, every bounce order in a corridor confines the UE to the same cross-corridor line: the along-corridor coordinate is fixed by any one path, the cross-corridor offset by none of them. The heading slider repeats §3.4's lesson two rungs up: a wrong δ wedges line 3 off line 1 by δ/2, the wedge closing exactly at the sliding UE₁ — heading error keeps masquerading as wall tilt at every order. No amount of bouncing between parallel walls ever breaks the slide — an off-axis wall, a LOS path, or an anchor does. §3.6 runs both verdicts through the joint, all-unknowns problem.

3.6Estimating jointly: motion collapses the family — the optimization view

§2.6 collapsed the VA family by motion with known poses. Here nothing is known: not the walls, not the trajectory, not even which way the UE is facing — only each path's delay and BS-side departure angle, the UE's local arrival angles, and the relative motion between poses (odometry). This is the graphSLAM setting: every unknown enters one joint problem. Without a heading, §3.1's bisector is out of reach, and a single snapshot leaves a two-parameter family per wall — direction and offset. Motion pays that bill in two installments: odometry within a family buys the wall's direction (and throws in a parity check for free); the one link across the families buys both offsets — exactly when the walls disagree in orientation.

the unknown split — two families

layers

poses t₁–t₂ resolve only the wall-A path, t₃–t₄ only wall-B — no pose knows its heading; the gap at the t₂→t₃ link is all that knows the truth
Read it. Four poses, disjoint visibility: t₁–t₂ hear a single bounce off wall A, t₃–t₄ only off wall B — and no pose knows its own heading, so nothing here ever uses an arrival angle as a global direction. What survives is the BS side: E = BS + L·d̂ needs only the departure angle, and each E is still the exact mirror of its pose across the unseen wall. But one snapshot alone now says nothing about position at all: any candidate p pairs with the wall = perpendicular bisector of p↔E and a heading to match — a two-parameter family. The first installment comes from motion inside a side: both poses mirror across the same wall, so p₂ − p₁ is E₂ − E₁ reflected in the wall — the offset drops out entirely. Matching that to the known o₁ checks ‖o₁‖ = ‖E₂ − E₁‖ (reflections preserve length: a free parity test on the data) and hands the wall normal in closed form, n̂ ∝ (E₂ − E₁) − o₁. Decisive about the angle, still blind to the offset — that is why the rails exist but the sliders remain. The second installment is the one row crossing the families: p₃ − p₂ = o₂ gives two equations in (c_A, c_B) with matrix [−2n̂_A | 2n̂_B], invertible exactly when the normals differ. The red gap is that row's residual; solve zeroes it and recovers trajectory and walls from snapshots that were, individually, hopeless — and only now are the arrival angles spent, one per pose, reading each heading off the recovered geometry. Under jitter the recovered angles hang on the short o₁, o₃ baselines — the price of the missing compass; more poses per wall would buy it back. This is the smallest instance of the batch problem: the full graphSLAM system just stacks rows like these (and the same counting at a single pose — two unknowns per wall, one heading, two equations per extra path — is why three NLOS paths make one snapshot solvable, the classic single-anchor result).

corridor edition — the family that survives the graph

layers

same construction, parallel walls — the solve returns a line of answers, not a point; the third slider walks it
Read it. The identical machinery in a corridor: t₁–t₂ hear wall R, t₃–t₄ wall L, headings unknown. Motion buys the directions exactly as before — n̂ ∝ ΔE − o, no compass consulted — and both come out parallel, pointing across the corridor. So the one informative row, [−2n̂_R | 2n̂_L], is rank one: the along-corridor component of the o₂ link is satisfied automatically, and the cross-corridor component fixes only the sum of the offsets, never the pair. Solve therefore returns σ = [·, 0]: §3.2's least-squares crossing has become a least-squares line, its direction the null vector — walls and trajectory translating across the corridor as one rigid ghost. This is §3.4–3.5's slide surviving the full batch treatment, headings and all: optimization does not conjure information, it detects what is missing. That detection is the useful product — σ_min of the stacked system is the honest observability monitor to run online, and the moment any off-axis wall, LOS interval, or second anchor enters the graph, the same monitor shows the null direction snapping shut.

04Why the diffuse case decides the representation

With smooth walls the two readings are equivalent bookkeeping. Rough walls break the tie. A diffuse surface returns energy from a whole patch, not a point: the measurement becomes a family of slightly different (τ, φ, ψ, PL) tuples. Map those into incidence points and you get a weighted point cloud hugging the physical wall — each point carrying its pathloss as a weight. Exactly the kind of data a splat-style surface representation wants (this is what the point-cloud view in Gaussian_Splatting_Test exports, and what the SAGE bistatic triangulation estimates). Map the same family into virtual anchors and the crisp mirror-image anchor smears into a blob that no longer behaves like a point feature — the VA cloud in the demo above.

Virtual anchor (BP-SLAM)Incidence point (this pipeline)
constructionwalk the full length L along the AoA raystop where the AoA ray crosses the delay ellipse (foci BS & UE; for bounce n ≥ 2: foci VAⁿ⁻¹ & UE)
needs walls?no — wall-agnostic, that is its superpowerno wall model either, but multi-bounce needs the previous-order VA (a hypothesis about the bounce sequence)
as the UE movesfixed — behaves like a static LOS anchor, ideal as a SLAM feature with BP data associationslides along the surface — successive positions paint the wall
double bouncestill one clean point: VA² = mirror(mirror(BS, A), B)two points P₁, P₂; naive BS–UE ellipse fails, VA¹–UE ellipse fixes it
triple bouncestill one clean point: VA³ = mirror³(BS) — the full-length walk lands on it regardless of bounce orderthree points P₁, P₂, P₃; the end bounces are picked by the measured AoA/AoD, the middle bounce only by map (aim at VA²) — order n leaves n−2 map-picked bounces
diffuse wallsanchor smears into a cloud — point-feature model degradescloud lands on the wall — weighted by pathloss, it is a surface sample: a splat
natural outputanchor map + UE trajectory (positioning)surface point cloud (environment mapping / Gaussian splatting)
The one-liner. The VA answers “where do I hear a phantom transmitter?” — great for localizing the UE. The incidence point answers “where is the physical wall?” — great for mapping. Delay + angle feed both; pathloss weights both.