NEO-M8U: UDR never activates after 30+ min calibration drive (100 km/h, turns, blue LED waited) — zero DR fixes

Hi,

I’m using the SparkFun NEO-M8U for a vehicle-mounted system and need Dead
Reckoning to maintain position in GPS-denied areas like tunnels. After
following the calibration procedure from the datasheet, UDR still doesn’t seem
to activate.

Hardware Setup

  • GPS Module: SparkFun NEO-M8U (USB connection)
  • Host: NVIDIA Jetson Orin Nano (Linux, kernel 5.15.148-tegra)
  • Connection: /dev/ttyACM0 at 115200 baud
  • Mounting: Fixed on the dashboard, X-axis pointing forward, Y-axis pointing
    right (as per the integration manual)

Calibration Procedure I Followed

  1. Powered on the module with the vehicle engine running
  2. Waited several minutes until the blue LED started blinking (indicating
    satellite fix)
  3. Drove for over 30 minutes on urban roads
  4. Reached speeds up to 100 km/h
  5. Made multiple left and right turns throughout the drive

This matches what the NEO-M8U documentation describes for IMU calibration.

UBX Configuration Sent at Startup

I send the following UBX commands every time the application starts, then save
to flash with CFG-CFG:

  1. CFG-RATE: 15 Hz (67ms interval)
  2. CFG-NAV5: dynModel=4 (Automotive), fixMode=Auto
  3. CFG-ESFALG: doAutoMntAlgUpdate bit set (auto alignment enabled)
  4. CFG-MSG: Disabled GSV on UART1
  5. CFG-CFG: Save all to flash

15Hz

set_15hz_cmd = b"\xB5\x62\x06\x08\x06\x00\x43\x00\x01\x00\x01\x00\x59\x4C"

dynModel=4 (Automotive)

set_automotive_cmd = (
b"\xB5\x62\x06\x24\x24\x00"
b"\x01\x00" # mask: apply dynModel only
b"\x04" # dynModel=4 (Automotive)
b"\x03" # fixMode=Auto
b"\x00\x00\x00\x00"
b"\x10\x27\x00\x00"
b"\x05\x00"
b"\xFA\x00\xFA\x00"
b"\x64\x00\x5E\x01"
b"\x00\x3C\x00\x00"
b"\x00\x00"
b"\xC8\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x4D\x22" # checksum
)

Auto alignment

set_auto_align_cmd = (
b"\xB5\x62\x06\x56\x0C\x00"
b"\x00\x01\x00\x00" # bitfield: doAutoMntAlgUpdate=1
b"\x00\x00\x00\x00"
b"\x00\x00"
b"\x00\x00"
b"\x69\x1D"
)

Save config

save_cfg_cmd = b"\xB5\x62\x06\x09\x0D\x00\x00\x00\x00\x00\xFF\xFF\x00\x00\x00
x00\x00\x00\x17\x31\xBF"

What I Observe (37-minute drive data)

My goal is to get continuous position data even inside tunnels, but UDR never
engages:

  • 33,559 GGA messages received at 15.1 Hz (rate setting works fine)
  • fix_quality distribution:
    • 2 (DGPS): 33,378 (99.5%)
    • 1 (GNSS): 181 (0.5%)
    • 6 (DR): 0 — Dead Reckoning never reported
  • 4 coordinate jumps > 50m during the drive (would expect smooth interpolation
    if UDR was active):
    • Frame 24270: 267.8m jump
    • Frame 25885: 361.1m jump
    • Frame 28377: 144.8m jump
    • Frame 29298: 1,293m jump
  • All jumps occurred while fix_quality remained at 2 (DGPS)

What I’ve Checked

  • Module is a genuine NEO-M8U (not NEO-M8N) — confirmed by product label
  • Mounted correctly on dashboard: X forward, Y right
  • Waited for blue LED blink before driving
  • dynModel=4 (Automotive) is being sent with correct checksum
  • Auto alignment is enabled via CFG-ESFALG
  • Config is saved to flash via CFG-CFG
  • Have not yet polled UBX-ESF-STATUS during a drive to check fusionMode
    (planning to do this next)

My Questions

  1. Is my UBX configuration sufficient to enable UDR? Am I missing any required
    step (e.g., enabling ESF sensor messages, a specific initialization
    sequence)?
  2. Could the 15 Hz rate interfere with IMU fusion? Should I try a lower rate?
  3. Are the large coordinate jumps (up to 1.3 km) expected behavior without
    UDR, or do they point to a separate issue?
  4. Is there a way to confirm the internal IMU is actually producing data?
    (e.g., polling ESF-MEAS or ESF-RAW)
  5. Any other suggestions for getting UDR to actually engage?

I’d really appreciate any help — the whole reason I chose the NEO-M8U over the
M8N was for Dead Reckoning in tunnels. Thanks!

Ok, try this procedure per a u-blox post:

Test Procedure

  1. Cold start: Power on with clear sky view, engine running, vehicle stationary

  2. Poll ESF-STATUS every 5 seconds until fusionMode changes from 0 to 1

  3. Drive pattern:

  • Stationary for 2-3 minutes (engine on)

  • Drive to open area (parking lot), perform 3-5 figure-8 patterns

  • Maintain >30 km/h for sustained periods (5+ minutes) in open sky conditions

  1. Monitor: fusionMode should transition to 1 during this drive. If it stays 0 after 30 minutes, there’s a hardware or mounting issue

  2. Tunnel test: Only attempt tunnel navigation after fusionMode=1 is confirmed

If fusionMode remains 0 after following this procedure, check that your module firmware supports UDR (some early firmware versions had fusion issues) and verify the backup battery is actually holding charge

Let me know how it pans out, you may have a defective device if not

Look at that video:

Now, what I’m doing with F9R for auto-alignment is starting in a quite open area, driving in the city and, then, going to the motorway, trying to take advantage of access loops to have a 8 shaped trajectory. My typical trip for that:

BTW, auto-alignment should not be done at each startup. Once determined, it should be stored and reloaded at next startup. This is what I’m doing in my project:

Thanks for the help, everyone! I followed your advice and successfully completed the calibration. However, I’m encountering a few issues during testing:

  1. Coordinate Clustering: The moment I enter a tunnel and the mode switches from DGPS to UDR, the spacing between coordinate points becomes extremely narrow (as if the velocity is under-reported).

  2. Rapid Accuracy Decay: The positioning remains fairly accurate for about 1 to 2 minutes, but after that, the accuracy drops off a cliff. In the worst sections, the coordinates drift completely off-track and show wildly incorrect positions.

Are these known technical limitations of this specific module? Are there any recommended algorithms, filtering techniques, or “pro-tips” to compensate for this drift during extended GNSS outages?