Example #1
0
    def plot_obstype_availability(
        self,
        figure_name: str = "plot_obstype_availability_{system}.{FIGURE_FORMAT}",
    ) -> List[pathlib.PosixPath]:
        """Generate GNSS observation type observation type availability based on RINEX observation file
 
        Args:
            figure_name: File name of figure.
            
        Returns:
            List with figure path for observation type availability depending on GNSS. File name ends with GNSS 
            identifier (e.g. 'E', 'G'), for example 'plot_obstype_availability_E.png'.
        """

        figure_paths = list()

        for sys in sorted(self.dset.unique("system")):

            x_arrays = []
            y_arrays = []

            idx_sys = self.dset.filter(system=sys)
            num_sat = len(set(self.dset.satellite[idx_sys]))

            figure_path = self.figure_dir / figure_name.replace(
                "{system}", sys).replace("{FIGURE_FORMAT}", FIGURE_FORMAT)
            figure_paths.append(figure_path)

            for sat in sorted(self.dset.unique("satellite"), reverse=True):
                if not sat.startswith(sys):
                    continue

                idx_sat = self.dset.filter(satellite=sat)
                keep_idx = np.full(self.dset.num_obs, False, dtype=bool)

                for obstype in self._sort_string_array(
                        self.dset.meta["obstypes"][sys]):
                    keep_idx[idx_sat] = np.logical_not(
                        np.isnan(self.dset.obs[obstype][idx_sat]))
                    #time_diff = np.diff(self.dset.time.gps.gps_seconds[keep_idx])
                    #time_diff = np.insert(time_diff, 0, float('nan'))

                    if np.any(keep_idx):
                        num_obs = len(self.dset.time[keep_idx])
                        x_arrays.append(self.dset.time.gps.datetime[keep_idx])
                        y_arrays.append(np.full(num_obs, f"{sat}_{obstype}"))

            plot(
                x_arrays=x_arrays,
                y_arrays=y_arrays,
                xlabel="Time [GPS]",
                ylabel="Satellite and observation type",
                figure_path=figure_path,
                y_unit="",
                opt_args={
                    "colormap": "tab20",
                    "figsize": (1.0 * num_sat, 3.0 * num_sat),
                    "fontsize": 5,
                    "plot_to": "file",
                    "plot_type": "scatter",
                    #"title": "Satellite and observation type",
                },
            )

        return figure_paths
Example #2
0
    def plot_skyplot(
        self,
        figure_name: str = "plot_skyplot_{system}.{FIGURE_FORMAT}",
    ) -> List[pathlib.PosixPath]:
        """Plot skyplot for each GNSS
        
        Args:
            figure_name: File name of figure.
    
        Returns:
            List with figure path for skyplot depending on GNSS. File name ends with GNSS identifier (e.g. 'E', 'G'), 
            for example 'plot_skyplot_E.png'.
        """
        figure_paths = list()

        # Convert azimuth to range 0-360 degree
        azimuth = self.dset.site_pos.azimuth
        idx = azimuth < 0
        azimuth[idx] = 2 * np.pi + azimuth[idx]

        # Convert zenith distance from radian to degree
        zenith_distance = np.rad2deg(self.dset.site_pos.zenith_distance)

        # Generate x- and y-axis data per system
        for sys in sorted(self.dset.unique("system")):
            x_arrays = []
            y_arrays = []
            labels = []

            figure_path = self.figure_dir / figure_name.replace(
                "{system}", sys).replace("{FIGURE_FORMAT}", FIGURE_FORMAT)
            figure_paths.append(figure_path)

            for sat in sorted(self.dset.unique("satellite")):
                if not sat.startswith(sys):
                    continue
                idx = self.dset.filter(satellite=sat)
                x_arrays.append(azimuth[idx])
                y_arrays.append(zenith_distance[idx])
                labels.append(sat)

            # Plot with polar projection
            # TODO: y-axis labels are overwritten after second array plot. Why? What to do?
            plot(
                x_arrays=x_arrays,
                y_arrays=y_arrays,
                xlabel="",
                ylabel="",
                y_unit="",
                labels=labels,
                figure_path=figure_path,
                opt_args={
                    "colormap": "hsv",
                    "figsize": (7, 7.5),
                    "legend": True,
                    "legend_ncol": 6,
                    "legend_location": "bottom",
                    "plot_to": "file",
                    "plot_type": "scatter",
                    "projection": "polar",
                    "title":
                    f"Skyplot for {enums.gnss_id_to_name[sys]}\n Azimuth [deg] / Elevation[deg]",
                    "xlim": [0, 2 * np.pi],
                    "ylim": [0, 90],
                    "yticks": (range(0, 90, 30)),  # sets 3 concentric circles
                    "yticklabels":
                    (map(str, range(90, 0, -30))
                     ),  # reverse labels from zenith distance to elevation
                },
            )

        return figure_paths
