Facts checked on 16 October 2026

Short answer

Both phone platforms now support UWB downlink TDoA (DL-TDoA): anchors exchange timestamped messages, and the phone only listens and computes its own position. iOS 27 (released 14 September 2026) removed the entitlement that iOS 26 required, and Android 17 (16 June 2026) added the API at level 37.

Anchors must run FiRa DL-TDoA over IEEE 802.15.4z with wireless synchronisation, carry their coordinates and be discoverable over Bluetooth LE or Wi-Fi. Qorvo staff recommend the QM35825 for such anchors. The app, not the phone or Qorvo, computes the position.

How DL-TDoA works

DL-TDoA cluster: an initiator anchor and responder anchors exchange messages, the phone only listens, and the app computes the phone's position.

In DL-TDoA, anchors at known positions exchange UWB messages in a fixed schedule. A cluster has one initiator anchor and several responders. The initiator sends a poll, responders answer, and every message carries transmit timestamps in the cluster’s common time. A phone overhears these messages, measures when each one arrives and calculates its position from the time differences.

FiRa calls this “Untracked Indoor Navigation”: “no message is transmitted by user devices” and “the location of the user device is determined by the user device itself”. It entered the FiRa 2.0 specifications in November 2023 and became certifiable in December 2023. Because the phone never transmits, the infrastructure cannot track it, and any number of phones can navigate at once.

One ranging round, message by message

Qorvo’s QM35 documentation calls the anchors’ messages DTMs and draws them as SP1 frames, which carry a payload. A cluster’s common time is the initiator’s local clock. With DS-TWR between anchors, one round looks like this:

MessageSent byContent
Poll DTMInitiatorIts transmit timestamp (this defines common time) and the responder schedule
Response DTMEach responder, in its slotTransmit timestamp in common time, reply time, time of flight to the initiator
Final DTMInitiator (DS-TWR only)Transmit timestamp and its reply time for each responder

Any DTM can also carry the sender’s coordinates and its list of active rounds. SS-TWR drops the Final DTM, and responders then take the clock frequency offset (CFO) from their receiver instead of computing it from the Poll and Final timestamps, which Qorvo calls more accurate. Qorvo’s iPhone-verified Python example uses DS-TWR.

A responder’s timestamps become usable one block after it first hears a Poll. Until it knows its time of flight and CFO, it sends local-time timestamps and flags them; Android exposes the flag as isTxTimestampInCommonTimeBase() and states that such a timestamp “cannot be used for localization”.

From timestamps to a position

The phone’s clock is not synchronised with the anchors, but its unknown offset to the cluster’s common time is the same for every anchor and cancels in a difference. Qorvo’s documentation gives the range difference between anchors i and j as:

D_i - D_j = c * ((Trx_i - Trx_j) - (Ttx_i - Ttx_j))

Trx = receive timestamp in the phone's local time
Ttx = transmit timestamp in the cluster's common time
c   = speed of light

Each range difference defines a hyperbola; Qorvo’s illustration uses three anchors for a 2D fix, and a 3D fix needs at least four. Besides pairing messages only within one cluster, the solver has to do two things:

  • Correct the clock rate. FiRa requires a ±15 ppm clock for timing round trips and replies, so phone and initiator may differ by 30 ppm: over a 2 ms gap between DTMs that is 60 ns, about 18 m of range difference. Use the offsets both APIs report (carrierFrequencyOffset and responderClockFrequencyOffset on iOS, getCfo() and getAnchorCfo() in ppm on Android), or estimate the ratio from the Poll and Final timestamps as Qorvo’s responders do.
  • Handle the timestamp width. One ranging tick is about 15.65 ps, or 4.7 mm of light travel. A 40-bit counter wraps after about 17.2 s, so unwrap it; Qorvo’s sample requests 64-bit common-time timestamps through DL_TDOA_TX_TIMESTAMP_CONF.

What changed on the phones

