Beispiel #1
0
def add(config_key, dset):
    delta_pos = list()

    for _ in dset.for_each_suffix("station"):
        if position.is_position(dset.site_pos):
            sta_delta = position.PositionDelta(np.zeros((dset.num_obs, 3)),
                                               system="gcrs",
                                               ref_pos=dset.site_pos)
        elif position.is_posvel(dset.site_pos):
            sta_delta = position.PosVelDelta(np.zeros((dset.num_obs, 6)),
                                             system="gcrs",
                                             ref_pos=dset.site_pos)

        if config_key not in dset.fields:
            return list(sta_delta)

        pos_fields = [
            f for f in dset[config_key]._fields
            if f.endswith(dset.default_field_suffix)
        ]

        for field in pos_fields:
            sta_delta += dset[config_key][field]
        delta_pos.append(sta_delta)
    return delta_pos
Beispiel #2
0
def ocean_pole_tides_station(dset, opt):
    """Calculate the ocean pole tide corrections for a station

    Ocean pole tide corrections are returned in meters in the Geocentric Celestial Reference System for each
    observation.

    Args:
        dset:        A Dataset containing model data.
        version:     Version number of conventional mean pole model
        opt:         Apriori ocean pole tide coefficients

    Returns:
        Numpy array with ocean tide corrections in meters.
    """
    eop = apriori.get("eop", time=dset.time)

    # Constants in IERS Conventions equation 7.29
    H_p = np.sqrt(8 * np.pi / 15) * constant.omega ** 2 * constant.a ** 4 / constant.GM
    K = 4 * np.pi * constant.G * constant.a * constant.rho_w * H_p / (3 * constant.g_E)
    # @todo make these global? related to love numbers somehow
    gamma_2_R = 0.6870
    gamma_2_I = 0.0036

    # Equation (7.24) IERS Conventions 2010
    m_1 = (eop.x - eop.x_pole) * Unit.arcsec2rad
    m_2 = (eop.y_pole - eop.y) * Unit.arcsec2rad

    lat, lon, _ = dset.site_pos.pos.llh.T

    u_enu_R = np.array(
        [opt["u_e_R"](lon, lat, grid=False), opt["u_n_R"](lon, lat, grid=False), opt["u_r_R"](lon, lat, grid=False)]
    )
    u_enu_I = np.array(
        [opt["u_e_I"](lon, lat, grid=False), opt["u_n_I"](lon, lat, grid=False), opt["u_r_I"](lon, lat, grid=False)]
    )

    # Equation (7.29) IERS Conventions 2010
    denu = (K * ((m_1 * gamma_2_R + m_2 * gamma_2_I) * u_enu_R + (m_1 * gamma_2_R - m_2 * gamma_2_I) * u_enu_I)).T

    if position.is_position(dset.site_pos):
        pos_correction = position.PositionDelta(denu, system="enu", ref_pos=dset.site_pos, time=dset.time)
    elif position.is_posvel(dset.site_pos):
        # set velocity to zero
        denu = np.concatenate((denu, np.zeros(denu.shape)), axis=1)
        pos_correction = position.PosVelDelta(denu, system="enu", ref_pos=dset.site_pos, time=dset.time)
    else:
        log.fatal(f"dset.site_pos{dset.default_field_suffix} is not a PositionArray or PosVelArray.")

    return pos_correction.gcrs
Beispiel #3
0
def solid_pole_tides_station(dset):
    """Calculate the solid pole tide corrections for a station

    Solid pole tide corrections are returned in meters in the Geocentric Celestial Reference System for each
    observation.

    Args:
        dset:        A Dataset containing model data.
        version:     Version number of conventional mean pole model

    Returns:
        Numpy array with ocean tide corrections in meters.
    """
    eop = apriori.get("eop", time=dset.time)

    # Equation (7.24) IERS Conventions 2010
    m_1 = eop.x - eop.x_pole
    m_2 = eop.y_pole - eop.y

    lat, lon, _ = dset.site_pos.pos.llh.T
    theta = np.pi / 2 - lat
    coslon, sinlon = np.cos(lon), np.sin(lon)

    # Equation (7.26) IERS Conventions 2010
    dup = -33 * np.sin(2 * theta) * (m_1 * coslon + m_2 * sinlon) * Unit.mm2m
    dsouth = -9 * np.cos(2 * theta) * (m_1 * coslon + m_2 * sinlon) * Unit.mm2m
    deast = 9 * np.cos(theta) * (m_1 * sinlon + m_2 * coslon) * Unit.mm2m

    # Correction in topocentric (east, north, up) coordinates
    denu = np.vstack((deast, -dsouth, dup)).T
    if position.is_position(dset.site_pos):
        pos_correction = position.PositionDelta(denu,
                                                system="enu",
                                                ref_pos=dset.site_pos,
                                                time=dset.time)
    elif position.is_posvel(dset.site_pos):
        # set velocity to zero
        denu = np.concatenate((denu, np.zeros(denu.shape)), axis=1)
        pos_correction = position.PosVelDelta(denu,
                                              system="enu",
                                              ref_pos=dset.site_pos,
                                              time=dset.time)
    else:
        log.fatal(
            f"dset.site_pos{dset.default_field_suffix} is not a PositionArray or PosVelArray."
        )

    return pos_correction.gcrs