Example #3
0
    def plot_satellite_elevation(
        self,
        figure_name: str = "plot_satellite_elevation_{system}.{FIGURE_FORMAT}",
    ) -> List[pathlib.PosixPath]:
        """Plot satellite elevation for each GNSS
    
        Args:
            figure_name: File name of figure.
            
        Returns:
            List with figure path for skyplot depending on GNSS. File name ends with GNSS identifier (e.g. 'E', 'G'), 
            for example 'plot_skyplot_E.png'.
        """
        figure_paths = list()

        # Convert elevation from radian to degree
        elevation = np.rad2deg(self.dset.site_pos.elevation)

        # Limit x-axis range to rundate
        day_start, day_end = self._get_day_limits()

        # Generate x- and y-axis data per system
        for sys in sorted(self.dset.unique("system")):
            x_arrays = []
            y_arrays = []
            labels = []

            figure_path = self.figure_dir / figure_name.replace(
                "{system}", sys).replace("{FIGURE_FORMAT}", FIGURE_FORMAT)
            figure_paths.append(figure_path)

            for sat in sorted(self.dset.unique("satellite")):
                if not sat.startswith(sys):
                    continue
                idx = self.dset.filter(satellite=sat)
                x_arrays.append(self.dset.time.gps.datetime[idx])
                y_arrays.append(elevation[idx])
                labels.append(sat)

            # Plot with scatter plot
            plot(
                x_arrays=x_arrays,
                y_arrays=y_arrays,
                xlabel="Time [GPS]",
                ylabel="Elevation [deg]",
                y_unit="",
                labels=labels,
                figure_path=figure_path,
                opt_args={
                    "colormap": "hsv",
                    "figsize": (7, 8),
                    "legend": True,
                    "legend_ncol": 6,
                    "legend_location": "bottom",
                    "plot_to": "file",
                    "plot_type": "scatter",
                    "title":
                    f"Satellite elevation for {enums.gnss_id_to_name[sys]}",
                    "xlim": [day_start, day_end],
                },
            )

        return figure_paths
def _plot_velocity_error(
    dfs_day: Dict[str, pd.core.frame.DataFrame],
    dfs_month: Dict[str, pd.core.frame.DataFrame],
    figure_dir: "pathlib.PosixPath",
    file_vars: Dict[str, Any],
) -> None:
    """Plot 2D and 3D velocity error plots (95th percentile) 

    Args:
       dfs_day:     Dictionary with fields as keys (e.g. site_vel_h, site_vel_3d) and the belonging dataframe as value  
                    with DAILY samples of 95th percentile and stations as columns.
       dfs_month:   Dictionary with fields as keys (e.g. hpe, vpe) and the belonging dataframe as value with MONTHLY
                    samples of 95th percentile and stations as columns.
       figure_dir:  Figure directory
    """
    ylabel = {
        "site_vel_h": "2D VE 95%",
        "site_vel_3d": "3D VE 95%",
    }

    opt_args = {
        "colormap": "tab20",
        "figsize": (7, 3),
        # "grid": True,
        "marker": "o",
        "markersize": "4",
        "linestyle": "solid",
        "plot_to": "file",
        "plot_type": "plot",
        # "statistic": ["rms", "mean", "std", "min", "max", "percentile"], #TODO: Is only shown for data, which are plotted at last.
        "title": file_vars["solution"].upper(),
    }

    colors = (config.tech.gnss_vel_comparison_report.colors.list
              if config.tech.gnss_vel_comparison_report.colors.list else
              ["orange", "red", "violet", "blue", "green"])

    colors = (config.tech.gnss_vel_comparison_report.colors.list
              if config.tech.gnss_vel_comparison_report.colors.list else
              ["orange", "red", "violet", "blue", "green"])

    # Loop over sampled data
    samples = {"daily": dfs_day, "monthly": dfs_month}
    for sample_name, sample_data in samples.items():

        # Loop over fields to plot
        for field in ["site_vel_h", "site_vel_3d"]:

            if field == "site_vel_h":
                opt_args["ylim"] = [0.0, 0.03]
            elif field == "site_vel_3d":
                opt_args["ylim"] = [0.0, 0.07]

            # Generate x- and y-arrays for plotting
            x_arrays = []
            y_arrays = []
            labels = []
            for station in sample_data[field].columns:
                # if sample_name == "monthly":
                #    opt_args.update({"xlim": "auto", "ylim": [0.0, 3.0]})
                x_data = (sample_data[field].index.to_pydatetime() if
                          isinstance(sample_data[field].index,
                                     pd.core.indexes.datetimes.DatetimeIndex)
                          else sample_data[field].index)
                x_arrays.append(list(x_data))
                y_arrays.append(list(sample_data[field][station]))
                labels.append(station.upper())

            # Generate plot
            plot(
                x_arrays=x_arrays,
                y_arrays=y_arrays,
                xlabel="Time [GPS]",
                ylabel=f"{ylabel[field]}",
                y_unit="m/s",
                labels=labels,
                colors=colors,
                figure_path=figure_dir /
                f"plot_{field}_{sample_name}_{file_vars['date']}_{file_vars['solution'].lower()}.{FIGURE_FORMAT}",
                opt_args=opt_args,
            )