DL-TDoA timeline: FiRa 2.0 specifications (Nov 2023), certification (Dec 2023), QM35825 FiRa Core 3.0 certification (Jul 2025), iOS 26 API with entitlement, Android 17 (16 Jun 2026) and iOS 27 without entitlement (14 Sep 2026).
iPhoneAndroid
First APIiOS 26.0, with an entitlementAndroid 17, API level 37
NowiOS 27: “DL-TDOA no longer requires an entitlement”Android 17 released 16 June 2026
Main classesNIDLTDOAConfiguration, NIDLTDOAMeasurement, session(_:didUpdateDLTDOA:)DlTdoaRangingParams, RawDtTagRangingConfig, DlTdoaMeasurement, onDlTdoaResults
Capability checksupportsDLTDOAMeasurement at runtime; no published model listUwbRangingCapabilities.isDlTdoaSupported()
Anchor standard named“comply with the IEEE 802.15.4z standard”FiRa: release notes say “Supports FiRA 4.0 for privacy-preserving indoor navigation”
Anchor discoveryWi-Fi or Bluetooth LE; Bluetooth LE is the default (iOS 27)Parameters built from a FiRa out-of-band configuration packet received over Bluetooth LE or Wi-Fi
PositionThe app computes it from per-anchor measurementsThe app computes it from DL-TDoA measurements
PermissionsLocation authorization; background start for up to 30 s from iOS 27.2 with Always authorizationFine location, plus background location for background use

Two details matter for anchor makers. On iPhone, the app’s network identifier “corresponds to the session ID configured in the DL-TDOA anchors”, and anchors with the same session ID form one tracked area that can span several clusters. On Android, DlTdoaRangingParams accepts slot durations of 1 ms or 2 ms only, so anchor schedules must use one of those values.

iOS 27 also added fields that only make sense with well-behaved anchors: the cluster initiator address, the responder clock frequency offset and the floor elevation. Apple notes that “drift between the clocks of anchors represents a persistent issue in DL-TDOA deployments that the responder clock frequency offset helps mitigate”.

Why the QM35825 for anchors

Qorvo staff wrote in September 2026 that “the QM35825 is the recommendation for developing a FiRa DT-Anchor or DT-Tag” and that the SDK example, the Python run_fira_dt script, “has been verified with iPhones though Qorvo is not providing an app for this”. Qorvo’s DW3000 and QM33 products are different: staff state there is no FiRa DT-Tag support on them and that the QM33 SDK does not support TDoA use cases.

What the QM35825 brings:

  • FiRa certification. FiRa lists the QM35825 as Core 3.0 certified (July 2025) for OWR DL-TDoA Anchor, Tag, Hop count, Final DTM, transmit timestamps in common time and Block Skipping.
  • In-band synchronisation. Qorvo describes synchronisation “done wirelessly via the DTMs”: one anchor is the reference initiator, and responders sync to its local time.
  • Host control. The QM35825 runs Qorvo’s pre-compiled firmware. Your anchor application runs on a host and controls it over UCI, for example through the Cherry C API in the QM35 SDK.
  • Scheduling isolated from the host. Qorvo staff confirmed that Bluetooth LE activity on the host cannot disturb UWB round scheduling, since the UWB stack runs on the QM35825.

And what it leaves to you:

  • No position solver. The SDK documentation states that multilateration “has to be implemented in the host application”, here meaning the phone app.
  • No custom sync. Qorvo staff note that the QM35825 firmware is closed, so “you do not have the ability to modify the software to develop a synchronization scheme”.
  • No module. “There is not a module offering for the QM35825”, so you design the anchor board.

One open question needs a direct answer from Qorvo for Android projects. Android 17 states FiRa 4.0 DL-TDoA support, while the QM35825’s FiRa certification is Core 3.0. FiRa’s list for a Core 4.0 device from another vendor includes “OWR DL-TDoA Measurement NTF V2” and “Slot Duration 1 MS”, which the QM35825 entry does not. The QM35 SDK has a v2 mode for anchor location and measurement notifications, so test the combination with your target Android phones early.

What anchors must support

