Пример #1
0
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
Пример #2
0
    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)
Пример #3
0
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
Пример #4
0
    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}
Пример #5
0
    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
Пример #6
0
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]
Пример #7
0
    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]
Пример #8
0
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)
Пример #9
0
    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
Пример #10
0
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
Пример #11
0
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]
Пример #12
0
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]
Пример #13
0
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]
Пример #14
0
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)