Example #5
0
def _plot_position_error(
    dfs_day: Dict[str, pd.core.frame.DataFrame],
    dfs_month: Dict[str, pd.core.frame.DataFrame],
    figure_dir: PosixPath,
    file_vars: Dict[str, Any],
) -> None:
    """Plot horizontal and vertical position error plots 

    Args:
        dfs_day:    Dictionary with function type as keys ('mean', 'percentile', 'rms', 'std') and a
                    dictionary as values. The dictionary has fields as keys (e.g. hpe, vpe) and the 
                    belonging dataframe as value with DAILY samples of 95th percentile and stations as
                    columns.
        dfs_month   Dictionary with function type as keys ('mean', 'percentile', 'rms', 'std') and a
                    dictionary as values. The dictionary has fields as keys (e.g. hpe, vpe) and the
                    belonging dataframe as value with MONTHLY samples of 95th percentile and stations as
                    columns.
       figure_dir:  Figure directory
    """

    ylabel_def = {
        "mean": "MEAN",
        "percentile": "95%",
        "rms": "RMS",
        "std": "STD",
    }

    opt_args = {
        "colormap": "tab20",
        "figsize": (7, 3),
        # "grid": True,
        "marker": "o",
        "markersize": "4",
        "linestyle": "solid",
        "plot_to": "file",
        "plot_type": "plot",
        # "statistic": ["rms", "mean", "std", "min", "max", "percentile"], #TODO: Is only shown for data, which are plotted at last.
        "title": config.tech.gnss_comparison_report.title.str.upper(),
    }

    colors = (config.tech.gnss_comparison_report.colors.list
              if config.tech.gnss_comparison_report.colors.list else
              ["orange", "red", "violet", "blue", "green"])

    colors = (config.tech.gnss_comparison_report.colors.list
              if config.tech.gnss_comparison_report.colors.list else
              ["orange", "red", "violet", "blue", "green"])

    # Loop over statistical solutions
    for type_ in dfs_day.keys():

        # Get used samples
        samples = dict()
        for sample in config.tech.gnss_comparison_report.samples.list:
            if "daily" == sample:
                samples["daily"] = dfs_day[type_]
            elif "monthly" == sample:
                samples["monthly"] = dfs_month[type_]
            else:
                log.fatal(
                    f"Sample '{sample}' is not defined. Only 'daily' and/or 'monthly' can be chosen as sample."
                )

        # Loop over sampled data
        for sample, sample_data in samples.items():

            # Loop over fields to plot
            for field in [
                    "east", "north", "up", "hpe", "vpe", "pos_3d", "pdop",
                    "hdop", "vdop"
            ]:

                # Get y-range limits
                if field == "hpe":
                    ylim = config.tech.gnss_comparison_report.ylim_hpe.list
                elif field == "vpe":
                    ylim = config.tech.gnss_comparison_report.ylim_vpe.list
                elif field == "pos_3d":
                    ylim = config.tech.gnss_comparison_report.ylim_pos_3d.list
                else:
                    ylim = config.tech.gnss_comparison_report.ylim.list

                opt_args["ylim"] = [float(ylim[0]),
                                    float(ylim[1])] if ylim else ylim

                # Generate x- and y-arrays for plotting
                x_arrays = []
                y_arrays = []
                labels = []

                for station in sample_data[field].columns:
                    #if sample == "monthly":
                    #    opt_args.update({"xlim": "auto", "ylim": "auto"})
                    x_arrays.append(list(sample_data[field].index))
                    y_arrays.append(list(sample_data[field][station]))
                    labels.append(station.upper())

                # Generate plot
                plot(
                    x_arrays=x_arrays,
                    y_arrays=y_arrays,
                    xlabel="Time [GPS]",
                    ylabel=f"3D {ylabel_def[type_]}" if field == "pos_3d" else
                    f"{field.upper()} {ylabel_def[type_]}",
                    y_unit="m",
                    labels=labels,
                    colors=colors,
                    figure_path=figure_dir /
                    f"plot_{type_}_{field}_{sample}_{file_vars['date']}_{file_vars['solution'].lower()}.{FIGURE_FORMAT}",
                    opt_args=opt_args,
                )