Beispiel #4
0
def solid_tides_station(dset, correction_cache):
    """Calculate the solid tide corrections for a station

    Solid tide corrections are returned in meters in the Geocentric Celestial Reference System for each observation.

    Args:
        dset:        A Dataset containing model data.

    Returns:
        Numpy array with solid tide corrections in meters.
    """
    eph = apriori.get("ephemerides", time=dset.time)
    dxyz = np.zeros((dset.num_obs, 3))
    obs_dt = dset.time.utc.datetime
    hour_of_day = dset.time.utc.jd_frac * 24

    sun_itrs = eph.pos_itrs("sun")
    moon_itrs = eph.pos_itrs("moon")

    # Calculate correction
    for obs in range(dset.num_obs):
        cache_key = (dset.station[obs], obs_dt[obs])
        if cache_key in correction_cache:
            dxyz[obs] = correction_cache[cache_key]
        else:
            dxyz[obs] = iers.dehanttideinel(
                dset.site_pos.pos[obs],
                obs_dt[obs].year,
                obs_dt[obs].month,
                obs_dt[obs].day,
                hour_of_day[obs],
                sun_itrs[obs],
                moon_itrs[obs],
            )
            correction_cache[cache_key] = dxyz[obs]

    if position.is_position(dset.site_pos):
        pos_correction = position.PositionDelta(dxyz, system="trs", ref_pos=dset.site_pos, time=dset.time)
    elif position.is_posvel(dset.site_pos):
        # set velocity to zero
        dxyz = np.concatenate((dxyz, np.zeros(dxyz.shape)), axis=1)
        pos_correction = position.PosVelDelta(dxyz, system="trs", ref_pos=dset.site_pos, time=dset.time)
    else:
        log.fatal(f"dset.site_pos{dset.default_field_suffix} is not a PositionArray or PosVelArray.")

    return pos_correction.gcrs
Beispiel #5
0
def non_tidal_atmospheric_loading_station(ntapl, dset):
    """Apply non tidal atmospheric loading displacements for a station field.

    Corrections are returned in meters in the Geocentric
    Celestial Reference System for each observation.

    Args:
        dset:        A Dataset containing model data

    Returns:
        Numpy array: GCRS corrections in meters.
    """
    lat, lon, _ = dset.site_pos.pos.llh.T
    try:
        dup = ntapl["up"](dset.time, lon, lat)
        deast = ntapl["east"](dset.time, lon, lat)
        dnorth = ntapl["north"](dset.time, lon, lat)
    except KeyError:
        log.warn(
            f"No non-tidal atmospheric loading available for {dset.rundate}")
        dup = np.zeros(len(dset.time))
        deast = np.zeros(len(dset.time))
        dnorth = np.zeros(len(dset.time))

    denu = np.stack((deast, dnorth, dup), axis=1)
    if position.is_position(dset.site_pos):
        pos_correction = position.PositionDelta(denu,
                                                system="enu",
                                                ref_pos=dset.site_pos,
                                                time=dset.time)
    elif position.is_posvel(dset.site_pos):
        # set velocity to zero
        denu = np.concatenate((denu, np.zeros(denu.shape)), axis=1)
        pos_correction = position.PosVelDelta(denu,
                                              system="enu",
                                              ref_pos=dset.site_pos,
                                              time=dset.time)
    else:
        log.fatal(
            f"dset.site_pos{dset.default_field_suffix} is not a PositionArray or PosVelArray."
        )

    return pos_correction.gcrs
