{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"# **Navigator: v1.0.0 - GNSS Toolkit**"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"## **Introduction**\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"The Navigator is a python based GNSS library and toolkit tailored for GNSS PVT solutions. It provides a uniform object-oriented API to do baisc GNSS data processing, including RINEX parsing, satellite position calculation, and user PVT calculation. The library is well documented and easy to use. It also provides a set of CLI tools for RINEX data collection from public FTP servers.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"jp-MarkdownHeadingCollapsed": true,
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### **Submodules**\n",
"There are five submodules in the Navigator library, each of which serves a specific purpose. The following is a brief description of each submodule:\n",
"\n",
"1. **parse** \\\n",
" This module houses tools for parsing [RINEX](https://igs.org/wg/rinex/) files, supporting RINEXv3 and RINEXv2. It proficiently parses observation and navigation files into a [pandas.DataFrame](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html), serving as the backend data format for processing.\n",
"\n",
"2. **satlib**\\\n",
" This module incorporates mathematical tools for computing satellite and user positions. It divides into two key segments:\n",
" 1. **satellite**\\\n",
" This section encompasses the satellite class and its associated methods, including the computation of satellite positions, velocities, and clock corrections. \n",
" \n",
" 2. **triangulate**\\\n",
" This module contains various methods for computing user positions, including the least-squares method, the weighted least-squares method, and the Kalman filter method.\n",
"\n",
"3. **download**\\\n",
" Primarily used for fetching RINEX files from [CDDIS](https://cddis.nasa.gov/Data_and_Derived_Products/CDDIS_Archive_Access.html) and others FTP servers, this module handles both RINEXv2 and RINEXv3 downloads. While users typically don't interact with this module programmatically, CLI tools are available for this purpose.\n",
"\n",
"4. **utility**\\\n",
" Encompassing various utility functions within the library, including the *Epoch Class*, *Transformations*, *CLI Tools*, etc.\n",
"\n",
"5. **dispatch**\\\n",
" Though currently unimplemented, this module is intended for potential future utilization, specifically for scaling the library to a distributed system for processing extensive datasets.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## **Installation**\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"To install the library, user need to clone the repository from **Pntf Lab Server**(*10.116.24.69*) which is only accessible to authorized lab members. To use the **Lab Git Server**, user need to have access to the git user account. If you don't have access to the git user account, please contact the lab administrator.\n",
"\n",
"**Note: The server is only accessible from the UA network. If you are not on the UA network, you need to connect to the UA VPN first.**\n",
"\n",
"To install the library, first create a python virtual environment and activate it. \n",
"**Note: This assumes that you have python3.10 installed on your system.**\n",
"```bash\n",
"# Create a virtual environment\n",
"python3 -m venv .venv\n",
"\n",
"# Activate the virtual environment\n",
"source .venv/bin/activate\n",
"```\n",
"\n",
"Now, after activating the virtual environment, you can install the library from the git server using a single command:\n",
"```bash\n",
"pip install git+ssh://git@10.116.24.69:2222/home/git/Navigator.git\n",
"```\n",
"\n",
"Optionally to clone the repository from the git server, use the following command:\n",
"```bash\n",
"git clone ssh://git@10.116.24.69:2222/home/git/Navigator.git\n",
"```\n",
"**Note: A password prompt will appear if you are not using an SSH key.**\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## **Documentation**\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"The illustration of basic usage of library is provided in the `/docs` directory of the repository. To generate the API documentation, activate the virtual environment and run the following command:\n",
"```bash\n",
"pdoc -o $DOC_DIR -d google navigator\n",
"```\n",
"where `$DOC_DIR` is the directory where the documentation will be generated. The documentation will be generated in the `$DOC_DIR/navigator` directory. To view the documentation, open the `index.html` file in the `$DOC_DIR/` directory in a web browser.\n",
"\n",
"It is recommended to generated directly from the clone repository using the following command:\n",
"```bash\n",
"pdoc -o $DOC_DIR -d google Navigator/src/navigator\n",
"```\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## **Usage**\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"The introduction usage of the library is documented in the *docs* directory. It provides basic usage of the library and its modules. Curretly available introductory notebooks are:\n",
"1. [Intro to Parsing](../../intro/intro_parsing_and_interpolation.ipynb)\n",
"2. [Intro to Traingulation](../../intro/intro_triangulation.ipynb)\n",
"3. [Intro to Epoch Directory](../../intro/epoch_directory_tutorial.ipynb)\n",
"4. [Intro to SP3 Orbit](../../intro/intro_sp3_orbit.ipynb)\n",
"5. [Intro to Unscented Kalman Filter](../../intro/unscented_kalman_filter_gps.ipynb)\n",
"\n",
"Other notebooks will be added in the future to provide more detailed usage of the library."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## **Data Collection and File Formats**"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### **File Formats and Definitions**"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"To perform SPP (Single Point Positioning) and PPP (Precise Point Positioning), generally many data and file formats are required (see [fileformats](https://gssc.esa.int/education/library/standards-and-data-formats/)).\n",
"\n",
"There are two main files need to perorm the SPP. These files are:\n",
"1. [RINEX Observation File](https://server.gage.upc.edu/gLAB/HTML/Observation_Rinex_v3.01.html)\\\n",
" The observtional file is recorded by the receiver and contains the *pseudorange* and *carrier phase* measurements of the satellites.\n",
" \n",
"- [RINEX Navigation File](https://server.gage.upc.edu/gLAB/HTML/GPS_Navigation_Rinex_v3.04.html)\\\n",
" The navigation file contains the satellite ephemeris data which is used to compute the satellite position.\n",
"\n",
"\n",
"A useful visual representation of the data and file formats is available [here](https://gage.upc.edu/en/learning-materials/library/gnss-format-descriptions).\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### **Data Collection**"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"The data collection is done by using publicly available FTP servers. The primary source of the data is the [CDDIS](https://cddis.nasa.gov/Data_and_Derived_Products/CDDIS_Archive_Access.html) server. The data is collected using the download tools provided by the library.\n",
"\n",
"There are two way of downloading the files from the FTP server.\n",
"\n",
"- Using API provided by download module\n",
"- Using CLI tools provided by navigator library (Recommended)\n",
"\n",
" Two CLI tools are available for downloading the data. These tools are:\n",
"- rinex3-download-nasa (For downloading RINEXv3 files)\n",
"- rinex3-dir-ops (For standerdizing the directory structure of the downloaded files)\n",
"\n",
"The command line tools are accessible only after activating the virtual environment where the navigator library is installed. To activate the virtual environment, run the following command:\n",
"```bash\n",
"source .navigator/bin/activate\n",
"```\n",
"\n",
"For convenience, the API tools are demonstrated below. The CLI tools are easy to use and are self explanatory. \n"
]
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# # Let's make a temorary directory to store the downloaded files\n",
"# import tempfile\n",
"# from pathlib import Path\n",
"\n",
"# # Get the path to the temporary directory\n",
"# tmpdir = Path(tempfile.mkdtemp())\n",
"\n",
"# print(tmpdir)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Now that the temporary directory is created, we can download the data into it!"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Download Data Here \n",
"\n",
"\n",
"\n",
"\n",
"# #--------------------------------------Ref--------------------------------------------------------------------#\n",
"# #Import the downloader function\n",
"# from navigator.download import NasaCddisV3\n",
"\n",
"# #Create a downloader object\n",
"# downloader = NasaCddisV3(logging=True, threads=5) # Threads is optional but recommended for faster downloads"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Now let's download the rinex data pairs form the server!"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Show download function here!\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"# #--------------------------------------Ref--------------------------------------------------------------------#\n",
"# #Fetch the files\n",
"# #We will download data from 2021/01/07 for CUSV00 station\n",
"# downloader.download(\n",
"# year=2021,\n",
"# day =7 , # Days must be from [1, 366]\n",
"# save_path=tmpdir,\n",
"# match_string=\"CUSV00\"\n",
"# )"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Let's see what's in the directory now!"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Show the directory contents\n",
"\n",
"# #--------------------------------------Ref--------------------------------------------------------------------#\n",
"# !ls $tmpdir"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"We seem to have downloaded two RINEX files obs and nav from \"CUSV00\" IGS station."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Now that we have the data downloaded, we can use the navigator module to process the data. "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### **Data Processing**"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"The processing of the data is done by using the `parse` libray. Let's see how we can parse the data."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# Grab the downloaded files!\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"\n",
"# --------------------------------------Ref--------------------------------------------------------------------\n",
"# # Lets first wrap all our paths in Path objects\n",
"# from pathlib import Path\n",
"\n",
"# # Tempdir path \n",
"# tmpdir = Path(tmpdir)\n",
"\n",
"# # Let's get the path to the files\n",
"# nav_file = list(tmpdir.rglob(\"*GN*\"))[0]\n",
"# obs_file = list(tmpdir.rglob(\"*MO*\"))[0]\n",
"\n",
"# # Note that globbing only works if there is only one file in the directory. If there are more than one, we need to manually select the file we want\n"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# #--------------------------------------Ref--------------------------------------------------------------------#\n",
"# #Let's see what are the paths to the files\n",
"# print(f\"Path to the nav file: {nav_file}\")\n",
"# print(f\"Path to the obs file: {obs_file}\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Let's get our parser ready to parse the data into a pandas dataframe for data analysis."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Show parsing interface!\n",
"\n",
"\n",
"# #--------------------------------------Ref--------------------------------------------------------------------#\n",
"# # Import parser \n",
"# from navigator.parse import Parser, IParseGPSNav, IParseGPSObs\n",
"\n",
"# # Let's create a parser object for respective files\n",
"# nav_parser = Parser(iparser=IParseGPSNav())\n",
"# obs_parser = Parser(iparser=IParseGPSObs())\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Let's parse the data into a pandas dataframe. This will take some time depending on the size of the data."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# Calling interface \n",
"\n",
"\n",
"\n",
"##--------------------------------------Ref--------------------------------------------------------------------#\n",
"# nav_metadata , nav_data = nav_parser(filepath=nav_file)\n",
"# obs_metadata , obs_data = obs_parser(filepath=obs_file)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Now the data is parsed, we can see the contents of the dataframe."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Show Obs Data\n",
"\n",
"\n",
"\n",
"#--------------------------------------Ref--------------------------------------------------------------------#\n",
"# # Let's see what obs data we have\n",
"# obs_data.head()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"As you can see, the observational data are structured by (time, sv) coordinate. The time here is in (GPS time) which also contains reciever \n",
"clock bias since it it recorded by the reciever."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# Show nav data\n",
"\n",
"\n",
"#--------------------------------------Ref--------------------------------------------------------------------#\n",
"# # Let's see what nav data we have\n",
"# nav_data.head()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"The navigation data are structured by (time, sv) coordinate. The time here is in (GPS time) which contains the satellite clock bias since it is recorded by the satellite. "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## **Ephemris Data and Satellite Position in ECEF Frame**"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Show Ephemeris Interface!\n",
"\n",
"\n",
"\n",
"# #--------------------------------------Ref--------------------------------------------------------------------#\n",
"# # from navigator.core import Satellite, IGPSEphemeris\n",
"\n",
"# # Create a gps satellite object \n",
"# sat = Satellite(iephemeris=IGPSEphemeris())"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Let's plot the trajectory of the satellite for two hours from the t_sv."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# Show how to work with data!\n",
"\n",
"\n",
"##--------------------------------------Ref--------------------------------------------------------------------#\n",
"# # Data of zero hour\n",
"# hr0 = nav_data.index[:2] # Grab Ephemeris for first 2 satellite\n",
"# data_hr0 = nav_data.loc[hr0] \n",
"# data_hr0.head()"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Grab the start of the interpolation time!\n",
"\n",
"\n",
"\n",
"\n",
"##--------------------------------------Ref--------------------------------------------------------------------#\n",
"# # Let's get timestamp of the first row\n",
"# t_sv = data_hr0.index[0][0]\n",
"# t_sv"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Let's get the two hours of data from the t_sv and plot the trajectory of the satellite."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# Show the trajectory function!\n",
"\n",
"\n",
"##--------------------------------------Ref--------------------------------------------------------------------#\n",
"# trajectory = sat.trajectory(\n",
"# t_sv=t_sv, metadata=None, data=data_hr0, interval=2 * 60 * 60, step=100\n",
"# );"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Let's plot the trajectory."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"##--------------------------------------Plot Code!--------------------------------------------------------------------#\n",
"# import matplotlib.pyplot as plt\n",
"\n",
"# # Plot the trajectory\n",
"# fig = plt.figure(figsize=(10, 8))\n",
"# ax = fig.add_subplot(111, projection='3d')\n",
"# ax.set_title('Trajectory of GPS Satellite G32 and G05 through 2 hours in ECEF Frame')\n",
"# for i in range(trajectory.shape[0]): # For each satellite\n",
"# ax.plot(trajectory[i][0], trajectory[i][1], trajectory[i][2]) # Plot"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"If we try to interplote to more that 150 minutes, we will get exponential error in satellite position."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"##--------------------------------------Long Plot--------------------------------------------------------------------#\n",
"# trajectory = sat.trajectory(\n",
"# t_sv=t_sv,\n",
"# metadata=None,\n",
"# data=data_hr0, \n",
"# interval=12 * 60 * 60, # 12 hours\n",
"# step=100,\n",
"# )\n",
"\n",
"# fig = plt.figure(figsize=(10, 8))\n",
"# ax = fig.add_subplot(111, projection='3d')\n",
"# ax.set_title('Trajectory of GPS Satellite G32 and G05 through 12 hours in ECEF Frame')\n",
"# for i in range(trajectory.shape[0]): # For each satellite\n",
"# ax.plot(trajectory[i][0], trajectory[i][1], trajectory[i][2]) # Plot the trajectory"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"As we can see the satellite trajectory is not exactly elliptical. This is because we observed the satellite from the earth which is rotating. To obtain the epllipse,\n",
"we have to rotate the satellite position by the earth rotation angle. "
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"##--------------------------------------Rotation Plot--------------------------------------------------------------------#\n",
"# # Earth rotation rate\n",
"# omega_e = 7.2921151467e-5 # rad/s\n",
"# # Let's rotate the trajectory by 90 degrees starting from 0 index\n",
"# from navigator.utility.transforms.crs_to_trs_rotations import R3\n",
"# rotated_trajectory = trajectory.copy()\n",
"# # For each satellite\n",
"# for i in range(rotated_trajectory.shape[0]):\n",
"# # For each time step\n",
"# for j in range(rotated_trajectory.shape[2]):\n",
"# # Rotate the trajectory\n",
"# rotated_trajectory[i, :, j] = R3(-1 * omega_e * j * 100) @ rotated_trajectory[i, :, j]\n",
"# # Plot the rotated trajectory\n",
"# fig = plt.figure(figsize=(10, 8))\n",
"# ax = fig.add_subplot(111, projection='3d')\n",
"# ax.set_title(\n",
"# 'Trajectory of GPS Satellite G02 and G05 through 12 hours in Inertial Frame'\n",
"# )\n",
"# for i in range(rotated_trajectory.shape[0]): # For each satellite\n",
"# ax.plot(\n",
"# rotated_trajectory[i][0],\n",
"# rotated_trajectory[i][1],\n",
"# rotated_trajectory[i][2],\n",
"# label=f'Satellite {i}',\n",
"# ) # Plot the trajectory\n",
"# ax.legend();"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## **Triangulating User Position**"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### **Available Triangulation Methods**\n",
"As we now can calculate the satellite position at time of emission, we can now triangulate the user position. \n",
"\n",
"Two triangulation methods are available in the library. These are:\n",
"\n",
"- Weighted Least Squares(WLS) Method (See [ESA](https://gssc.esa.int/navipedia/GNSS_Book/ESA_GNSS-Book_TM-23_Vol_I.pdf) Chapter 6 : Code Based Positioning).\n",
"- Unscented Kalman Filter (See [UKF](https://ieeexplore.ieee.org/document/882463))\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### **Epoch: The Unit**\n",
"Theoretically, we don't need anything beside the rinex files to perform the triangulation. However, I have implemented a class called `Epoch` which is a wrapper around the rinex files. This class contatis the following information:\n",
"- Observation Data for a given receiver epoch\n",
"- Nearest satellite ephemeris data for a given receiver epoch\n",
"- Epoch time\n",
"\n",
"This forms a unit of data for triangulation. Moreover, Epoch class also contains guards for invalid data. Let's see how the Epoch class works."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# Show Epoch interface!\n",
"\n",
"##--------------------------------------Ref--------------------------------------------------------------------#\n",
"# from navigator.core import Epoch\n",
"\n",
"# # Get the epoches in the file\n",
"# epoches = list(Epoch.epochify(obs=obs_file, nav=nav_file, mode='maxsv')) # Mode specifies how to pair the obs and nav data, here we are using the maximum number of satellites"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"This breaks down the epoches in the nav and obs files and wraps them in the `Epoch` class. Let's see how many epoches are there in the data."
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# print(f\"There are {len(epoches)} epoches which are exactly equal to 1 day sampling with 30s sampling rate i.e 24 x 60 x 2 = {24 * 60 * 2}\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"There are 2880 epoches in the data which can be indivually triangulated. "
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# first_epoch = epoches[0]"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# first_epoch"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"As we can see that first epoch of observation is at 2021-01-07 00:00:00 which contains 10 satellites and their ephemeris. Let's see what it contains."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# first_epoch.obs_data"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# first_epoch.nav_data"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"All the navigation and observation data are stored in the `Epoch` class in their respective attribute."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Moreover, if the rinex files are named in NASA convention and taken from one of the IGS stations, the `Epoch` class can identify\n",
"the station code. This provides a way to know the coordinate of station to compare with the triangulated position."
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"## Show IGS Network Class! \n",
"\n",
"\n",
"# from navigator.utility import IGSNetwork\n",
"\n",
"# network = IGSNetwork()\n",
"\n",
"# network.get_igs_station(first_epoch.station)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### **Triangulating using WLS Method**"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Now let's triangulate the first epoch of the data to get the user position. We need the triangulate class along with the algorithm we want to use for triangulation."
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# Show triangulation Interface!\n",
"\n",
"##--------------------------------------Ref--------------------------------------------------------------------#\n",
"# from navigator.core import Triangulate, IterativeTriangulationInterface\n",
"\n",
"# # Create a triangulation object\n",
"# triangulator = Triangulate(interface=IterativeTriangulationInterface())"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Create a `Triangulate` class instance and add the IterativeTriangulation interface to it."
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# Show trinagulate function!\n",
"\n",
"# # Triangulate the first epoch\n",
"# triangulator(obs=first_epoch, obs_metadata=obs_metadata, nav_metadata=nav_metadata) # Pass the epoch object and the metadata and we are good to go!\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"As we can see, the traingulator returns *x,y,z* coordinates of the user position with *lat, long* and *height*. The dilution of precision (DOP) is also calculated for the triangulated position."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"##### **Time Series Epoch Triangulation**"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"We can also triangulate the whole data to get a time series of user position and see the error with respect to the time. Let's triangulate the whole data and plot the coordinates and error.\n",
"\n",
"If the data is from an IGS station, we can also get the euclidean distance between the triangulated position and the station position and plot it. This is achieved by `Triangulate.igs_diff` method."
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# Epoch Sort!\n",
"\n",
"##--------------------------------------Ref--------------------------------------------------------------------#\n",
"# # Sort the epoches with respect to time\n",
"# epoches.sort()"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# epoches[:10]"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Now, the epoches are linearly time spaced with 30s sampling rate! Now let's triangulate the whole data and plot the coordinates and error."
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# Show the triangulation loop!\n",
"\n",
"\n",
"##--------------------------------------Ref--------------------------------------------------------------------#\n",
"# import pandas as pd\n",
"\n",
"# # Lets traingulate all the epoches\n",
"# df = []\n",
"\n",
"# for epoch in epoches:\n",
"# df.append(triangulator.igs_diff(obs=epoch, obs_metadata=None, nav_metadata=None))\n",
"\n",
"# # Convert to dataframe for easy visualization\n",
"# df = pd.DataFrame(df)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Now, we can see the dataframe of the triangulated position and the error with respect to the time."
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# df.head() "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"As we had 2880 epoches, we have 2880 triangulated position. Let's plot the error (diff) distribution."
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# Show the error distribution using WLS\n",
"\n",
"##----------------------------------------Ref------------------------------------------------------------\n",
"# import seaborn as sns\n",
"# ax = sns.histplot(data=df, x='diff', kde=True, bins=25)\n",
"\n",
"# ax.set_xlabel('Difference between real and calculated position (meters)');"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Let's calcuate the statistics of the error distribution."
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# df[\"diff\"].describe()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"As we can see the diff is normally distributed around 15m. We are obtatining a bias of 15m in the position. "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Let's see how error varies with respect to the time."
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"##----------------------------------------Diff Plot------------------------------------------------------------\n",
"# ax = sns.lineplot(x=df.index, y=df['diff'], linewidth=0.5, label='Difference', color='blue', alpha=0.5)\n",
"# ax.set_xlabel('Time Step')\n",
"# ax.set_ylabel('Difference (meters)')\n",
"# ax.set_title('Time series of difference between real and calculated position (meters)')\n",
"\n",
"# # Add a horizontal line at mean of the difference\n",
"# ax.axhline(df['diff'].mean(), color='red', linewidth=0.5);\n",
"\n",
"# # Superimpose GDOP on the plot\n",
"# ax.plot(df.index, df['GDOP'], linewidth=0.5, label='GDOP', color='green', alpha=1)\n",
"# ax.legend();"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Clearly, the error is spikin as some point. The GDOP (Geometric Dilution of Precision) is also spiking at the same time. This implies that the error is due to the satellite geometry."
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# ax = sns.lineplot(x=df.index, y=df['GDOP'], linewidth=0.5)\n",
"# ax.set_xlabel('Time Step')\n",
"# ax.set_ylabel('GDOP')\n",
"# ax.set_title('Time series of GDOP')\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Let's scatter plot the calculated position and the station position."
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"# fig = plt.figure(figsize=(6, 6), dpi=300)\n",
"# ax = fig.add_subplot(111, projection='3d')\n",
"# # Plot the calculated positions\n",
"# ax.scatter(df['x'], df['y'], df['z'], c='blue', label='Calculated Position', alpha=0.25)\n",
"# # Real coords\n",
"# real_coord = IGSNetwork().get_xyz(station=first_epoch.station)\n",
"# # Add a red marker for real position\n",
"# ax.scatter(real_coord[0],real_coord[1],real_coord[2],c='red', label='Real Position',marker='o',s=20,)\n",
"# ax.set_xlabel('X (meters)')\n",
"# ax.set_ylabel('Y (meters)')\n",
"# ax.set_zlabel('Z (meters)')\n",
"# ax.set_title('Scatter plot of calculated and real position of station KOKB00USA')\n",
"# fig.tight_layout()\n",
"# ax.legend()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### **Triangulating using UKF Method**"
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"##----------------------------------------Ref------------------------------------------------------------\n",
"# from navigator.core import UnscentedKalmanTriangulationInterface\n",
"\n",
"# # Create a triangulation object\n",
"# unscented_triangulator = Triangulate(interface=UnscentedKalmanTriangulationInterface())"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Now we have the unscented kalman filter triangulator ready to use. Let's see how it predicts the user position and error. \n",
"\n",
"**Note: The UKF is not only able to predict the user position but also the velocity and clock drift.**"
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# Show UKF loop!\n",
"\n",
"\n",
"##----------------------------------------Ref------------------------------------------------------------\n",
"# df_ukf = []\n",
"\n",
"# for epoch in epoches:\n",
"# df_ukf.append(unscented_triangulator.igs_diff(obs=epoch, obs_metadata=obs_metadata, nav_metadata=nav_metadata)) # The API is same as before \n",
"# # The only difference is that we are using a different interface hence different triangulation method! \n",
"\n",
"# # Convert to dataframe for easy visualization\n",
"# df_ukf = pd.DataFrame(df_ukf)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"**Note: The UKF only works with a time series of data since it is a filter.**\n",
"\n",
"Let's see how the UKF triangulator predicts the user position and error."
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"##----------------------------------------Error Plots------------------------------------------------------------\n",
"# # Let's add a time step column\n",
"# df_ukf[\"time_step\"] = df_ukf.index\n",
"# # Let's plot the first 15 timestep of the ukf prediction of dynamic variables x, y, z\n",
"# fig, ax = plt.subplots(1,3, figsize=(15,5), dpi=150)\n",
"# def plot_timesteps(df, start: int =0, end: int = 15, column :str = 'x', ax=None):\n",
"# \"\"\"Plot the time series of a column in dataframe\"\"\"\n",
"# ax.plot(df['time_step'][start:end], df[column][start:end], label=column)\n",
"# ax.set_xlabel('Time Step')\n",
"# ax.set_ylabel(f'{column} (meters)')\n",
"# ax.set_title(f'Time series of {column} prediction (meters)')\n",
"# ax.legend()\n",
"# return ax\n",
"# plot_timesteps(df_ukf, column='x', ax=ax[0]);\n",
"# plot_timesteps(df_ukf, column='y', ax=ax[1]);\n",
"# plot_timesteps(df_ukf, column='z', ax=ax[2]);"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"It seems like the UKF is converging after just 4 epoches."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Let's plot the convergence of the velocity and clock drift."
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"##----------------------------------------Ref------------------------------------------------------------\n",
"# # Let's plot the first 15 timestep of the ukf prediction of error in velocity\n",
"# fig, ax = plt.subplots(1,3, figsize=(15,5), dpi=150)\n",
"\n",
"# plot_timesteps(df_ukf, column='x_dot', ax=ax[0]);\n",
"# plot_timesteps(df_ukf, column='y_dot', ax=ax[1]);\n",
"# plot_timesteps(df_ukf, column='z_dot', ax=ax[2]);\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Clearly this is a static receiver and it makes sense that the velocity and clock drift is converging to zero."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Let's see the oscilation of the error with respect to the time."
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"##----------------------------------------Ref------------------------------------------------------------\n",
"# # Let's plot the first 15 timestep of the ukf prediction of error in position\n",
"# fig, ax = plt.subplots(1,1, figsize=(5,5), dpi=150)\n",
"\n",
"# plot_timesteps(df_ukf, column='diff', ax=ax);"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Clearly the UKF is converging to the true position. Let's see the error distribution."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Now let's see the oscillation in the clock bias and clock drift."
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"##----------------------------------------Ref------------------------------------------------------------\n",
"# fig , ax = plt.subplots(1,2, figsize=(15,5), dpi=150)\n",
"# plot_timesteps(df_ukf, column='cdt', ax=ax[0], start=30, end=500);\n",
"# plot_timesteps(df_ukf, column='cdt_dot', ax=ax[1], start=30, end=500);"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Clearlt, the clock drift is almost zero and oscillating around zero. The clock bias is also oscillating around zero. \n",
"\n",
"**Note the the units are meter here since it is multiplied by speed of light!**"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Now let's plot the time series of the error just like WLS method."
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"##----------------------------------------Ref------------------------------------------------------------\n",
"# # We will skip the first 10 timesteps since they are too large to plot!\n",
"# fig, ax = plt.subplots(1,1, figsize=(15,5), dpi=150)\n",
"# ax = sns.lineplot(data=df_ukf.iloc[10:], x='time_step', y=\"diff\", label=\"Time series of difference between real and calculated position (meters)\", ax = ax)\n",
"# ax = sns.lineplot(x=df_ukf.index[10:], y=df[\"GDOP\"][10:], label=\"Time series of GDOP (meters)\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Let's 3D plot the position and true position."
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"outputs": [],
"source": [
"##----------------------------------------Ref------------------------------------------------------------\n",
"# fig = plt.figure(figsize=(10, 8), dpi=150)\n",
"# ax = fig.add_subplot(111, projection='3d')\n",
"# # Plot the calculated positions\n",
"# ax.scatter(df_ukf['x'][10:], df_ukf['y'][10:], df_ukf['z'][10:], c='blue', label='Calculated Position', alpha=0.25)\n",
"# # Real coords\n",
"# real_coord = IGSNetwork().get_xyz(station=first_epoch.station)\n",
"# # Add a red marker for real position\n",
"# ax.scatter(real_coord[0],real_coord[1],real_coord[2],c='red',label='Real Position',marker='o',s=50,)\n",
"# ax.set_xlabel('X (meters)')\n",
"# ax.set_ylabel('Y (meters)')\n",
"# ax.set_zlabel('Z (meters)')\n",
"# ax.set_title('Scatter plot of calculated and real position of station KOKB00USA')\n",
"# fig.tight_layout()"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"### **Comparing WLS and UKF**"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Now let's compare the WLS and UKF method. Let's plot the error distribution of both methods."
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"##----------------------------------------Ref------------------------------------------------------------\n",
"# # Error distribution in UKF and WLS\n",
"# fig, ax = plt.subplots(1,1, figsize=(15,5), dpi=150)\n",
"\n",
"# ax = sns.histplot(data=df_ukf[10:], x='diff', kde=True, bins=25, label='UKF', color='blue', alpha=0.5)\n",
"# ax = sns.histplot(data=df, x='diff', kde=True, bins=25, label='WLS', color='red', alpha=0.5)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"Clearly, the UKF probability distribution is a bit more centered!"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"Let's plot the error time series of both methods."
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"outputs": [],
"source": [
"# fig , ax = plt.subplots(1,1, figsize=(15,5), dpi=150)\n",
"\n",
"# ax = sns.lineplot(data=df_ukf[10:], x='time_step', y=\"diff\", label=\"Time series of difference UKF\", ax = ax)\n",
"# ax = sns.lineplot(x=df[10:].index, y=df[\"diff\"][10:], label=\"Time series of difference between WLS\", ax = ax)"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## **Conclusion**"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"The library demonstrates its ability to parse RINEX files and calculate PVT solutions. Each component within the library operates independently, enabling its use with other GNSS systems such as GLONASS, Galileo, etc. Furthermore, it can handle the download, storage, and processing of large batches of RINEX files.\n",
"\n",
"The two triangulation interface seems to be performing similarly. However, the UKF is able to predict the velocity and clock drift which is not easy with WLS method. Moreover, the UKF can be tuned by user to get better results."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## **Future Works**"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"We've developed a comprehensive, modular, and scalable library for GNSS navigation and positioning. However, several tasks remain outstanding. Here are some key areas for future development, along with the necessary skill sets:\n",
"\n",
"1. **Addition of Other Constellations** \\\n",
" This requires a good understanding of RINEX format, rinex parsing, satellite ephemeris and the `satlib` module. This task requires a good understanding of the navigator library and its inner workings. The candidate should also have a good understanding of the `satlib` module and its classes.\n",
"2. **Addition of Other Triangulation Methods** \\\n",
" The library can be improved by adding models for ionospheric and tropospheric corrections. Moreover Single Difference and Double Difference methods can be added to the library for improved accuracy. This task requires a good understanding of the `triangulate` module and its classes. The candidate should also have a good understanding of the `satlib` module and its classes.\n",
"3. **Extending the parsing RINEX files to v4** \\\n",
" Ideally, this should be done by a candidate who has a good knowledge of programming in Python and software engineering (CS majors after sophmore year). Adapeters need to be written to make the rinex files compatible with the current library data format of `pandas.Dataframe`. Parsing format is tightly coupled with other modules like `satlib` and `triangulate`. Hence, the candidate should have a good understanding of the library and its modules.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## **AI/ML integration into the GNSS toolkit** \n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
" There are many places where AI/ML can be integrated into the GNSS problem. While doing some research, these are few places where AI/ML can be integrated into the GNSS problem:\n",
"\n",
"1. **PINNs based Satellite Orbit Prediction** \\\n",
" The satellite brocast ephemeris are accurate upto 1m, see [SP3](../../intro/intro_sp3_orbit.ipynb) and increases exponentially if user tries to predict the obrbit for more that 150 minutes. This is due to the fact that the satellite orbits are pertubed by the gravitational forces of the sun, moon, earth's oblateness, solar wind! A basic model of pertubation theory can be developed for the satellite orbit which includes known pertubations. The rest of the unknown pertubations can be learned by PINNs (Physics Informed Neural Networks) to model the orbit of the satellite accurately.\n",
"\n",
" This has and added advantage that if we develop such a model, we can predict the orbit of the satellite for certaion time period and simulate the real satellite constellation. This can be used to test the GNSS receiver in the lab.\n",
"\n",
"2. **AI based Triangulation** \\\n",
" There is a very good paper on Set Transformer based GNSS positioning. The paper can be found [here](https://arxiv.org/abs/2110.09581). The paper uses Set Transformer to iteratevly correct the user position. The paper is very interesting and can be used to improve the triangulation method in the library.\n",
"\n",
"3. **AI based GNSS filtering** \\\n",
" For the time series data, neural networks like [KalmanNet](https://arxiv.org/pdf/2107.10043.pdf) which is based on Recurrent Neural Network can be used to filter the GNSS data. This can be used to improve the triangulation method in the library. Using the single differenced and phase correction method, the triangulation can be stablized and improved.\n",
"\n",
"\n",
"Other ideas are welcome and can be discussed with the lab members."
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "slide"
}
},
"source": [
"## **References**"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### ESA Data Processing and Analysis Consortium (DPAC) GNSS Book"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"- Sanz Subirana, J., Juan Zornoza, J.M., Hernández-Pajares, M. (2013). GNSS Data Processing, Vol. 1: Fundamentals and Algorithms (ESA TM-23/1). Noordwijk, the Netherlands: ESA Communications. ISBN 978-92-9221-886-7.\n",
"\n",
"- Sanz Subirana, J., Juan Zornoza, J.M., Hernández-Pajares, M. (2013). GNSS Data Processing, Vol. 2: Laboratory exercises (ESA TM-23/2). Noordwijk, the Netherlands: ESA Communications. ISBN 978-92-9221-886-7.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Interface Control Document ICD-GPS-200"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"- Interface Specification IS-GPS-200. (2922). Retrieved from GPS.gov: [https://www.gps.gov/technical/icwg/IS-GPS-200N.pdf]"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### Set Transformer based GNSS Positioning\n",
"Kanhere, Ashwin & Gupta, Shubh & Shetty, Akshay & Gao, Grace. (2021). Improving GNSS Positioning using Neural Network-based Corrections. "
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "subslide"
}
},
"source": [
"### KalmanNet"
]
},
{
"cell_type": "markdown",
"metadata": {
"slideshow": {
"slide_type": "fragment"
}
},
"source": [
"G. Revach, N. Shlezinger, R. J. G. van Sloun and Y. C. Eldar, \"Kalmannet: Data-Driven Kalman Filtering,\" ICASSP 2021 - 2021 IEEE International Conference on Acoustics, Speech and Signal Processing (ICASSP), Toronto, ON, Canada, 2021, pp. 3905-3909, doi: 10.1109/ICASSP39728.2021.9413750."
]
}
],
"metadata": {
"celltoolbar": "Slideshow",
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.5"
}
},
"nbformat": 4,
"nbformat_minor": 4
}