Consumer & Developer

Android Raw GNSS Measurements: From the API to On-Device RTK

By openRECEIVER Updated 29 June 2026
A hand holding a smartphone running a city map navigation app outdoors
Photo by Stanislav Kondratiev on Pexels (pexels.com/photo/8824105)

Android’s raw Global Navigation Satellite System (GNSS) measurements Application Programming Interface (API) hands you the observables, not a position: pseudorange components, carrier phase and the state flags, which is exactly what you need to build a positioning engine on the phone. The catch is that you compute the pseudorange yourself, the carrier phase is only usable when no cycle slip has occurred, and the real value comes from feeding it into an on-device Real-Time Kinematic (RTK) or Precise Point Positioning (PPP) engine.

Key takeaways:

  • The API gives observables, not a fix. Each GnssMeasurement event hands you raw timing and clock fields, so you reconstruct the pseudorange yourself rather than reading a finished distance.
  • Gate every measurement on its state flags. Trust the timing only once the tracking-state bits are set, and use carrier phase only when the Accumulated Delta Range (ADR) state is valid and free of resets and cycle slips.
  • Carrier phase is chipset-dependent. Pseudorange is broadly available, but valid ADR varies by device, so detect capability at runtime instead of trusting a phone list.
  • Full tracking is mandatory for precision. Disabling duty cycling stops the chipset from manufacturing cycle slips, at a real battery cost.
  • Corrections plus an embedded engine reach centimeters. Feeding gated observables and an RTK or PPP correction stream into an on-device engine takes commodity dual-frequency phones from meters to decimeters or better.

This is for Android developers who have outgrown FusedLocationProvider and want the measurement layer: the GnssMeasurement stream, what each field means, and how to turn it into a high-precision fix rather than a blue dot.

What the raw gnss measurements API actually gives you

Raw GNSS measurements have been accessible since Android 7.0 Nougat (API level 24), through a GnssMeasurementsEvent.Callback you register on the LocationManager. Google made support mandatory on Android 10 (API 29) and higher, so well over 90% of fielded Android phones expose them. Each event carries a GnssClock and a set of GnssMeasurement objects, one per tracked signal.

What it does not give you is a finished pseudorange. The API hands you the raw timing fields and expects you to do the arithmetic. That surprises people coming from a normal location API, and it is the first thing to get right.

LocationManager lm = getSystemService(LocationManager.class);
lm.registerGnssMeasurementsCallback(executor, new GnssMeasurementsEvent.Callback() {
  @Override public void onGnssMeasurementsReceived(GnssMeasurementsEvent event) {
    GnssClock clock = event.getClock();
    for (GnssMeasurement m : event.getMeasurements()) {
      // m.getReceivedSvTimeNanos(), m.getAccumulatedDeltaRangeMeters(),
      // m.getCn0DbHz(), m.getCarrierFrequencyHz(), m.getState() ...
    }
  }
});

Reconstructing the pseudorange yourself

The pseudorange is the distance implied by signal travel time, and you build it from the clock and measurement fields. In short: derive the receiver clock in Global Positioning System (GPS) time from GnssClock.getTimeNanos() minus getFullBiasNanos() and getBiasNanos(), subtract the satellite transmit time getReceivedSvTimeNanos(), apply getTimeOffsetNanos(), handle the week rollover, and multiply the result in seconds by the speed of light. The European GNSS Agency (GSA), now the European Union Agency for the Space Programme (EUSPA), white paper on Android raw measurements is the canonical reference for the exact reconstruction, and it is worth following precisely.

Before you trust a measurement, gate it on the tracking state. getState() is a bitmask, and you want at least STATE_TOW_DECODED (or STATE_TOW_KNOWN) and STATE_CODE_LOCK set before the timing is meaningful. A measurement that is still acquiring will otherwise poison your solution.

FieldWhat it isGotcha
getReceivedSvTimeNanos()Satellite transmit timeCombine with GnssClock to get pseudorange
getState()Tracking-state bitmaskRequire STATE_TOW_DECODED + STATE_CODE_LOCK
getAccumulatedDeltaRangeMeters()Carrier phase (ADR)Only valid with the ADR state check below
getCarrierFrequencyHz()Signal frequencyHow you tell L1 from L5/E5a
getCn0DbHz()Carrier-to-noise densityYour signal-quality and multipath tell

Carrier phase and cycle slipsReconstruct and filter

Carrier phase is what makes centimeters possible, and on Android it arrives as the accumulated delta range (ADR), being a carrier-phase-derived observable expressed in meters: getAccumulatedDeltaRangeMeters(), the change in range since the channel last reset. It is far more precise than the code pseudorange, but it is ambiguous by a whole number of cycles and brittle.

The non-negotiable check: use the ADR only when getAccumulatedDeltaRangeState() has ADR_STATE_VALID set and neither ADR_STATE_RESET nor ADR_STATE_CYCLE_SLIP set. A cycle slip means the integer ambiguity has jumped and any RTK or PPP filter must re-fix it; using a slipped value silently corrupts the solution. On many phones the chipset duty-cycles the GNSS hardware to save power, which manufactures cycle slips wholesale; GnssMeasurementRequest.Builder.setFullTracking(true) disables that and is effectively mandatory for any carrier-phase work.