Requirements for DL-TDoA anchors serving iPhone (iOS 27) and Android 17 phones, and how the QM35825 and QM35 SDK meet them.
RequirementWhy it mattersQM35825 and QM35 SDK
IEEE 802.15.4z HRP UWBApple names it as the anchor standardSupported
FiRa DL-TDoA messages (poll, response, final)Both phone APIs report these message typesCertified features, FiRa Core 3.0
Wireless sync with a time referencePhones compare timestamps across anchorsIn-band sync; at least one anchor set as time reference
Anchor coordinates in the messagesThe app needs anchor positionsWGS-84, relative, floor and height options in the Cherry API
A session ID per tracked areaiOS uses it as the network identifierSet per session
Slot duration of 1 ms or 2 msAndroid accepts only these valuesSample defaults use 2 ms (tools) and 1 ms (Cherry)
Discovery over Bluetooth LE or Wi-FiPhones find anchors and load configuration this wayYour host’s job: the QM35825 is UWB only
Measurement and location format for FiRa 4.0Android 17 targets FiRa 4.0v2 options in the SDK; confirm interoperability with Qorvo

Plan cluster sizes with Qorvo’s documented limit in mind: “the total number of distinct addresses one anchor can communicate with across all rounds is 8”. Large venues therefore need several clusters, linked through hop count or role swapping, and every anchor and phone must share the same slot duration, slots per round and ranging duration.

Anchor coordinates in the DTMs

Positions are “provided in the DTMs or via OOB method”, in Qorvo’s words; for the DTMs, set the location presence flag on every anchor. The three stacks describe them as follows:

  • QM35 SDK. DL_TDOA_ANCHOR_LOCATION carries the presence flag, the coordinate type (0 for WGS-84, 1 for relative) and x, y, z. In v2 mode, DL_TDOA_ANCHOR_LOCATION_V2 adds types with a Z-element: floor number, a “planned to be moved” flag, height above floor and its uncertainty. The Cherry API takes relative coordinates as signed millimetres, about ±134 km for x and y and ±8.4 km for z.
  • iOS. coordinatesType is geodetic (latitude, longitude, altitude) or relative (a 3D Cartesian triplet). From iOS 27, floorElevation gives floorNumber, negative below ground, and height above the floor in metres.
  • Android. getAnchorLocation() returns WGS-84 or relative coordinates, each optionally with a Z-element. Relative values are in millimetres, and ZElementExtension gives floor number, height above floor, its uncertainty and whether the anchor is expected to move.

Configuring QM35825 anchors

The anchor application configures each DL-TDoA session over UCI, directly or through Cherry. Qorvo’s documentation requires consistent values across all anchors and tags, in particular SLOT_DURATION, SLOTS_PER_RR and RANGING_DURATION.

Session parameters

Illustrative UCI sequence for the time-reference initiator of Qorvo’s run_fira_dt example, with names from Qorvo’s UCI specification and the script’s defaults:

SESSION_INIT_CMD                session ID 42 (iOS: networkIdentifier)
SESSION_SET_APP_CONFIG_CMD
  DEVICE_ROLE                   0x07    DT-Anchor (DT-Tag is 0x08)
  RANGING_ROUND_USAGE           0x05    OWR DL-TDoA
  MULTI_NODE_MODE               0x01    one-to-many
  DEVICE_MAC_ADDRESS            0x00A1
  CHANNEL_NUMBER                9
  PREAMBLE_CODE_INDEX           10
  RFRAME_CONFIG                 0x01    SP1 (the UCI default is SP3)
  STS_CONFIG                    0x00    static STS
  VENDOR_ID, STATIC_STS_IV      07 08, 01 02 03 04 05 06 (same on all devices)
  SLOT_DURATION                 2400    RSTU, i.e. 2 ms
  SLOTS_PER_RR                  25
  RANGING_DURATION              200     ms
  DL_TDOA_RANGING_METHOD        0x01    DS-TWR, adds the Final DTM
  DL_TDOA_TX_TIMESTAMP_CONF     0x03    common time base, 64-bit
  DL_TDOA_TIME_REFERENCE_ANCHOR 1       reference anchor only
  DL_TDOA_ANCHOR_LOCATION       presence 1, type 1 (relative), x, y, z
SESSION_UPDATE_DT_ANCHOR_RANGING_ROUNDS_CMD
  round 0: initiator, responders 0x00A2, 0x00A3
SESSION_START_CMD               -> SESSION_INFO_NTF every RANGING_DURATION

