Simple Python GNSS processing library

commaai, updated 🕥 2023-03-17 21:08:43

Introduction

Laika is an open-source GNSS processing library. Laika is similar to projects like RTKlib and GPSTK, but in Python and with a strong focus on readability, usability and easy integration with other optimizers. Laika can process raw GNSS observations with data gathered online from various analysis groups to produce data ready for position/velocity estimation. Laika is designed to produce accurate results whilst still being readable and easy to use. Laika is the perfect tool to develop accurate GNSS-only or GNSS-fusion localisation algorithms.

alt text

The GNSS problem

GNSS satellites orbit the earth broadcasting signals that allow the receiver to determine the distance to each satellite. These satellites have known orbits and so their positions are known. This makes determining the receiver's position a basic 3-dimensional trilateration problem. In practice observed distances to each satellite will be measured with some offset that is caused by the receiver's clock error. This offset also needs to be determined, making it a 4-dimensional trilateration problem.

Since this problem is generally overdetermined (more than 4 satellites to solve the 4d problem) there is a variety of methods to compute a position estimate from the measurements. Laika provides a basic weighted least squares solver for experimental purposes. This is far from optimal due to the dynamic nature of the system, this makes a Bayesian estimator like a Kalman filter the preferred estimator.

However, the above description is over-simplified. Getting accurate distance estimates to satellites and the satellite's position from the receiver observations is not trivial. This is what we call processing of the GNSS observables and it is this procedure laika is designed to make easy.

Astrodog

Astrodog is the main component of the laika. It is a python object, and like the soviet space dogs to which it owes its name, an astrodog will do everything to make the life of its owner easier. Which in this case is fetch and process all the necessary data to transform raw GNSS observables into usable distance measurements and satellite positions ready for position estimation.

Satellite info

Astrodog has a get_sat_info function that will provide an accurate position, velocity and clock error for any satellite at any time in history.

Pseudorange corrections

Astrodog has a get_delay function that will provide a pseudorange delay correction for any satellite at any time in history for the requested receiver position. This delay correction includes a correction for the tropospheric signal delay, ionospheric signal delay and differential code biases (DCBs) of the transmitting satellite.

This delay can either be estimated with mathematical models or with DGPS station observations, which is more accurate, but slower and only supported in the continental United States.

Architecture

GNSS processing requires getting data from the internet from various analysis groups such as NASA's CDDIS. AstroDog downloads files from FTP servers from these groups when it needs them. Downloading and parsing all this data can be slow. AstroDog caches all downloaded files locally to avoid re-downloading.

These files are then parsed by AstroDog and kept in memory. Every one of these parsed objects (DCBs, ionospheric models, satellite orbit polynomials, etc.) has a valid location area and/or a valid time window. Within those windows these objects can provide information relevant to GNSS processing.

Design principles of laika

  • Should work without configuration or setup
  • Default configuration should not compromise accuracy for anything

Laika's accuracy

To confirm the quality of Laika's GNSS processing, we ran laika's processing and a simple Kalman filter (procedure described in examples) on 2000 minutes of driving of a regular commute in San Francisco. The data comes from a "u-blox M8" chip. The fixes computed with laika's processed data are compared to the live navigation fixes given by the u-blox chip. They compared by looking at the standard deviation of all measured altitudes within every 5×5 m² in the dataset. There is no way to compare horizontal accuracy without ground truth, but there is no reason to believe that vertical and horizontal accuracy are not equally correlated for laika computed positions and u-blox's live positions. Data with the antenna on the roof and antenna inside the car are compared separately, since the results are significantly different. altitude distributionplot

Examples

Installation

Laika runs in Python 3.8.2, and has only been tested on Ubuntu 20.04. Running in a virtual environment is recommended.

laika

If you do not yet have numpy and scipy installed. Install them with pip. Having accelerated numpy will make laika much faster. pip install numpy scipy Then laika can be installed with python setup.py install The tests should now pass.

Eathdata account

It is no longer possible to download GNSS data from NASA servers without an account. You can make an account here. Then create a .netrc file in the laika folder with content: machine urs.earthdata.nasa.gov login your_username password your_password

notebook examples

The notebook examples require some visualisation packages. To install them first you need sudo apt-get install libfreetype6-dev and then with pip pip install -r requirements_examples.txt --user Then you should be able to run the notebooks. The notebooks can be opened by running jupyter notebook and then navigating to the desired .ipynb file.

Useful GNSS references

Issues

Walkthrough jupyter notebook doesn't work

opened on 2023-03-09 21:16:46 by iMurfyD

First time user who gets runtime error on trying the Walkthrough jupyter notebook:

