Programme en Python de Propagation et pour viser un point de réception….

Cette section est dédiée aux logiciels utilisés en radioamateurisme, comme ceux pour la gestion des contacts, la communication numérique, ou encore les concours. Vous y trouverez des conseils et des discussions sur les outils essentiels pour faciliter vos activités radio.
Avatar de l’utilisateur
mattlemoel
ARML Member
Messages : 1
Inscription : mar. avr. 01, 2025 8:43 pm

Programme en Python de Propagation et pour viser un point de réception….

Message par mattlemoel »

Ceci est un programme en Python, je donne en open source à tout ceux qui veulent le modifier, upgrader, tout est le bienvenue

Matthieu
Maritime R/O et futur Jambon de Pays

Programme en Python3 avec des libs :

pip install Geopy matplotlib datetime logging
pip install iri2016 (nternational Reference Ionosphere )

Code : Tout sélectionner

import math
from geopy.distance import geodesic
import matplotlib.pyplot as plt
from datetime import datetime
import logging
from iri2016 import IRI

# Configuration du logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# Constantes
RAYON_TERRE = 6371  # Rayon moyen de la Terre en kilomètres
HAUTEUR_BASE_IONOSPHERE = 300  # Hauteur de base de l'ionosphère en kilomètres

def calculer_distance(coords_emetteur, coords_recepteur):
    """
    Calcule la distance entre deux coordonnées géographiques.

    Paramètres :
    coords_emetteur (tuple) : Latitude et longitude de l'émetteur.
    coords_recepteur (tuple) : Latitude et longitude du récepteur.

    Retourne :
    float : Distance en kilomètres.
    """
    try:
        return geodesic(coords_emetteur, coords_recepteur).km
    except ValueError as e:
        logging.error(f"Erreur lors du calcul de la distance : {e}")
        return None

def calculer_angle_elevation(distance, hauteur_ionosphere):
    """
    Calcule l'angle d'élévation pour une onde radio.

    Paramètres :
    distance (float) : Distance entre l'émetteur et le récepteur.
    hauteur_ionosphere (float) : Hauteur de l'ionosphère.

    Retourne :
    float : Angle d'élévation en degrés.
    """
    try:
        angle_incidence_rad = math.asin(distance / (2 * (RAYON_TERRE + hauteur_ionosphere)))
        return 90 - math.degrees(angle_incidence_rad)
    except ValueError as e:
        logging.error(f"Erreur lors du calcul de l'angle d'élévation : {e}")
        return None

def calculer_distance_saut(hauteur_ionosphere, angle_elevation_deg):
    """
    Calcule la distance de saut d'une onde radio.

    Paramètres :
    hauteur_ionosphere (float) : Hauteur de l'ionosphère.
    angle_elevation_deg (float) : Angle d'élévation en degrés.

    Retourne :
    float : Distance de saut en kilomètres.
    """
    try:
        angle_incidence_rad = math.radians(90 - angle_elevation_deg)
        return 2 * (RAYON_TERRE + hauteur_ionosphere) * math.sin(angle_incidence_rad)
    except ValueError as e:
        logging.error(f"Erreur lors du calcul de la distance de saut : {e}")
        return None

def calculer_nombre_rebonds(distance_totale, distance_saut):
    """
    Calcule le nombre de rebonds nécessaires pour couvrir une distance totale.

    Paramètres :
    distance_totale (float) : Distance totale entre l'émetteur et le récepteur.
    distance_saut (float) : Distance de saut de l'onde radio.

    Retourne :
    int : Nombre de rebonds.
    """
    try:
        return math.ceil(distance_totale / distance_saut)
    except ZeroDivisionError as e:
        logging.error(f"Erreur lors du calcul du nombre de rebonds : {e}")
        return None

def frequence_critique(densite_electronique):
    """
    Calcule la fréquence critique basée sur la densité électronique.

    Paramètres :
    densite_electronique (float) : Densité électronique.

    Retourne :
    float : Fréquence critique en MHz.
    """
    try:
        return 9 * math.sqrt(densite_electronique) * 1e-6
    except ValueError as e:
        logging.error(f"Erreur lors du calcul de la fréquence critique : {e}")
        return None