A responder uses the same values with its own address, a responder role and no time-reference flag, which the script refuses on an anchor that never initiates. Cherry’s cherry_fira_session_create_dt_anchor() takes the same essentials, with the slot duration in RSTU (1200 RSTU is 1 ms) and at most 8 responders per round. Qorvo’s Cherry guide adds: “Only FiRa DL-TDoA accepts Static STS, the application enables it by default.”

ChoiceDefault toAlternative, and when
Anchor ranging methodDS-TWR (Final DTM, CFO from timestamps)SS-TWR saves one slot per round
Slot duration2 ms1 ms if the rounds do not fit; test every phone
CoordinatesRelative, one building frame (the only system from which Qorvo’s responders derive time of flight)WGS-84 to merge with outdoor maps
Linking clustersRole swapping (chain or tree)Hop count when anchors have several sync paths

Bring-up order with pass/fail checks

  1. Start the time-reference initiator first. Cherry’s header states that other devices cannot start their sessions until the root initiator, the initiator of round 0, is running. Pass: its session reports CHERRY_FIRA_SESSION_STATE_ACTIVE.
  2. Add responders one at a time. A non-reference anchor stays silent until it hears a Poll DTM in one of its responder rounds and takes the block and round index from it. Pass: both anchors report Twr measurements with status Ok, as in Qorvo’s three-device example.
  3. Add a reference tag, a development kit in the DT-Tag role. Pass: OwrDltdoa measurements with status Ok for every anchor; a silent responder appears as RangingRxTimeout (0x21).
  4. Check the time base. After the first block, every Response DTM must report common time (common_time_base in Cherry tag reports).
  5. Check coordinates. Each anchor’s location, as reported to the reference tag, must match the survey file to the millimetre.
  6. Only then add phones, one platform at a time.

Hosting the anchor application

Qorvo’s examples run on Linux (OpenWrt in the guide), where Cherry opens /dev/uci0. For a microcontroller host, the SDK’s Zephyr platform targets the nRF52840 (DK-06), with an nRF54L15 PDK overlay as well, and reaches the QM35825 over Qorvo’s high-speed SPI:

/* boards/nrf52840dk_nrf52840.overlay */
&spi3 {
    qm35: hsspi@0 {
        status = "okay";
        compatible = "qorvo,hsspi";
        reg = <0>;
        spi-max-frequency = <5000000>;
        ss-irq-gpios = <&gpio1 10 0>;
        reset-gpios = <&gpio1 8 0>;
        exton-gpios = <&gpio1 6 0>;
    };
};

# prj.conf
CONFIG_HSSPI=y
CONFIG_UCI_TRANSPORT_USE_QMUTILS=y
CONFIG_CHERRY_EXAMPLES_ZEPHYR=y
CONFIG_CHERRY_EXAMPLES_GETOPT_SUPPORT=y
CONFIG_SHELL=y

The sample configuration enables no Bluetooth, so a deployable system must add discovery: Bluetooth LE or Wi-Fi carrying the FiRa out-of-band configuration packet that Android parses with createFromFiraConfigPacket().

Planning clusters and synchronisation between clusters

Blocks, rounds and update rate

A slot carries one message, a round is one cluster’s exchange of SLOTS_PER_RR slots, and a block of RANGING_DURATION holds the rounds, one round index per cluster. Three numbers follow:

  • Slots per round. One for the Poll, one per responder and one for the Final with DS-TWR, so eight responders need at least 10 slots. Both Qorvo samples use 25.
  • Rounds per block = RANGING_DURATION / (SLOTS_PER_RR × slot duration). Qorvo’s Python defaults (200 ms, 25 slots, 2 ms) give 4 rounds, and the Cherry defaults with 1 ms slots give 8. Cherry’s predefined six-anchor configuration uses round indexes up to 9, so the sample raises the interval to 250 ms.
  • Update rate. Each round repeats once per block, so a phone gets one set of DTMs per cluster every RANGING_DURATION: 5 Hz at 200 ms and 4 Hz at Android’s builder default of 250 ms.

Linking clusters: role swapping or hop count

