Image d'illustration de l'article Simuler un ciel étoilé avec Python et Skyfield !

Simuler un ciel étoilé avec Python et Skyfield !

Skyfield est une librairie Python pour tous les passionnés d’astronomie. Elle permet de positionner n’importe quel objet céleste dans l’espace, de suivre les comètes et les planètes, de calculer les phases de la Lune et même de tenir compte de la déformation du ciel en fonction de la température. C’est dingue non ?

Je me suis alors mis en tête de reproduire la cartographie 3D d’un ciel étoilé, à la façon de l’excellente application Stellarium. Voici le résultat : La cartographie du ciel étoilé vu de Québec

Les sources de données

Première étape : avoir de bonnes données. Voici les trois sources que j’ai utilisées :

  • Pour les étoiles, le catalogue Hipparcos terminé en 1996 fournit la position de 120 000 étoiles et leurs mouvements. Oui, 30 ans de données — mais elles sont encore valides aujourd’hui et restent la référence pour l’observation depuis la Terre. Des catalogues plus complets existent (Tycho-1), mais on n’en a pas besoin ici.
  • Pour les planètes, on parle d’éphémérides. Un fichier de421.bsp suffit pour consulter leur position dans le système solaire.
  • Pour les constellations, c’est un tableau de relations entre étoiles. Je me suis basé directement sur le fichier fourni par Stellarium.

Le code pour récupérer toutes ces données est au final très simple :

from skyfield.data import hipparcos, stellarium

CONSTELLATION_URL = (
    "https://raw.githubusercontent.com/Stellarium/stellarium/"
    "eb47095a9282cf6b981f6e37fe1ea3a3ae0fd167/"
    "skycultures/modern_st/constellationship.fab"
)

def load_data():
    # load celestial data
    # de421 shows position of earth and sun in space, this is a file in local
    eph = load("de421.bsp")

    # hipparcos dataset contains star location data
    with load.open(hipparcos.URL) as f:
        stars = hipparcos.load_dataframe(f)

    with load.open(CONSTELLATION_URL) as f:
        constellations = stellarium.parse_constellations(f)

    return eph, stars, constellations

Ces fichiers contiennent beaucoup de données. Voici un aperçu de ce que donne le catalogue d’étoiles :

| hip | magnitude | ra_degrees | dec_degrees | parallax_mas | ra_mas_per_year | dec_mas_per_year | ra_hours  | epoch_year |
|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| 1 | 9.10 | 0.000912 | 1.089013 | 3.54 | -5.20 | -1.88 | 0.000061 | 1991.25 |
| 2 | 9.27 | 0.003797 | -19.498837 | 21.90 | 181.21 | -0.93 | 0.000253 | 1991.25 |
| 3 | 6.61 | 0.005008 | 38.859286 | 2.81 | 5.24 | -2.91 | 0.000334 | 1991.25 |
| 4 | 8.06 | 0.008382 | -51.893546 | 7.75 | 62.85 | 0.16 | 0.000559 | 1991.25 |
| 5 | 8.55 | 0.009965 | -40.591224 | 2.87 | 2.53 | 9.07 | 0.000664 | 1991.25 |

Pas d’inquiétude si c’est du charabia, retenez juste l’essentiel :

  • magnitude : plus la valeur est élevée, moins l’étoile brille (9 = presque invisible, 3 = bien visible !)
  • ra_degrees (Right Ascension) : position horizontale de l’étoile par rapport au centre de la Terre, en partant de la droite — négatif = vers la gauche.
  • dec_degrees (Déclinaison) : position verticale par rapport au centre de la Terre — négatif = vers le bas.
Schéma expliquant les mesures Right Ascension et Déclinaison

Le calcul des positions

Alors là, on rentre dans les maths. À nous les cosinus, les calculs de vecteurs… sauf qu’en pratique, Skyfield fait pratiquement tout le travail à notre place.

L’objectif est de convertir la position des étoiles en fonction d’une date et d’un point précis sur le globe — c’est ce qu’on appelle un Observateur Topocentrique :

Schéma de représentation des coordonnées Horizontales (Altitude & Azimuth)

En résumé :

  • Right Ascension & Déclinaison : coordonnées par rapport au centre de la Terre.
  • Altitude & Azimuth : coordonnées par rapport à un point précis sur le globe (le vôtre !).
    # Getted from Ephemeride
    earth = eph["earth"]
    # define observation time from our UTC datetime
    ts = load.timescale()
    t = ts.from_datetime(utc_dt)

    # Topocentric observator (Earth ground)
    observer = earth + wgs84.latlon(latitude_degrees=lat, longitude_degrees=long)

    # Observe stars table from our Observer object
    star_vectors = observer.at(t).observe(Star.from_dataframe(stars)).apparent()

    # Convert Right Ascension & Declinaison into Altitude and Azimuth
    alt, az, _ = star_vectors.altaz()
    data = stars.copy()
    data["alt"] = alt.degrees
    data["az"] = az.degrees

Petit avertissement pour les curieux : Skyfield ne supporte que la Terre comme point d’observation, pas question de simuler le ciel depuis Mars :D !

Convertir nos étoiles en coordonnées cartésiennes

On commence par filtrer les étoiles selon la magnitude souhaitée. L’œil humain perçoit jusqu’à une magnitude d’environ 5, selon la pollution lumineuse de l’endroit.

Ensuite, on convertit l’Altitude et l’Azimuth en coordonnées cartésiennes pour placer chaque étoile sur un dôme virtuel.

    # Filtre: étoiles visibles à l'œil nu et au-dessus de l'horizon
    data = data[
        (data["alt"] > 0) & data["magnitude"].notna() & (data["magnitude"] <= MAGNITUDE)
    ].copy()

    # Conversion sphérique -> cartésien (dôme)
    alt_r = np.radians(data["alt"].to_numpy())
    az_r = np.radians(data["az"].to_numpy())

    data["x"] = np.cos(alt_r) * np.sin(az_r)  # Est +
    data["y"] = np.sin(alt_r)  # Zénith +
    data["z"] = np.cos(alt_r) * np.cos(az_r)  # Nord +

Une fois ces coordonnées prêtes, il suffit de les passer à ThreeJS — je te laisse fouiller le GitHub pour cette partie ! :)

Attention cependant : le repère cartésien calculé ici est celui d’un observateur extérieur au dôme. Comme on veut le voir de l’intérieur, il faut appliquer une symétrie sur la coordonnée x.

Vers l’infini, et au-delà !

Il y a encore plein de choses à explorer :

  • Afficher le nom des principales étoiles et des constellations
  • Ajouter les planètes du système solaire (de minuscules points, à peine visibles à l’œil nu)
  • Intégrer les Deep Sky Objects — galaxies et nébuleuses observables aux jumelles ou au télescope
  • Placer la Voie lactée et la Lune pour une immersion totale

Le code source est disponible sur GitHub : Skyfield-Observatory