<p>The <a href="https://rhodesmill.org/skyfield" rel="nofollow noreferrer"><code>skyfield</code></a> library  has <a href="https://rhodesmill.org/skyfield/searches.html" rel="nofollow noreferrer"><code>find_maxima</code> and <code>find_minima</code></a> functions that take as an argument a function that accepts a <code>timelib.Time</code> object and returns an <a href="https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html" rel="nofollow noreferrer"><code>numpy.ndarray</code></a> object that gives a value for the requested time, and has  a <code>step_days</code> parameter for the initial step size.</p>
<p>So, let's make a function that returns functions that produce the distance from earth for the specified body, and use those in <code>find_maxima</code> and <code>find_minima</code>. Our search window will be the calendar year 2024 in the UTC time zone.</p>
<pre><code>from typing import Callable

import numpy as np
import pytz
import skyfield.searchlib
from skyfield import api, timelib
from skyfield.jpllib import ChebyshevPosition, SpiceKernel
from skyfield.vectorlib import VectorSum


def kilometers_from_earth(ephemeris: SpiceKernel,
                          target: str | VectorSum | ChebyshevPosition,
                          step_days: float = 25) -&gt; Callable[[timelib.Time], np.ndarray[float]]:
    &quot;&quot;&quot;
    Creates functions that can be used to find maximum or minimum distances from Earth
    :param ephemeris: Ephemeris
    :param target: Target distance
    :param step_days: Step days for search function. Default 25
    :return: Function compatible with find_minima and find_maxima skyfield functions.
    &quot;&quot;&quot;
    earth = ephemeris['earth']
    if isinstance(target, str):
        target = ephemeris[target]

    def function(t: timelib.Time) -&gt; np.ndarray[float]:
        &quot;&quot;&quot;
        Distance from earth function
        :param t: Time object representing time
        :return: ndarray of corresponding distances in kilometers
        &quot;&quot;&quot;
        distance = earth.at(t).observe(target).distance().km
        return distance

    function.step_days = step_days

    return function


def main():
    ts = api.load.timescale()
    utc = pytz.timezone(&quot;UTC&quot;)
    start, stop = ts.utc(2024), ts.utc(2025)
    ephemeris = api.load('de421.bsp')
    sun_from_earth = kilometers_from_earth(ephemeris, 'sun')

    pe_times, pe_distances = skyfield.searchlib.find_minima(start, stop, sun_from_earth)
    ap_times, ap_distances = skyfield.searchlib.find_maxima(start, stop, sun_from_earth)

    print(&quot;\nResults for Sun-From-Earth&quot;)
    print(f&quot;{'Perihelion Time':^25}\t{'Distance':^13}\t\t{'Aphelion Time':^25}\t{'Distance':^13}&quot;)
    print('\n'.join(f&quot;{pt:%F %T %z}\t{pd:1.4E} km\t\t{at:%F %T %z}\t{ad:1.4E} km&quot;
                    for pt, pd, at, ad in
                    zip(pe_times.astimezone(utc), pe_distances, ap_times.astimezone(utc), ap_distances)))

    moon_from_earth = kilometers_from_earth(ephemeris, 'moon', step_days=7)

    pe_times, pe_distances = skyfield.searchlib.find_minima(start, stop, moon_from_earth)
    ap_times, ap_distances = skyfield.searchlib.find_maxima(start, stop, moon_from_earth)

    print(&quot;\nResults for Moon-From_earth&quot;)
    print(f&quot;{'Perigee Time':^25}\t{'Distance':^9}\t\t{'Apogee Time':^25}\t{'Distance':^9}&quot;)
    print('\n'.join(f&quot;{pt:%F %T %z}\t{pd:6.0f} km\t\t{at:%F %T %z}\t{ad:6.0f} km&quot;
                    for pt, pd, at, ad in
                    zip(pe_times.astimezone(utc), pe_distances, ap_times.astimezone(utc), ap_distances)))


if __name__ == '__main__':
    main()