Beispiel #6
0
def atmospheric_tides_station(dset):
    """Calculate the atmospheric tides corrections for a station

    Atmospheric tides corrections are returned in meters in the Geocentric Celestial Reference System for each
    observation.

    Args:
        dset:        A Dataset containing model data

    Returns:
        Numpy array with atmospheric tide corrections in meters.

    """
    coeff = apriori.get("atmospheric_tides")
    use_cmc = config.tech.atmospheric_tides_cmc.bool

    # S1 has a period of 1 cycle/day, S2 has a period of 2 cycle/day
    omega_1 = 2 * np.pi
    omega_2 = 4 * np.pi

    # Time argument is fraction of UT1 day, see [2].
    t = dset.time.ut1.jd_frac
    lat, lon, _ = dset.site_pos.pos.llh.T

    # Equation 7.19a and 7.19b from IERS Conventions 2010
    de = (
        coeff["A_d1_e"](lon, lat, grid=False) * np.cos(omega_1 * t)
        + coeff["B_d1_e"](lon, lat, grid=False) * np.sin(omega_1 * t)
        + coeff["A_d2_e"](lon, lat, grid=False) * np.cos(omega_2 * t)
        + coeff["B_d2_e"](lon, lat, grid=False) * np.sin(omega_2 * t)
    )
    dn = (
        coeff["A_d1_n"](lon, lat, grid=False) * np.cos(omega_1 * t)
        + coeff["B_d1_n"](lon, lat, grid=False) * np.sin(omega_1 * t)
        + coeff["A_d2_n"](lon, lat, grid=False) * np.cos(omega_2 * t)
        + coeff["B_d2_n"](lon, lat, grid=False) * np.sin(omega_2 * t)
    )
    du = (
        coeff["A_d1_u"](lon, lat, grid=False) * np.cos(omega_1 * t)
        + coeff["B_d1_u"](lon, lat, grid=False) * np.sin(omega_1 * t)
        + coeff["A_d2_u"](lon, lat, grid=False) * np.cos(omega_2 * t)
        + coeff["B_d2_u"](lon, lat, grid=False) * np.sin(omega_2 * t)
    )
    denu = np.vstack([de, dn, du]).T * Unit.mm2m

    if position.is_position(dset.site_pos):
        pos_correction = position.PositionDelta(denu, system="enu", ref_pos=dset.site_pos, time=dset.time)
    elif position.is_posvel(dset.site_pos):
        # set velocity to zero
        denu = np.concatenate((denu, np.zeros(denu.shape)), axis=1)
        pos_correction = position.PosVelDelta(denu, system="enu", ref_pos=dset.site_pos, time=dset.time)
    else:
        log.fatal(f"dset.site_pos{dset.default_field_suffix} is not a PositionArray or PosVelArray.")

    # Add center of mass corrections
    if use_cmc:
        # Equation (7.20) in [1]
        coeff_cmc = apriori.get("atmospheric_tides_cmc")
        cmc = (
            coeff_cmc["A1"][None, :] * np.cos(omega_1 * t)[:, None]
            + coeff_cmc["B1"][None, :] * np.sin(omega_1 * t)[:, None]
            + coeff_cmc["A2"][None, :] * np.cos(omega_2 * t)[:, None]
            + coeff_cmc["B2"][None, :] * np.sin(omega_2 * t)[:, None]
        )
        if position.is_position(dset.site_pos):
            cmc_correction = position.PositionDelta(cmc, system="trs", ref_pos=dset.site_pos, time=dset.time)
        elif position.is_posvel(dset.site_pos):
            # set velocity to zero
            cmc = np.concatenate((cmc, np.zeros(cmc.shape)), axis=1)
            cmc_correction = position.PosVelDelta(cmc, system="trs", ref_pos=dset.site_pos, time=dset.time)
        pos_correction = pos_correction.trs + cmc_correction.trs

    return pos_correction.gcrs
