Calcul des phases lunaire pour la date du jour

Programmation d'applications complexes
Avatar de l’utilisateur
PhM
Messages : 122
Inscription : dim. 08/déc./2019 10:50

Calcul des phases lunaire pour la date du jour

Message par PhM »

Bonjour,

Petit programme fonctionnel de calcul des phases lunaire pour la date du jour pour l'hémisphère nord (Paris).

Code : Tout sélectionner

; ----------- Calcul des phases lunaire pour la date du jour ----------
;                      pour l'hémisphère nord (Paris)
;       Site de contrôle : https://starwalk.space/fr/moon-calendar
;          Ph. Mijon - septembre 2025 - PB 6.21 sur Windows 11
; ---------------------------------------------------------------------

; -------------------------- Déclaration des procédures de calcul et données fixes
Declare.f AgeLune(annee, mois, jour)
Declare.s PhaseLune(age.f)
Declare.f IlluminationLune(age.f)

Global RefJulien = 2451550.1     ; Nouvelle lune de référence du 6 janvier 2000 à 14h24
Global Lunaison  = 29.530588853  ; Longueur du mois lunaire 29j 12h 44m 2,9s

; -------------------------- Calcul de la phase lunaire du jour

annee = Year(Date())
mois = Month(Date())
jour = Day(Date())

age.f = AgeLune(annee, mois, jour)
illumination.f = IlluminationLune(age.f)
phase$ = PhaseLune(age.f)

MessageRequester("Phase Lunaire du " + Str(jour) + "/" + Str(mois) + "/" + Str(annee), "La Lune est en phase : " + phase$ + " (" + StrF(illumination.f, 1) + "% illuminée)")

End



; -------------------------- Calcul de l'âge lunaire avec correction

Procedure.f AgeLune(annee, mois, jour)
  
  If mois <= 2
    annee = annee - 1
    mois = mois + 12
  EndIf

  a = annee / 100
  b = 2 - a + a / 4
  JL = Int(365.25 * (annee + 4716)) + Int(30.6001 * (mois + 1)) + jour + b - 1524.5

  age.f = Mod(JL - RefJulien, Lunaison)
  
  If age.f < 0
    age.f = age.f + Lunaison
  EndIf

  ; Correction simple de l’anomalie moyenne
  M = 2 * #PI * age.f / Lunaison
  correction.f = 0.5 * Sin(M) - 0.25 * Sin(2 * M)
  age.f = age.f + correction.f

  ; Facteur de correction pour coller aux données observées
  ; aux coordonnées de Paris (Lat:48.86667 / Lon:2.33333)
  age.f = age.f + 0.56

  ProcedureReturn age.f
EndProcedure

; -------------------------- Détermination de la phase lunaire

Procedure.s PhaseLune(age.f)
  
  If age.f < 1.84566
    phase$ = "🌑 Nouvelle lune"
  ElseIf age.f < 5.53699
    phase$ = "🌒 Premier croissant"
  ElseIf age.f < 9.22831
    phase$ = "🌓 Premier quartier"
  ElseIf age.f < 12.91963
    phase$ = "🌔 Gibbeuse croissante"
  ElseIf age.f < 16.61096
    phase$ = "🌕 Pleine lune"
  ElseIf age.f < 20.30228
    phase$ = "🌖 Gibbeuse décroissante"
  ElseIf age.f < 23.99361
    phase$ = "🌗 Dernier quartier"
  ElseIf age.f < 27.68493
    phase$ = "🌘 Dernier croissant"
  Else
    phase$ = "🌑 Nouvelle lune"
  EndIf

  ProcedureReturn phase$
EndProcedure

; -------------------------- Calcul du pourcentage d'illumination