def frequence_muf(f_critique, angle_elevation_deg):
    """
    Calcule la Fréquence Maximale Utilisable (MUF).

    Paramètres :
    f_critique (float) : Fréquence critique.
    angle_elevation_deg (float) : Angle d'élévation en degrés.

    Retourne :
    float : MUF en MHz.
    """
    try:
        return f_critique / math.cos(math.radians(angle_elevation_deg))
    except ZeroDivisionError as e:
        logging.error(f"Erreur lors du calcul de la MUF : {e}")
        return None

def plot_zone_reception(coords_emetteur, distance_saut):
    """
    Trace la zone de réception du signal radio.

    Paramètres :
    coords_emetteur (tuple) : Latitude et longitude de l'émetteur.
    distance_saut (float) : Distance de saut de l'onde radio.
    """
    try:
        fig, ax = plt.subplots()
        ax.scatter(coords_emetteur[1], coords_emetteur[0], color='red', label='Émetteur')
        circle = plt.Circle(coords_emetteur[::-1], distance_saut, color='blue', fill=False, linestyle='--', label='Zone de Réception')
        ax.add_patch(circle)
        ax.set_xlim(coords_emetteur[1] - 1.5 * distance_saut, coords_emetteur[1] + 1.5 * distance_saut)
        ax.set_ylim(coords_emetteur[0] - 1.5 * distance_saut, coords_emetteur[0] + 1.5 * distance_saut)
        ax.set_xlabel('Longitude')
        ax.set_ylabel('Latitude')
        ax.set_title('Zone de Réception du Signal Radio')
        ax.legend()
        plt.grid(True)
        plt.show()
    except Exception as e:
        logging.error(f"Erreur lors du tracé de la zone de réception : {e}")

def ajuster_densite_electronique(heure, saison, indice_f107):
    """
    Ajuste la densité électronique en fonction de l'heure, de la saison et de l'activité solaire.

    Paramètres :
    heure (int) : Heure de la journée.
    saison (str) : Saison ("été" ou "hiver").
    indice_f107 (float) : Indice d'activité solaire.

    Retourne :
    float : Facteur de densité électronique ajusté.
    """
    try:
        facteur_jour = 1.2 if 6 <= heure <= 18 else 1.0
        facteur_saison = 1.1 if saison == "été" else 1.0
        facteur_solaire = 1 + 0.05 * (indice_f107 - 70) / 100
        return facteur_jour * facteur_saison * facteur_solaire
    except Exception as e:
        logging.error(f"Erreur lors de l'ajustement de la densité électronique : {e}")
        return None

def obtenir_donnees_iri_local(latitude, longitude, heure, date):
    """
    Obtient les données IRI en utilisant la bibliothèque locale IRI-2016.

    Paramètres :
    latitude (float) : Latitude de l'émetteur.
    longitude (float) : Longitude de l'émetteur.
    heure (int) : Heure de la journée.
    date (str) : Date au format AAAA-MM-JJ.

    Retourne :
    dict : Données IRI incluant la hauteur de l'ionosphère.
    """
    try:
        # Initialisation du modèle IRI
        iri = IRI()

        # Configuration des paramètres
        iri.set_date_time(date, heure)
        iri.set_geographic(latitude, longitude)

        # Exécution du modèle
        result = iri.run()

        # Extraction de la hauteur de l'ionosphère
        hauteur_ionosphere = result['hmF2']  # Exemple d'extraction, à adapter
        return {'hauteur_ionosphere': hauteur_ionosphere}

    except Exception as e:
        logging.error(f"Erreur lors de la récupération des données IRI locales : {e}")
        return None

def validate_coordinates(latitude, longitude):
    """
    Valide les valeurs de latitude et de longitude.

    Paramètres :
    latitude (float) : Valeur de la latitude.
    longitude (float) : Valeur de la longitude.

    Retourne :
    bool : True si valide, False sinon.
    """
    if not (-90 <= latitude <= 90):
        logging.error("Latitude invalide. Doit être comprise entre -90 et 90.")
        return False
    if not (-180 <= longitude <= 180):
        logging.error("Longitude invalide. Doit être comprise entre -180 et 180.")
        return False
    return True

def validate_heure(heure):
    """
    Valide l'heure de la journée.

    Paramètres :
    heure (int) : Heure de la journée.

    Retourne :
    bool : True si valide, False sinon.
    """
    if not (0 <= heure < 24):
        logging.error("Heure invalide. Doit être comprise entre 0 et 23.")
        return False
    return True