</code></pre>
<p>Which produces the following results for the year 2024, in the maybe-more-precise-than-useful formats below:</p>
<pre><code>Results for Sun-From-Earth
     Perihelion Time          Distance                Aphelion Time           Distance   
2024-01-03 00:38:40 +0000   1.4710E+08 km       2024-07-05 05:06:07 +0000   1.5210E+08 km

Results for Moon-From_earth
      Perigee Time          Distance               Apogee Time          Distance 
2024-01-13 10:28:04 +0000   362283 km       2024-01-01 15:24:04 +0000   404873 km
2024-02-10 18:46:22 +0000   358096 km       2024-01-29 08:04:56 +0000   405751 km
2024-03-10 06:57:57 +0000   356895 km       2024-02-25 14:42:46 +0000   406303 km
2024-04-07 17:44:38 +0000   358841 km       2024-03-23 15:30:16 +0000   406306 km
2024-05-05 21:56:41 +0000   363146 km       2024-04-20 02:02:01 +0000   405650 km
2024-06-02 07:07:56 +0000   368072 km       2024-05-17 18:54:59 +0000   404676 km
2024-06-27 11:36:29 +0000   369252 km       2024-06-14 13:34:31 +0000   404116 km
2024-07-24 05:48:41 +0000   364895 km       2024-07-12 08:13:38 +0000   404400 km
2024-08-21 05:08:49 +0000   360184 km       2024-08-09 01:38:02 +0000   405328 km
2024-09-18 13:28:47 +0000   357282 km       2024-09-05 15:05:56 +0000   406229 km
2024-10-17 00:57:22 +0000   357179 km       2024-10-02 19:55:51 +0000   406515 km
2024-11-14 11:22:38 +0000   360122 km       2024-10-29 23:02:20 +0000   406142 km
2024-12-12 13:28:19 +0000   365384 km       2024-11-26 12:02:03 +0000   405281 km

</code></pre>

CC BY-SA 4.0
2023-12-30T23:03:32.393Z
Whoops, I'm missing one of the apogee times for the moon, because I naiively assumed that there'd be the same number of apoapses and periapses in the chosen time period, and so `zip` ruthlessly omitted the December 2024 lunar apogee.
notovny
CC BY-SA 4.0
2023-12-30T23:14:50.653Z
There's also a chance to grab the endpoints of the time window when they're not  acutally apogees or perigees, so care should be taken to avoid that as well.
notovny
CC BY-SA 4.0
2024-01-01T04:01:10.257Z
I saw that, I wanted it for the Sun more which was helpful for me, so thank you!
dimitri33
CC BY-SA 4.0
2023-12-29T23:37:39.060Z
2023-12-29T23:37:39.060Z
notovny
<p>The perigee does not necessarily happen when we have a full moon, using <code>swevents</code> we can easily determine the moon phases. Now can we use python to determine when the Moon and the Sun are the closest and furthest  from the Earth for a year lets say?</p>

CC BY-SA 4.0
2023-12-29T16:06:54.933Z
Swiss Ephemeris is primarily aimed at the astrological community, so questions of "distance" are not very important (astrologers are mostly interested in the relative positions in the sky, and not the 3d positions in space.) You should use an astronomy package, like `astropy` or `skyfield` instead of one designed for astrology.  There are several questions/examples on [stackoverflow.com]
James K
CC BY-SA 4.0
2023-12-29T16:08:53.777Z
How do you get a short link to [stackoverflow.com] in the way that you can do [physics.se]?
James K
CC BY-SA 4.0
2023-12-29T18:07:42.447Z
@James Just use `[SO]`. Thus: [SO]
PM 2Ring
CC BY-SA 4.0
2023-12-29T20:23:06.540Z
James, I am genuinely curious based on this printout. Are those calculations correct in terms of when the moon reaches  full moon?   ``` ./swevents -p1 -dolphase -b1.1.2024| grep Full
2024 Jan 25  17:54:02   Full 3  5 LE 14'44"
2024 Feb 24  12:30:28   Full 3  5 VI 23' 4"
```     Let's forget the astrology nonsense.
dimitri33
CC BY-SA 4.0
2023-12-29T20:43:24.590Z
They should be correct, the swevents program isn't making stuff up.  But it's not designed to do astronomy.  You should probably shift to astropy or similar.  Astropy certainly can do distance calculations.
James K
<the-sun><orbit><the-moon><planet><earth>
calculating apogee and perigee for moon and sun
dimitri33
# calculating apogee and perigee for moon and sun

