How to Create a New NavData Class

This tutorial explains how to create a new parser if necessary.

Load gnss_lib_py into the Python workspace

[1]:
import gnss_lib_py as glp

The modular and versatile functionality of this gnss_lib_py repository is enabled by loading all data types into a custom Python NavData class. If you are using a type of data or dataset that is not yet supported, you will need to create a new child class of the NavData Python class. This tutorial will guide you on how to set up your new Python child class. Once complete, please feel free to submit a pull request to our GitHub repository so other users can make use of the added functionality.

For this example, say that we have a new type of data called MyReceiver that is a csv file with columns of a timestamp, satellite identifier, and pseudorange. The contents of a sample myreceiver.csv is the following:

myTimestamp

mySatId

myPseudorange

10

10

270000001

10

14

270000007

10

7

270000004

10

3

270000005

11

10

270000002

11

14

270000008

11

7

270000003

11

3

270000004

The first step is importing the base NavData class and creating a new class type that inherits from NavData

class MyReceiver(NavData):
    """Class handling measurements from MyReceiver.

    Inherits from NavData().
    """

The __init__ function should have a call to the parent NavData __init__ function. Based on your data input, you should call the corresponding initializer.

For csv files, call: super().__init__(csv_path=input_path)
For pandas DataFrames, call super().__init__(pandas_df=input_path)
For numpy ndarrays, call super().__init__(numpy_array=input_path)

In our case, we have a csv file, so our __init__ function looks like the following:

def __init__(self, input_path):
    """MyReceive specific loading and preprocessing

    Parameters
    ----------
    input_path : string
        Path to MyReceiver csv file

    """

    # call NavData initialization with csv path
    super().__init__(csv_path=input_path)

After our data is loaded, we may want to make known changes to our data. We can make those changes by defining a postprocess function. NavData’s __init__ function that we call in our initializer already makes a call to the postprocess function, so we don’t have to repeat that call in MyReceiver’s __init__ function.

One thing that we need to do to make use of the common functionality of gnss_lib_py is to standardize the names of our variables. See the Standard Naming Conventions section in the Reference tab of the documentation for the list of standardized names.

In our case, we will convert mySatId to sv_id and myPseudorange to raw_pr_m. We make these conversions by simply updating the _row_map function.

@staticmethod
def _row_map():
    """Map of column names from loaded to gnss_lib_py standard

    Returns
    -------
    row_map : Dict
        Dictionary of the form {old_name : new_name}
    """
    row_map = {'mySatId' : 'sv_id',
               'myPseudorange' : 'raw_pr_m',
               }
    return row_map

As an additional postprocessing step, we may want to offset our pseudorange due to a known error or create the common timestamp variable gps_millis based on our unique timestamp row. Adding the gps_millis row enables the use of some of the common algorithms. The time conversion utilities can be used to create gps_millis from the GPS Week & Time of week, GPS milliseconds, or a datetime object.

# correct pseudorange
self['corr_pr_m'] = self['raw_pr_m'] + 100.

# create common timestamp
self['gps_millis'] = self['myTimestamp'] + 5629719023

Putting it all together, we have:

[2]:
class MyReceiver(glp.NavData):
    """Class handling measurements from MyReceiver.

    Inherits from NavData().
    """
    def __init__(self, input_path):
        """MyReceive specific loading and preprocessing

        Parameters
        ----------
        input_path : string
            Path to MyReceiver csv file

        """

        # call NavData initialization with csv path
        super().__init__(csv_path=input_path)

    def postprocess(self):
        """MyReceiver specific postprocessing

        """

        # correct pseudorange
        self['corr_pr_m'] = self['raw_pr_m'] + 100.

        # create common timestamp
        self['gps_millis'] = self['myTimestamp'] + 1659075505350


    @staticmethod
    def _row_map():
        """Map of column names from loaded to gnss_lib_py standard

        Returns
        -------
        row_map : Dict
            Dictionary of the form {old_name : new_name}
        """
        row_map = {'mySatId' : 'sv_id',
                   'myPseudorange' : 'raw_pr_m',
                   }
        return row_map

We can now create a instance of our new MyReceiver class with the path to our csv called myreceiver.csv.

[3]:
# download myreceiver.csv file
glp.make_dir("../data")
!wget https://raw.githubusercontent.com/Stanford-NavLab/gnss_lib_py/main/notebooks/tutorials/data/myreceiver.csv --quiet -O "../data/myreceiver.csv"

# create instance of MyReceiver
my_receiver_data = MyReceiver("../data/myreceiver.csv")

Let’s print out our corrected pseudorange to make sure everything worked correctly.

[4]:
my_receiver_data["corr_pr_m"]
[4]:
array([2.70000101e+08, 2.70000107e+08, 2.70000104e+08, 2.70000105e+08,
       2.70000102e+08, 2.70000108e+08, 2.70000103e+08, 2.70000104e+08])

We can now take advantage of all the tools gnss_lib_py has to offer!

[5]:
fig = glp.plot_metric(my_receiver_data,"gps_millis","corr_pr_m",groupby="sv_id")
../../_images/tutorials_parsers_tutorials_new_parsers_notebook_22_0.png