def __init__(self, altitude, inclination, solarmodulation=None): self.Alt = altitude # instrument altitude (km) self.magl = inclination # orbit inclination (deg.) self.geomlat = inclination # geomagnetic latitude (deg.) TODO # The inclination was used to approximate the average Magnetic Latitude """ solar modulation potential (MV): ~550 for solar minimum ~1100 for solar maximum """ if solarmodulation is None: self.solmod = 650. else: self.solmod = solarmodulation EarthRadius = R_earth.to('km').value """ Average Geomagnetic cutoff in GV for a dipole approximations Equation 4 Smart et al. 2005 doi:10.1016/j.asr.2004.09.015 """ R_E = R_earth.to('cm').value # g 01 term (in units of G) from IGRF-12 for 2015 g10 = 29442 * 10**(-9) * 10**4 # G M = g10 * R_E * 300 / 10**9 # GV/cm2 self.AvGeomagCutOff = (M / 4 * (1 + self.Alt / EarthRadius)**(-2.0) * np.cos(np.deg2rad(self.geomlat))**4) AtmosphereHeight = 40 # km self.HorizonAngle = 90.0 + np.rad2deg( np.arccos( (EarthRadius + AtmosphereHeight) / (EarthRadius + self.Alt)))
def moon_distance(loc, t2, t3): # Get the distance to the moon via the umbra diameter estimate if isinstance(loc, type(u'')) or isinstance(loc, type('')): loc = EarthLocation.of_address(loc) # Location of Sun & Moon sun = get_sun(t2) moon = get_moon(t2, loc) # Umbra size, from time and location su = estimate_umbra(loc, t2, t3) # Constants in km re = R_earth.to(u.km).value ds = sun.distance.to(u.km).value rs = R_sun.to(u.km).value rm = 1738.1 dm = (2 * rm - su) * ds / (2 * (rs - rm)) + re print( 'Distance estimate: {:.7} km (Actual: {:.7}). Ratio: {:.2}. Difference: {:.7}' .format(dm, moon.distance, moon.distance / (dm * u.km), dm * u.km - moon.distance)) # moon.distance is from surface, not center return dm, moon.distance.to(u.km).value + re
def test_vallado61(self): alt_i = 191.34411 # km alt_f = 35781.34857 # km k = k_earth.to(units.km ** 3 / units.s ** 2).value R = R_earth.to(units.km).value expected_dv = 3.935224 # km/s expected_t_trans = 5.256713 # h r_i = R + alt_i r_f = R + alt_f dva, dvb, _, t_trans = hohmann(k, r_i, r_f) dv = abs(dva) + abs(dvb) assert_almost_equal(dv, expected_dv, decimal=2) assert_almost_equal(t_trans / 3600, expected_t_trans, decimal=2)
def test_vallado62(self): alt_i = 191.34411 # km alt_b = 503873 # km alt_f = 376310.0 # km k = k_earth.to(units.km ** 3 / units.s ** 2).value R = R_earth.to(units.km).value expected_dv = 3.904057 # km/s expected_t_trans = 593.919803 # h r_i = R + alt_i r_b = R + alt_b r_f = R + alt_f dva, dvb, dvc, _, _, t_trans1, t_trans2 = bielliptic(k, r_i, r_b, r_f) dv = abs(dva) + abs(dvb) + abs(dvc) t_trans = t_trans1 + t_trans2 assert_almost_equal(dv, expected_dv, decimal=2) assert_almost_equal(t_trans / 3600, expected_t_trans, decimal=0)
def get_umbra(loc, t2): # Get the real size of the umbra sun = get_sun(t2) moon = get_moon(t2, loc) sun_altaz = sun.transform_to(AltAz(obstime=t2, location=loc)) # Constants in km re = R_earth.to(u.km).value ds = sun.distance.to(u.km).value rs = R_sun.to(u.km).value rm = 1738.1 alpha = np.arcsin((rs - rm) / (ds - moon.distance.to(u.km).value)) su = 2 * (rm / np.sin(alpha) - (moon.distance.to(u.km).value - re)) * np.tan(alpha) su / np.tan(sun_altaz.alt.to(u.rad)) # not 100% sure of this return su
symbol : str, optional Symbol for the body, default to None. R : Quantity, optional Radius of the body, default to 0 km. """ if not check_units((k, R), (u.m ** 3 / u.s ** 2, u.m)): raise u.UnitsError("Units must be consistent") self.k = k self.name = name self.symbol = symbol self.R = R def __str__(self): return u"{} ({})".format(self.name, self.symbol) def _repr_latex_(self): """Creates a LaTeX representation. Used by the IPython notebook. """ return self.__str__() Sun = Body(k=132712440018 * u.km ** 3 / u.s ** 2, name="Sun", symbol=u"\u2609") Earth = Body(k=398600 * u.km ** 3 / u.s ** 2, name="Earth", symbol=u"\u2641", R=R_earth.to(u.km))
self.k = k self.name = name self.symbol = symbol self.R = R @classmethod @u.quantity_input(k=u.km**3 / u.s**2, R=u.km) def from_parameters(cls, k, name, symbol, R): return cls(k, name, symbol, R) def __str__(self): return u"{0} ({1})".format(self.name, self.symbol) def _repr_latex_(self): """Creates a LaTeX representation. Used by the IPython notebook. """ return self.__str__() Sun = Body.from_parameters(k=132712440018 * u.km**3 / u.s**2, name="Sun", symbol=u"\u2609", R=695700 * u.km) Earth = Body.from_parameters(k=398600 * u.km**3 / u.s**2, name="Earth", symbol=u"\u2641", R=R_earth.to(u.km))
def estimate_umbra(loc, t2, t3, verbose=False): # Estimate the size of the umbra from the contact times (not 100% sure on this yet) delta_t = t3 - t2 # Location of Sun sun = get_sun(t2) sun_altaz = sun.transform_to(AltAz(obstime=t2, location=loc)) # Umbra size, from time and location s_umbra = 2 * np.pi * R_earth * np.cos(loc.latitude.to( u.rad)) / u.day * delta_t s_umbra /= np.tan(sun_altaz.alt.to( u.rad)) # correction for altitude (correct?) su = s_umbra.to(u.km).value # Extra corrections for projection/location from central line # TODO: Need to check if this is the right approach df = get_path() df['sep'] = ang_sep(loc, df['clon'], df['clat']) * 2 * np.pi * R_earth.to( u.km).value / 360. ind = df[df['sep'] == min(df['sep'])].index[0] # Replace with finer version x2 = 41. x1 = 0. df_fine = pd.DataFrame(np.arange(x1, x2, 1), columns=['xval']) df_fine['clat'] = make_fine('clat', ind, df, x1=x1, x2=x2) df_fine['clon'] = make_fine('clon', ind, df, x1=x1, x2=x2) df_fine['width'] = make_fine('width', ind, df, x1=x1, x2=x2) # Get new best index df_fine['sep'] = ang_sep(loc, df_fine['clon'], df_fine['clat']) * 2 * np.pi * R_earth.to( u.km).value / 360. ind = df_fine[df_fine['sep'] == min(df_fine['sep'])].index[0] df = df_fine # Get the angle factor = np.pi / 180 lat1 = df.loc[ind, 'clat'] * factor lat2 = lat1 dlon = (df.loc[ind, 'clon'] - df.loc[ind - 1, 'clon']) * factor theta1 = np.arctan2( np.sin(dlon) * np.cos(lat2), np.cos(lat1) * np.sin(lat2) - np.sin(lat1) * np.cos(lat2) * np.cos(dlon)) / factor lat2 = df.loc[ind - 1, 'clat'] * factor theta2 = np.arctan2( np.sin(dlon) * np.cos(lat2), np.cos(lat1) * np.sin(lat2) - np.sin(lat1) * np.cos(lat2) * np.cos(dlon)) / factor theta = abs(theta1 - theta2) # Modify the umbra size # TODO: This still needs more work as it doesn't give an approximately correct value ratio = 0.5 * df.loc[ind, 'width'] / (0.5 * df.loc[ind, 'width'] - df.loc[ind, 'sep']) su2 = su * ratio / np.cos(theta * factor) if verbose: print(df.loc[ind, ['clon', 'clat', 'width', 'sep']]) print('Angle: {} (Cosine: {})'.format(theta, np.cos(theta * factor))) print('Ratio: {}'.format(ratio)) print('{} -> {}'.format(su, su2)) return su2
def get_observatory_info(self, time_delta=None): """Compute the lat/lon and altitude of HST. Using the expstart and expend, generate a series MJD dates that correspond to 1 minute intervals. Calculate the lat/lon and altitude at each time step. """ altitude_list = [] lat_list = [] lon_list = [] # Break up the exposure into one minute intervals if time_delta is None: time_delta = self.metadata['expend'] - self.metadata['expstart'] num_intervals = 2*int(time_delta.to('minute').value) expend = self.metadata['expend'].mjd else: expend= self.metadata['expstart'].mjd + time_delta/86400.0 num_intervals = int(np.round(time_delta/60)) # If the number of one minute intervals is less than 2, set the number # of intervals to 5 (arbitrarily chosen) if num_intervals < 2: num_intervals = 5 # Generate MJD dates correspond to these one minute intervals time_intervals = np.linspace(self.metadata['expstart'].mjd, expend, num_intervals, endpoint=True) # Generate the path to the engineering file self._engineering_file() # Using the telemetry data for the SPT file, compute HST (lon, lat, z) if os.path.isfile(self.telemetry_file): orbital_params = orbit.HSTOrbit(self.telemetry_file) # compute coords at beginning and end of exposure for t in time_intervals: rect, vel = orbital_params.getPos(t) r, ra, dec = rectToSph(rect) altitude_list.append(r - R_earth.to('km').value) lat = dec lon = ra - 2 * np.pi * gmst(t) if lon < 0: lon += 2 * np.pi lon /= DEGtoRAD lat /= DEGtoRAD lat_list.append(lat) lon_list.append(lon) self.metadata['latitude'] = np.asarray(lat_list) self.metadata['longitude'] = np.asarray(lon_list) self.metadata['altitude'] = np.asarray(altitude_list) self.metadata['time_intervals'] = time_intervals else: LOG.info('SPT file not found, place it in the data directory') # If the SPT file for some reason doesn't exist, save NaNs self.metadata['altitude'] = np.nan self.metadata['latitude'] = np.nan self.metadata['longitude'] = np.nan self.metadata['time_intervals'] = np.nan