Every cluster must be chained to the reference. Without hop count, a non-reference cluster’s initiator also responds in an earlier cluster, as in this six-anchor, three-cluster example from Qorvo’s Cherry guide:

anchor1: cherry-dl-tdoa-app -a 12 -R '0,init,13/14' -T
anchor2: cherry-dl-tdoa-app -a 13 -R '0,respf'
anchor3: cherry-dl-tdoa-app -a 14 -R '0,respf 1,init,15/16'
anchor4: cherry-dl-tdoa-app -a 15 -R '1,respf'
anchor5: cherry-dl-tdoa-app -a 16 -R '1,respf 2,init,17'
anchor6: cherry-dl-tdoa-app -a 17 -R '2,respf'
tag:     cherry-dl-tdoa-app -t -a 24 -I '0 1 2'

Anchor 14 responds in round 0 and initiates round 1, and anchor 16 bridges rounds 1 and 2. Such a tree has one path to the reference, so if an intermediate anchor stops, the sub-tree behind it loses synchronisation.

With DL_TDOA_HOP_COUNT enabled, the Poll DTM carries a hop count, 0 at the reference and one more per initiator downstream. Anchors accept synchronisation only from a lower hop count than the last one they acknowledged. This prevents loops in meshed layouts and, by design, makes the whole network fail when the reference anchor is lost, so give it reliable power and monitoring.

A non-reference anchor declares synchronisation lost when k × 30 ppm > block duration × 100 ppm, where k is the time since its last Poll DTM: about 3.3 blocks, or 0.67 s with 200 ms blocks. It then reacquires.

Time bases across clusters

A shared schedule is not a shared time base: each cluster’s common time is its initiator’s clock, so group measurements by clusterInitiatorAddress on iOS or by round index on Android. Android 17 also reports an optional supercluster ID, whose common time base “is valid for Initiator DT-Anchors using the same Supercluster ID”. Qorvo’s UCI specification lists a vendor parameter, DL_TDOA_CROSS_CLUSTER_TIME_SYNC, enabled by default but not described; confirm its behaviour with Qorvo before computing TDoA across clusters.

Installation: where accuracy is won or lost

The phone trusts the coordinates the anchors broadcast. An anchor recorded 20 cm off its real position shifts every fix near it. Four practices help:

  1. Survey or auto-position the anchors, and store coordinates in one managed configuration, not by hand on each device.
  2. Design geometry per area: surround the navigable space with anchors, vary mounting heights where floors and ramps matter, and avoid long thin clusters.
  3. Separate floors explicitly. iOS 27 reports floor elevation, so set floor numbers and heights in the anchor configuration.
  4. Monitor sync and health after installation: lost anchors, drift outliers and battery or power problems.

Coordinates also feed synchronisation: with relative coordinates, Qorvo’s firmware derives each responder’s time of flight from them, ignoring the Poll DTM’s value, so a survey error also biases that responder’s common-time timestamps. Check the survey independently before go-live.

Accuracy figures published so far are vendor claims, not independent results. Estimote wrote in September 2026 that its “typical deployments deliver around 20–30 cm positioning accuracy”. A FiRa-listed prototyping kit from Pinpoint, built on the DWM3001C, quotes “up to 30 cm accuracy” over areas of up to 500 m² in line of sight.

Phone-side code for iOS 27 and Android 17

Both snippets are illustrative, use only documented API names and leave the solver to you.

iPhone

Illustrative, iOS 27:

import NearbyInteraction

final class DLTDOAListener: NSObject, NISessionDelegate {
    private let session = NISession()
    var onCluster: (([NIDLTDOAMeasurement]) -> Void)?

    func start(networkID: Int) {                 // = anchors' session ID
        guard NISession.deviceCapabilities.supportsDLTDOAMeasurement else { return }
        session.delegate = self
        let config = NIDLTDOAConfiguration(networkIdentifier: networkID,
                                           discoveryMethod: .bluetoothLowEnergy)
        session.run(config)
    }

    func session(_ session: NISession,
                 didUpdateDLTDOA measurements: [NIDLTDOAMeasurement]) {
        // one common time base per cluster: never pair across clusters
        let clusters = Dictionary(grouping: measurements) { $0.clusterInitiatorAddress }
        for (_, m) in clusters where Set(m.map(\.address)).count >= 3 {
            onCluster?(m)            // coordinates, raw times, CFO fields
        }
    }