```

RuntimeError Traceback (most recent call last) Cell In[4], line 11 9 # We use RINEX3 PRNs to identify satellites 10 sat_prn = 'G07' ---> 11 sat_pos, sat_vel, sat_clock_err, sat_clock_drift, ephemeris = dog.get_sat_info(sat_prn, time) 12 print("Satellite's position in ECEF (m) : \n", sat_pos, '\n') 13 print("Satellite's velocity in ECEF (m/s) : \n", sat_vel, '\n')

File ~/opt/anaconda3/envs/missile-tid/lib/python3.10/site-packages/laika/astro_dog.py:265, in AstroDog.get_sat_info(self, prn, time) 263 eph = None 264 if self.pull_orbit: --> 265 eph = self.get_orbit(prn, time) 266 if not eph and self.pull_nav: 267 eph = self.get_nav(prn, time)

File ~/opt/anaconda3/envs/missile-tid/lib/python3.10/site-packages/laika/astro_dog.py:106, in AstroDog.get_orbit(self, prn, time) 104 def get_orbit(self, prn: str, time: GPSTime): 105 skip_download = time in self.orbit_fetched_times --> 106 orbit = self._get_latest_valid_data(self.orbits[prn], self.cached_orbit[prn], self.get_orbit_data, time, skip_download) 107 if orbit is not None: 108 self.cached_orbit[prn] = orbit

File ~/opt/anaconda3/envs/missile-tid/lib/python3.10/site-packages/laika/astro_dog.py:363, in AstroDog._get_latest_valid_data(self, data, latest_data, download_data_func, time, skip_download, recv_pos) 361 download_data_func(time, recv_pos) 362 else: --> 363 download_data_func(time) 364 latest_data = get_closest(time, data, recv_pos=recv_pos) 365 if is_valid(latest_data):

File ~/opt/anaconda3/envs/missile-tid/lib/python3.10/site-packages/laika/astro_dog.py:211, in AstroDog.get_orbit_data(self, time, only_predictions) 209 ephems_sp3 = self.download_parse_orbit(time) 210 if sum([len(v) for v in ephems_sp3.values()]) < 5: --> 211 raise RuntimeError(f'No orbit data found. For Time {time.as_datetime()} constellations {self.valid_const} valid ephem types {self.valid_ephem_types}') 213 self.add_orbits(ephems_sp3)

RuntimeError: No orbit data found. For Time 2018-01-07 00:00:00 constellations ['GPS', 'GLONASS'] valid ephem types (, , ) ```

Output of this code block: ```

For example if we want the position and speed of satellite 7 (a GPS sat)

at the start of January 7th 2018. Laika's custom GPSTime object is used throughout

and can be initialized from python's datetime.

from datetime import datetime from laika.gps_time import GPSTime time = GPSTime.from_datetime(datetime(2018, 1, 7))

We use RINEX3 PRNs to identify satellites

sat_prn = 'G07' sat_pos, sat_vel, sat_clock_err, sat_clock_drift, ephemeris = dog.get_sat_info(sat_prn, time) print("Satellite's position in ECEF (m) : \n", sat_pos, '\n') print("Satellite's velocity in ECEF (m/s) : \n", sat_vel, '\n') print("Satellite's clock error (s) : \n", sat_clock_err, '\n\n')

we can also get the pseudorange delay (tropo delay + iono delay + DCB correction)

in the San Francisco area

receiver_position = [-2702584.60036925, -4325039.45362552, 3817393.16034817] delay = dog.get_delay(sat_prn, time, receiver_position) print("Satellite's delay correction (m) in San Fransisco \n", delay) ```

Which prints out: Downloading https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/18006/final/Sta19826.sp3 Downloading https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/18008/final/Sta19831.sp3 Downloading https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/18007/final/Sta19830.sp3 Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1982/igs19826.sp3.Z Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1983/igs19830.sp3.Z Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1983/igs19831.sp3.Z Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1982/igr19826.sp3.Z Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1983/igr19831.sp3.Z Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1983/igr19830.sp3.Z Downloading https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/18007/rapid/Sta19830.sp3 Downloading https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/18008/rapid/Sta19831.sp3 Downloading https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/18006/rapid/Sta19826.sp3 Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1983/igu19831_18.sp3.Z Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1982/igu19826_18.sp3.Z Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1983/igu19830_18.sp3.Z Downloading https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/18006/ultra/Sta19826.sp3 Downloading https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/18007/ultra/Sta19830.sp3 Downloading https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/18008/ultra/Sta19831.sp3 Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1983/igu19831_12.sp3.Z Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1982/igu19826_12.sp3.Z Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1983/igu19830_12.sp3.Z Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1983/igu19831_06.sp3.Z Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1983/igu19831_00.sp3.Z Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1982/igu19826_06.sp3.Z Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1983/igu19830_06.sp3.Z Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1983/igu19830_00.sp3.Z Downloading https://github.com/commaai/gnss-data/raw/master/gnss/products/1982/igu19826_00.sp3.Z

I tried signing up for an Earthdata account and installing laika from source with a .netrc file in the root folder with python setup.py install. Also tried wiping the cache, which is at /tmp/gnss. Cache looks like this:

``` ls /tmp/gnss cddis_products russian_products

ls /tmp/gnss/cddis_products 1982 1983

ls /tmp/gnss/cddis_products/1982 igr19826.sp3.attempt_time igs19826.sp3.attempt_time igu19826_00.sp3.attempt_time igu19826_06.sp3.attempt_time igu19826_12.sp3.attempt_time igu19826_18.sp3.attempt_time ```

Running on a Mac with python 3.10.

IGS Product renaming

opened on 2023-01-10 19:13:50 by gast04

