Back to Repositories

Testing GPS Time To First Fix Performance in OpenPilot

This test suite evaluates the Time To First Fix (TTFF) performance of GPS modules in the OpenPilot system. It measures how quickly the system can establish a satellite fix with more than 4 satellites across multiple iterations, providing statistical performance data for GPS initialization.

Test Coverage Overview

The test validates GPS acquisition performance by measuring TTFF across 20 iterations. It covers:

  • Satellite fix acquisition timing
  • Minimum satellite threshold validation (>4 satellites)
  • Process management for ubloxd and pigeond services
  • Timeout handling for failed fixes

Implementation Analysis

The implementation uses a systematic approach to measure GPS performance. It leverages the cereal messaging system for inter-process communication and implements process management through managed_processes. The test cycles through multiple iterations with controlled start/stop sequences and precise timing measurements.

Technical Details

Key technical components include:

  • Python subprocess management via managed_processes
  • Cereal messaging system for GPS data communication
  • Monotonic timing for accurate measurements
  • ANSI color-coded console output
  • Graceful process cleanup using atexit

Best Practices Demonstrated

The test exemplifies robust testing practices including:

  • Proper process cleanup and resource management
  • Clear success/failure criteria
  • Statistical averaging for reliable results
  • Timeout handling for error cases
  • Clear console feedback with color coding

commaai/openpilot

system/sensord/tests/ttff_test.py

            
#!/usr/bin/env python3

import time
import atexit

from cereal import messaging
from openpilot.system.manager.process_config import managed_processes

TIMEOUT = 10*60

def kill():
  for proc in ['ubloxd', 'pigeond']:
    managed_processes[proc].stop(retry=True, block=True)

if __name__ == "__main__":
  # start ubloxd
  managed_processes['ubloxd'].start()
  atexit.register(kill)

  sm = messaging.SubMaster(['ubloxGnss'])

  times = []
  for i in range(20):
    # start pigeond
    st = time.monotonic()
    managed_processes['pigeond'].start()

    # wait for a >4 satellite fix
    while True:
      sm.update(0)
      msg = sm['ubloxGnss']
      if msg.which() == 'measurementReport' and sm.updated["ubloxGnss"]:
        report = msg.measurementReport
        if report.numMeas > 4:
          times.append(time.monotonic() - st)
          print(f"\033[94m{i}: Got a fix in {round(times[-1], 2)} seconds\033[0m")
          break

      if time.monotonic() - st > TIMEOUT:
        raise TimeoutError("\033[91mFailed to get a fix in {TIMEOUT} seconds!\033[0m")

      time.sleep(0.1)

    # stop pigeond
    managed_processes['pigeond'].stop(retry=True, block=True)
    time.sleep(20)

  print(f"\033[92mAverage TTFF: {round(sum(times) / len(times), 2)}s\033[0m")