(Asked by: username_0 on 2023-12-29)

The perigee does not necessarily happen when we have a full moon, using `swevents` we can easily determine the moon phases. Now can we use python to determine when the Moon and the Sun are the closest and furthest from the Earth for a year lets say?

**Tags:** the-sun, orbit, the-moon, planet, earth

> **Comments:**
> **username_1**: Swiss Ephemeris is primarily aimed at the astrological community, so questions of "distance" are not very important (astrologers are mostly interested in the relative positions in the sky, and not the 3d positions in space.) You should use an astronomy package, like `astropy` or `skyfield` instead of one designed for astrology.  There are several questions/examples on [stackoverflow.com]  
> **username_1**: How do you get a short link to [stackoverflow.com] in the way that you can do [physics.se]?  
> **username_2**: @username_1 Just use `[SO]`. Thus: [SO]  
> **username_0**: username_1, I am genuinely curious based on this printout. Are those calculations correct in terms of when the moon reaches  full moon?   ``` ./swevents -p1 -dolphase -b1.1.2024| grep Full 2024 Jan 25  17:54:02   Full 3  5 LE 14'44" 2024 Feb 24  12:30:28   Full 3  5 VI 23' 4" ```     Let's forget the astrology nonsense.  
> **username_1**: They should be correct, the swevents program isn't making stuff up.  But it's not designed to do astronomy.  You should probably shift to astropy or similar.  Astropy certainly can do distance calculations.  

---

## 1 answers

### Answer by username_3 on 2023-12-29. Score: 2 | Accepted answer