    func session(_ session: NISession, didInvalidateWith error: Error) {
        // e.g. NIError.Code.resourceUsageTimeout: no anchor within 30 s in background
    }
}

The discovery-method initialiser and clusterInitiatorAddress need iOS 27. Background starts need iOS 27.2, Always authorization and the Background Modes capability with “Uses Nearby Interaction”.

Android

Illustrative, Android 17 (API level 37):

// Lab test against QM35825 anchors running run_fira_dt defaults
val params = DlTdoaRangingParams.Builder(42)            // anchors' session ID
    .setSlotDuration(UwbRangingParams.DURATION_2_MS)   // SLOT_DURATION 2400 RSTU
    .setSlotsPerRangingRound(25)                       // SLOTS_PER_RR
    .setRangingIntervalMillis(200)                     // RANGING_DURATION
    .setRangingRoundIndexes(byteArrayOf(0))            // rounds to listen to
    // .setSessionKeyInfo(...) must match the anchors' static STS settings
    .build()
// Field use: DlTdoaRangingParams.createFromFiraConfigPacket(oobBytes, null)

val tag = RawRangingDevice.Builder()
    .setRangingDevice(RangingDevice.Builder().build())
    .setDlTdoaRangingParams(params)
    .build()
val preference = RangingPreference.Builder(
        RangingPreference.DEVICE_ROLE_DT_TAG,
        RawDtTagRangingConfig.Builder(tag).build())
    .setSessionConfig(SessionConfig.Builder().build())
    .build()
rangingSession.start(preference)

// in RangingSession.Callback
override fun onDlTdoaResults(peer: RangingDevice, measurement: DlTdoaMeasurement) {
    val m = measurement
    if (!m.isTxTimestampInCommonTimeBase) return       // local time: unusable
    solver.add(m.roundIndex, m.messageType, m.txTimestamp, m.rxTimestamp,
               m.cfo, m.anchorLocation)
}

In production, build the parameters from the anchors’ FiRa OOB packet with createFromFiraConfigPacket(), as Google’s sample does; it throws IllegalArgumentException for a malformed packet. The builder defaults (10 slots per round, 250 ms) differ from Qorvo’s samples, and the session key information (default {0x00, 0x00, 0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6}) must correspond to the anchors’ static STS settings. RangingSession.start() requires ACCESS_FINE_LOCATION for DL-TDoA, and ACCESS_BACKGROUND_LOCATION if ranging continues in the background.

Testing with phones

Lab tests with development boards are not enough; the phones decide whether the system works.

  • Phone matrix: iPhones that report supportsDLTDOAMeasurement, Android 17 phones that report isDlTdoaSupported(), and the OS versions your users run. On Android, also log getSupportedSlotDurations() and getMinimumRangingInterval().
  • App states: foreground, background start on iOS 27.2 and later, permission refusals.
  • Accuracy and latency against ground truth at fixed points and along walking routes, including stairs and lifts.
  • Air-interface evidence: a sniffer with FiRa decoders shows whether a failure sits in the anchor messages or in the phone. One developer reported on the Qorvo forum in 2026 that an iPhone on iOS 27 found the preamble, SFD and STS of self-built DL-TDoA frames but failed to decode the PHY header (status 0x18). Certified stacks avoid such surprises.
  • Scale: many phones at once do not load the anchors, but busy areas change the radio environment. Test at peak occupancy.

Test procedure

For a formal report, ISO/IEC 18305 identifies metrics and test scenarios for localisation systems, mainly indoors. A minimum procedure:

  1. Survey ground truth on a grid of test points, including corners, doorways, cluster boundaries and points under anchors, in the anchors’ frame.
  2. Freeze the configuration, record it with firmware versions, and change one variable per run.
  3. Static test. Log 60 s per point, phone and holding position (about 300 fixes at 200 ms); report the median and 95th-percentile horizontal error and the share of fixes on the correct floor.
  4. Rate and availability. Count DTM sets per cluster per second against 1/RANGING_DURATION, log gaps longer than two blocks and count local-time responses separately.
  5. Dynamic test. Walk fixed routes through stairs, lifts and cluster boundaries with timestamped markers; report route error and time to first fix.
  6. Pass/fail. Set thresholds from the use case before testing, such as the error that still identifies the right gate, and apply them per phone model.

