Skip to content

nmea

The NMEA 0183 protocol is a standard communication protocol used in marine and navigation systems to exchange data between different electronic devices. It stands for "National Marine Electronics Association 0183."

nmea_0183(path, encoding='UTF-8', nmea_delimiter='$')

Parse NMEA 0183 standard protocol file into a pandas dataframe

Parameters:

Name Type Description Default
path str

[description]

required
encoding str

[description]. Defaults to "UTF-8".

'UTF-8'
nmea_delimiter str

[description]. Defaults to "$".

'$'

Returns:

Type Description
Dataset

xarray.Dataset: [description]

Source code in ocean_data_parser/parsers/nmea.py
def nmea_0183(
    path: str, encoding: str = "UTF-8", nmea_delimiter: str = "$"
) -> xarray.Dataset:
    """Parse NMEA 0183 standard protocol file into a pandas dataframe

    Args:
        path (str): [description]
        encoding (str, optional): [description]. Defaults to "UTF-8".
        nmea_delimiter (str, optional): [description]. Defaults to "$".

    Returns:
        xarray.Dataset: [description]
    """
    """Parse a file containing NMEA information into a pandas dataframe"""

    def rename_variable(name):
        """Rename variable based on variable mapping dictionary or return name"""
        if name == ("Heave", "heading"):
            # fix in https://github.com/Knio/pynmea2/pull/129 but not included in pipy yet
            return ("Heave", "heave")
        return name

    nmea = []
    long_names = {}
    with open(path, encoding=encoding) as f:
        for row, line in enumerate(f):
            if not line:
                continue
            elif nmea_delimiter and nmea_delimiter not in line:
                logger.warning(
                    "Missing NMEA deliminter %s - ignore line %s",
                    nmea_delimiter,
                    line[:-1],
                )
                continue
            try:
                prefix, nmea_string = line.split(nmea_delimiter, 1)
                parsed_line = pynmea2.parse(nmea_delimiter + nmea_string)

                # Retrieve long_names from nmea fields
                long_names.update({field[1]: field[0] for field in parsed_line.fields})
                parsed_items = parsed_line.__dict__
                parsed_dict = {
                    "row": row,
                    "prefix": prefix,
                    "talker": parsed_items.get("talker"),
                    "sentence_type": parsed_items.get("sentence_type"),
                    "subtype": parsed_items.get("subtype"),
                    "manufacturer": parsed_items.get("manufacturer"),
                    **{
                        rename_variable(field[1]): value
                        for field, value in zip(parsed_line.fields, parsed_line.data)
                    },
                }
                # Get extra fields
                extra = _generate_extra_terms(parsed_dict)
                if extra:
                    long_names.update(
                        {short_name: long_names for long_names, short_name in extra}
                    )
                    # add extra fields
                    parsed_dict.update(
                        {short_name: value for (_, short_name), value in extra.items()}
                    )
                nmea += [parsed_dict]
            except (pynmea2.ParseError, AttributeError, ValueError, KeyError):
                logger.error("Unable to parse line: %s", line[:-1])

    # Convert NMEA to a dataframe
    df = pd.DataFrame(nmea).replace({np.nan: None, "": None})
    df = df.astype(
        {
            var: dtype
            for var, dtype in NMEA_0183_DTYPES.items()
            if var in df and dtype != datetime
        }
    )

    # Cast variables to the appropriate type
    unknown_variables_dtype = [var for var in df if var not in NMEA_0183_DTYPES]
    if unknown_variables_dtype:
        logger.warning("unknown dtype for nmea columns: %s", unknown_variables_dtype)
    # Convert datetime columns
    for col in df:
        if NMEA_0183_DTYPES.get(col) != datetime:
            continue
        df[col] = pd.to_datetime(df[col], utc=True).dt.tz_convert(None)

    df = df.replace({np.nan: None, "": None, "None": None})

    # Convert to xarray
    ds = df.to_xarray()
    ds.attrs = global_attributes

    # Add attributes
    # TODO Apply vocabulary
    for var in ds:
        if var in long_names:
            ds[var].attrs["long_name"] = long_names[var]
    return ds