def mid_latitude_pressure_winter(self, h): """Section 3.2 of Recommendation ITU-R P.835-6 """ P10 = standard_pressure(10).to(u.hPa).value P72 = standard_pressure(72).to(u.hPa).value return np.where(np.logical_and((0 <= h), (h <= 10)), 1018.8627 - 124.2954 * h + 4.8307 * h**2, np.where(np.logical_and((10 < h), (h <= 72)), P10 * np.exp(-0.147 * (h - 10.)), np.where(np.logical_and((72 < h), (h <= 100)), P72 * np.exp(-0.155 * (h - 72.)), np.nan)))
def high_latitude_pressure_summer(self, h): """Section 4.1 of Recommendation ITU-R P.835-5 """ P10 = standard_pressure([10]).to(u.hPa).value P72 = standard_pressure([72]).to(u.hPa).value return np.where(np.logical_and((0 <= h), (h <= 10)), 1008.0278 - 113.2494 * h + 3.9408 * h**2, np.where(np.logical_and((10 < h), (h <= 72)), P10 * np.exp(-0.140 * (h - 10.)), np.where(np.logical_and((72 < h), (h <= 100)), P72 * np.exp(-0.165 * (h - 72.)), np.nan)))
def high_latitude_pressure_winter(self, h): """Section 4.2 of Recommendation ITU-R P.835-5 """ P10 = standard_pressure([10]).to(u.hPa).value P72 = standard_pressure([72]).to(u.hPa).value return np.where(np.logical_and((0 <= h), (h <= 10)), 1010.8828 - 122.2411 * h + 4.554 * h**2, np.where(np.logical_and((10 < h), (h <= 72)), P10 * np.exp(-0.147 * (h - 10.)), np.where(np.logical_and((72 < h), (h <= 100)), P72 * np.exp(-0.150 * (h - 72.)), np.nan)))
def low_latitude_pressure(self, h): """Section 2 of Recommendation ITU-R P.835-5 """ P10 = standard_pressure([10]).to(u.hPa).value P72 = standard_pressure([72]).to(u.hPa).value return np.where(np.logical_and((0 <= h), (h <= 10)), 1012.0306 - 109.0338 * h + 3.6316 * h**2, np.where(np.logical_and((10 < h), (h <= 72)), P10 * np.exp(-0.147 * (h - 10.)), np.where(np.logical_and((72 < h), (h <= 100)), P72 * np.exp(-0.165 * (h - 72.)), np.nan)))
def mid_latitude_pressure_summer(self, h): """Section 3.1 of Recommendation ITU-R P.835-5 """ P10 = standard_pressure([10]).to(u.hPa).value P72 = standard_pressure([72]).to(u.hPa).value return np.where(np.logical_and((0 <= h), (h <= 10)), 1012.8186 - 111.5569 * h + 3.8646 * h**2, np.where(np.logical_and((10 < h), (h <= 72)), P10 * np.exp(-0.147 * (h - 10.)), np.where(np.logical_and((72 < h), (h <= 100)), P72 * np.exp(-0.165 * (h - 72.)), np.nan)))
def atmospheric_attenuation_slant_path(lat, lon, f, el, p, D, tau, eta, hs=None, rho=None, R001=None, T=None, H=None, P=None, hL=1e3, V_t=None, mode='approx', return_contributions=False, include_rain=True, include_gas=True, include_scintillation=True, include_clouds=True): """ Calculation of long-term atmospheric attenuation statistics. The following procedure provides estimates of the long-term statistics of the slant-path atmospheric attenuation at a given location for frequencies up to 55 GHz and percentages of time 0.001 % < p < 50 %. Parameters ------------- - lat : number, sequence, or numpy.ndarray Latitudes of the receiver points - lon : number, sequence, or numpy.ndarray Longitudes of the receiver points - f : number or Quantity Frequency (GHz) - el : sequence, number or Quantity Elevation angle (degrees) - p : number Percetage of the time the rain attenuation value is exceeded. - D: number or Quantity Physical diameter of the earth-station antenna (m) - tau : number Polarization tilt angle relative to the horizontal (degrees) (tau = 45 deg for circular polarization). - eta: number Antenna efficiency. - hs : number, sequence, or numpy.ndarray, optional Heigh above mean sea level of the earth station (km). If local data for the earth station height above mean sea level is not available, an estimate is obtained from the maps of topographic altitude given in Recommendation ITU-R P.1511. - rho : number or Quantity, optional Water vapor density (g/m3). If not provided, an estimate is obtained from Recommendation Recommendation ITU-R P.836. - R001: number or Quantity, optional Point rainfall rate for the location for 0.01% of an average year \ (mm/h). If not provided, an estimate is obtained from Recommendation ITU-R P.837. Some useful values: * 0.25 mm/h : Drizzle * 2.5 mm/h : Light rain * 12.5 mm/h : Medium rain * 25.0 mm/h : Heavy rain * 50.0 mm/h : Downpour * 100 mm/h : Tropical * 150 mm/h : Monsoon - T: number, sequence, or numpy.ndarray, optional Average surface ambient temperature (°C) at the site. If None, uses the ITU-R P.453 to estimate the wet term of the radio refractivity. - H: number, sequence, or numpy.ndarray, optional Average surface relative humidity (%) at the site. If None, uses the ITU-R P.453 to estimate the wet term of the radio refractivity. - P: number, sequence, or numpy.ndarray, optional Average surface pressure (hPa) at the site. If None, uses the ITU-R P.453 to estimate the wet term of the radio refractivity. - hL : number, optional Height of the turbulent layer (m). Default value 1000 m - V_t : number or Quantity, optional Integrated water vapour content along the path (kg/m2 or mm). If not provided this value is estimated using Recommendation ITU-R P.836. Default value None - mode : string, optional Mode for the calculation of gaseous attenuation. Valid values are 'approx', 'exact'. If 'approx' Uses the method in Annex 2 of Recommendation ITU-R P.676, else uses the method described in Section 1. Default, 'approx' - return_contributions: bool, optional Determines whether individual contributions from gases, rain, clouds and scintillation are returned in addition ot the total attenuation (True), or just the total atmospheric attenuation (False). Default is False - include_rain: bool, optional Determines whether to include the rain contribution in the total atmospheric attenuation calculation or not. Default is True - include_gas: bool, optional Determines whether to include the gaseous contribution in the total atmospheric attenuation calculation or not. Default is True - include_scintillation: bool, optional Determines whether to include the scintillation contribution in the total atmospheric attenuation calculation or not. Default is True - include_clouds: bool, optional Determines whether to include the clouds contribution in the total atmospheric attenuation calculation or not. Default is True Returns --------- - A : Quantity Total atmospheric attenuation (dB) - Ag, Ac, Ar, As, A : tuple Gaseous, Cloud, Rain, Scintillation contributions to total attenuation, and total attenuation (dB) References ------------- [1] Propagation data and prediction methods required for the design of Earth-space telecommunication systems: https://www.p.int/dms_pubrec/itu-r/rec/p/R-REC-P.618-12-201507-I!!PDF-E.pdf """ if np.logical_or(p < 0.001, p > 50).any(): warnings.warn( RuntimeWarning( 'The method to compute the total ' 'atmospheric attenuation in recommendation ITU-P 618-13 ' 'is only recommended for unavailabilities (p) between ' '0.001 % and 50 %')) # This takes account of the fact that a large part of the cloud attenuation # and gaseous attenuation is already included in the rain attenuation # prediction for time percentages below 1%. Eq. 61 and Eq. 62 in # Recommendation ITU 618-13 p_c_g = np.maximum(1, p) # Estimate the ground station altitude if hs is None: hs = topographic_altitude(lat, lon) # Surface mean temperature if T is None: T = surface_mean_temperature(lat, lon) # Estimate the surface Pressure if P is None: P = standard_pressure(hs) # Estimate the surface Pressure if V_t is None: V_t = total_water_vapour_content(lat, lon, p_c_g, alt=hs) # Estimate the surface water vapour density if rho is None: rho = surface_water_vapour_density(lat, lon, p_c_g, alt=hs) # Compute the attenuation components if include_rain: Ar = rain_attenuation(lat, lon, f, el, p, tau, hs=hs, R001=R001) else: Ar = 0 * u.dB if include_gas: Ag = gaseous_attenuation_slant_path(f, el, rho, P, T, V_t=V_t, h=hs, mode=mode) else: Ag = 0 * u.dB if include_clouds: Ac = cloud_attenuation(lat, lon, el, f, p_c_g) else: Ac = 0 * u.dB if include_scintillation: As = scintillation_attenuation(lat, lon, f, el, p, D, eta, T=T, H=H, P=P, hL=hL) else: As = 0 * u.dB # Compute the total attenuation according to A = Ag + np.sqrt((Ar + Ac)**2 + As**2) if return_contributions: return Ag, Ac, Ar, As, A else: return A
def total_attenuation_synthesis(self, lat, lon, f, el, p, D, Ns, tau, eta, Ts=1, hs=None, rho=None, H=None, P=None, hL=1000, return_contributions=False): t_disc = int(5e6) # Step A Correlation coefficients: C_RC = 1 C_CV = 0.8 # Step B Scintillation polynomials def a_Fade(p): return -0.061 * np.log10(p)**3 + 0.072 * \ np.log10(p)**2 - 1.71 * np.log10(p) + 3 def a_Enhanc(p): return -0.0597 * np.log10(p)**3 - 0.0835 * \ np.log10(p)**2 - 1.258 * np.log10(p) + 2.672 # Step C1-C3: n_R = np.random.normal(0, 1, int((Ns * Ts + t_disc))) n_L0 = np.random.normal(0, 1, int((Ns * Ts + t_disc))) n_V0 = np.random.normal(0, 1, int((Ns * Ts + t_disc))) # Step C4-C5: n_L = C_RC * n_R + np.sqrt(1 - C_RC**2) * n_L0 n_V = C_CV * n_L + np.sqrt(1 - C_CV**2) * n_V0 # Step C6: Compute the rain attenuation time series if hs is None: hs = topographic_altitude(lat, lon) Ar = rain_attenuation_synthesis(lat, lon, f, el, tau, Ns, hs=hs, Ts=1, n=n_R).value Ar = Ar[t_disc:] # Step C7: Compute the cloud integrated liquid water content time # series L = cloud_liquid_water_synthesis(lat, lon, Ns, Ts=1, n=n_L).value L = L[t_disc:] Ac = L * \ specific_attenuation_coefficients(f, T=0) / np.sin(np.deg2rad(el)) Ac = Ac.flatten() # Step C9: Identify time stamps where A_R > 0 L > 1 idx = np.where(np.logical_and(Ar > 0, L > 1))[0] idx_no = np.where(np.logical_not(np.logical_and(Ar > 0, L > 1)))[0] # Step C10: Discard the previous values of Ac and re-compute them by # linear interpolation vs. time starting from the non-discarded cloud # attenuations values Ac[idx] = np.interp(idx, idx_no, Ac[idx_no]) # Step C11: Compute the integrated water vapour content time series V = integrated_water_vapour_synthesis(lat, lon, Ns, Ts=1, n=n_V).value V = V[t_disc:] # Step C12: Convert the integrated water vapour content time series # V into water vapour attenuation time series AV(kTs) Av = zenith_water_vapour_attenuation(lat, lon, p, f, V_t=V).value # Step C13: Compute the mean annual temperature Tm for the location of # interest using experimental values if available. Tm = surface_mean_temperature(lat, lon).value # Step C14: Convert the mean annual temperature Tm into mean annual # oxygen attenuation AO following the method recommended in # Recommendation ITU-R P.676. if P is None: P = standard_pressure(hs).value if rho is None: rho = standard_water_vapour_density(hs).value e = Tm * rho / 216.7 go = gamma0_exact(f, P, rho, Tm).value ho, hw = slant_inclined_path_equivalent_height(f, P).value Ao = ho * go * np.ones_like(Ar) # Step C15: Synthesize unit variance scintillation time series sci_0 = scintillation_attenuation_synthesis(Ns, Ts=1).value # Step C16: Compute the correction coefficient time series Cx(kTs) in # order to distinguish between scintillation fades and enhancements: Q_sci = 100 * stats.norm.sf(sci_0) C_x = np.where(sci_0 > 0, a_Fade(Q_sci) / a_Enhanc(Q_sci), 1) # Step C17: Transform the integrated water vapour content time series # V(kTs) into the Gamma distributed time series Z(kTs) as follows: kappa, lambd = integrated_water_vapour_coefficients(lat, lon, None) Z = stats.gamma.ppf(np.exp(-(V / lambd)**kappa), 10, scale=0.1) # Step C18: Compute the scintillation standard deviation σ following # the method recommended in Recommendation ITU-R P.618. sigma = scintillation_attenuation_sigma(lat, lon, f, el, D, eta, Tm, H, P, hL).value # Step C19: Compute the scintillation time series sci: As = np.where(Ar > 1, sigma * sci_0 * C_x * Z * Ar**(5 / 12), sigma * sci_0 * C_x * Z) # Step C20: Compute total tropospheric attenuation time series A(kTs) # as follows: A = Ar + Ac + Av + Ao + As if return_contributions: return (Ao + Av)[::Ts], Ac[::Ts], Ar[::Ts], As[::Ts], A[::Ts] else: return A[::Ts]
def test_gaseous_attenuation(self): # Define atmospheric parameters rho_wet = 7.5 * u.g / u.m**3 rho_dry = 0 * u.g / u.m**3 P = 1013.25 * u.hPa T = 15 * u.deg_C # Define frequency logspace parameters N_freq = 1000 fs = np.linspace(0, 1000, N_freq) # Compute the attenuation values att_wet = gammaw_exact(fs, P, rho_wet, T) att_dry = gamma0_exact(fs, P, rho_dry, T) # Plot the results plt.figure() plt.plot(fs, att_wet.value, 'b--', label='Wet atmosphere') plt.plot(fs, att_dry.value, 'r', label='Dry atmosphere') plt.xlabel('Frequency [GHz]') plt.ylabel('Specific attenuation [dB/km]') plt.yscale('log') plt.xscale('linear') plt.xlim(0, 1000) plt.ylim(1e-3, 1e5) plt.legend() plt.grid(which='both', linestyle=':', color='gray', linewidth=0.3, alpha=0.5) plt.grid(which='major', linestyle=':', color='black') plt.title('FIGURE 1. - Specific attenuation due to atmospheric gases,' '\ncalculated at 1 GHz intervals, including line centres') plt.tight_layout() ####################################################################### # Specific attenuation at different altitudes # ####################################################################### # Define atmospheric parameters hs = np.array([0, 5, 10, 15, 20]) * u.km # Define frequency logspace parameters N_freq = 2001 fs = np.linspace(50, 70, N_freq) # Plot the results plt.figure() # Loop over heights and compute values for h in hs: rho = standard_water_vapour_density(h) P = standard_pressure(h) T = standard_temperature(h) atts_dry = gamma0_exact(fs * u.GHz, P, rho, T).value atts_wet = gammaw_exact(fs * u.GHz, P, rho, T).value atts = atts_dry + atts_wet plt.plot(fs, atts, label='Altitude {0} km'.format(h.value)) plt.xlabel('Frequency [GHz]') plt.ylabel('Specific attenuation [dB/km]') plt.yscale('log') plt.xscale('linear') plt.xlim(50, 70) plt.ylim(1e-3, 1e2) plt.legend() plt.grid(which='both', linestyle=':', color='gray', linewidth=0.3, alpha=0.5) plt.grid(which='major', linestyle=':', color='black') plt.title('FIGURE 2. - Specific attenuation in the range 50-70 GHz' ' at the\n altitudes indicated, calculated at intervals of' ' 10 MHz\nincluding line centers (0, 5, 10 15, 20) km') plt.tight_layout() ####################################################################### # Comparison of line-by-line and approximate method # ####################################################################### # Define atmospheric parameters el = 90 rho = 7.5 * u.g / u.m**3 P = 1013.25 * u.hPa T = 15 * u.deg_C e = rho.value * T.value / 216.7 # Water vapour partial pressure p = P.value - e # Dry Air Pressure # Define frequency logspace parameters N_freq = 350 fs = np.linspace(1, 350, N_freq) # GHz # Initialize result vectors atts_approx = [] atts_exact = [] # Loop over frequencies and compute values atts_approx = gaseous_attenuation_slant_path(fs, el, rho, p, T, mode='approx') atts_exact = gaseous_attenuation_slant_path(fs, el, rho, p, T, h=0, mode='exact') # Plot the results plt.figure() plt.plot(fs, atts_approx.value, 'b--', label='Approximate method Annex 2') plt.plot(fs, atts_exact.value, 'r', label='Exact line-by-line method') plt.xlabel('Frequency [GHz]') plt.ylabel('Attenuation [dB]') plt.yscale('log') plt.xscale('log') plt.legend() plt.grid(which='both', linestyle=':', color='gray', linewidth=0.3, alpha=0.5) plt.grid(which='major', linestyle=':', color='black') plt.title('Comparison of line-by-line method to approximate method') plt.tight_layout()