Typical failures

SymptomLikely causeHow to confirmFix
One anchor’s Response missingResponder off, not in the round list or out of rangeRangingRxTimeout (0x21) on a tag; empty slot in a captureFix the round list; move or add an anchor
A whole cluster is silentIts initiator never heard the upstream Poll, or the reference is downSniffer on the upstream roundStart the reference first; repair the bridge link
Errors of metres, worse for later slotsNo CFO correction, or pairs mixed across clustersReplay logs with correction and per-cluster groupingFix the solver
Constant bias near one anchorWrong surveyed coordinatesCompare the broadcast location with the surveyRe-survey; update the configuration
iPhone session runs but didUpdateDLTDOA never firesnetworkIdentifier differs from the session ID, discovery fails, or a PHY or STS mismatchCompare configurations; look for DTMs in a sniffer captureCorrect the ID, discovery or PHY settings

Where DL-TDoA fits

Qorvo’s QM35 documentation lists “airports, museums, hospitals, shopping malls, corporate headquarters” as target sectors. Qorvo showed DL-TDoA navigation on commercial smartphones at CES and Embedded World in 2023 and claimed infrastructure costs “up to 10 times” lower. As of October 2026 we found no publicly named venue running smartphone DL-TDoA at scale. The platform support is new, and early projects are pilots.

From our engineering work

needCode engineers developed an RTLS with TDoA algorithms inside Qorvo’s UWB programme and represented Qorvo at 5+ FiRa interoperability test events. Our Autopositioning Anchors building block places anchors automatically, needs a known reference point and orientation only for the master anchor, and includes a mobile app for indoor navigation. Our Modular UWB Sniffer decodes FiRa traffic for interoperability debugging.

How needCode helps

We build DL-TDoA anchor products and pilots on the QM35825: the anchor hardware and host software, FiRa DL-TDoA configuration and synchronisation, Bluetooth LE or Wi-Fi discovery, anchor provisioning and the phone-side solver for iOS and Android. We test with real phones and sniffer captures and prepare FiRa certification for the anchors.

Frequently asked questions

Which Qorvo chip supports FiRa DL-TDoA?

The QM35825 with the QM35 SDK. Qorvo staff recommend it for FiRa DT-Anchors and DT-Tags, and FiRa lists it as Core 3.0 certified for DL-TDoA anchor and tag features. Qorvo’s DW3000 and QM33 SDKs do not include DL-TDoA, although partner stacks run DL-TDoA on DWM3001C hardware.

What does iOS 27 Nearby Interaction require from DL-TDoA anchors?

Apple’s documentation says that anchors and the framework’s anchor interaction “comply with the IEEE 802.15.4z standard”. The app’s network identifier must match the anchors’ session ID, anchors are discovered over Bluetooth LE or Wi-Fi, and the app computes position from the measurements. iOS 27 removed the entitlement that iOS 26 required.

Does Android support UWB DL-TDoA?

Yes, from Android 17 at API level 37. Apps check isDlTdoaSupported(), build DlTdoaRangingParams from a FiRa configuration packet received over Bluetooth LE or Wi-Fi, and receive DlTdoaMeasurement results. The release notes state FiRa 4.0 support.

Can a venue track my phone with UWB DL-TDoA?

Not through UWB. In DL-TDoA the phone only receives and computes its own position, so the anchors never see it. Apps still need location permission, and an app could share its position like any other location app.

Does Qorvo provide a navigation app or a position solver?

No. Qorvo staff wrote that the QM35825 example “has been verified with iPhones though Qorvo is not providing an app for this”, and the QM35 SDK documentation leaves multilateration to the host application.

How accurate is DL-TDoA indoor navigation?

Published figures are vendor claims: Estimote reports around 20–30 cm in typical deployments, and Pinpoint’s FiRa-listed kit quotes up to 30 cm. Real accuracy depends on anchor geometry, survey precision, line of sight and synchronisation quality.