Example #6
0
def _plot_satellite_overview(
        dset: "Dataset", figure_dir: "pathlib.PosixPath") -> Union[None, Enum]:
    """Plot satellite observation overview

    Args:
       dset:        A dataset containing the data.
       figure_dir:  Figure directory
       
    Returns:
       Error exit status if necessary datasets could not be read
    """
    figure_path = figure_dir / f"plot_satellite_overview.{FIGURE_FORMAT}"

    # Limit x-axis range to rundate
    day_start, day_end = _get_day_limits(dset)

    # Get time and satellite data from read and orbit stage
    file_vars = {**dset.vars, **dset.analysis}
    file_vars["stage"] = "read"
    file_path = config.files.path("dataset", file_vars=file_vars)
    if file_path.exists():
        time_read, satellite_read = _sort_by_satellite(
            _get_dataset(dset,
                         stage="read",
                         systems=dset.meta["obstypes"].keys()))
        time_orbit, satellite_orbit = _sort_by_satellite(
            _get_dataset(dset,
                         stage="orbit",
                         systems=dset.meta["obstypes"].keys()))
        time_edit, satellite_edit = _sort_by_satellite(
            _get_dataset(dset,
                         stage="edit",
                         systems=dset.meta["obstypes"].keys()))

    else:
        # NOTE: This is the case for concatencated Datasets, where "read" and "edit" stage data are not available.
        log.warn(
            f"Read dataset does not exists: {file_path}. Plot {figure_path} can not be plotted."
        )
        return enums.ExitStatus.error

    # Generate plot
    plot(
        x_arrays=[time_read, time_orbit, time_edit],
        y_arrays=[satellite_read, satellite_orbit, satellite_edit],
        xlabel="Time [GPS]",
        ylabel="Satellite",
        y_unit="",
        # labels = ["Rejected in orbit stage", "Rejected in edit stage", "Kept observations"],
        colors=["red", "orange", "green"],
        figure_path=figure_path,
        opt_args={
            "colormap": "tab20",
            "figsize": (7, 6),
            "marker": "|",
            "plot_to": "file",
            "plot_type": "scatter",
            "title": "Overview over satellites",
            "xlim": [day_start, day_end],
        },
    )