def main():
    try:
        lat_emetteur = float(input("Entrez la latitude de l'émetteur : "))
        lon_emetteur = float(input("Entrez la longitude de l'émetteur : "))
        lat_recepteur = float(input("Entrez la latitude du récepteur : "))
        lon_recepteur = float(input("Entrez la longitude du récepteur : "))

        if not (validate_coordinates(lat_emetteur, lon_emetteur) and validate_coordinates(lat_recepteur, lon_recepteur)):
            return

        print("Sélectionnez le type d'antenne : ")
        print("1. Filaire (Dipôle)")
        print("2. Yagi")
        print("3. Log-Périodique")
        print("4. Verticale")
        print("5. Boucle")
        type_antenne = int(input("Entrez le numéro correspondant au type d'antenne : "))

        if type_antenne not in [1, 2, 3, 4, 5]:
            logging.error("Type d'antenne invalide. Veuillez sélectionner un nombre entre 1 et 5.")
            return

        coords_emetteur = (lat_emetteur, lon_emetteur)
        coords_recepteur = (lat_recepteur, lon_recepteur)

        heure = datetime.now().hour
        if not validate_heure(heure):
            return

        saison = "été" if 6 <= datetime.now().month <= 8 else "hiver"
        indice_f107 = 150  # Peut être configurable

        # Utilisation de la date actuelle pour les données en temps réel
        date_actuelle = datetime.now().strftime('%Y-%m-%d')

        # Tentative d'obtenir les données IRI
        donnees_iri = obtenir_donnees_iri_local(lat_emetteur, lon_emetteur, heure, date_actuelle)
        hauteur_ionosphere = donnees_iri['hauteur_ionosphere'] if donnees_iri else HAUTEUR_BASE_IONOSPHERE

        densite_electronique = 1e12 * ajuster_densite_electronique(heure, saison, indice_f107)

        distance_totale = calculer_distance(coords_emetteur, coords_recepteur)
        angle_elevation = calculer_angle_elevation(distance_totale, hauteur_ionosphere)

        if type_antenne in [2, 3]:
            angle_elevation = min(angle_elevation, 15)

        distance_saut = calculer_distance_saut(hauteur_ionosphere, angle_elevation)
        nombre_rebonds = calculer_nombre_rebonds(distance_totale, distance_saut)
        f_critique = frequence_critique(densite_electronique)
        muf = frequence_muf(f_critique, angle_elevation)

        print(f"Distance entre l'émetteur et le récepteur : {distance_totale:.2f} km")
        print(f"Angle d'élévation optimal : {angle_elevation:.2f} degrés")
        print(f"Nombre de rebonds nécessaires : {nombre_rebonds}")
        print(f"Fréquence critique : {f_critique:.2f} MHz")
        print(f"Fréquence maximale utilisable (MUF) : {muf:.2f} MHz")

        plot_zone_reception(coords_emetteur, distance_saut)

    except ValueError as e:
        logging.error(f"Entrée invalide : {e}")
    except Exception as e:
        logging.error(f"Une erreur inattendue s'est produite : {e}")

if __name__ == "__main__":
    main()

Dernière modification par mattlemoel le lun. avr. 07, 2025 9:48 pm, modifié 4 fois.
Avatar de l’utilisateur
F4HUX
Grand Manitou des Ondes
Messages : 6
Inscription : lun. mars 17, 2025 9:26 pm

Re: Programme en Python de Propagation et pour viser un point de réception….

Message par F4HUX »

Pour moi ca marche (ubuntu24-04)
pip3 install Geopy matplotlib datetime logging
pip3 install iri2016
L'IA m'a ecrit le prog avec la bonne indentation avant que tu ne le re-postes avec le BBcode

pierre@pierre-Latitude-5480:~/Téléchargements$ python3 -m venv myenv
pierre@pierre-Latitude-5480:~/Téléchargements$ source myenv/bin/activate
(myenv) pierre@pierre-Latitude-5480:~/Téléchargements$ python3 geo.py
Entrez la latitude de l'émetteur :


Cyril ! ..... 5 mn ! :lol:

Merci Matthieu :D
Pierre
F4HUX
Répondre