How many anchors does a DL-TDoA cluster need, and how many can it have?

At least three in view for a 2D fix and four for 3D. Qorvo’s Cherry API allows up to eight responders per initiator, and an anchor can communicate with at most eight distinct addresses across all rounds, so a cluster has up to nine anchors.

Which slot duration should DL-TDoA anchors use?

2 ms, unless the rounds do not fit the block: it is the UCI default (2400 RSTU), Android’s default and the value in Qorvo’s iPhone-verified script. FiRa lists 1 ms DL-TDoA slots as a separate feature that the QM35825’s Core 3.0 listing does not include.

Sources

  1. Apple: Downlink time difference of arrival ranging and NIDLTDOAConfiguration
  2. Apple: NIDLTDOAMeasurement and supportsDLTDOAMeasurement
  3. MacRumors: Apple announces iOS 27 release date (September 2026)
  4. Android Developers: Android 17 features and APIs (DL-TDoA) and Android 17 release notes
  5. Android Developers: DlTdoaRangingParams and DlTdoaMeasurement
  6. Android Developers Blog: “Android 17 is here” (June 2026)
  7. FiRa: Untracked Indoor Navigation (2023) and FiRa 2.0 technical specifications (November 2023)
  8. FiRa: QM35825 Core 3.0 certification and a Core 4.0 device listing for comparison
  9. Qorvo Tech Forum: QM35825, Apple Nearby Interaction and DL-TDoA (staff reply, September 2026)
  10. Qorvo Tech Forum: QM35825 DL-TDoA time sync (September 2025) and closed QM35825 firmware (July 2026)
  11. Qorvo Tech Forum: DT-Tag support on DW3/QM33 (June 2026) and “Getting DL-TDoA working with iOS devices” (2026)
  12. Qorvo QM35 SDK repository (documentation and DL-TDoA samples)
  13. Estimote: “Downlink TDoA – finally, the Indoor GPS we’ve been waiting for” (September 2026) and FiRa 2.0 prototyping kit by Pinpoint
  14. MobileKnowledge: “iOS 27 Brings UWB Indoor Navigation Closer to Commercial Reality” (June 2026)
  15. Qorvo: UWB indoor navigation on commercial smartphones at Embedded World 2023
  16. Qorvo: UWB FiRa Protocol, QM35 SDK specification (July 2025)
  17. Qorvo: UWB Command Interface (UCI) Specification, QM35 SDK (July 2025)
  18. Qorvo: Cherry API and Integration Guide and cherry_fira.h
  19. Qorvo: Cherry DL-TDoA sample (dl_tdoa_app) and predefined multi-cluster rounds (util_dltdoa_config.c)
  20. Qorvo: run_fira_dt example and UWB Qorvo Tools Guide
  21. Qorvo QM35 SDK: Zephyr prj.conf and nRF52840 DK overlay
  22. Apple: NIDLTDOAConfiguration.DiscoveryMethod, NISession.deviceCapabilities and NIError.Code.resourceUsageTimeout
  23. Apple: clusterInitiatorAddress, rawTransmitTime, carrierFrequencyOffset, responderClockFrequencyOffset, floorElevation with floorNumber and height, and NIDLTDOACoordinatesType
  24. Android Developers: DlTdoaRangingParams.Builder, RawRangingDevice.Builder, RangingPreference, RangingSession and RangingSession.Callback
  25. Android Developers: DlTdoaMeasurement.AnchorLocation, ZElementExtension, UwbRangingParams and UwbRangingCapabilities
  26. Qorvo: UWB-based indoor navigation via smartphones at CES 2023 (January 2023)
  27. ISO/IEC 18305:2016, test and evaluation of localization and tracking systems

About needCode

needCode is an independent embedded engineering company from Kraków, Poland, and an official UWB business partner of Qorvo. Its engineers have worked inside Qorvo’s wireless programmes for more than eight years, including on the public QM35 SDK. needCode is certified to ISO 9001:2015 and ISO/IEC 27001:2022 and is a member of the UWB Alliance. This article does not represent Qorvo. All trademarks belong to their respective owners.