Source code on a screen, the kind that parses a raw GnssMeasurement stream The raw stream is a firehose of timing fields and state flags. The discipline is in the flag checks, not the math. Photo by Al Nahian on Pexels.

Which chipsets actually emit carrier phase?

Pseudorange is broadly available; carrier phase is not. ADR support is optional and chipset-dependent. In practice, Pixel devices and flagship phones that do not use Qualcomm Snapdragon chipsets reliably provide ADR, while some Snapdragon-based devices have historically withheld or limited it. Dual-frequency L1/L5 raw measurements, meanwhile, are available on most current flagships (Pixel, Samsung, Xiaomi, Motorola, OnePlus).

The implication for your code is to detect capability at runtime, not from a spreadsheet: subscribe to measurements, check whether ADR ever arrives valid and whether getCarrierFrequencyHz() shows an L5 frequency, and adapt. A device that only gives you code pseudoranges caps you at meters; one with clean carrier phase opens the door to decimeters.

Building an on-device RTK or PPP pipeline

This is where raw measurements earn their keep, and where the documentation goes quiet. The pipeline on the phone looks like this:

  1. Capture. In the onGnssMeasurementsReceived callback, buffer the GnssClock and GnssMeasurement set per epoch. Many teams serialize to a Receiver Independent Exchange Format (RINEX) observation buffer so they can reuse standard tooling.
  2. Reconstruct and filter. Build pseudoranges and carrier-phase observations, applying the timing-state and ADR-state gates above. Monitor GnssClock discontinuities as well, since they can invalidate assumptions about continuous timing across epochs. Finally, tag dual-frequency observations via getCarrierFrequencyHz().
  3. Bring in corrections. Feed a correction source: a Networked Transport of RTCM via Internet Protocol (NTRIP) stream carrying Radio Technical Commission for Maritime Services (RTCM) messages for RTK, or state-space corrections such as the Galileo High Accuracy Service (HAS) for PPP. The corrections are what take you from meters to centimeters.
  4. Run an engine. Hand the observations and corrections to a positioning engine embedded in the app, RTKLIB and goGPS are the common open-source choices, and read back a float or fixed solution.
  5. Publish. Surface the high-precision fix to your user interface (UI), or to the rest of the device.

Real-time RTK on commodity dual-frequency Android phones has been demonstrated at centimeter-level static and decimeter-level kinematic accuracy, with recent on-device services reaching sub-50 cm. It is not free: full tracking costs battery, the antenna and multipath set a hard ceiling, and cycle slips will test your ambiguity handling. But the building blocks are all in the standard API.

Frequently asked questions

What Android version added raw GNSS measurements?

Raw GNSS measurements arrived in Android 7.0 Nougat (API level 24, 2016) via the android.location GnssMeasurement and GnssMeasurementsEvent classes. Google later made support mandatory on devices running Android 10 (API level 29) or higher, so the large majority of phones in use today expose raw measurements, though which individual fields they populate still varies by chipset.

How do I get pseudorange and carrier phase from GnssMeasurement?

The API does not return a ready pseudorange; you compute it from the timing fields. Derive receiver GPS time from GnssClock (getTimeNanos, getFullBiasNanos, getBiasNanos), subtract getReceivedSvTimeNanos, handle the week rollover, and multiply by the speed of light. Carrier phase comes directly from getAccumulatedDeltaRangeMeters(), but only when its state flags say it is valid.

Is accumulated delta range the same as carrier phase?

Effectively yes. Accumulated delta range (ADR) is the change in satellite-to-receiver range measured on the carrier since the channel last reset, which is the carrier-phase observable expressed in meters. It is far more precise than the code pseudorange but ambiguous by an integer number of cycles, which is what an RTK or PPP engine resolves.

How do I detect a cycle slip on Android?

Read getAccumulatedDeltaRangeState(). Use the carrier phase only when ADR_STATE_VALID is set and both ADR_STATE_RESET and ADR_STATE_CYCLE_SLIP are clear. If either of those bits is set, the carrier phase is discontinuous and your filter must re-resolve the integer ambiguity. Duty cycling is a common cause, so disable it with full tracking for precise work.

Which Android phones support carrier-phase raw measurements?

Carrier phase (ADR) is optional and chipset-dependent. Pixel devices and flagship phones not based on Qualcomm Snapdragon chipsets are the most reliable sources, while support on Snapdragon devices has been inconsistent. Rather than trust a device list, subscribe to measurements at runtime and check whether valid ADR actually arrives.

Can you do real-time RTK on an Android phone with raw data?

Yes, on dual-frequency phones with corrections and an embedded engine, real-time RTK has reached centimeter-level static and decimeter-level kinematic accuracy, with some on-device implementations under 50 cm. It is fragile in motion because of the small antenna, multipath and cycle slips, so full tracking and a clear sky view matter a great deal, and an external antenna helps further.

Why doesn’t Android give me a ready-made pseudorange?

Because the platform exposes the receiver at the measurement layer rather than the solution layer, giving you the raw timing observables so you can build any positioning algorithm you like (SPP, RTK, PPP) rather than being locked to the chipset’s internal fix. It is more work, but it is the only way to apply your own corrections and integrity logic on the phone.

Sources and further reading

Field availability and accuracy depend on the device, chipset and Android version; figures here are representative as of June 2026. Test on your target hardware rather than assuming a phone populates every field.