def _search_oppositions(start_time: Time, end_time: Time) -> [Event]: earth = get_skf_objects()['earth'] sun = get_skf_objects()['sun'] aster = None def is_oppositing(time: Time) -> [bool]: earth_pos = earth.at(time) sun_pos = earth_pos.observe( sun).apparent() # Never do this without eyes protection! aster_pos = earth_pos.observe( get_skf_objects()[aster.skyfield_name]).apparent() _, lon1, _ = sun_pos.ecliptic_latlon() _, lon2, _ = aster_pos.ecliptic_latlon() return (lon1.degrees - lon2.degrees) > 180 is_oppositing.rough_period = 1.0 events = [] for aster in ASTERS: if not isinstance(aster, Planet) or aster.skyfield_name in [ 'MERCURY', 'VENUS' ]: continue times, _ = find_discrete(start_time, end_time, is_oppositing) for time in times: events.append(Event('OPPOSITION', [aster], time.utc_datetime())) return events
def get_moon_phase(compute_date: datetime.date) -> MoonPhase: earth = get_skf_objects()['earth'] moon = get_skf_objects()['moon'] sun = get_skf_objects()['sun'] def moon_phase_at(time: Time): time._nutation_angles = get_iau2000b(time) current_earth = earth.at(time) _, mlon, _ = current_earth.observe( moon).apparent().ecliptic_latlon('date') _, slon, _ = current_earth.observe(sun).apparent().ecliptic_latlon( 'date') return (((mlon.radians - slon.radians) // (tau / 8)) % 8).astype(int) moon_phase_at.rough_period = 7.0 # one lunar phase per week today = get_timescale().utc(compute_date.year, compute_date.month, compute_date.day) time1 = get_timescale().utc(compute_date.year, compute_date.month, compute_date.day - 10) time2 = get_timescale().utc(compute_date.year, compute_date.month, compute_date.day + 10) times, phase = find_discrete(time1, time2, moon_phase_at) return skyfield_to_moon_phase(times, phase, today)
def _search_conjunction(start_time: Time, end_time: Time) -> [Event]: earth = get_skf_objects()['earth'] aster1 = None aster2 = None def is_in_conjunction(time: Time): earth_pos = earth.at(time) _, aster1_lon, _ = earth_pos.observe( aster1.get_skyfield_object()).apparent().ecliptic_latlon() _, aster2_lon, _ = earth_pos.observe( aster2.get_skyfield_object()).apparent().ecliptic_latlon() return ((aster1_lon.radians - aster2_lon.radians) / pi % 2.0).astype('int8') == 0 is_in_conjunction.rough_period = 60.0 computed = [] conjunctions = [] for aster1 in ASTERS: # Ignore the Sun if isinstance(aster1, Star): continue for aster2 in ASTERS: if isinstance(aster2, Star) or aster2 == aster1 or aster2 in computed: continue times, is_conjs = find_discrete(start_time, end_time, is_in_conjunction) for i, time in enumerate(times): if is_conjs[i]: aster1_pos = (aster1.get_skyfield_object() - earth).at(time) aster2_pos = (aster2.get_skyfield_object() - earth).at(time) distance = aster1_pos.separation_from(aster2_pos).degrees if distance - aster2.get_apparent_radius( time, earth) < aster1.get_apparent_radius( time, earth): occulting_aster = [ aster1, aster2 ] if aster1_pos.distance().km < aster2_pos.distance( ).km else [aster2, aster1] conjunctions.append( Event('OCCULTATION', occulting_aster, time.utc_datetime())) else: conjunctions.append( Event('CONJUNCTION', [aster1, aster2], time.utc_datetime())) computed.append(aster1) return conjunctions
def get_sun(self, start_time, end_time) -> dict: times, is_risen = find_discrete( start_time, end_time, almanac.sunrise_sunset(get_skf_objects(), self.position)) sunrise = times[0] if is_risen[0] else times[1] sunset = times[1] if not is_risen[1] else times[0] return {'rise': sunrise, 'set': sunset}
def get_asters_ephemerides_for_aster(aster, date: datetime.date, position: Position) -> Object: skyfield_aster = get_skf_objects()[aster.skyfield_name] def get_angle(time: Time) -> float: return position.get_planet_topos().at(time).observe( skyfield_aster).apparent().altaz()[0].degrees def is_risen(time: Time) -> bool: return get_angle(time) > RISEN_ANGLE get_angle.rough_period = 1.0 is_risen.rough_period = 0.5 start_time = get_timescale().utc(date.year, date.month, date.day) end_time = get_timescale().utc(date.year, date.month, date.day, 23, 59, 59) rise_times, arr = find_discrete(start_time, end_time, is_risen) try: culmination_time, _ = find_maxima(start_time, end_time, f=get_angle, epsilon=1. / 3600 / 24, num=12) culmination_time = culmination_time[0] if len( culmination_time) > 0 else None except ValueError: culmination_time = None if len(rise_times) == 2: rise_time = rise_times[0 if arr[0] else 1] set_time = rise_times[1 if not arr[1] else 0] else: rise_time = rise_times[0] if arr[0] else None set_time = rise_times[0] if not arr[0] else None # Convert the Time instances to Python datetime objects if rise_time is not None: rise_time = rise_time.utc_datetime().replace(microsecond=0) if culmination_time is not None: culmination_time = culmination_time.utc_datetime().replace( microsecond=0) if set_time is not None: set_time = set_time.utc_datetime().replace( microsecond=0) if set_time is not None else None aster.ephemerides = AsterEphemerides(rise_time, culmination_time, set_time) return aster
def test_find_discrete_with_a_sub_epsilon_jag_right_at_zero(): t0, t1 = make_t() f = make_stairstep_f([0.5, 0.5 + 0.99 * epsilon]) # We hard-code num=12, just in case the default ever changes to # another value that might not trigger the symptom. t, y = find_discrete(t0, t1, f, epsilon, 12) # Note that we always return the last of several close solutions, so # that `y` correctly reflects the new state that persists after the # flurry of changes is complete. assert is_close(t.tt, (0.5 + 0.99 * epsilon, )) assert list(y) == [2]
def __init__(self, data: AstroData, time: Time, type: int, closest_approach_radians: float, moon_radius_radians: float, penumbra_radius_radians: float, umbra_radius_radians: float): self.data = data self.closest_approach_time = time self.type = eclipselib.LUNAR_ECLIPSES[type] self.closest_approach_radians = closest_approach_radians self.moon_radius_radians = moon_radius_radians self.penumbra_radius_radians = penumbra_radius_radians self.umbra_radius_radians = umbra_radius_radians calc = EclipseTool(data) f = lambda t: calc.angle_between(t) < calc.umbra_radius( t) + calc.moon_radius(t) f.step_days = 0.01 times, values = find_discrete(data.timescale.tt_jd(time.tt - 1), data.timescale.tt_jd(time.tt + 1), f) if self.type != 'Penumbral': assert len(times) == 2 assert (values == [1, 0]).all() assert times[0].tt < time.tt assert times[1].tt > time.tt self.partial_eclipse_begin = times[0] self.partial_eclipse_end = times[1] f = lambda t: calc.angle_between(t) < calc.umbra_radius( t) - calc.moon_radius(t) f.step_days = 0.01 times, values = find_discrete(data.timescale.tt_jd(time.tt - 0.5), data.timescale.tt_jd(time.tt + 0.5), f) if self.type == 'Total': assert len(times) == 2 assert (values == [1, 0]).all() assert times[0].tt < time.tt assert times[1].tt > time.tt self.total_eclipse_begin = times[0] self.total_eclipse_end = times[1]
def get_moon_phase(compute_date: datetime.date, timezone: int = 0) -> MoonPhase: earth = get_skf_objects()['earth'] moon = get_skf_objects()['moon'] sun = get_skf_objects()['sun'] def moon_phase_at(time: Time): time._nutation_angles = get_iau2000b(time) current_earth = earth.at(time) _, mlon, _ = current_earth.observe(moon).apparent().ecliptic_latlon( 'date') _, slon, _ = current_earth.observe(sun).apparent().ecliptic_latlon( 'date') return (((mlon.radians - slon.radians) // (tau / 8)) % 8).astype(int) moon_phase_at.rough_period = 7.0 # one lunar phase per week today = get_timescale().utc(compute_date.year, compute_date.month, compute_date.day) time1 = get_timescale().utc(compute_date.year, compute_date.month, compute_date.day - 10) time2 = get_timescale().utc(compute_date.year, compute_date.month, compute_date.day + 10) try: times, phase = find_discrete(time1, time2, moon_phase_at) except EphemerisRangeError as error: start = translate_to_timezone(error.start_time.utc_datetime(), timezone) end = translate_to_timezone(error.end_time.utc_datetime(), timezone) start = datetime.date(start.year, start.month, start.day) + datetime.timedelta(days=12) end = datetime.date(end.year, end.month, end.day) - datetime.timedelta(days=12) raise OutOfRangeDateError(start, end) return skyfield_to_moon_phase(times, phase, today)
def get_seasons(year: int) -> dict: start_time = get_timescale().utc(year, 1, 1) end_time = get_timescale().utc(year, 12, 31) times, almanac_seasons = find_discrete( start_time, end_time, almanac.seasons(get_skf_objects())) seasons = {} for time, almanac_season in zip(times, almanac_seasons): if almanac_season == 0: season = 'MARCH' elif almanac_season == 1: season = 'JUNE' elif almanac_season == 2: season = 'SEPTEMBER' elif almanac_season == 3: season = 'DECEMBER' else: raise AssertionError seasons[season] = time.utc_iso() return seasons
def get_ephemerides(date: datetime.date, position: Position, timezone: int = 0) -> [AsterEphemerides]: ephemerides = [] def get_angle(for_aster: Object): def fun(time: Time) -> float: return position.get_planet_topos().at(time).observe(for_aster.get_skyfield_object()).apparent().altaz()[0]\ .degrees fun.rough_period = 1.0 return fun def is_risen(for_aster: Object): def fun(time: Time) -> bool: return get_angle(for_aster)(time) > RISEN_ANGLE fun.rough_period = 0.5 return fun start_time = get_timescale().utc(date.year, date.month, date.day, -timezone) end_time = get_timescale().utc(date.year, date.month, date.day, 23 - timezone, 59, 59) try: for aster in ASTERS: rise_times, arr = find_discrete(start_time, end_time, is_risen(aster)) try: culmination_time, _ = find_maxima(start_time, end_time, f=get_angle(aster), epsilon=1. / 3600 / 24, num=12) culmination_time = culmination_time[0] if len( culmination_time) > 0 else None except ValueError: culmination_time = None if len(rise_times) == 2: rise_time = rise_times[0 if arr[0] else 1] set_time = rise_times[1 if not arr[1] else 0] else: rise_time = rise_times[0] if arr[0] else None set_time = rise_times[0] if not arr[0] else None # Convert the Time instances to Python datetime objects if rise_time is not None: rise_time = translate_to_timezone( rise_time.utc_datetime().replace(microsecond=0), to_tz=timezone) if culmination_time is not None: culmination_time = translate_to_timezone( culmination_time.utc_datetime().replace(microsecond=0), to_tz=timezone) if set_time is not None: set_time = translate_to_timezone( set_time.utc_datetime().replace(microsecond=0), to_tz=timezone) ephemerides.append( AsterEphemerides(rise_time, culmination_time, set_time, aster=aster)) except EphemerisRangeError as error: start = translate_to_timezone(error.start_time.utc_datetime(), timezone) end = translate_to_timezone(error.end_time.utc_datetime(), timezone) start = datetime.date(start.year, start.month, start.day + 1) end = datetime.date(end.year, end.month, end.day - 1) raise OutOfRangeDateError(start, end) return ephemerides
def test_find_discrete_with_a_barely_detectable_jag_right_at_zero(): t0, t1 = make_t() f = make_stairstep_f([0.5, 0.5 + 3.1 * epsilon]) t, y = find_discrete(t0, t1, f, epsilon) assert is_close(t.tt, (0.5, 0.5 + 3.1 * epsilon)) assert list(y) == [1, 2]
def test_find_discrete_near_right_edge(): t0, t1 = make_t() f = make_stairstep_f([0.5, 1.0 - bump]) t, y = find_discrete(t0, t1, f, epsilon) assert is_close(t.tt, (0.5, 1.0 - bump)) assert list(y) == [1, 2]
def test_find_discrete_near_left_edge(): t0, t1 = make_t() f = make_stairstep_f([bump, 0.5]) t, y = find_discrete(t0, t1, f, epsilon) assert is_close(t.tt, (bump, 0.5)) assert list(y) == [1, 2]
def test_find_discrete_that_finds_nothing(): t0, t1 = make_t() f = make_stairstep_f([-0.1, +1.1]) t, y = find_discrete(t0, t1, f, epsilon) assert not len(t.tt) assert not len(y)