Beispiel #7
0
def ocean_tides_station(dset, amplitudes, phases, correction_cache):
    """Calculate the ocean tide corrections for a station

    Ocean tide corrections are returned in meters in the Geocentric Celestial Reference System for each observation.

    Args:
        dset:        A Dataset containing model data.

    Returns:
        Numpy array with ocean tide corrections in meters.
    """
    denu = np.zeros((dset.num_obs, 3))
    use_cmc = config.tech.ocean_tides_cmc.bool

    # Calculate correction
    for obs, site_id in enumerate(dset.site_id):
        if site_id not in amplitudes:
            # Warn about missing Ocean Tides Coefficients
            if site_id in _WARNED_MISSING:
                continue
            station = dset.unique("station", site_id=site_id)[0]
            log.error(
                f"Missing ocean loading coefficients for site id {site_id!r} ({station}). Correction set to zero."
            )
            _WARNED_MISSING.add(site_id)
            continue

        cache_key = (dset.station[obs], dset.time.utc.datetime[obs])
        if cache_key in correction_cache:
            denu[obs] = correction_cache[cache_key]
        else:
            epoch = [float(t) for t in dset.time.utc[obs].yday.split(":")]
            dup, dsouth, dwest = iers.hardisp(epoch, amplitudes[site_id],
                                              phases[site_id], 1, 1.0)

            # Correction in topocentric (east, north, up) coordinates
            denu[obs] = np.array([-dwest[0], -dsouth[0], dup[0]])
            correction_cache[cache_key] = denu[obs]

    if position.is_position(dset.site_pos):
        pos_correction = position.PositionDelta(denu,
                                                system="enu",
                                                ref_pos=dset.site_pos,
                                                time=dset.time)
    elif position.is_posvel(dset.site_pos):
        # set velocity to zero
        denu = np.concatenate((denu, np.zeros(denu.shape)), axis=1)
        pos_correction = position.PosVelDelta(denu,
                                              system="enu",
                                              ref_pos=dset.site_pos,
                                              time=dset.time)
    else:
        log.fatal(
            f"dset.site_pos{dset.default_field_suffix} is not a PositionArray or PosVelArray."
        )

    # Center of mass corrections
    if use_cmc:
        coeff_cmc = apriori.get("ocean_tides_cmc")
        in_phase = coeff_cmc["in_phase"]
        cross_phase = coeff_cmc["cross_phase"]
        cmc = np.zeros((dset.num_obs, 3))
        for obs, time in enumerate(dset.time.utc):
            year, doy = time.datetime.year, float(
                time.datetime.strftime("%j")) + time.mjd_frac
            angle = iers.arg2(year, doy)[:, None]
            cmc[obs] += np.sum(in_phase * np.cos(angle) +
                               cross_phase * np.sin(angle),
                               axis=0)

        if position.is_position(dset.site_pos):
            cmc_correction = position.PositionDelta(cmc,
                                                    system="trs",
                                                    ref_pos=dset.site_pos,
                                                    time=dset.time)
        elif position.is_posvel(dset.site_pos):
            # set velocity to zero
            cmc = np.concatenate((cmc, np.zeros(cmc.shape)), axis=1)
            cmc_correction = position.PosVelDelta(cmc,
                                                  system="trs",
                                                  ref_pos=dset.site_pos,
                                                  time=dset.time)
        pos_correction = pos_correction.trs + cmc_correction

    return pos_correction.gcrs
