Page 1 sur 1

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

Publié : lun. avr. 07, 2025 2:06 pm
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()


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

Publié : lun. avr. 07, 2025 8:53 pm
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