def test_reliabilities(self):
        # test scalar vs vector version
        frequency = 573.3
        height1 = 194.0
        height2 = 9.1
        refractivity = 314.0  # Typical
        dielectric = 15  # Typical for ground
        conductivity = 0.005  # Typical for ground
        climate = 5  # Continental temperate
        polarization = 0  # Vertical
        confidence = 0.5
        mdvar = 12
        refractivity_final = False

        reliabilities = np.arange(0.1, 1.0, 0.1)
        losses, _, _, _, _ = itm.point_to_point(PROFILE, height1, height2,
                                                dielectric, .005, refractivity,
                                                frequency, climate,
                                                polarization, confidence,
                                                reliabilities)
        self.assertEqual(len(losses), len(reliabilities))

        for rel, exp_loss in zip(reliabilities, losses):
            loss, _, _, _, _ = itm.point_to_point(PROFILE, height1, height2,
                                                  dielectric, .005,
                                                  refractivity, frequency,
                                                  climate, polarization,
                                                  confidence, rel)
            self.assertEqual(loss, exp_loss)
    def test_default_arg(self):
        frequency = 573.3
        height1 = 194.0
        height2 = 9.1
        refractivity = 314.0  # Typical
        dielectric = 15  # Typical for ground
        conductivity = 0.005  # Typical for ground
        climate = 5  # Continental temperate
        polarization = 0  # Vertical
        confidence = 0.5
        reliability = 0.5
        # Expected default arguments
        mdvar = 12
        refractivity_final = False

        loss1, _, _, _, _ = itm.point_to_point(PROFILE, height1, height2,
                                               dielectric, .005, refractivity,
                                               frequency, climate,
                                               polarization, confidence,
                                               reliability, mdvar,
                                               refractivity_final)
        loss2, _, _, _, _ = itm.point_to_point(PROFILE, height1, height2,
                                               dielectric, .005, refractivity,
                                               frequency, climate,
                                               polarization, confidence,
                                               reliability)
        self.assertEqual(loss1, loss2)
    def test_qkpfl_path1979_its(self):
        confidence_vals = [0.5, 0.9, 0.1]
        reliability_vals = [0.01, 0.1, 0.5, 0.9, 0.99]
        expected_losses = [
            144.3, 154.1, 134.4, 150.9, 159.5, 142.3, 157.6, 165.7, 149.4,
            161.6, 169.9, 153.3, 164.9, 173.6, 156.2
        ]

        frequency = 573.3
        height1 = 194.0
        height2 = 9.1
        refractivity = 314.0  # Typical
        refractivity_final = True
        dielectric = 15  # Typical for ground
        conductivity = 0.005  # Typical for ground
        climate = 5  # Continental temperate
        polarization = 0  # Vertical
        mdvar = 12
        k = 0
        for reliability in reliability_vals:
            for confidence in confidence_vals:
                loss, ver0, ver1, err, mode = itm.point_to_point(
                    PROFILE, height1, height2, dielectric, .005, refractivity,
                    frequency, climate, polarization, confidence, reliability,
                    mdvar, refractivity_final)
                self.assertAlmostEqual(loss, expected_losses[k], 1)
                k += 1
 def test_qkpfl_path2200_its(self):
     confidence_vals = [0.5, 0.9, 0.1]
     reliability_vals = [0.01, 0.1, 0.5, 0.9, 0.99]
     expected_losses = [
         128.6, 137.6, 119.6, 132.2, 140.8, 123.5, 135.8, 144.3, 127.2,
         138.0, 146.5, 129.4, 139.7, 148.4, 131.0
     ]
     frequency = 41.5
     height1 = 143.9
     height2 = 8.5
     refractivity = 314.0  # Typical
     refractivity_final = True
     dielectric = 15  # Typical for ground
     conductivity = 0.005  # Typical for ground
     climate = 5  # Continental temperate
     polarization = 0  # Vertical
     mdvar = 12
     k = 0
     for reliability in reliability_vals:
         for confidence in confidence_vals:
             loss, ver0, ver1, err, mode = itm.point_to_point(
                 PROFILE, height1, height2, dielectric, .005, refractivity,
                 frequency, climate, polarization, confidence, reliability,
                 mdvar, refractivity_final)
             self.assertAlmostEqual(loss, expected_losses[k], 1)
             k += 1
 def test_horizon_angles_los(self):
     refractivity = 314.
     PROFILE = [5, 28.5, 10, 10, 8, 9, 11, 12]
     a0, a1, _, _ = _GetHorizonAnglesLegacy(PROFILE, 100, 50, refractivity)
     _, v0, v1, _, _ = itm.point_to_point(PROFILE,
                                          100,
                                          50,
                                          dielectric=15,
                                          conductivity=.005,
                                          refractivity=refractivity,
                                          freq_mhz=41.5,
                                          climate=5,
                                          polarization=0,
                                          confidence=0.5,
                                          reliabilities=0.5)
     self.assertEqual(a0, v0)
     self.assertEqual(a1, v1)
 def test_horizon_angles(self):
     refractivity = 314.
     a0, a1, d0, d1 = _GetHorizonAnglesLegacy(PROFILE, 143.9, 8.5,
                                              refractivity)
     _, v0, v1, _, _ = itm.point_to_point(PROFILE,
                                          143.9,
                                          8.5,
                                          dielectric=15,
                                          conductivity=.005,
                                          refractivity=refractivity,
                                          freq_mhz=41.5,
                                          climate=5,
                                          polarization=0,
                                          confidence=0.5,
                                          reliabilities=0.5)
     self.assertAlmostEqual(a0, np.arctan(-0.003900) * 180. / np.pi, 4)
     self.assertAlmostEqual(a1, np.arctan(0.000444) * 180. / np.pi, 4)
     self.assertAlmostEqual(d0, 55357.7, 1)
     self.assertAlmostEqual(d1, 19450.0, 1)
     # test exactness of new method vs old method
     self.assertEqual(a0, v0)
     self.assertEqual(a1, v1)