Example #7
0
def _plot_gnss_signal_in_space_status(dset: "Dataset",
                                      figure_dir: "pathlib.PosixPath") -> None:
    """Generate GNSS Signal-in-Space (SIS) status plot based on SIS status given in RINEX navigation file

    The SIS status can be:

     | CODE | SIS STATUS      | PLOTTED COLOR | DESCRIPTION                     |
     |------|-----------------|---------------|---------------------------------|
     |   0  | healthy         |         green | SIS status used by all GNSS     |
     |   1  | marginal (SISA) |        yellow | SIS status only used by Galileo |
     |   2  | marignal (DVS)  |        orange | SIS status only used by Galileo |
     |   3  | unhealthy       |           red | SIS status used by all GNSS     |

    Args:
       dset:        A dataset containing the data.
       figure_dir:  Figure directory
    """
    colors = ["green", "yellow", "orange", "red"]
    labels = ["healthy", "marginal (sisa)", "marginal (dvs)", "unhealthy"]
    status_def = [0, 1, 2, 3]
    signal = None

    # Select only one Galileo signal
    # Note: Navigation message for Galileo can include I/NAV and F/NAV messages for different signals (E1, E5a, E5b).
    #       For plotting we choose only one of them.
    if "E" in dset.unique("system"):
        signal, _ = _get_first_galileo_signal(dset)

    # Generate time and satellite data for given SIS status
    x_arrays = []
    y_arrays = []
    for status in status_def:
        time, satellite = _get_gnss_signal_in_space_status_data(
            dset, status, signal)
        x_arrays.append(time)
        y_arrays.append(satellite)

    # Limit x-axis range to rundate
    day_start, day_end = _get_day_limits(dset)

    # Generate plot
    plot(
        x_arrays=x_arrays,
        y_arrays=y_arrays,
        xlabel="Time [GPS]",
        ylabel="Satellite",
        y_unit="",
        labels=labels,
        colors=colors,
        figure_path=figure_dir /
        f"plot_gnss_signal_in_space_status.{FIGURE_FORMAT}",
        opt_args={
            "figsize": (7, 11),
            "marker": "s",
            "marksersize": 10,
            "legend_ncol": 4,
            "legend_location": "bottom",
            "plot_to": "file",
            "plot_type": "scatter",
            "tick_labelsize": ("y", 7),  # specify labelsize 7 for y-axis
            "title": f"GNSS signal-in-space status",
            "xlim": [day_start, day_end],
        },
    )
Example #8
0
def _plot_galileo_signal_in_space_status(
        dset: "Dataset", figure_dir: "pathlib.PosixPath") -> None:
    """Generate Galileo Signal-in-Space (SIS) status plot based on Galileo signal health status (SHS), SIS Accuracy 
    (SISA) and data validity status (DVS) given in RINEX navigation file.

    The SIS status can be:

     | CODE | SIS STATUS      | PLOTTED COLOR | DESCRIPTION                     |
     |------|-----------------|---------------|---------------------------------|
     |   0  | healthy         |         green | SIS status used by all GNSS     |
     |   1  | marginal (SISA) |        yellow | SIS status only used by Galileo |
     |   2  | marignal (DVS)  |        orange | SIS status only used by Galileo |
     |   3  | unhealthy       |           red | SIS status used by all GNSS     |

    Args:
       dset:        A dataset containing the data.
       figure_dir:  Figure directory.
    """
    colors = ["green", "yellow", "orange", "red"]
    labels = ["healthy", "marginal (sisa)", "marginal (dvs)", "unhealthy"]
    status_def = [0, 1, 2, 3]
    signals = _select_galileo_signal(dset)

    # Generate plot for each given Galileo signal (e.g. E1, E5a, E5b)
    for signal, nav_type in sorted(signals.items()):

        x_arrays = []
        y_arrays = []
        for status in status_def:
            time, satellite = _get_gnss_signal_in_space_status_data(
                dset, status, signal, only_galileo=True)

            x_arrays.append(time)
            y_arrays.append(satellite)

        # Limit x-axis range to rundate
        day_start, day_end = _get_day_limits(dset)

        # Generate plot
        plot(
            x_arrays=x_arrays,
            y_arrays=y_arrays,
            xlabel="Time [GPS]",
            ylabel="Satellite",
            y_unit="",
            labels=labels,
            colors=colors,
            figure_path=figure_dir /
            f"plot_galileo_signal_in_space_status_{signal}.{FIGURE_FORMAT}",
            opt_args={
                "figsize": (7, 5),
                "marker": "s",
                "marksersize": 10,
                "legend_ncol": 4,
                "legend_location": "bottom",
                "plot_to": "file",
                "plot_type": "scatter",
                "title":
                f"Galileo signal-in-space status for signal {signal.upper()} ({nav_type})",
                "xlim": [day_start, day_end],
            },
        )