The [`skyfield`](https://rhodesmill.org/skyfield) library has [`find_maxima` and `find_minima`](https://rhodesmill.org/skyfield/searches.html) functions that take as an argument a function that accepts a `timelib.Time` object and returns an [`numpy.ndarray`](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html) object that gives a value for the requested time, and has a `step_days` parameter for the initial step size.

So, let's make a function that returns functions that produce the distance from earth for the specified body, and use those in `find_maxima` and `find_minima`. Our search window will be the calendar year 2024 in the UTC time zone.

```
from typing import Callable

import numpy as np
import pytz
import skyfield.searchlib
from skyfield import api, timelib
from skyfield.jpllib import ChebyshevPosition, SpiceKernel
from skyfield.vectorlib import VectorSum

def kilometers_from_earth(ephemeris: SpiceKernel,
                          target: str | VectorSum | ChebyshevPosition,
                          step_days: float = 25) -> Callable[[timelib.Time], np.ndarray[float]]:
    """
    Creates functions that can be used to find maximum or minimum distances from Earth
    :param ephemeris: Ephemeris
    :param target: Target distance
    :param step_days: Step days for search function. Default 25
    :return: Function compatible with find_minima and find_maxima skyfield functions.
    """
    earth = ephemeris['earth']
    if isinstance(target, str):
        target = ephemeris[target]

    def function(t: timelib.Time) -> np.ndarray[float]:
        """
        Distance from earth function
        :param t: Time object representing time
        :return: ndarray of corresponding distances in kilometers
        """
        distance = earth.at(t).observe(target).distance().km
        return distance

    function.step_days = step_days

    return function

def main():
    ts = api.load.timescale()
    utc = pytz.timezone("UTC")
    start, stop = ts.utc(2024), ts.utc(2025)
    ephemeris = api.load('de421.bsp')
    sun_from_earth = kilometers_from_earth(ephemeris, 'sun')

    pe_times, pe_distances = skyfield.searchlib.find_minima(start, stop, sun_from_earth)
    ap_times, ap_distances = skyfield.searchlib.find_maxima(start, stop, sun_from_earth)

    print("\nResults for Sun-From-Earth")
    print(f"{'Perihelion Time':^25}\t{'Distance':^13}\t\t{'Aphelion Time':^25}\t{'Distance':^13}")
    print('\n'.join(f"{pt:%F %T %z}\t{pd:1.4E} km\t\t{at:%F %T %z}\t{ad:1.4E} km"
                    for pt, pd, at, ad in
                    zip(pe_times.astimezone(utc), pe_distances, ap_times.astimezone(utc), ap_distances)))

    moon_from_earth = kilometers_from_earth(ephemeris, 'moon', step_days=7)

    pe_times, pe_distances = skyfield.searchlib.find_minima(start, stop, moon_from_earth)
    ap_times, ap_distances = skyfield.searchlib.find_maxima(start, stop, moon_from_earth)

    print("\nResults for Moon-From_earth")
    print(f"{'Perigee Time':^25}\t{'Distance':^9}\t\t{'Apogee Time':^25}\t{'Distance':^9}")
    print('\n'.join(f"{pt:%F %T %z}\t{pd:6.0f} km\t\t{at:%F %T %z}\t{ad:6.0f} km"
                    for pt, pd, at, ad in
                    zip(pe_times.astimezone(utc), pe_distances, ap_times.astimezone(utc), ap_distances)))

if __name__ == '__main__':
    main()

```

Which produces the following results for the year 2024, in the maybe-more-precise-than-useful formats below:

```
Results for Sun-From-Earth
     Perihelion Time          Distance                Aphelion Time           Distance   
2024-01-03 00:38:40 +0000   1.4710E+08 km       2024-07-05 05:06:07 +0000   1.5210E+08 km

Results for Moon-From_earth
      Perigee Time          Distance               Apogee Time          Distance 
2024-01-13 10:28:04 +0000   362283 km       2024-01-01 15:24:04 +0000   404873 km
2024-02-10 18:46:22 +0000   358096 km       2024-01-29 08:04:56 +0000   405751 km
2024-03-10 06:57:57 +0000   356895 km       2024-02-25 14:42:46 +0000   406303 km
2024-04-07 17:44:38 +0000   358841 km       2024-03-23 15:30:16 +0000   406306 km
2024-05-05 21:56:41 +0000   363146 km       2024-04-20 02:02:01 +0000   405650 km
2024-06-02 07:07:56 +0000   368072 km       2024-05-17 18:54:59 +0000   404676 km
2024-06-27 11:36:29 +0000   369252 km       2024-06-14 13:34:31 +0000   404116 km
2024-07-24 05:48:41 +0000   364895 km       2024-07-12 08:13:38 +0000   404400 km
2024-08-21 05:08:49 +0000   360184 km       2024-08-09 01:38:02 +0000   405328 km
2024-09-18 13:28:47 +0000   357282 km       2024-09-05 15:05:56 +0000   406229 km
2024-10-17 00:57:22 +0000   357179 km       2024-10-02 19:55:51 +0000   406515 km
2024-11-14 11:22:38 +0000   360122 km       2024-10-29 23:02:20 +0000   406142 km
2024-12-12 13:28:19 +0000   365384 km       2024-11-26 12:02:03 +0000   405281 km

```

> **Comments:**
> **username_3**: Whoops, I'm missing one of the apogee times for the moon, because I naiively assumed that there'd be the same number of apoapses and periapses in the chosen time period, and so `zip` ruthlessly omitted the December 2024 lunar apogee.  
> **username_3**: There's also a chance to grab the endpoints of the time window when they're not  acutally apogees or perigees, so care should be taken to avoid that as well.  
> **username_0**: I saw that, I wanted it for the Sun more which was helpful for me, so thank you!  

---