Esempio n. 7
0
def CalcItmPropagationLoss(lat_cbsd,
                           lon_cbsd,
                           height_cbsd,
                           lat_rx,
                           lon_rx,
                           height_rx,
                           cbsd_indoor=False,
                           reliability=0.5,
                           freq_mhz=3625.,
                           its_elev=None,
                           is_height_cbsd_amsl=False,
                           return_internals=False):
    """Implements the WinnForum-compliant ITM point-to-point propagation model.

  According to WinnForum spec R2-SGN-17, R2-SGN-22 and R2-SGN-5 to 10.

  One can use this routine in 3 ways:
    reliability = -1 : to get the average path loss
    reliability in [0,1] : to get a pathloss for given quantile
    sequence of reliabilities: to get an array of pathloss. Used to obtain
      inverse CDF of the pathloss.

  Inputs:
    lat_cbsd, lon_cbsd, height_cbsd: Lat/lon (deg) and height AGL (m) of CBSD
    lat_rx, lon_rx, height_rx:       Lat/lon (deg) and height AGL (m) of Rx point
    cbsd_indoor:         CBSD indoor status - Default=False.
    reliability:         Reliability. Default is 0.5 (median value)
                         Different options:
                           value in [0,1]: returns the CDF quantile
                           -1: returns the mean path loss
                           iterable sequence: returns a list of path losses
    freq_mhz:            Frequency (MHz). Default is mid-point of band.
    its_elev:            Optional profile to use (in ITM format). Default=None
                           If not specified, it is extracted from the terrain.
    is_height_cbsd_amsl: If True, the CBSD height shall be considered as AMSL (Average
                         mean sea level).
    return_internals: If True, returns internal variables.

  Returns:
    A namedtuple of:
      db_loss            Path Loss in dB, either a scalar if reliability is scalar
                           or a list of path losses if reliability is an iterable.

      incidence_angles:  A namedtuple of
          hor_cbsd:        Horizontal departure angle (bearing) from CBSD to Rx
          ver_cbsd:        Vertical departure angle at CBSD
          hor_rx:          Horizontal incidence angle (bearing) from Rx to CBSD
          ver_rx:          Vertical incidence angle at Rx

      internals:         A dictionary of internal data for advanced analysis
                         (only if return_internals=True):
          itm_err_num:     ITM error code from ItmErrorCode (see GetInfoOnItmCode).
          itm_str_mode:    String containing description of dominant prop mode.
          dist_km:         Distance between end points (km).
          prof_d_km        ndarray of distances (km) - x values to plot terrain.
          prof_elev        ndarray of terrain heightsheights (m) - y values to plot terrain,

  Raises:
    Exception if input parameters invalid or out of range.
  """
    # Case of same points
    if (lat_cbsd == lat_rx and lon_cbsd == lon_rx):
        return _PropagResult(db_loss=0 if np.isscalar(reliability) else [0] *
                             len(reliability),
                             incidence_angles=_IncidenceAngles(0, 0, 0, 0),
                             internals=None)

    # Sanity checks on input parameters
    if freq_mhz < 40.0 or freq_mhz > 10000:
        raise Exception('Frequency outside range [40MHz - 10GHz]')

    if is_height_cbsd_amsl:
        altitude_cbsd = drive.terrain_driver.GetTerrainElevation(
            lat_cbsd, lon_cbsd)
        height_cbsd = height_cbsd - altitude_cbsd

    # Ensure minimum height of 1 meter
    if height_cbsd < 1:
        height_cbsd = 1
    if height_rx < 1:
        height_rx = 1

    # Internal ITM parameters are always set to following values in WF version:
    confidence = 0.5  # Confidence (always 0.5)
    dielec = 25.  # Dielectric constant (always 25.)
    conductivity = 0.02  # Conductivity (always 0.02)
    polarization = 1  # Polarization (always vertical = 1)
    mdvar = 13

    # Get the terrain profile, using Vincenty great circle route, and WF
    # standard (bilinear interp; 1500 pts for all distances over 45 km)
    if its_elev is None:
        its_elev = drive.terrain_driver.TerrainProfile(lat1=lat_cbsd,
                                                       lon1=lon_cbsd,
                                                       lat2=lat_rx,
                                                       lon2=lon_rx,
                                                       target_res_meter=30.,
                                                       do_interp=True,
                                                       max_points=1501)

    # Find the midpoint of the great circle path
    dist_km, bearing_cbsd, bearing_rx = vincenty.GeodesicDistanceBearing(
        lat_cbsd, lon_cbsd, lat_rx, lon_rx)
    latmid, lonmid, _ = vincenty.GeodesicPoint(lat_cbsd, lon_cbsd,
                                               dist_km / 2., bearing_cbsd)

    # Determine climate value, based on ITU-R P.617 method:
    climate = drive.climate_driver.TropoClim(latmid, lonmid)
    # If the common volume lies over the sea, the climate value to use depends
    # on the climate values at either end. A simple min() function should
    # properly implement the logic, since water is the max.
    if climate == 7:
        climate = min(drive.climate_driver.TropoClim(lat_cbsd, lon_cbsd),
                      drive.climate_driver.TropoClim(lat_rx, lon_rx))

    # Look up the refractivity at the path midpoint, if not explicitly provided
    refractivity = drive.refract_driver.Refractivity(latmid, lonmid)

    # Call ITM prop loss.
    reliabilities = reliability
    do_avg = False
    if np.isscalar(reliabilities) and reliability == -1:
        # Pathloss mean: average the value for 1% to 99% included
        reliabilities = np.arange(0.01, 1.0, 0.01)
        do_avg = True

    db_loss, ver_cbsd, ver_rx, str_mode, err_num = itm.point_to_point(
        its_elev, height_cbsd, height_rx, dielec, conductivity, refractivity,
        freq_mhz, climate, polarization, confidence, reliabilities, mdvar,
        False)
    if do_avg:
        db_loss = -10 * np.log10(np.mean(10**(-np.array(db_loss) / 10.)))

    # Add indoor losses
    if cbsd_indoor:
        if np.isscalar(db_loss):
            db_loss += 15
        else:
            db_loss = [loss + 15 for loss in db_loss]

    # Create distance/terrain arrays for plotting if desired
    internals = None
    if return_internals:
        prof_d_km = (its_elev[1] / 1000.) * np.arange(len(its_elev) - 2)
        prof_elev = np.asarray(its_elev[2:])
        internals = {
            'itm_err_num': err_num,
            'itm_str_mode': str_mode,
            'dist_km': dist_km,
            'prof_d_km': prof_d_km,
            'prof_elev': prof_elev
        }

    return _PropagResult(db_loss=db_loss,
                         incidence_angles=_IncidenceAngles(
                             hor_cbsd=bearing_cbsd,
                             ver_cbsd=ver_cbsd,
                             hor_rx=bearing_rx,
                             ver_rx=ver_rx),
                         internals=internals)