Beispiel #8
0
def eccentricity_vector_station(ecc, dset):
    """Calculate the eccentricity vector for a station.

    Corrections are returned in meters in the Geocentric
    Celestial Reference System for each observation.

    Args:
        dset:        A Dataset containing model data

    Returns:
        Numpy array: GCRS corrections in meters.
    """
    if position.is_position(dset.site_pos):
        ecc_vector = position.PositionDelta(np.zeros((dset.num_obs, 3)),
                                            system="enu",
                                            ref_pos=dset.site_pos,
                                            time=dset.time)
    elif position.is_posvel(dset.site_pos):
        ecc_vector = position.PosVelDelta(np.zeros((dset.num_obs, 6)),
                                          system="enu",
                                          ref_pos=dset.site_pos,
                                          time=dset.time)
    else:
        log.fatal(
            f"dset.site_pos{dset.default_field_suffix} is not a PositionArray or PosVelArray."
        )

    fieldnames = config.tech.eccentricity.identifier.list
    fielddata = [dset[field] for field in fieldnames]
    if len(fieldnames) > 1:
        keys = set(tuple(zip(*fielddata)))
    else:
        keys = fielddata[0].tolist()

    for key in keys:
        if len(fieldnames) > 1:
            filters = dict(zip(fieldnames, key))
        else:
            filters = dict(zip(fieldnames, [key]))

        if key not in ecc:
            ecc_vector[dset.filter(**filters), 0:3] = np.zeros(3)
            if key in _WARNED_MISSING:
                continue
            log.warn(
                f"Missing eccentricity data for {key}. Vector set to zero.")
            _WARNED_MISSING.add(key)
            continue

        if ecc[key]["coord_type"] == "ENU":
            ecc_vector[dset.filter(**filters), 0:3] = ecc[key]["vector"]

    ecc_vector = ecc_vector.trs
    for key in keys:
        if len(fieldnames) > 1:
            filters = dict(zip(fieldnames, key))
        else:
            filters = dict(zip(fieldnames, [key]))

        if key not in ecc:
            ecc_vector[dset.filter(**filters), 0:3] = np.zeros(3)
            continue

        if ecc[key]["coord_type"] == "XYZ":
            ecc_vector[dset.filter(**filters), 0:3] = ecc[key]["vector"]

    return ecc_vector.gcrs
    def satellite_phase_center_offset(
        self,
        dset: "Dataset",
        sys_freq: Union[None, Dict[str, Dict[str, str]]] = None
    ) -> "PosVelDeltaArray":
        """Determine satellite phase center offset correction vectors given in ITRS

        Satellite phase center offset (PCO) corrections are frequency dependent. The argument 'sys_freq' defines, which
        frequencies for a given GNSS should be used. If two frequencies for a GNSS are given, then the PCO is
        determined as ionospheric-free linear combination. If 'sys_freq' is not defined as input argument, then 
        'sys_freq' is generated based on the given observation types in dataset 'dset'.

        Args:
            dset:      Model data.
            sys_freq:  Dictionary with frequency or frequency combination given for GNSS identifier:
                         sys_freq = { <sys_id>: <freq> }  (e.g. sys_freq = {'E': 'E1',  'G': 'L1_L2'} )

        Returns:
            Satellite phase center offset correction vectors given in ITRS in meter

        """
        # GNSS          Freq number      GNSS freq
        #               L<num>/C<num>
        # ___________________________________________
        # C (BeiDou):   2                'B1'
        #               7                'B2'
        #               6                'B3'
        # G (GPS):      1                'L1'
        #               2                'L2'
        #               5                'L5'
        # R (GLONASS):  1                'G1'
        #               2                'G2'
        #               3                'G3'
        # E (Galileo):  1                'E1'
        #               8                'E5 (E5a+E5b)'
        #               5                'E5a'
        #               7                'E5b'
        #               6                'E6'
        # I (IRNSS):    5                'L5'
        #               9                'S'
        # J (QZSS):     1                'L1'
        #               2                'L2'
        #               5                'L5'
        #               6                'LEX'
        # S (SBAS):     1                'L1'
        #               5                'L5'
        obstype_to_gnss_freq = {
            "C": {
                "2": "B1",
                "7": "B2",
                "6": "B3"
            },
            "E": {
                "1": "E1",
                "8": "E5",
                "5": "E5a",
                "7": "E5b",
                "6": "E6"
            },
            "G": {
                "1": "L1",
                "2": "L2",
                "5": "L5"
            },
            "I": {
                "5": "L5",
                "9": "S"
            },
            "J": {
                "1": "L1",
                "2": "L2",
                "5": "L5",
                "6": "LEX"
            },
            "R": {
                "1": "G1",
                "2": "G2",
                "3": "G3"
            },
            "S": {
                "1": "L1",
                "5": "L5"
            },
        }

        correction = position.PosVelDelta(val=np.zeros((dset.num_obs, 6)),
                                          system="yaw",
                                          ref_pos=dset.sat_posvel,
                                          time=dset.time)
        used_date = None

        # Get GNSS frequency based on observation type
        if sys_freq is None:
            sys_freq = dict()
            obstypes = dset.meta["obstypes"]
            for sys in obstypes:
                freqs = {o[1] for o in obstypes[sys]}
                sys_freq[sys] = "_".join(f"{obstype_to_gnss_freq[sys][f]}"
                                         for f in sorted(freqs))

        # Loop over all satellites given in RINEX observation file and configuration file
        for sat in dset.unique("satellite"):

            # Skip satellites, which are not given in ANTEX file
            if sat not in self.data:
                log.warn(
                    f"Satellite {sat} is not given in ANTEX file {self.file_path}. That means no satellite antenna "
                    f"phase center offset correction can be applied for satellite {sat}."
                )
                continue

            # Get array with information about, when observation are available for the given satellite (indicated by True)
            idx = dset.filter(satellite=sat)

            # Get used date
            used_date = self._used_date(sat, dset.analysis["rundate"])
            if used_date is None:
                continue

            # Add PCO to Dataset meta
            pco_sat = self.get_pco_sat(sat, sys_freq, used_date)
            dset.meta.setdefault("pco_sat",
                                 dict()).update({sat: pco_sat.tolist()})
            correction[idx] = np.repeat(np.append(pco_sat,
                                                  np.zeros(3))[None, :],
                                        np.sum(idx),
                                        axis=0)

        # Transform PCO given in satellite body-fixed reference frame (for GPS and Galileo assumed to be aligned
        # to yaw-steering reference frame) to ITRS
        return correction.trs