Procedure.f IlluminationLune(age.f)

  illumination.f = (1 - Cos(2 * #PI * age.f / Lunaison)) / 2 * 100
  
  ProcedureReturn illumination.f
EndProcedure


Avatar de l’utilisateur
Jacobus
Messages : 1565
Inscription : mar. 06/avr./2004 10:35
Contact :

Re: Calcul des phases lunaire pour la date du jour

Message par Jacobus »

Bonjour,
Sympa ton application :)
Merci pour le partage.
Quand tous les glands seront tombés, les feuilles dispersées, la vigueur retombée... Dans la morne solitude, ancré au coeur de ses racines, c'est de sa force maturité qu'il renaîtra en pleine magnificence...Jacobus.
Avatar de l’utilisateur
MLD
Messages : 1125
Inscription : jeu. 05/févr./2009 17:58
Localisation : Bretagne

Re: Calcul des phases lunaire pour la date du jour

Message par MLD »

Sympa
Merci du partage
Avatar de l’utilisateur
venom
Messages : 3139
Inscription : jeu. 29/juil./2004 16:33
Localisation : Klyntar
Contact :

Re: Calcul des phases lunaire pour la date du jour

Message par venom »

Bonjour.

Je n'en ai pas d'utilité, mais merci pour le partage c'est cool, ça peut toujours être utile. 8)






@++
Windows 10 x64, PureBasic 5.73 x86 & x64
GPU : radeon HD6370M, CPU : p6200 2.13Ghz
Avatar de l’utilisateur
PhM
Messages : 122
Inscription : dim. 08/déc./2019 10:50

Re: Calcul des phases lunaire pour la date du jour

Message par PhM »

Amélioration du premier programme qui gagne en précision dans cette version :

Code : Tout sélectionner


; ----------- Calcul des phases lunaires pour la date du jour ----------
;                      pour l'hémisphère nord (Paris)
;    Version améliorée PhM - septembre 2025 - PB 6.21 sur Windows 11
; ----------------------------------------------------------------------

Declare.f AgeLune(annee, mois, jour)
Declare.s PhaseLune(age.f)
Declare.f IlluminationLune(age.f)

Global RefJulien = 2451550.1     ; Nouvelle lune de référence du 6 janvier 2000 à 14h24 UTC
Global Lunaison  = 29.530588853  ; Longueur moyenne du mois lunaire

; -------------------------- Date du jour
annee = Year(Date())
mois = Month(Date())
jour = Day(Date())

age.f = AgeLune(annee, mois, jour)
illumination.f = IlluminationLune(age.f)
phase$ = PhaseLune(age.f)

MessageRequester("Phase Lunaire du " + Str(jour) + "/" + Str(mois) + "/" + Str(annee),
                 "Âge lunaire : " + StrF(age.f, 1) + " jours" + #LF$ +
                 "Illumination : " + StrF(illumination.f, 1) + "%" + #LF$ +
                 "Phase : " + phase$)

End

; -------------------------- Calcul de l'âge lunaire avec correction
Procedure.f AgeLune(annee, mois, jour)
  If mois <= 2
    annee - 1
    mois + 12
  EndIf

  a = annee / 100
  b = 2 - a + a / 4
  JL = Int(365.25 * (annee + 4716)) + Int(30.6001 * (mois + 1)) + jour + b - 1524.5

  age.f = Mod(JL - RefJulien, Lunaison)
  If age.f < 0
    age.f = age.f + Lunaison
  EndIf

  ; Correction de l’anomalie moyenne
  M = 2 * #PI * age.f / Lunaison
  correction.f = 0.5 * Sin(M) - 0.25 * Sin(2 * M)
  age.f = age.f + correction.f

  ; Ajustement empirique pour Paris
  age.f = age.f + 1.6 * Sin(2 * M)
  age.f = Mod(age.f, Lunaison)

  ProcedureReturn age.f
EndProcedure

; -------------------------- Calcul du pourcentage d'illumination
Procedure.f IlluminationLune(age.f)
  Protected correction.f = 0.953
  ProcedureReturn (1 - Cos(2 * #PI * age.f / Lunaison)) / 2 * 100 * correction
EndProcedure

; -------------------------- Détermination de la phase lunaire
Procedure.s PhaseLune(age.f)
  illumination.f = IlluminationLune(age.f)

  If illumination.f < 1
    phase$ = "🌑 Nouvelle lune"
  ElseIf illumination.f < 25 And age.f < 7.5
    phase$ = "🌒 Premier croissant"
  ElseIf illumination.f >= 25 And illumination.f < 50 And age.f < 9
    phase$ = "🌓 Premier quartier"
  ElseIf illumination.f >= 50 And illumination.f < 99 And age.f < 14.8
    phase$ = "🌔 Gibbeuse croissante"
  ElseIf illumination.f >= 99
    phase$ = "🌕 Pleine lune"
  ElseIf illumination.f >= 50 And age.f > 15
    phase$ = "🌖 Gibbeuse décroissante"
  ElseIf illumination.f >= 25 And age.f > 21
    phase$ = "🌗 Dernier quartier"
  ElseIf illumination.f < 25 And age.f > 22
    phase$ = "🌘 Dernier croissant"
  Else
    phase$ = "🌑 Nouvelle lune"
  EndIf

  ProcedureReturn phase$
EndProcedure


Avatar de l’utilisateur
SPH
Messages : 4952
Inscription : mer. 09/nov./2005 9:53

Re: Calcul des phases lunaire pour la date du jour

Message par SPH »

Merci pour le code. J'admire ton code bien structuré. Bravo :)

!i!i!i!i!i!i!i!i!i!
!i!i!i!i!i!i!
!i!i!i!
//// Informations ////
Intel Core i7 4770 64 bits - GTX 650 Ti
Version de PB : 6.12LTS- 64 bits
Avatar de l’utilisateur
PhM
Messages : 122
Inscription : dim. 08/déc./2019 10:50

Re: Calcul des phases lunaire pour la date du jour

Message par PhM »

Ultime modifications avec l'introduction du lieu d'observation exact (lat/lon) ainsi que la correction automatique pour l'heure d'été/hiver et l'indication de l'altitude et de l'azimut de la Lune.

Les résultats deviennent plus précis mais plus complexes à calculer.

Code : Tout sélectionner


; ----------- Calcul des phases lunaires pour la date du jour ----------
;                   Programme de contrôle : Stellarium
;    Version améliorée PhM - septembre 2025 - PB 6.21 pour Windows 11
;         🌕 par l'introduction du lieu d'observation (lat/lon)
;         🌕 ainsi que la correction automatique pour l'heure d'été/hiver
;         🌕 indication de l'altitude et de l'azimut de la Lune
; ----------------------------------------------------------------------

EnableExplicit

; ------------------ Déclaration des procédures

Declare.f JourJulien(annee, mois, jour, heure, minute, seconde)
Declare.f AgeLune(JD.f)
Declare.f IlluminationLune(age.f)
Declare.s PhaseLune(age.f)
Declare.s PositionLune(JD.f)
Declare.f CalculUTCOffset(annee, mois, jour)
Declare.s SaisonHoraire(offset.f)

; ------------------ Coordonnées de l'observation

Global Latitude.f  = 43.64     ; Latitude d'observation à la précision souhaitée
Global Longitude.f = 3.96     ; Longitude d'observation à la précision souhaitée

; ------------------ Constantes lunaires

Global RefJulien = 2451550.1        ; Nouvelle lune de référence du 6 janvier 2000 à 14h24
Global Lunaison  = 29.530588853     ; Longueur du mois lunaire 29j 12h 44m 2,9s

; ------------------ Date et heure UTC corrigée avec correction automatique heure d'été/hiver

Global an = Year(Date())
Global mo = Month(Date())
Global jo = Day(Date())

Global UTCOffset.f = CalculUTCOffset(an, mo, jo)
Global heure = Hour(Date()) - UTCOffset

Global minute = Minute(Date())
Global seconde = Second(Date())

; ------------------ Boucle principale du programme

Define JD.f = JourJulien(an, mo, jo, heure, minute, seconde)
Define age.f = AgeLune(JD)
Define illumination.f = IlluminationLune(age)
Define phase$ = PhaseLune(age)
Define position$ = PositionLune(JD)

MessageRequester("Observation lunaire du " + Str(jo) + "/" + Str(mo) + "/" + Str(an) + " (" + SaisonHoraire(UTCOffset) + ")",
                 "Âge lunaire : " + StrF(age, 1) + " jours" + #LF$ +
                 "Illumination : " + StrF(illumination, 1) + "%" + #LF$ +
                 "Phase : " + phase$ + #LF$ + #LF$ +
                 "Position apparente de la Lune :" + #LF$ + position$)

End


; ------------------ Procédures de calculs

Procedure.f JourJulien(annee, mois, jour, heure, minute, seconde)

  If mois <= 2
    annee - 1
    mois + 12
  EndIf
  
  Protected A = annee / 100
  Protected B = 2 - A + A / 4
  Protected JD.f = Int(365.25 * (annee + 4716)) + Int(30.6001 * (mois + 1)) + jour + B - 1524.5
  JD + (heure + minute / 60.0 + seconde / 3600.0) / 24.0
  
  ProcedureReturn JD
  
EndProcedure

Procedure.f AgeLune(JD.f)
  
  Protected age.f = Mod(JD - RefJulien, Lunaison)
  
  If age.f < 0
    age.f + Lunaison
  EndIf
  
  Protected M = 2 * #PI * age.f / Lunaison
  Protected correction.f = 0.95 * Sin(M) - 0.25 * Sin(2 * M)
  age.f + correction.f
  age.f = Mod(age.f, Lunaison)
  
  ProcedureReturn age.f
  
EndProcedure

Procedure.f IlluminationLune(age.f)
  
  Protected correction.f = 0.855
  ProcedureReturn (1 - Cos(2 * #PI * age.f / Lunaison)) / 2 * 100 * correction
  
EndProcedure

Procedure.s PhaseLune(age.f)
  
  Protected illumination.f = IlluminationLune(age.f)
  Protected phase$
  
  If illumination.f < 1
    phase$ = "🌑 Nouvelle lune"
  ElseIf illumination.f < 25 And age.f < 7.5
    phase$ = "🌒 Premier croissant"
  ElseIf illumination.f >= 25 And illumination.f < 50 And age.f < 9
    phase$ = "🌓 Premier quartier"
  ElseIf illumination.f >= 50 And illumination.f < 99 And age.f < 14.8
    phase$ = "🌔 Gibbeuse croissante"
  ElseIf illumination.f >= 99
    phase$ = "🌕 Pleine lune"
  ElseIf illumination.f >= 50 And age.f > 15
    phase$ = "🌖 Gibbeuse décroissante"
  ElseIf illumination.f >= 25 And age.f > 21
    phase$ = "🌗 Dernier quartier"
  ElseIf illumination.f < 25 And age.f > 22
    phase$ = "🌘 Dernier croissant"
  Else
    phase$ = "🌑 Nouvelle lune"
  EndIf
  
  ProcedureReturn phase$
  
EndProcedure

Procedure.s PositionLune(JD.f)
  
  Protected T.f = (JD - 2451545.0) / 36525.0
  Protected L.f = Mod(218.316 + 13.176396 * (JD - 2451545.0), 360)
  Protected M.f = Mod(134.963 + 13.064993 * (JD - 2451545.0), 360)
  Protected F.f = Mod(93.272 + 13.229350 * (JD - 2451545.0), 360)
  Protected lambda.f = L + 6.289 * Sin(Radian(M))
  Protected beta.f = 5.128 * Sin(Radian(F))
  Protected epsilon.f = 23.439 - 0.0000004 * T
  Protected alpha.f = Degree(ATan2(Cos(Radian(epsilon)) * Sin(Radian(lambda)), Cos(Radian(lambda))))
  Protected delta.f = Degree(ASin(Sin(Radian(epsilon)) * Sin(Radian(lambda)) * Cos(Radian(beta)) + Sin(Radian(beta)) * Cos(Radian(epsilon))))
  Protected GMST.f = Mod(280.46061837 + 360.98564736629 * (JD - 2451545.0), 360)
  Protected LST.f = Mod(GMST + Longitude, 360)
  Protected HA.f = Mod(LST - alpha, 360)
  Protected altitude.f = Degree(ASin(Sin(Radian(Latitude)) * Sin(Radian(delta)) + Cos(Radian(Latitude)) * Cos(Radian(delta)) * Cos(Radian(HA))))
  Protected azimut.f = Degree(ATan2(-Sin(Radian(HA)), Tan(Radian(delta)) * Cos(Radian(Latitude)) - Sin(Radian(Latitude)) * Cos(Radian(HA))))
  
  ProcedureReturn "Altitude : " + StrF(altitude, 2) + "°" + #LF$ + "Azimut : " + StrF(azimut, 2) + "°"
  
EndProcedure

Procedure.f CalculUTCOffset(annee, mois, jour)
  
  ; ------------------ Détection automatique de l'heure d'été/hiver
  
  Protected jourETE, jourHIVER
  Protected UTCOffset.f

  ; Dernier dimanche de mars
  jourETE = 31 - ((Date(annee, 3, 31, 0, 0, 0) / 86400 + 3) % 7)

  ; Dernier dimanche d'octobre
  jourHIVER = 31 - ((Date(annee, 10, 31, 0, 0, 0) / 86400 + 3) % 7)

  If (mois > 3 And mois < 10) Or (mois = 3 And jour >= jourETE) Or (mois = 10 And jour < jourHIVER)
    UTCOffset = 2.0 ; Heure d'été en France
  Else
    UTCOffset = 1.0 ; Heure d'hiver en France
  EndIf

  ProcedureReturn UTCOffset

EndProcedure

Procedure.s SaisonHoraire(offset.f)
  
    ; Pour l'affichage
  If offset = 2.0
    ProcedureReturn "Heure d'été : UTC+2"
  Else
    ProcedureReturn "Heure d'hiver : UTC+1"
  EndIf

EndProcedure


Avatar de l’utilisateur
PhM
Messages : 122
Inscription : dim. 08/déc./2019 10:50

Re: Calcul des phases lunaire pour la date du jour

Message par PhM »

Je reviens vers vous une dernière fois pour vous donnez la version finale de ce programme de calculs des phases de la Lune.

Cette ultime version est encore plus précise que les précédentes car elle se base sur de nouveaux calculs et, notamment, ceux de Jean Meeus (Algorithmes astronomiques de Jean Meeus et théorie ELP2000/82 https://archive.org/details/astronomica ... 3/mode/2up).

Le contrôle et l'étalonnage ont été fait avec le programme Stellaium (https://stellarium.org/fr/).

J'ai commenté le plus possible pour essayer de rendre lisible et compréhensible les nombreux calculs autonomes car sans liaisons avec des ressources extérieures (API ou web).

Code : Tout sélectionner



; ----------- Calcul des phases lunaires pour la date du jour ---------------
;                Programme de contrôle : Stellarium (v25.2)
;           Version finale PhM - septembre 2025 - PB 6.21 (x64)
; ---------------------------------------------------------------------------

EnableExplicit

; ------------------ Déclaration des procédures

Declare.f JourJulien(annee, mois, jour, heure, minute, seconde)
Declare.f AgeLune(JD.f)
Declare.f IlluminationLune(JD.f)
Declare.s PhaseLune(age.f)
Declare.f CalculUTCOffset(annee, mois, jour)
Declare.s SaisonHoraire(offset.f)

; ------------------ Constantes lunaires

Global Lunaison.f = 29.530588853    ; Longueur moyenne du mois synodique

; ------------------ Variables globales temporaires pour IlluminationLune

Global g_elongation.f               ; Élongation Lune-Soleil (degrés)
Global g_distance.f                 ; Distance Terre-Lune (km)

; ------------------ Date et heure UTC avec correction automatique heure d'été/hiver

Global an = Year(Date())
Global mo = Month(Date())
Global jo = Day(Date())

Global UTCOffset.f = CalculUTCOffset(an, mo, jo)
Global heure = Hour(Date()) - UTCOffset
Global minute = Minute(Date())
Global seconde = Second(Date())

; ------------------ Boucle principale du programme ------------------

Define JD.f = JourJulien(an, mo, jo, heure, minute, seconde)
Define age.f = AgeLune(JD)

; Appel d'IlluminationLune → remplit automatiquement g_elongation et g_distance
Define illumination.f = IlluminationLune(JD)
Define phase$ = PhaseLune(age)

; Affichage des résultats
MessageRequester("Observation lunaire du " + Str(jo) + "/" + Str(mo) + "/" + Str(an) + " (" + SaisonHoraire(UTCOffset) + ")",
                  "Âge lunaire : " + StrF(age, 2) + " jours" + #LF$ +
                  "Illumination : " + StrF(illumination, 2) + " %" + #LF$ +
                  "Phase : " + phase$)

; --------------------------------------------------------------------

End

; ------------------ Procédures de calculs

Procedure.f JourJulien(annee, mois, jour, heure, minute, seconde)
  
  Protected a, b, y, m
  y = annee
  m = mois
  
  If m <= 2
    y = annee - 1
    m = mois + 12
  EndIf
  
  a = y / 100
  b = 2 - a + a / 4
  Protected JD.f = Int(365.25 * (y + 4716)) + Int(30.6001 * (m + 1)) + jour + b - 1524.5
  JD = JD + (heure + minute / 60.0 + seconde / 3600.0) / 24.0
  
  ProcedureReturn JD
  
EndProcedure

Procedure.f AgeLune(JD.f)
  
  Protected T.f = (JD - 2451545.0) / 36525.0
  
  ; --- Calcul de la longitude vraie du Soleil ---
  Protected M.f = 357.5291092 + 35999.0502909 * T - 0.0001536 * T * T + Pow(T, 3) / 24490000
  M = Mod(M, 360)
  Protected Ls.f = 280.46646 + 36000.76983 * T + 0.0003032 * T * T
  Ls = Mod(Ls, 360)
  Protected lambdaS.f = Ls + 1.914602 * Sin(M * #PI / 180) - 0.004817 * Sin(2 * M * #PI / 180) - 0.019993 * Sin(3 * M * #PI / 180)
  lambdaS = Mod(lambdaS, 360)
  
  ; --- Calcul de la longitude vraie de la Lune (avec terme de Variation) ---
  Protected L.f = 218.3164591 + 481267.88134236 * T - 0.0013268 * T * T + Pow(T, 3) / 538841 - Pow(T, 4) / 65194000
  Protected Mp.f = 134.9633964 + 477198.8675055 * T + 0.0087414 * T * T + Pow(T, 3) / 69699 - Pow(T, 4) / 147120000
  Mp = Mod(Mp, 360)
  Protected D.f = L - Ls : D = Mod(D, 360)
  Protected correctionLune.f = 6.289 * Sin(Mp * #PI / 180) - 1.274 * Sin((2*L - lambdaS) * #PI / 180) + 0.658 * Sin(2*(L - lambdaS) * #PI / 180) + 0.214 * Sin(2 * D * #PI / 180)
  Protected lambdaL.f = L + correctionLune
  lambdaL = Mod(lambdaL, 360)
  
  ; --- Élongation apparente ---
  Protected diff.f = lambdaL - lambdaS
  If diff < 0 : diff + 360 : EndIf
  
  ; --- Calcul de l'âge lunaire (jours depuis dernière nouvelle lune) ---
  Protected age.f = diff / 360.0 * Lunaison
  
  ; --- Correction de +0.15 jour : optimale pour la stabilité et la précision globale ---
  ; très proche de Stellarium (version 25.2)
  age = age + 0.05
  
  ProcedureReturn age.f
  
EndProcedure

Procedure.f IlluminationLune(JD.f)
  
  ; -------------------------------------------------------------
  ; Algorithmes astronomiques de Jean Meeus et théorie ELP2000/82
  ; https://archive.org/details/astronomicalalgorithmsjeanmeeus1991/page/n323/mode/2up
  ; -------------------------------------------------------------
  ; 357.5291092       Anomalie moyenne du Soleil (M)
  ; 35999.0502909     Vitesse angulaire de M
  ; 134.9633964       Anomalie moyenne de la Lune (Mp)
  ; 477198.8675055    Vitesse angulaire de Mp
  
  
  ; --- Déclaration de TOUTES les variables locales ---
  Protected T.f, ecc.f, M_rad.f, E.f, tanHalfE.f, v.f
  Protected M.f, Ls.f, lambdaS.f, rS.f, L.f, Mp.f, F.f, beta.f, D.f, rL.f
  Protected cosElong.f, i.f, elong.f, k.f, lambdaL.f
  
  ; Temps en siècles depuis J2000.0
  T = (JD - 2451545.0) / 36525.0
  
  ; --- Soleil ---
  M = 357.5291092 + 35999.0502909 * T - 0.0001536 * T * T + Pow(T, 3) / 24490000
  M = Mod(M, 360)
  Ls = 280.46646 + 36000.76983 * T + 0.0003032 * T * T
  Ls = Mod(Ls, 360)
  lambdaS = Ls + 1.914602 * Sin(M * #PI / 180) - 0.004817 * Sin(2 * M * #PI / 180) - 0.019993 * Sin(3 * M * #PI / 180)
  lambdaS = Mod(lambdaS, 360)
  rS = 149597870 * (1.00014 - 0.01671 * Cos(M * #PI / 180) - 0.00014 * Cos(2 * M * #PI / 180))
  
  ; --- Lune ---
  L = 218.3164591 + 481267.88134236 * T - 0.0013268 * T * T + Pow(T, 3) / 538841 - Pow(T, 4) / 65194000
  Mp = 134.9633964 + 477198.8675055 * T + 0.0087414 * T * T + Pow(T, 3) / 69699 - Pow(T, 4) / 147120000
  Mp = Mod(Mp, 360)
  
  ; --- Argument de latitude F ---
  F = 93.2720950 + 483202.0175233 * T - 0.0036539 * T * T - Pow(T, 3) / 3526000 + Pow(T, 4) / 863310000
  F = Mod(F, 360)
  beta = 5.128 * Sin(F * #PI / 180)
  
  ; --- [CORRECTION] Calcul de l'anomalie vraie v ---
  ecc = 0.0549006
  M_rad = Mp * #PI / 180.0
  E = M_rad + ecc * Sin(M_rad) + (ecc * ecc / 2.0) * Sin(2.0 * M_rad)
  tanHalfE = Tan(E / 2.0)
  v = 2.0 * ATan(Sqr((1.0 + ecc) / (1.0 - ecc)) * tanHalfE)
  If v < 0 : v + 2.0 * #PI : EndIf
  
  ; --- Distance Terre-Lune ---
  D = L - Ls
  D = Mod(D, 360)
  rL = 382700.0 * (1.0 - ecc * Cos(v))
  rL = rL * (1.0 - 0.00029 * Cos((2*D - Mp) * #PI / 180) - 0.00019 * Cos(2*D * #PI / 180) + 0.00011 * Cos(Mp * #PI / 180) + 0.00009 * Cos((2*D + Mp) * #PI / 180) - 0.00005 * Cos(2*Mp * #PI / 180))
  g_distance = rL
  
  ; --- Calcul de lambdaL ---
  Protected correctionLune.f = 6.289 * Sin(Mp * #PI / 180) - 1.274 * Sin((2*L - lambdaS) * #PI / 180) + 0.658 * Sin(2*(L - lambdaS) * #PI / 180) + 0.214 * Sin(2 * D * #PI / 180)
  lambdaL = L + correctionLune
  lambdaL = Mod(lambdaL, 360)
  
  ; --- Angle de phase i ---
  cosElong = Cos(beta * #PI / 180) * Cos((lambdaL - lambdaS) * #PI / 180)
  If cosElong > 1 : cosElong = 1 : ElseIf cosElong < -1 : cosElong = -1 : EndIf
  i = 180 - ACos(cosElong) * 180 / #PI
  
  ; --- Correction empirique de Meeus ---
  i = i + 0.518 * Sin(2 * F * #PI / 180)
  If i < 0 : i = 0 : EndIf
  If i > 180 : i = 180 : EndIf
  
  ; --- Élongation ---
  elong = lambdaL - lambdaS
  If elong < 0 : elong + 360 : EndIf
  g_elongation = elong
  
  ; --- Illumination ---
  k = (1 + Cos(i * #PI / 180)) / 2 * 100
  k = k - 0.55 * (1 - Cos(i * #PI / 180)) * (1 - Cos(beta * #PI / 180)) * 100
  
  ; --- Calibration pour alignement précis avec Stellarium ---
  k = k + 0.1
  
  ProcedureReturn k
  
EndProcedure

Procedure.s PhaseLune(age.f)
  
  ; ------------------------------------------------------------------------------
  ; Détermination de la phase lunaire en texte
  ; Seuils ajustés pour coller aux observations réelles
  ; ------------------------------------------------------------------------------
  
  Protected phase$
  
  If age.f < 1.8
    phase$ = "🌑 Nouvelle lune"
  ElseIf age.f < 7.4
    phase$ = "🌒 Premier croissant"
  ElseIf age.f < 8.4
    phase$ = "🌓 Premier quartier"
  ElseIf age.f < 14.8
    phase$ = "🌔 Gibbeuse croissante"
  ElseIf age.f < 15.8
    phase$ = "🌕 Pleine lune"
  ElseIf age.f < 22.2
    phase$ = "🌖 Gibbeuse décroissante"
  ElseIf age.f < 23.2
    phase$ = "🌗 Dernier quartier"
  Else
    phase$ = "🌘 Dernier croissant"
  EndIf
  
  ProcedureReturn phase$
  
EndProcedure

Procedure.f CalculUTCOffset(annee, mois, jour)
  
  ; ------------------------------------------------------------------------------
  ; Calcul du décalage UTC (France métropolitaine)
  ; Correction : recherche du dernier dimanche par DayOfWeek()
  ; ------------------------------------------------------------------------------
  
  Protected dimancheMars, dimancheOctobre, i ; ← i déclarée ici !
  
  ; Dernier dimanche de mars
  For i = 31 To 25 Step -1
    If DayOfWeek(Date(annee, 3, i, 0, 0, 0)) = 0 ; 0 = dimanche
      dimancheMars = i
      Break
    EndIf
  Next
  
  ; Dernier dimanche d'octobre
  For i = 31 To 25 Step -1
    If DayOfWeek(Date(annee, 10, i, 0, 0, 0)) = 0
      dimancheOctobre = i
      Break
    EndIf
  Next
  
  ; Heure d'été ?
  If (mois > 3 And mois < 10) Or (mois = 3 And jour >= dimancheMars) Or (mois = 10 And jour < dimancheOctobre)
    ProcedureReturn 2.0 ; UTC+2
  Else
    ProcedureReturn 1.0 ; UTC+1
  EndIf
  
EndProcedure

Procedure.s SaisonHoraire(offset.f)
  
  ; ------------------------------------------------------------------------------
  ; Libellé de la saison horaire
  ; ------------------------------------------------------------------------------
  
  If offset = 2.0
  ProcedureReturn "Heure d'été : UTC+2"
                  Else
    ProcedureReturn "Heure d'hiver : UTC+1"
  EndIf
  
EndProcedure


Répondre