Ultra Rapid Orbits filenames changed to more descriptive names Long_Product_Filenames_v1.0.pdf

Missing dependency in setup.py

opened on 2022-12-27 10:31:51 by jmccartin

This is a pretty simple issue at first glance.

You have a missing dependency in setup.py for the library atomicwrites. The actual library calls are in downloader.py, for example: https://github.com/commaai/laika/blob/5eb0c3c2596dd12a232b83bdb057a716810e89cf/laika/downloader.py#L283

This means that any project that uses laika as a dependency itself will fail unless it explicitly knows to install atomicwrites itself. But on the project homepage, the author of that package recommends its deprecation:

I thought it'd be a good time to deprecate this package. Python 3 has os.replace and os.rename which probably do well enough of a job for most usecases.

So, what's the need for that atomic_write function? Could it not be simply replaced by a basic wrapper using native library calls?

dog.get_delay fails : IndexError: index 4 is out of bounds for axis 0 with size 4

opened on 2022-12-06 14:37:54 by jonathanmuller

Hi,

First of all thank you for the awesome repo. I would like to help solving the following issue

Simple code to reproduce ``` import laika from laika import AstroDog from laika.lib.coordinates import geodetic2ecef from laika.gps_time import GPSTime import datetime

constellations = ['GPS', 'GLONASS', 'GALILEO'] dog = AstroDog(valid_const=constellations, dgps=True) prn ="G07" pos_ecef = geodetic2ecef([46, 6, 123]) gps_time = GPSTime.from_datetime(datetime.datetime(2022, 11, 25, 9, 43, 6))

delay = dog.get_delay(prn, gps_time, pos_ecef)

print(delay) ```

Result : Downloading https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/22328/rapid/Sta22374.sp3 Downloading https://github.com/commaai/gnss-data-alt/raw/master/MCC/PRODUCTS/22329/rapid/Sta22375.sp3 ... pulling from https://geodesy.noaa.gov/corsdata/coord/coord_14/ to /tmp/gnss/cors_coord/zsu4_14.coord.txt pulling from https://geodesy.noaa.gov/corsdata/coord/coord_14/ to /tmp/gnss/cors_coord/ztl4_14.coord.txt ... File "/home/jonathan/Desktop/cloud_locate/laika/laika/dgps.py", line 31, in download_and_parse_station_postions with open(coord_file_path, 'r+') as coord_file: FileNotFoundError: [Errno 2] No such file or directory: '/tmp/gnss/cors_coord/ab27_14.coord.txt' (Let's download it manually : cd /tmp/gnss/cors_coord && wget https://geodesy.noaa.gov/corsdata/coord/coord_14/ab49_14.coord.txt ) Then, relaunching the same code now gives : Traceback (most recent call last): File "/tmp/test_laika.py", line 14, in <module> delay = dog.get_delay(prn, gps_time, pos_ecef) File "/home/jonathan/Desktop/cloud_locate/laika/laika/astro_dog.py", line 325, in get_delay return self._get_delay_dgps(prn, rcv_pos, time) File "/home/jonathan/Desktop/cloud_locate/laika/laika/astro_dog.py", line 340, in _get_delay_dgps dgps_corrections = self.get_dgps_corrections(time, rcv_pos) File "/home/jonathan/Desktop/cloud_locate/laika/laika/astro_dog.py", line 124, in get_dgps_corrections latest_data = self._get_latest_valid_data(self.dgps_delays, self.cached_dgps, self.get_dgps_data, time, recv_pos=recv_pos) File "/home/jonathan/Desktop/cloud_locate/laika/laika/astro_dog.py", line 361, in _get_latest_valid_data download_data_func(time, recv_pos) File "/home/jonathan/Desktop/cloud_locate/laika/laika/astro_dog.py", line 239, in get_dgps_data station_names = get_closest_station_names(recv_pos, k=8, max_distance=MAX_DGPS_DISTANCE, cache_dir=self.cache_dir) File "/home/jonathan/Desktop/cloud_locate/laika/laika/dgps.py", line 65, in get_closest_station_names return np.array(station_ids)[idxs] IndexError: index 4 is out of bounds for axis 0 with size 4

Any suggestion on why this happens and how to solve it ?

Satellite Outages reported as online

opened on 2022-12-01 15:27:10 by jgutierrezm113

I'm using Laika to obtain satellite positions in the sky at different times, but I was comparing data from Laika with real measurements and for one of the tests, G28 is offline (sensor doesn't show it, and almanac I use with another similar tool to Laika, also don't report it), but Laika does report it. Is there a way to get Laika to report whether the satellite is online or not? I dug through the code a bit and I can look at the healthy attribute from the PolyEphemeris, but that one still seems to be set to True regardless. Not sure if it's a bug or not, just hoping to get some help so I can use Laika to estimate satellite positions.

Thanks in advance!

Ntrip data for correction

opened on 2021-12-16 10:23:56 by felrock

Will Laika support RTCM correction data? Would be really nice to have, and perhaps not to difficult to add.

comma.ai

Make driving chill

GitHub Repository

gnss gps glonass rtklib hacktoberfest