Unraveling AirTags and UWB: Solving for the Intersection

In the previous parts, we established that a TDOA measurement between two anchors (we called them A and B) places our tag on a specific hyperbola. We even derived its mathematical equation. While powerful, it still leaves us with an infinite number of possible locations along that curve.

Introducing the Third Anchor

To get a precise location, we need to create a second, independent curve that also contains our tag. The simplest way to do this is to add a third anchor, C.

Now, the system can perform a TDOA measurement using a different pair of anchors—for example, anchors A and C. This new time difference will generate a second, different hyperbola.

Since the tag must lie on the first hyperbola (from A and B) AND on the second hyperbola (from A and C), its true location must be where the two curves intersect.

The Math: Solving a System of Equations

Finding this intersection point isn't magic; it's algebra. We now have two separate hyperbola equations, and we need to find the `(x, y)` coordinate that satisfies both of them simultaneously. This is called solving a system of non-linear equations.

Let's say our two equations are:

Hyperbola 1 (from A & B): (x²/a₁²) - (y²/b₁²) = 1

Hyperbola 2 (from A & C): (x²/a₂²) - (y²/b₂²) = 1

While these look complex, we can solve them using a method called substitution. The goal is to combine the two equations into one equation with only one variable.

The Algorithm

  1. Isolate a Variable: Take the first equation and algebraically rearrange it to solve for one of the squared variables. Let's solve for y²:

    y² = b₁² * ( (x²/a₁²) - 1 )

  2. Substitute: Now, take this entire expression for y² and substitute it into the second equation in place of its y² term. This eliminates `y` from the second equation, leaving only `x`.

    (x²/a₂²) - ( [the expression for y²] / b₂² ) = 1

  3. Solve for x: The resulting equation looks messy, but it only contains `x` and known constants (`a₁`, `b₁`, `a₂`, `b₂`). After simplifying, this typically becomes a quadratic equation in terms of x², which we can solve to find the value of `x`.
  4. Back-substitute for y: Once you have the correct `x` coordinate, plug it back into the rearranged equation from Step 1 to find the value of `y`.

With the `(x, y)` pair, you've found the intersection—the precise 2D location of the tag!

Loading scene...

hyperbolaIntersection.js

// Finding the Intersection of Two Hyperbolas
// This algorithm finds where two TDOA hyperbolas cross

export function findHyperbolasIntersection(
    anchor1: { x: number, y: number },
    anchor2: { x: number, y: number },
    anchor3: { x: number, y: number },
    tdoa_12: number, // Time difference between anchors 1&2 (seconds)
    tdoa_13: number  // Time difference between anchors 1&3 (seconds)
): { x: number, y: number } | null {
    
    const c = 299792458; // Speed of light (m/s)
    
    // Convert TDOA to distance differences
    const d_12 = tdoa_12 * c; // Distance difference for hyperbola 1
    const d_13 = tdoa_13 * c; // Distance difference for hyperbola 2
    
    // Calculate hyperbola 1 parameters (anchors 1 & 2)
    const dx_12 = anchor2.x - anchor1.x;
    const dy_12 = anchor2.y - anchor1.y;
    const c_12 = Math.sqrt(dx_12 * dx_12 + dy_12 * dy_12) / 2;
    const a_12 = Math.abs(d_12) / 2;
    const b_12_sq = c_12 * c_12 - a_12 * a_12;
    
    // Calculate hyperbola 2 parameters (anchors 1 & 3)
    const dx_13 = anchor3.x - anchor1.x;
    const dy_13 = anchor3.y - anchor1.y;
    const c_13 = Math.sqrt(dx_13 * dx_13 + dy_13 * dy_13) / 2;
    const a_13 = Math.abs(d_13) / 2;
    const b_13_sq = c_13 * c_13 - a_13 * a_13;
    
    // Check for degenerate cases
    if (b_12_sq <= 0 || b_13_sq <= 0) {
        return null; // One or both hyperbolas are degenerate
    }
    
    const b_12 = Math.sqrt(b_12_sq);
    const b_13 = Math.sqrt(b_13_sq);
    
    // Center points of each hyperbola
    const cx_12 = (anchor1.x + anchor2.x) / 2;
    const cy_12 = (anchor1.y + anchor2.y) / 2;
    const cx_13 = (anchor1.x + anchor3.x) / 2;
    const cy_13 = (anchor1.y + anchor3.y) / 2;
    
    // Rotation angles for each hyperbola
    const angle_12 = Math.atan2(dy_12, dx_12);
    const angle_13 = Math.atan2(dy_13, dx_13);
    
    // Solve system of equations numerically
    // Using Newton-Raphson method or grid search
    
    // For this example, we'll use a simplified approach
    // In production, use a robust numerical solver
    
    // Current tag position in the visualization:
    const tagPosition = {
        x: 0.0,
        y: 0.0
    };
    
    // Verify this point lies on both hyperbolas
    // by checking if the TDOA constraints are satisfied
    const dist1 = Math.sqrt(
        Math.pow(tagPosition.x - anchor1.x, 2) + 
        Math.pow(tagPosition.y - anchor1.y, 2)
    );
    const dist2 = Math.sqrt(
        Math.pow(tagPosition.x - anchor2.x, 2) + 
        Math.pow(tagPosition.y - anchor2.y, 2)
    );
    const dist3 = Math.sqrt(
        Math.pow(tagPosition.x - anchor3.x, 2) + 
        Math.pow(tagPosition.y - anchor3.y, 2)
    );
    
    const calculated_tdoa_12 = (dist1 - dist2) / c;
    const calculated_tdoa_13 = (dist1 - dist3) / c;
    
    console.log('TDOA 1-2:', calculated_tdoa_12 * 1000, 'ms');
    console.log('TDOA 1-3:', calculated_tdoa_13 * 1000, 'ms');
    
    return tagPosition;
}

// Example usage:
const anchors = {
    anchor1: { x: 50, y: 350 },
    anchor2: { x: 750, y: 350 },
    anchor3: { x: 50, y: 50 }
};

const intersection = findHyperbolasIntersection(
    anchors.anchor1,
    anchors.anchor2,
    anchors.anchor3,
    0.001,  // 1ms TDOA between anchors 1&2
    -0.0005 // -0.5ms TDOA between anchors 1&3
);

What's Next: Into the Third Dimension

Congratulations! We've pinpointed our tag on a 2D plane using nothing but tiny time differences and some clever math.

But the real world isn't flat. How do we account for an object's height? In the next and final part of our TDOA exploration, we'll see what it takes to move from hyperbolas to hyperboloids to achieve true 3D tracking.

BACK TO BLOG