plandevol01-ok4.pl to HTML.

index -|- end

Generated: Mon Aug 29 19:34:55 2016 from plandevol01-ok4.pl 2014/10/08 73.7 KB. text copy

#!/usr/bin/perl
# 20140930 from : http://seb.marque.free.fr/fichiers/scripts/perl/plandevol
# 20141003 - Load ALL airports - for a light plane vfr, need to also supply re-fueling airport
# 20141001 - Add a log file
# l'option -w a été enlevée pour éviter l'affichage des warnings inutiles décrits ci-dessous:
# Use of implicit split to @_ is deprecated at fgfs/plandevol-dev line ...
# main::construction_route() called too early to check prototype at fgfs/plandevol-dev line ...
#######################################################################################################################################################
##             ***********************************************
##             ***** TRES IMPORTANT ***** VERY IMPORTANT *****
##             ***********************************************
##
## CE SCRIPT NE PROPOSE PAS UNE SOLUTION DE VOL FIABLE POUR LA CONSTRUCTION D'UN PLAN DE VOL RÉEL!!!!!!!!
## IL N'EST QU'UNE SOLUTION POUR PROPOSER UN TRAJET DE _LOISIR_ AVEC FLIGHTGEAR FS ET NE GARANTIT EN AUCUN CAS LA FIABILITÉ DES INFORMATIONS
## QU'IL DÉLIVRE
##
#######################################################################################################################################################
######################################################################################################################################################
##
## script écrit par seb marque, paris, france
##
## plandevol, version 0.5.9 qui s'approche dangereusement de la version 0.6.0
## --help pour une aide sur l'utilisation du script
##
## script placé sous licence GPL par Sébastien MARQUE
## texte complet disponible sur http://www.gnu.org/licenses/gpl.txt
##
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
#######################################################################################################################################################
##
## les fonctions connect, set_prop, get_prop et send proviennent du script telnet.pl trouvé dans le code source de fgfs 0.98 (de Curtis L. Olson,
##   avec "courtesy" pour Melchior Franz.
##
## les fonctions round, ll2xyz, xyz2ll, llll2dir (dont provient llll2dir_), distance (dont provient distance_) et coord_dist_sqr proviennent du script
##   de Melchior Franz "freq" trouvé sur http://members.aon.at/mfranz/freq, en attente de leur remplacement éventuel par les fonctions de Math::Trig
##
######################################################################################################################################################
##
## bugs connus: si une balise est situé dans l'enceinte du point d'arrivée elle n'est pas détectée... c'est dommage
##      fixé la compatibilité avec les différentes versions de nav.dat.gz
##
## version 0.6 -> réglage auto des instruments de vol
##             -> intégration des fix dans le plan de vol si nécessaire
##             -> gestion des sid/star
##             -> nettoyage du code
##
######################################################################################################################################################

use strict;
use POSIX qw(ceil floor);
use Getopt::Long;       # pour récupérer les options en ligne de commande
use IO::Socket;         # pour la connexion telnet avec FlightGear
use Env qw(HOME FGROOT FGHOME);    # pour lire HOME FGHOME et FGROOT
my $os = $^O;
my $perl_dir = '/home/geoff/bin';
my $PATH_SEP = '/';
my $temp_dir = '/tmp';
if ($os =~ /win/i) {
    $perl_dir = 'C:\GTools\perl';
    $temp_dir = $perl_dir;
    $PATH_SEP = "\\";
}
unshift(@INC, $perl_dir);
require 'lib_utils.pl' or die "Unable to load 'lib_utils.pl' Check paths in \@INC...\n";
require 'fg_wsg84.pl' or die "Unable to load fg_wsg84.pl ...\n";
# log file stuff
our ($LF);
my $pgmname = $0;
if ($pgmname =~ /(\\|\/)/) {
    my @tmpsp = split(/(\\|\/)/,$pgmname);
    $pgmname = $tmpsp[-1];
}
my $outfile = $temp_dir.$PATH_SEP."temp.$pgmname.txt";
my $out_xg = $temp_dir.$PATH_SEP."tempout.xg";

open_log($outfile);

## DECLARATION DES VARIABLES GLOBALES
#####################################
#              sid     icao   lat    lon    apt-array
#              0       1      2      3      4 
my @depart  = (undef, "LFPG", undef, undef, undef);
# tableau contenant des infos sur l'aéroport de départ (sid, ICAO, lat, lon, nom complet de l'aéroport)
my @arrivee = (undef, "LFBD", undef, undef, undef, undef);
# tableau contenant des infos sur l'aéroport d'arrivée (star, ICAO, lat, lon, nom complet de l'aéroport)

my $fgfs;           # socket de connexion à fgfs
my @route;          # contient la route à suivre
my ($navaid, $fix);     # pointent sur les tableaux contenant les aides à la navigation
my $erreur;         # contient les messages d'erreur éventuels
my $version;            # pour la compatibilité avec différentes version de nav.dat.gz
my $sous_fonction;      # pointe vers des sous fonctions anonymes définies localement
my @all_airports = ();  # load ALL airports
my $done_all = 0;       # only done once

my $VERS = "0.7.0 2014-10-03";
# my $VERS = "0.6.0";
my $load_log = 0;
my $verbosity = 1;  # default verbosity (range 0,1,2,5,9)

# VARIABLES DES OPTIONS DU SCRIPT
#################################
my $FGROOT = (exists $ENV{FGROOT})? $FGROOT : "F:\\fgdata";
my $FGHOME = (exists $ENV{FGHOME})? $FGHOME : "$HOME/.fgfs";
my $vor_a_vor;          # exclusivement du vor
my $vor_preferes;       # si on veut du vor, mais sinon du ndb
my $deviation_max = 30;     # virage maximal
my $dist_min = 74; # = 40 nm # was 10; # distance minimale entre deux waypoints (km?)
my $km;             # affichage des distances en kilomètres
my $help;           # demande d'aide
my $csv_conf=':,';      # les séparateurs pour les fichiers .csv
my $no_stdout;          # pas de sortie sur le terminal
my ($sidx, $starx);     # protocole sid/star demandé sans spécifier de piste
my ($sid, $star);       # protocole sid/star demandé en spécifiant une piste
my $add_couleur;     # applique des couleurs à la sortie standard
my ($com, $com_dep, $com_app);  # pour afficher les fréqences de communication
my $INSTRFILE;          # création d'un fichier .xml
my $wpt;            #enregistrer la route
my $WPFILE;         # le fichier dans lequel on enregistre les points de passage
my $CSVFILE;        # le fichier dans lequel on enregistre les points de passage
my $XGFILE;         # le fichier dans lequel on enregistre les points de passage

my $incl_refuel_ap = 1;
my $aircraft = "c172p";
my $range = 696;    # nm
my $cuise_speed = 122;  # kts (kn) = 87 km/h

# Performance
# Cruise speed: 122 kn (140 mph; 226 km/h)
# Stall speed: 47 kn (54 mph; 87 km/h) (power off, flaps down)[73]
# Never exceed speed: 163 kn (188 mph; 302 km/h) (IAS)[8]
# Range: 696 nmi (801 mi; 1,289 km) with 45 minute reserve, 55% Power, at 12,000 ft
# Service ceiling: 13,500 ft (4,100 m)
# Rate of climb: 721 ft/min (3.66 m/s)
# Wing loading: 14.1 lb/sq ft (68.6 kg/m2)

my $options = GetOptions ( "v|vor-a-vor" => \$vor_a_vor,
               "preferer-vor"=> \$vor_preferes,
               "km"          => \$km,
               "dev-max=i"   => \$deviation_max,
               "dist-min=i"  => \$dist_min,
               "fg-root=s"   => \$FGROOT,
               "wpt"         => \$wpt,
               "instr"       => \$INSTRFILE,
               "csv=s"       => \$CSVFILE,
               "xg=s"        => \$XGFILE,
               "csv-conf=s"  => \$csv_conf,
               "d|dep=s"     => \$depart[1],
               "a|arr=s"     => \$arrivee[1],
               "no-stdout"   => \$no_stdout,
               "help"        => \$help,
               "sidx"    => \$sidx,
               "starx"   => \$starx,
               "sid=s"   => \$sid,
               "star=s"  => \$star,
               "com"     => \$com,
               "com-dep"     => \$com_dep,
               "com-app"     => \$com_app,
               "ansi"     => \$add_couleur);

($com_dep, $com_app) = ($com, $com) if $com;

## FICHIERS UTILISÉS PAR LE SCRIPT
## à modifier selon sa propre configuration
## accepte les fichiers en clair, ou gzipés
###########################################
my $NAVFILE       = "$FGROOT/Navaids/nav.dat.gz";   # le fichier contenant les aides à la navigation
my $FIXFILE       = "$FGROOT/Navaids/fix.dat.gz";   # le fichier contenant les fix
my $SIDFILE       = "$FGROOT/NavAids/sid.dat";      # le fichier contenant les procédures SID
my $STARFILE      = "$FGROOT/NavAids/star.dat";     # le fichier contenant les procédure STAR
my $APTFILE       = "$FGROOT/Airports/apt.dat.gz"# le fichier contenant les aéroports

## DÉCLARÉ COMME VARIABLE MAIS UTILISÉ COMME CONSTANTE
######################################################
my $texte_aide = <<EOH;
plandevol, v. $VERSS

trouve une route de balises entre deux points.

syntaxe: plandevol [-v | --vor-a-vor] [--preferer-vor] [--km]
                   [--fg-root </PATH/TO/FG_DATA_FILES>]
                   [--wpt]
                   [--csv </PATH/TO/CSV_FILE>]
                   [--csv-conf <colonnedécimal>]
                   [--xg </PATH/TO/XG_FILE>]
                   [-d | --dep <point de départ>]
                   [-a | --arr <point d'arrivée>]
                   [--dev-max <angle en degrés>]
                   [--dist-min <distance en km>]
                   [--sid <nom de piste>][--star <nom de piste>]
                   [--sidx][--starx]
                   [--com-dep][--com-app][--com]
                   [--ansi]
                   [--help]

-v | --vor-a-vor : ne sélectionne que les balises VOR et VOR-DME (pas de TACAN)

--preferer-vor   : route construite avec NDB et VOR, avec une préférence pour
                   les balises VOR

--km             : affiche la distance en km (défaut: affichage en nm)

--fg-root=path   : chemin contenant les fichiers de FlightGear
                   défaut: $FGROOTT

--wpt            : enregistre la route dans $FGROOT/Routes (nommage auto)
                   directory must exist. It will not be created.

--csv=file       : fichier CSV ( séparateur = : , virgule décimale = , ) pour
                   affichage du trajet en graphique (via oocalc par exemple)

--xg=file        : fichier XG. Can be viewed by polyView2D. See
                   https://sites.google.com/site/polyview2d/

--csv-conf=ab    : paramètre les séparateurs de colonne et de décimale pour la
                   fabrication du fichier csv.
                   format = séparateurdécimale (ex: --csv-conf=?ù) pour des
                            colonnes séparées par le caractère '?', et la
                            virgule représentée par le caractère 'ù'. par défaut
                            --csv-conf=$csv_conff

-d|--dep=icao    : point de départ. il est possible de spécifier:
                     - soit le code oaci de l'aéroport (ex: --dep=lfqq),
                       défaut --dep=lfpt --arr=lfbd
                     - soit la position actuelle de l'avion dans fgfs
                       (ex: --dep=telnet:5401)
                     - soit une position exprimée en lat, long
                       (ex: --dep=[45.564,-2.066])

-a|--arr=icao    : point d'arrivée. même possibilités que l'option --dep

--dev-max=degs   : déviation maximale d'une balise à une autre par rapport au
                   trajet en cours (défaut: $deviation_max°)

--dist-min=km    : distance minimale entre deux balises (défaut: $dist_min km)

--sid --star=rw  : cherche le trajet en tenant compte de la procédure sid (ou
                   star) de la piste <nom de la piste> codé sur deux ou trois
                   caractères (ex: --sid 09 --star 23, ou --sid 09R --star 23)
                   si aucun indicatif de piste (R, C ou L) n'est fourni ils
                   seront tous les trois inclus dans la recherche de procédure

--sidx, --starx  : idem que --sid et --star, mais avec une piste choisie par le
                   programme:
                   - pour le moment, le choix se porte sur la piste dont la
                     procédure rapproche du point de départ/arrivée
                   - dans le futur, il est prévu une implémentation avec les
                     METAR (décollage/atterrissage face au vent)
                   - selon l'évolution du fichier apt.dat, on peut imaginer un
                     choix en fonction des pistes réellement usitées

--com-dep,
--com-app        : affichent respectivement les fréquences COM pour le départ (dep)
                   ou l'approche (app)

--com            : affiche les fréquences COM pour le départ et l'arrivée (équi-
                   valent de --com-dep --com-app)

--ansi           : affiche les étapes en couleurs, pour les terminaux qui ne
                   supportent pas la norme ANSI, ou pour redirection du résultat

--help           : affiche ce message d'aide et quitte (même si d'autres options
                   ont été spécifiées)

CE SCRIPT NE PROPOSE PAS UNE SOLUTION DE VOL FIABLE POUR LA CONSTRUCTION D'UN PLAN DE VOL RÉEL!!!!!!!!
IL N'EST QU'UNE SOLUTION POUR PROPOSER UN TRAJET DE _LOISIR_ AVEC FLIGHTGEAR FS ET NE 
GARANTIT EN AUCUN CAS LA FIABILITÉ DES INFORMATIONS QU'IL DÉLIVRE

EOH

my $PI   = 3.1415926535897932384626433832795029;
my $D2R  = $PI / 180;
my $R2D  = 180 / $PI;
my $ERAD = 6378138.12;
#my $ERAD = 6378;
my $NDB  = 2;
my $VOR  = 3;
my $KM2FEET = 3280.84;
my $NM2KM = 1.852;
my $KM2NM = 0.539957;

sub VERB1() { return $verbosity >= 1; }
sub VERB2() { return $verbosity >= 2; }
sub VERB5() { return $verbosity >= 5; }
sub VERB9() { return $verbosity >= 9; }

# FONCTIONS DE CONNEXION AVEC FGFS PAR TELNET
#############################################
sub get_prop($$) {
    my( $handle ) = shift;

    &send( $handle, "get " . shift );
    eof $handle and die "\nconnection closed by host";
    $_ = <$handle>;
    s/\015?\012$//;
    /^-ERR (.*)/ and die "\nfgfs error: $1\n";

    return $_;
}


sub set_prop($$$) {
    my( $handle ) = shift;
    my( $prop ) = shift;
    my( $value ) = shift;

    &send( $handle, "set $prop $value");

    # eof $handle and die "\nconnection closed by host";
}

sub send($$) {
    my( $handle ) = shift;

    print $handle shift, "\015\012";
}

sub connect($$$) {
    my( $host ) = shift;
    my( $port ) = shift;
    my( $timeout ) = (shift || 120);
    my( $socket );
    STDOUT->autoflush(1);
    while ($timeout--) {
        if ($socket = IO::Socket::INET->new( Proto => 'tcp',
                                             PeerAddr => $host,
                                             PeerPort => $port) )
        {
            $socket->autoflush(1);
            return $socket;
        }
        prt( "Attempting to connect to $host ... " . $timeout . "\n" );
        sleep(1);
    }
    return 0;
}

# FONCTIONS DE CALCULS GEOGRAPHIQUES
# par Frank Melchior
####################################

sub round($)
{
    my $i = shift;
    my $m = (shift or 1);
    $i /= $m;
    $i = $i - &floor($i) >= 0.5 ? &ceil($i) : &floor($i);
    $i *= $m;
    return $i;
}

sub coord_dist_sq($$$$$$)
{
    my ($xa, $ya, $za, $xb, $yb, $zb) = @_;
    my $x = $xb - $xa;
    my $y = $yb - $ya;
    my $z = $zb - $za;
    return $x * $x + $y * $y + $z * $z;
}

sub ll2xyz($$)
{
    my $lat = (shift) * $D2R;
    my $lon = (shift) * $D2R;
    my $cosphi = cos $lat;
    my $di = $cosphi * cos $lon;
    my $dj = $cosphi * sin $lon;
    my $dk = sin $lat;
    return ($di, $dj, $dk);
}

# return km distance
sub distance_($)
{
    my $t  = shift;
    my @ll1 = ll2xyz($t->[0], $t->[1]);
    my @ll2 = ll2xyz($t->[2], $t->[3]);
    return $ERAD * sqrt(coord_dist_sq($ll1[0], $ll1[1], $ll1[2], $ll2[0], $ll2[1], $ll2[2])) / 1000;
}

sub llll2dir_($) {
    my $t = shift;
  
    my $latA = ($t->[0]) * $D2R;
    my $lonA = ($t->[1]) * $D2R;
    my $latB = ($t->[2]) * $D2R;
    my $lonB = ($t->[3]) * $D2R;
    my $xdist = sin($lonB - $lonA) * $ERAD * cos(($latA + $latB) / 2);
    my $ydist = sin($latB - $latA) * $ERAD;
    my $dir = atan2($xdist, $ydist) * $R2D;
    $dir += 360 if $dir < 0;
    return $dir;
}

# FONCTIONS DE CALCUL DU TRAJET
# passed reference to
# my @depart = (undef, "LFPG", undef, undef,undef);
# or @arrivee ...
###############################

sub configure_extremite ($$$) {
    my ($extremite, $proc, $procx) = @_;
    my $extremite_ok;   # positionné à 1 si l'extrémité a pu être configuré,
                # sera la valeur de retour de la fonction
    # to UPPER case
    $extremite->[1]   =~ tr/a-z/A-Z/;
    my $icao = $extremite->[1];    # extract icao

    sub getPositionParTelnet ($) {
        # si on est pas déjà connecté, alors on se connecte
        if (!$fgfs) {
            if ( !($fgfs = &connect("localhost", $_[0], 5)) ) {
                prt("Impossible de se connecter\n");
            }
        }

        # on récupère la position actuelle de l'appareil
        my $lat = get_prop ($fgfs,"/position/latitude-deg[0]");
        my $lon = get_prop ($fgfs, "/position/longitude-deg[0]");

        # si la postion est trouvée (limitation: ~ est différente de 0°00'00''N 0°00'00''E)
        if ($lat && $lon) {
            $extremite_ok = 1;
            return $lat, $lon;
        } else {
            $erreur = "Impossible de déterminer la position actuelle de l'appareil\n";
        }
    }

    $sous_fonction = sub {
        my @donnees_aeroport;
        my ($ver,$rtype,$myap,$line,$aptcnt);
        # si le fichier de base d'aéroports existe, on l'ouvre sinon on termine le programme
        if ( -e $APTFILE ) {
            open (APT, "gzip -d -c $APTFILE|") or die "je ne peux pas ouvrir $APTFILE\n" ;
            while (<APT>) {
                if (/^(\d+)\s+Version\s+/) {
                    $ver = $1;
                    last;
                }
            }
        } else {
            prt("fichier $APTFILE introuvable\n");
            prt("veuillez vérifier \$FGROOT\n");
            prt("ou utilisez l'option --fg-root=répertoire\n");
            prt("ou encore modifiez le script ver ligne 80\n");
            exit 1;
        }
        if ($ver) {
            prt("Searching file $APTFILE, version $ver, for $icao... moment...\n") if (VERB1());
        } else {
            close APT;
            prt("Failed to find version in $APTFILE!\n");
            exit 1;
        }

        $aptcnt = 0;
        # on parcours le fichier à la recherche de l'aéroport souhaité
        while ($line = <APT>) {
            chomp $line;
            next if ($line =~ /^\s*$/);
            my @header = split(/\s+/, $line);
            $rtype = $header[0];
            last if ($rtype == 99);

            if ($line =~ /^1\s+-?\d+\s\d\s\d\s(\w+)\s(.+)/) {
                # $myap = ($1 eq $icao) ? 1 : 0;
                $myap = ($header[4] eq $icao) ? 1 : 0;
                if ($myap) {
                    prt("Airport: $line\n") if (VERB5());
                    push @donnees_aeroport, \@header;
                } elsif (!$done_all) {
                    prt("airport: $line\n") if (VERB9());
                    push(@all_airports, \@header);
                }
                $aptcnt++;
                my $autre_bout;
                while ($line = <APT>) {
                    chomp $line;
                    last if ($line =~ /^\s*$/);
                    my @donnee = split (/\s+/, $line);
                    $rtype = $donnee[0];
                    last if ($rtype == 99);
                    # s'il s'agit d'une piste, on la renomme en ajoutant son autre extrémité
                    if ($rtype == 10 && $donnee[3] ne 'xxx') {
                        # 0   1          2          3   4       5    6         7         8   9       0 1 2 3    4 5
                        # 10  36.962213  127.031071 14x 131.52  8208 1595.0620 0000.0000 150 321321  1 0 3 0.25 0 0300.0300
                        # 10  36.969145  127.020106 xxx 221.51   329 0.0       0.0        75 161161  1 0 0 0.25 0 
                        $donnee[3] =~ /(..)(.)/;
                        $autre_bout = ($1 > 18)? $1 - 18 : $1 + 18;
                        $autre_bout = '0'.$autre_bout if ($autre_bout < 10);
                        $autre_bout .= 'L' if ($2 eq 'R');
                        $autre_bout .= 'R' if ($2 eq 'L');
                        $autre_bout .= 'C' if ($2 eq 'C');
                        if ($2 eq 'x') {
                            $donnee[3]   = $1.' ';
                            $autre_bout .= ' ';
                        }
                        $donnee[3] = $donnee[3].'/'.$autre_bout;
                        if ($myap) {
                            push (@donnees_aeroport, \@donnee);
                        } elsif (!$done_all) {
                            push(@all_airports,\@donnee);
                        }
                    } elsif ($rtype == 100) {
                        # See version 1000 specs
                        # 0   1     2 3 4    5 6 7 8  9           10           11   12   13 14 15 16 17 18          19           20   21   22 23 24 25
                        # 100 29.87 3 0 0.00 1 2 1 16 43.91080605 004.90321905 0.00 0.00 2  0  0  0  34 43.90662331 004.90428974 0.00 0.00 2  0  0  0
                        my $rwy = $donnee[8];
                        my $rwynm = $rwy;
                        $rwynm =~ /(..)(.)/;
                        $autre_bout = ($1 > 18) ? $1 - 18 : $1 + 18;
                        $autre_bout = '0'.$autre_bout if ($autre_bout < 10);
                        $autre_bout .= 'L' if ($2 eq 'R');
                        $autre_bout .= 'R' if ($2 eq 'L');
                        $autre_bout .= 'C' if ($2 eq 'C');
                        if ($2 eq 'x') {
                            $rwy = $1.' ';
                            $autre_bout .= ' ';
                        }
                        $rwynm = $rwy.'/'.$autre_bout;
                        my $rlat1 = $donnee[9];  # $of_lat1
                        my $rlon1 = $donnee[10]; # $of_lon1
                        my $rlat2 = $donnee[18]; # $of_lat2
                        my $rlon2 = $donnee[19]; # $of_lon2
                        my $rlat = ($rlat1 + $rlat2) / 2;
                        my $rlon = ($rlon1 + $rlon2) / 2;
                        my $dist = distance_( [$rlat1, $rlon1, $rlat2, $rlon2] );
                        $dist = int( $dist * $KM2FEET );    # runway length, in feet
                        my $az1  = llll2dir_( [$rlat1, $rlon1, $rlat2, $rlon2] );
                        $az1 = round( $az1 * 100 ) / 100;
                        my @a = ();
                        #push(@a,[10, $rlat, $rlon, $rwynm, $az1, $dist, 0.0, 0.0, 75, 161161, 1, 0, 0, 0.25, 0, 0]); 
                        @a = (10, $rlat, $rlon, $rwynm, $az1, $dist, 0.0, 0.0, 75, 161161, 1, 0, 0, 0.25, 0, 0);
                        if ($myap) {
                            push (@donnees_aeroport, \@a);
                            prt("Runway: 10, $rlat, $rlon, $rwynm, $az1, $dist, ...\n") if (VERB2());
                        } elsif (!$done_all) {
                            push(@all_airports, \@a);
                        }
                    }
                    # on garde aussi les fréquences COM...
                    if (($rtype >= 50)&&($rtype <= 59)) {
                        if ($myap) {
                            push (@donnees_aeroport, \@donnee);
                        } elsif (!$done_all) {
                            push(@all_airports,\@donnee);
                        }
                    }
                }
            }
        }
        close (APT);
        prt("Closed $APTFILE...\n") if (VERB9());
        if (!$done_all) {
            $done_all = scalar @all_airports;
            prt("Loaded $aptcnt airports, total $done_all lines...\n") if (VERB1());
        }
        $done_all = 1;
        
        # on récupère ses coordonnées de la première piste listée... pour l'instant
        my $lcnt = scalar @donnees_aeroport;
        if ($lcnt > 0) {
            my ($i,$ra,$alat,$alon);
            for ($i = 0; $i < $lcnt; $i++) {
                $ra = $donnees_aeroport[$i];
                $rtype = ${$ra}[0];
                ### prt("$i: rtype $rtype\n");
                if ($rtype == 10) {
                    $alat = ${$ra}[1];
                    $alon = ${$ra}[2];
                    $extremite_ok = 1;
                    last;
                }
            }
            if ($extremite_ok) {
                prt("Success: $icao lat/lon $alat,$alon, $lcnt records...\n") if (VERB2());
                # return @{$donnees_aeroport[1]}[1], @{$donnees_aeroport[1]}[2], \@donnees_aeroport;
                return $alat,$alon,\@donnees_aeroport;
            }
            $erreur = "Error: No runways found for $icao...";
        } else {
            # ces lignes ne atteintes que si aucun aéroport a été trouvé dans la base
            $erreur = "Error: $icao n'a pas été trouvé dans la base de données aéroports...";
        }
    };

    if ($extremite->[1] =~ /^TELNET:(\d+)/) {       # position actuelle de l'appareil, connue par telnet
        $extremite->[1] = "ici";
        ($extremite->[2], $extremite->[3]) = getPositionParTelnet ($1);
        $extremite->[4] = [[0, undef, undef, undef, undef, "position au ".`date`]];
        ($extremite->[0], $$proc, $$procx) = (undef, undef, undef);
    }
    elsif ($extremite->[1] =~ /^\[(.+),(.+)\]$/) {      # position exprimée en coordonnées cartésiennes
        $extremite->[1] = "pos";
        ($extremite->[2], $extremite->[3]) = ($1, $2);
        $extremite->[4] = [[0, undef, undef, undef,undef, $1.", ".$2]];
        if (abs($extremite->[2])<=90 && abs($extremite->[3])<=180) {
            $extremite_ok = 1;
        } else {
            $erreur = "format de coordonnées inconnu...: ".$extremite->[2]." ".$extremite->[3];
        }
        ($extremite->[0], $$proc, $$procx) = (undef, undef, undef);
    }
    else {
        # position par nom de l'aéroport
        # set positions and runway array
        ($extremite->[2], $extremite->[3], $extremite->[4]) = &$sous_fonction ($extremite);
    }

    # on ferme la connexion avec fgfs
    close ($fgfs) if $fgfs;

    # on retourne le résultat de nos recherches
    return $extremite_ok;
}

# NAV_TO_RAM
############
# 0   1 (lat)   2 (lon)     3(alt) 4(feq)  5(rng)     6    7   8++
# 2   38.087769 -077.324919  284     396    25     0.000 APH  A P Hill NDB
# 3   57.103719  009.995578   57   11670   100     1.000 AAL  Aalborg VORTAC
sub nav_to_ram ($$$) {
    my ($fichier, $phrase, $decale) = @_;
    
    prt("Loading file ${$fichier}, p=$phrase d=$decale\n") if (VERB1());
    
    my @selection; # tableau qui va contenir les aides à la navigation utiles
    my $marge = 2;

    my $lat_sup = (($depart[2] >= $arrivee[2])? $depart[2]:$arrivee[2]) + $marge;
    my $lat_inf = (($depart[2] <= $arrivee[2])? $depart[2]:$arrivee[2]) - $marge;

    my $long_sup = (($depart[3] >= $arrivee[3])? $depart[3]:$arrivee[3]) + $marge;
    my $long_inf = (($depart[3] <= $arrivee[3])? $depart[3]:$arrivee[3]) - $marge;

    prt("For lat/lon $lat_sup,$long_sup $lat_inf,$long_inf...\n") if (VERB5());
    
    if ( -e $$fichier ) {
        $$fichier =~ /.+\.(.+)$/;
        my $fichier_traite = ($1 eq 'gz')? 'gzip -d -c '.$$fichier.'|' : $$fichier;
        open (NAV, $fichier_traite) or die "je ne peux pas ouvrir $$fichier\n" ;
    } else {
        die "fichier $$fichier introuvable\n";
    }

    # on détermine la version du fichier nav.dat
    if ($$fichier eq $NAVFILE) {
        while (<NAV>) {
            if (/^(\d+) Version/) {
                $version = $1;
                last;
            }
        }
        # si la version est supérieure à 6.00 on incrémente de 1 les index des tableaux
        $version = ($version > 600)? 1 : 0;
    }

    my $ils = ($version)? '^(4|5)\s+\S+\s+\S+\s+\S+\s+(\S+)\s+\S+\s+\S+\s+\S+\s+(\S+)\s+(...)\s*'
                : '^(4|5)\s+\S+\s+\S+\s+\S+\s+(\S+)\s+\S+\s+\S+\s+(\S+)\s+(...)\s*';

    # on parcourt le fichier pour ne conserver que les balises intéressantes
    while (<NAV>) {
        chomp;
        if (/$phrase/) {
            push @selection, $_ if ($decale
                        &&  $2 <= $lat_sup
                        &&  $2 >= $lat_inf
                        &&  $3 <= $long_sup
                        &&  $3 >= $long_inf);
            push @selection, $_ if (!$decale
                        &&  $1 <= $lat_sup
                        &&  $1 >= $lat_inf
                        &&  $2 <= $long_sup
                        &&  $2 >= $long_inf);
            next;
        }
        # si par hasard on trouve des infos sur les balises ILS de notre aéroport d'arrivée, pourquoi se gêner?
        if (/$ils/ && $3 eq $arrivee[1]) { push (@{$arrivee[4]}, [$1, $4, $2/100]); }
    }
    close (NAV) or die "je ne peux pas fermer $$fichier";
    prt("Close ${$fichier}, returing ".scalar @selection." items.\n") if (VERB9());
    
    return @selection;
}

# FONCTIONS DE CALCUL DU TRAJET (HORS SID/STAR)
###############################################

sub getNavAidNearestMidPoint ($$$) {
    my $leg         = $_[0];
    my $milieu      = $_[1];

    my @ref_dist    = (undef, undef, $_[2], $_[2]);
    my @ref_navaid  = (undef, undef, undef, undef);

    my $heading_from = llll2dir_ ( [$leg->[0], $leg->[1], $milieu->[0], $milieu->[1]] );
    my $heading_to   = llll2dir_ ( [$milieu->[0], $milieu->[1], $leg->[2], $leg->[3]] );

    #RECHERCHE DE LA BALISE LA PLUS PROCHE
    prt("Searching ".scalar @$navaid."... hdgs from $heading_from to $heading_to...\n") if (VERB9());
    
    for (my $index = 0; $index < @$navaid; $index++) {
        # on récupère le type et les coordonnées
        # $1: type de balise
        # $2: latitude
        # $3: longitude
        $navaid->[$index] =~ /^(.)\s+(\S+)\s+(\S+)\s/;

        # on saute à la prochaine itération si la balise testée est celle d'une des
        # extrémités du segment
        next if ( ($2 == $leg->[0] && $3 == $leg->[1]) ||
              ($2 == $leg->[2] && $3 == $leg->[3]) );

        # on calcule l'écart de route en degrés
        my $deviation_to   = abs(llll2dir_ ([$leg->[0], $leg->[1], $2, $3]) - $heading_from);
        my $deviation_from = abs(llll2dir_ ([$2, $3, $leg->[2], $leg->[3]]) - $heading_to);

        # on saute à la prochaine itération si l'écart est supérieur à l'écart autorisé
        next if ($deviation_to > $deviation_max && $deviation_from > $deviation_max);

        # on calcule les distances...
        my $navaid_dist = distance_( [$milieu->[0], $milieu->[1], $2, $3] );
        my $dist_to     = distance_( [$leg->[0], $leg->[1], $2, $3] );
        my $dist_from   = distance_( [$2, $3, $leg->[2], $leg->[3]] );

        # si c'est la plus proche et si la distance est suffisante
        if ( $navaid_dist < $ref_dist[$1] &&
             $dist_to     > $dist_min     &&
             $dist_from   > $dist_min     ) {
            # on retient cette option et on sauve la nouvelle distance de référence
            $ref_navaid[$1] = $index;
            $ref_dist[$1]   = $navaid_dist;
        }
    }

    #RETOUR EN FONCTION DES CHOIX
    SWITCH : {
        #SI ON NE VEUT QUE DU VOR
        if ($vor_a_vor) { return $ref_navaid[$VOR]; last SWITCH; }

        #SI ON PREFERE LES VOR AUX NDB
        if ($vor_preferes && $ref_navaid[$NDB]) { return ($ref_navaid[$VOR])? $ref_navaid[$VOR] : $ref_navaid[$NDB]; last SWITCH; }

        #SI ON EST INDIFFERENT
        if ($ref_navaid[$VOR] && $ref_navaid[$NDB]) { return ($ref_dist[$VOR] < $ref_dist[$NDB])? $ref_navaid[$VOR] : $ref_navaid[$NDB]; last SWITCH; }

        #SI PAS DE VOR
        if (!$ref_navaid[$VOR] && $ref_navaid[$NDB]) { return $ref_navaid[$NDB]; last SWITCH; }

        #SI PAS DE NDB
        if ($ref_navaid[$VOR] && !$ref_navaid[$NDB]) { return $ref_navaid[$VOR]; }
        else    { return $ref_navaid[0]; }
    }
}

sub construction_route ($$$$) {
    # on récupère les arguments de la fonction
    my ($depuis, $vers, $plan, $lev) = @_;

    my $lat1 = $depuis->[0];
    my $lon1 = $depuis->[1];
    my $lat2 = $vers->[0];
    my $lon2 = $vers->[1];

    # les coordonnées du segments [depuis-vers]
    # my $coord_leg = [$depuis->[0], $depuis->[1], $vers->[0], $vers->[1]];
    my $coord_leg = [$lat1, $lon1, $lat2, $lon2];

    # on calcule les coordonnées du milieu du segment [depuis-vers]
    # par une méthode peu orthodoxe...
    my $mi_trajet = [ $depuis->[0]+(($vers->[0]-$depuis->[0])/2),
              $depuis->[1]+(($vers->[1]-$depuis->[1])/2) ];

    # on cherche la balise la plus proche du milieu du segment [depuis-vers]
    my $dist = distance_ ($coord_leg);
    
    prt("Leg: $lat1,$lon1 - $lat2,$lon2, dist ".(round($dist*100) / 100)." km\n") if (VERB9());
    
    my $indexPlusProcheNavAid = getNavAidNearestMidPoint ($coord_leg, $mi_trajet, $dist/2);

    # si on en trouve une
    if ($indexPlusProcheNavAid) {
        # on récupère les coordonnées
        # $1 = latitude
        # $2 = longitude
        $navaid->[$indexPlusProcheNavAid] =~ /^.\s+(\S+)\s+(\S+)\s/;

        # on la nomme "waypoint"
        prt("waypoint $1,$2\n") if (VERB9());
        my $waypoint =  [$1,$2];
        
        # on construit la route entre "depuis" et "waypoint"
        construction_route ($depuis, $waypoint, $plan, $lev + 1);

        # on sauve la balise la plus proche du milieu
        my @a = split /\s+/, $navaid->[$indexPlusProcheNavAid], 8 + $version;
        prt("Added: ".join(" ",@a)." to plan\n") if (VERB5());
        push @$plan, \@a;

        # on construit la route entre "waypoint" et "vers"
        construction_route ($waypoint, $vers, $plan, $lev + 1);
    } else {
        prt("Failed to get any navaids on route!\n") if (($lev == 0) && (VERB9()));
    }
}

# GESTION DES PROCÉDURES SID/STAR
#################################
sub teste_existence_procedure ($$$) {
    # on récupère les arguments de la fonction
    my ($sidstar, $fichier, $marqueur) = @_;
    my @trouvailles;

    # si le fichier sid.dat ou star.dat n'existe pas on abandonne la procédure
    if (! -e $$fichier) {
        prt(sprintf( "le fichier %s n'existe pas, la procédure %s est abandonnée", $$fichier, ($marqueur == 60)? 'SID' : 'STAR'));
        return 0;
    }

    # on ouvre le fichier
    $$fichier =~ /.+\.(.+)$/;
    my $fichier_traite = ($1 eq 'gz')? 'gzip -d -c '.$$fichier.'|' : $$fichier;
    open (FICHIER, $fichier_traite) or die "impossible d'ouvrir le fichier $$fichier!!!";

    # on parcourt le fichier à la recherche des procédures
    while (<FICHIER>) {
        chomp;
        if (/^$marqueur\s+(\S+)\s+(.+)/ && $1 eq $sidstar->[1]) {   # ouverture de procédure qui correspond
            my @procedure;
            push @procedure, $2;
            while (<FICHIER>) {
                chomp;
                last if (/^\s*$/);  # une ligne vide on arrête la recherche pour cette procédure
                push @procedure, $_;    # tant qu'il y a des données on prend
            }

            # on place la procédure entière dans @trouvailles
            push @trouvailles, \@procedure;
        }
    }

    # on ferme le fichier
    close (FICHIER);

    # @trouvaille contient toutes les procédures connues pour notre aéroport
    # on sauve les résultats là où qu'il faut
    $sidstar->[0] = \@trouvailles;

    # on renvoie la taille du tableau @trouvailles (0 = rien trouvé)
    my $taille = @trouvailles;
    return $taille;
}

sub mise_en_forme_procedure ($$) {
    my ($procedure, $extremite) = @_;

    my @procedure_exploitable# tableau contenant les étapes exploitables de la procédure entière
    my $nombre_d_entrees = 0;   # pour contrôler le nombre d'entrées de la procédure rééellement exploitables
                    # si il est nul, alors on abandonne

    # table de hachage utilisée par $sous_fonction
    my %type = ('F' => [$fix, '^\s*\S+\s+\S+\s+(\S+)\s*$'],
            'V' => [$navaid, ($version)? '^3\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)'
                           : '^3\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)'      ],
            'N' => [$navaid, ($version)? '^2\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)'
                           : '^2\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)'      ]);

    # retourne la ligne d'un fix ou d'une aide à la navigation depuis les bases de données correspondantes
    $sous_fonction = sub {
        my ($test, $nom) = @_;
        foreach my $element (@{$type{$test}->[0]}) {
            return $element if ($element =~ /$type{$test}->[1]/ && $1 eq $nom);
        }
    };

    # pour vérifier si la procédure sera modifiée
    my $modifie = @{$procedure};

    # on arrange chaque élément des procédures sid et star pour les incorporer à la route
    for (my $index = 1; $index < @{$procedure}; $index++) {
        $procedure->[$index] =~ /^(\S+)\s+(\S+)\s+(\S+)\s+(.+)$/;
        my $point_de_passage = $1;

        # si le point de passage est un fix, un vor ou un ndb...
        if ($point_de_passage == 65) {
            # on arrête si c'est l'arrivée (code A de la procédure star)
            # pour plus tard, les étapes de l'approche manquée seront sauvées quelque part pour traitement...
            last if ($2 eq 'A');

            # on récupère toute l'info le concernant
            $procedure->[$index] = &$sous_fonction ($2, $3);

            # on continue pour le prochain point de passage si on ne trouve rien
            next if !$procedure->[$index];

            # si c'est un vor ou un ndb on rajoute l'altitude minimale au dessus du point
            if ($2 eq 'V' || $2 eq 'N') {
                $procedure->[$index] .= " $4";
            }

            # s'il s'agit d'un fix, on reformate pour que la taille corresponde avec les autres point du plan de vol
            else {
                my $altitude_mini = $4;
                $procedure->[$index] =~ /^\s*(\S+)\s+(\S+)\s+(\S+)\s*$/;
                $procedure->[$index] = ($version)? "65 $1 $2 fix fix fix fix $3 $altitude_mini"
                                 : "65 $1 $2 fix fix fix $3 $altitude_mini";
            }
        }

        # ...ou si le point de passage est un gps, on reformate comme pour les fix
        elsif ($point_de_passage == 66) {
            my ($lat, $lon) = ($3/1000000, $4/1000000);
            $procedure->[$index] = ($version)? "66 $lat $lon gps gps gps gps gps $2"
                             : "66 $lat $lon gps gps gps gps $2";
        }

        # ...ou si c'est un trajet d'attente, on l'abandonne (pour l'instant)
        elsif ($point_de_passage == 64) {
            next;
        }

        # on découpe les points de passage
        my @etape = split (/\s+/, $procedure->[$index]);
        $nombre_d_entrees++;
        push @procedure_exploitable, \@etape;
    }

    # on ne retient dans $depart[0]/$arrivee[0] que le nom de la procédure
    # et on indique si elle a été modifiée
    my $a_ete_modifie = ($nombre_d_entrees != $modifie)? ' (modifiée)' : undef;
    $extremite->[0] = ($nombre_d_entrees)? @{$procedure}[0].$a_ete_modifie : undef;

    # on retourne la procédure exploitable
    return \@procedure_exploitable;
}

sub sid_star ($$$$$$) {
    # on récupère les paramètres
    my ($proc, $procx, $extremite, $fichier, $marqueur, $autre_extremite) = @_;

    my $ref_dist = 99999;   # distance de référence pour comparer
    my $ref_index;      # index de référence pour se souvenir
    my $dist;       # la distance entre les deux extrémités
    my @retenues;       # contiendra la liste des procédures correspondant à la demande d'une piste particulière
    my $phrase_a_matcher;   # faute de meilleur nom...

    # table de hachage utilisée par $sous_fonction
    my %type = ('F' => [$fix, '^\s*(\S+)\s+(\S+)\s+(\S+)\s*$'],
            'V' => [$navaid, ($version)? '^3\s+(\S+)\s+(\S+)\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)'
                           : '^3\s+(\S+)\s+(\S+)\s+\S+\s+\S+\s+\S+\s+(\S+)'      ],
            'N' => [$navaid, ($version)? '^2\s+(\S+)\s+(\S+)\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)'
                           : '^2\s+(\S+)\s+(\S+)\s+\S+\s+\S+\s+\S+\s+(\S+)'      ]);

    # retourne les coordonnées d'un fix ou d'une aide à la navigation
    $sous_fonction = sub {
        my ($test, $nom) = @_;
        foreach my $element (@{$type{$test}->[0]}) {
            return ($1, $2) if ($element =~ /$type{$test}->[1]/ && $3 eq $nom);
        }
    };

    # si on trouve au moins une procédure sid/star:
    # elle(s) est(sont) placées dans $depart[0]/$arrivee[0]
    # et on charge les données nav et fix.
    if (teste_existence_procedure ($extremite, $fichier, $marqueur)) {
        @$fix    = nav_to_ram (\$FIXFILE, '^\s*(\S+)\s+(\S+)\s+\S+\s*$', 0) if (@{$fix} == 0);
        @$navaid = nav_to_ram (\$NAVFILE, '^(2|3)\s+(\S+)\s+(\S+)\s', 1)    if (@{$navaid} == 0);
    }
    # sinon on annule la demande de procédure sid/star
    # et on sort de la fonction
    else {
        ($extremite->[0], $$proc, $$procx) = (undef, undef, undef);
        prt(sprintf( "Aucune procédure %s n'a été trouvée pour %s\n", ($marqueur == 60)? 'SID':'STAR', $extremite->[1]));
        return;
    }


    # on commence par chercher la ou les procédures voulues
    if ($$proc) {
        foreach my $procedure (@{$extremite->[0]}) {
            push @retenues, $procedure if ($procedure->[0] =~ /\[RW$$proc.\s*/);
        }
        # si on a trouvé les pistes correspondantes à la demande
        if (@retenues != 0) {
            $extremite->[0] = \@retenues;
        }
        # sinon on annule la demande --sid/--star qui devient une demande --sidx/--starx
        else {
            prt(sprintf( "Aucune procédure %s n'a été trouvée pour la piste $$proc sur $extremite->[1]\n", ($marqueur == 60)? 'SID':'STAR'));
            $$proc  = undef;
            $$procx = 1;
        }
    }

    # on choisit la meilleure procédure,
    # pour l'instant le choix se fait par le calcul de la plus petite distance

    # pour chaque procédure connue
    for (my $index = @{$extremite->[0]}; $index--; ) {
        my $entree = 1;

        # $1 contient l'info du type de dernier(sid)/premier(star) point de passage de la procédure:
        #   - 4, ou 7: trajet d'attente (uniquement pour star)
        #   - 5: vor, ndb ou fix
        #   - 6: coordonnées gps
        POINT_DE_PASSAGE : {

        # on atteint le dernier élément de la procédure sid numéro $index
        # ou le premier de la procédure star numéro $index
        $phrase_a_matcher = ($marqueur == 60)? $extremite->[0]->[$index]->[@{$extremite->[0]->[$index]} - $entree]
                             : $extremite->[0]->[$index]->[$entree];
        $phrase_a_matcher =~ /^6(.)\s+/;

            if ($1 == 4 || $1 == 7) {   # c'est un trajet d'attente (holding pattern)
                # ben ça attendra encore un peu ... on lit l'étape suivante
                $entree++;
                next POINT_DE_PASSAGE;  # je ne suis pas tout à fait certain de l'orthodoxie de cette syntaxe...
            }
            if ($1 == 5) {          # c'est un fix ou un vor, ou un ndb...
                            # ou un point d'arrivée (code A) de procédure star mais je considère cette possibilité comme nulle
                # on cherche le type et le nom
                $phrase_a_matcher =~ /^65\s+(\S)\s+(\S+)/;

                # on cherche ses coordonnées
                my ($lat, $lon) = &$sous_fonction ($1, $2);

                # on passe au point suivant si le point recherché n'est pas connu
                if (!$lat) {
                    $entree++;
                    next POINT_DE_PASSAGE;  # je ne suis pas tout à fait certain de l'orthodoxie de cette syntaxe...
                }

                # on calcule la distance entre les deux extrémités
                $dist = distance_ ( [$lat, $lon, $autre_extremite->[1], $autre_extremite->[2]] );

                # on retient la solution si la distance est inférieure à la distance de référence
                ($ref_dist, $ref_index) = ($dist, $index) if ($dist < $ref_dist);

                # on sort
                last POINT_DE_PASSAGE;
            }
            if ($1 == 6) {          # c'est un point gps
                # on lit les coordonnées du point
                $phrase_a_matcher =~ /^66\s+\S+\s+(\S+)\s+(\S+)/;

                # on calcule la distance entre les deux extrémités
                $dist = distance_ ([$1/100000, $2/100000, $autre_extremite->[2], $autre_extremite->[3]]);

                # on retient la solution si la distance est inférieure à la distance de référence
                ($ref_dist, $ref_index) = ($dist, $index) if ($dist < $ref_dist);

                # on sort
                last POINT_DE_PASSAGE;  # inutile mais c'est pour faire joli
            }
        } # POINT_DE_PASSAGE
    } # for (my $index = @{$extremite->[0]}; $index--; )

    # on met en forme la procédure trouvée
    my $procedure_finale = mise_en_forme_procedure ($extremite->[0]->[$ref_index], $extremite);

    # on enregistre les coordonnées de l'extrémité sid/star, si trouvées
    $extremite->[2] = @{$procedure_finale->[@{$procedure_finale} - 1]}[1] if @{$procedure_finale->[@{$procedure_finale} - 1]}[1];
    $extremite->[3] = @{$procedure_finale->[@{$procedure_finale} - 1]}[2] if @{$procedure_finale->[@{$procedure_finale} - 1]}[2];

    # on retourne la procédure sid/star
    return $procedure_finale;
}

## PLAN DE VOL
##############
sub plan_de_vol {
    # les aides à la navigation
    my @NDBVOR;
    $navaid = \@NDBVOR;

    # les fix
    my @FIX;
    $fix = \@FIX;

    # l'aéroport de départ est le premier point
    push @route, ($version)? [1, $depart[2], $depart[3], @{$depart[4]->[0]}[1], $depart[4], 'apt', 'apt', $depart[1], @{$depart[4]->[0]}[5]] :
                 [1, $depart[2], $depart[3], @{$depart[4]->[0]}[1], $depart[4], 'apt', $depart[1], @{$depart[4]->[0]}[5]];

    # on trouve les coordonnées de sortie de la procedure sid, qui deviendront $depart[2] et  $depart[3]
    # le trajet sera contenu dans $depart[0]
    my $procedure_sid = sid_star (\$sid, \$sidx, \@depart, \$SIDFILE, 60, \@arrivee) if ($sid || $sidx);

    # on trouve les coordonnées d'entrée de la procédure star, qui deviendront $arrivee[2] et  $arrivee[3]
    # le trajet sera contenu dans $arrivee[0]
    my $procedure_star = sid_star (\$star, \$starx, \@arrivee, \$STARFILE, 61, \@depart) if ($star || $starx);

    # si ce n'est déjà fait on place les données nécessaires en mémoire pour aller plus vite
    # (@FIX seulement dans le cadre des procédures sid/star, pour le moment...)
    @FIX    = nav_to_ram (\$FIXFILE, '^\s*(\S+)\s+(\S+)\s+\S+\s*$', 0) if (($sid || $sidx || $star || $starx) && (@{$fix} == 0));
    my ($type_navaid, $decale) = ($vor_a_vor && !($sid || $sidx || $star || $starx))? ('^3', 0) : ('^(2|3)', 1);
    @NDBVOR =  nav_to_ram (\$NAVFILE, $type_navaid.'\s+(\S+)\s+(\S+)\s', $decale) if (@{$navaid} == 0);

    prt("Constructing the route, deviation max = $deviation_max, dist min $dist_min km... moment...\n") if (VERB1());

    # on ajoute tous ses points de passage sid dans le plan de vol
    push @route, @{$procedure_sid} if $depart[0];

    # on construit la route entre les deux extrémités
    construction_route (    [$depart[2],  $depart[3]],
                [$arrivee[2], $arrivee[3]],
                \@route, 0);

    # on ajoute tous les points de passage star dans le plan de vol
    push @route, @{$procedure_star} if $arrivee[0];

    # on prend les coordonnées de la piste qui sera utilisée
    $sous_fonction = sub {
        my $extremite = shift;
        if ($extremite->[0] =~ /\[RW(...)\s*/) {
            my $piste = $1;
            foreach (@{$extremite->[4]}) { 
                ($extremite->[2], $extremite->[3]) = ($_->[1], $_->[2]) if ($_->[3] =~ /$piste/);
            }
        }
    };
    &$sous_fonction (\@depart);
    &$sous_fonction (\@arrivee);

    # IL RESTE À TROUVER LA DERNIÈRE BALISE (ÉVENTUELLE) SITUÉE SUR L'AÉROPORT D'ARRIVÉE
    # si aucune procédure star n'est demandée (ou valable)

    # l'aéroport d'arrivée est le dernier point
    push @route, ($version)? [1, $arrivee[2], $arrivee[3], @{$arrivee[4]->[0]}[1], $arrivee[4], 'apt', 'apt', $arrivee[1], @{$arrivee[4]->[0]}[5]] :
                 [1, $arrivee[2], $arrivee[3], @{$arrivee[4]->[0]}[1], $arrivee[4], 'apt', $arrivee[1], @{$arrivee[4]->[0]}[5]];

    my $cnt = scalar @route;
    prt("Done route with $cnt legs...\n") if (VERB1());

    # on détruit les listes des aides à la navigation désormais inutiles
    $navaid = undef;
    $fix    = undef;
}

# FONCTIONS DE SORTIE DU RESULTAT
#################################

sub fichier_csv () {
    $sous_fonction = sub {
        my $i = $_[0].$_[3].$_[1].$_[3].$_[2];
        $i =~ s/\./$_[4]/g;
        return $i;
    };

    # ouverture du fichier
    if (!open (CSV, ">$CSVFILE")) {
        prt("Error: Failed to open $CSVFILE!\n");
        return;
    }

    # on configure les séparateurs
    my ($separateur, $decimal);
    if ($csv_conf =~ /^(.)(.)$/) {
        $separateur = $1;
        $decimal    = $2;
    }

    # on écrit le contenu du fichier
    for (my $index = 0; $index < @route; $index++) {
        printf CSV "%s\n", &$sous_fonction ($route[$index]->[6 + $version], $route[$index]->[1], $route[$index]->[2], $separateur, $decimal);
    }

    # on ferme le fichier
    close (CSV);
    prt("CSV output written to $CSVFILE.\n");
}

#my @all_airports = ();
#my $done_all = 0;
sub get_airport($) {
    my $t = shift;
    my $ap = undef;
    my $marge = 2;
    my $latA = $t->[0]; # from
    my $lonA = $t->[1];
    my $latB = $t->[2]; # to
    my $lonB = $t->[3];
    my ($max,$max_lat,$max_lon,$min_lat,$min_lon);
    $max = scalar @all_airports;

    if ($latA < $latB) {
        $min_lat = $latA - $marge;
        $max_lat = $latB + $marge;
    } else {
        $min_lat = $latB - $marge;
        $max_lat = $latA + $marge;
    }
    if ($lonA < $lonB) {
        $min_lon = $lonA - $marge;
        $max_lon = $lonB + $marge;
    } else {
        $min_lon = $lonB - $marge;
        $max_lon = $lonA + $marge;
    }

    prt("Find an ap in $min_lon,$min_lat,$max_lon,$max_lat... in $max lines...\n") if (VERB9());

    my ($i,$i2,$raa,$rwa,$alat,$alon,$typ);
    my @aps = ();
    for ($i = 0; $i < $max; $i++) {
        $raa = $all_airports[$i];
        $typ = ${$raa}[0];
        if ($typ == 1) {
            $i2 = $i + 1;
            ### prt("Test ".join(" ",@{$raa})."\n");
            for (; $i2 < $max; $i2++) {
                $rwa = $all_airports[$i2];
                $typ = ${$rwa}[0];
                if ($typ == 10) {
                    $alat = ${$rwa}[1];
                    $alon = ${$rwa}[2];
                    if (($alat >= $min_lat)&&
                        ($alat <= $max_lat)&&
                        ($alon >= $min_lon)&&
                        ($alon <= $max_lon)) {
                        push(@aps,[$raa,$alat,$alon,$i]);
                        ###prt("Found ".join(" ",@{$raa})."\n");
                        $i = $i2;
                        last;
                    }
                } elsif ($typ == 1) {
                    if ($i2 > ($i + 1)) {
                        $i2--;
                        $i = $i2;
                    }
                    last;
                } 
            }
        }
    }
    $max = scalar @aps;
    if ($max) {
        # hmmm, this is based on DISTANCE only.
        # a 'better' idea might be one CLOSEST to this TRACK
        my ($ap2,$dist_min,$dist,$dist1,$dist2,$index);
        for ($i = 0; $i < $max; $i++) {
            $rwa = $aps[$i];
            $raa = ${$rwa}[0];
            $alat = ${$rwa}[1];
            $alon = ${$rwa}[2];
            $dist1 = distance_( [$latA,$lonA,$alat,$alon] );
            $dist2 = distance_( [$latB,$lonB,$alat,$alon] );
            $dist = ($dist1 < $dist2) ? $dist1 : $dist2;
            if ($i == 0) {
                $dist_min = $dist;
                $index = ${$rwa}[3];
                $ap2 = $rwa;
            } elsif ($dist < $dist_min) {
                $dist_min = $dist;
                $index = ${$rwa}[3];
                $ap2 = $rwa;
            }
        }
        # chosen airport - TODO: more choice criteria
        $raa  = ${$ap2}[0];
        $alat = ${$ap2}[1];
        $alon = ${$ap2}[2];
        prt("Found ".join(" ",@{$raa}).", $alat,$alon, of $max, index $index\n") if (VERB9());
        $max = scalar @all_airports;
        my @a = ();
        for ($i = $index; $i < $max; $i++) {
            $rwa = $all_airports[$i];
            $typ = ${$rwa}[0];
            if ($i == $index) {
                push(@a,$rwa);
            } else {
                last if ($typ == 1);
                push(@a,$rwa);
            }
        }
        # $raa = $all_airports[$index];   # get header of this chosen airport
        # $raa  = ${$ap2}[0];
        $ap = [$raa,$alat,$alon,\@a];   # pass back ALL entries for this airport
    } else {
        prt("No airport found in $min_lon,$min_lat,$max_lon,$max_lat! marg $marge\n");
        exit 1;
    }
    return $ap;
}

sub get_apt_pos($) {
    my $apt = shift;
    my $max = scalar @{$apt};
    my $rwys = '';
    my ($ra,$i,$rtyp,$rlat,$rlon);
    for ($i = 0; $i < $max; $i++) {
        $ra = ${$apt}[$i];
        $rtyp = ${$ra}[0];
        if ($rtyp == 10) {
            $rlat = ${$ra}[1];
            $rlon = ${$ra}[2];
            return ($rlat,$rlon);
        }
    }
    prt("Error: apt array contains NO runways! *** FIX ME ***\n");
    exit 1;
}

sub get_rw_list2($) {
    my $apt = shift;
    my $max = scalar @{$apt};
    my $rwys = '';
    my ($ra,$i,$i2,$rtyp,$rlat,$rlon,$rnm,$rhdg,$rlen);
    # 0   1          2          3      4       5    6         7         8   9       0 1 2 3    4 5
    # 10  36.962213  127.031071 14 /32 131.52  8208 1595.0620 0000.0000 150 321321  1 0 3 0.25 0 0300.0300
    my @arr = ();
    for ($i = 0; $i < $max; $i++) {
        $i2 = $i + 1;
        $ra = ${$apt}[$i];
        $rtyp = ${$ra}[0];
        if ($rtyp == 10) {
            $rlat = ${$ra}[1];
            $rlon = ${$ra}[2];
            $rnm  = ${$ra}[3];
            $rhdg = ${$ra}[4];
            $rlen = ${$ra}[5];
            push(@arr,[$rtyp,$rlat,$rlon,$rnm,$rhdg,$rlen]);
        }
    }
    $max = scalar @arr;
    for ($i = 0; $i < $max; $i++) {
        $i2 = $i + 1;
        $ra = $arr[$i];
        $rlat = ${$ra}[1];
        $rlon = ${$ra}[2];
        $rnm  = ${$ra}[3];
        $rhdg = ${$ra}[4];
        $rlen = ${$ra}[5];
        $rwys .= "{\"lat\":".sprintf("%.8f",$rlat).",\"lon\":".sprintf("%.8f",$rlon).",\"marks\":\"$rnm\",\"heading\":".sprintf("%.1f",$rhdg).",\"length_ft\":".sprintf("%d",int($rlen))."}";
        $rwys .= ",\n" if ($i2 < $max); # there are more
    }
    return $rwys;
}

sub get_rw_list($) {
    my $ap = shift;
    my $apt = ${$ap}[3];    # array of AIRPORT items
    return get_rw_list2($apt);
}

# ATC frequencies Example Usage 
# 53          Identifies this as an airport ATC frequency line. Codes in the 50 - 59 range are used to identity 
#            different ATC types. 
# 12190       Airport ATC frequency, in Megahertz multiplied by 100 (ie. 121.90 MHz in this example). 
# GND         Name of the ATC frequency.  This is often an abbreviation (such as GND for "Ground"). 
sub get_coms_list2($) {
    my $apt = shift;
    my $max = scalar @{$apt};
    my $coms = '';
    my ($ra,$i,$i2,$rtyp,$freq,$name);
    # 0   1     2
    # 53  12190 GND
    my @arr = ();
    for ($i = 0; $i < $max; $i++) {
        $ra = ${$apt}[$i];
        $rtyp = ${$ra}[0];
        # on garde aussi les fréquences COM...
        if (($rtyp >= 50)&&($rtyp <= 59)) {
            $freq = ${$ra}[1];
            $name = ${$ra}[2];
            push(@arr,[$rtyp,$freq,$name]);
        }
    }
    $max = scalar @arr;
    for ($i = 0; $i < $max; $i++) {
        $i2 = $i + 1;
        $ra = $arr[$i];
        $rtyp = ${$ra}[0];
        $freq = ${$ra}[1] / 100;
        $name = ${$ra}[2];
        $coms .= "{\"type\":".sprintf("%d",$rtyp).",\"freq\":".sprintf("%.2f",$freq).",\"name\":\"$name\"}";
        ##$coms .= "{\"t\":".sprintf("%d",$rtyp).",\"f\":".sprintf("%.2f",$freq).",\"n\":\"$name\"}";
        $coms .= ',' if ($i2 < $max);
    }
    return $coms;
}

sub get_coms_list($) {
    my $ap = shift;
    my $apt = ${$ap}[3];    # array of AIRPORT items
    return get_coms_list2($apt);
}

# output the track in 'xgraph' format
# should also consider generating a JSON string to load in a browser
# which could have a MAP, just show the track on a map
# Airport Line. eg '1 5355 1 0 KABQ Albuquerque Intl Sunport'
# 0 1      - this as an airport header line. 16 is a seaplane/floatplane base, 17 a heliport.
# 1 5355   - Airport elevation (in feet above MSL).  
# 2 1      - Airport has a control tower (1=yes, 0=no).
# 3 0      - Display X-Plane’s default airport buildings (1=yes, 0=no).
# 4 KABQ   - Identifying code for the airport (the ICAO code, if one exists).
# 5+ Albuquerque Intl Sunport - Airport name.

sub fichier_xg () {

    my ($name,$lat,$lon,$lat2,$lon2);
    my $div = ($km) ? 1 : $NM2KM;   # 1.852;
    my $unit = ($km) ? "km" : "nm";
    my $rng = ($km) ? $range * $NM2KM : $range; # max range without refuel = 696;    # nm

    # ouverture du fichier
    if (!open (XG, ">$XGFILE")) {
        prt("Error: Failed to open $XGFILE!\n");
        return;
    }

    my @air2air = ();   # airport to airport path
    my ($max_lat,$max_lon,$min_lat,$min_lon);
    my ($waypointtype,$wpinfo,$max,$index,$typ);
    my ($leg,$heading,$distance,$distance_totale,$dist_cum,$wp_count);
    my ($raa,$name,$alat,$alon,$icao,$aalt);
    my ($nalt,$nfrq,$nrng);

    my $json = "{\"success\":true,\"source\":\"plandevol01.pl\",\"generated\":\"".lu_get_YYYYMMDD_hhmmss_UTC(time())." UTC\",\"waypoints\":[\n";
    $distance_totale = 0;
    $dist_cum = 0;
    $wp_count = 0;
    $max_lat = -400;
    $max_lon = -400;
    $min_lat = 400;
    $min_lon = 400;

    # on écrit le contenu du fichier
    $max = scalar @route;
    print XG "# plan de vol dep ".$depart[1]." to ".$arrivee[1]." in $max legs\n";
    $lat = 0;
    $lon = 0;
    for ($index = 0; $index < $max; $index++) {
        $typ = $route[$index]->[0];
        $lat = $route[$index]->[1];
        $lon = $route[$index]->[2];
        if ($index > 0) {
            ###$leg      = [@{$route[$index-1]}[1],@{$route[$index-1]}[2],@{$route[$index]}[1],@{$route[$index]}[2]];
            $leg      = [$lat2,$lon2,$lat,$lon];
            $heading  = round (llll2dir_ ($leg));
            $distance = distance_ ($leg) / $div;
            # check for a re-fueling stop - if next beyond range, and NOT last waypoint
            if ((($dist_cum + $distance) > $rng) && (!($index == ($max - 1)))) {
                # time to insert an airport for refueling
                my $ap = get_airport($leg);
                if ($ap) {
                    # final point of this leg is this airport
                    $raa = ${$ap}[0];
                    $aalt = ${$raa}[1];
                    $icao = ${$raa}[4];
                    $name = join(' ', splice(@{$raa},5)); # Name
                    $alat = ${$ap}[1];
                    $alon = ${$ap}[2];
                    $leg      = [$lat2,$lon2,$alat,$alon];
                    $heading  = round (llll2dir_ ($leg));
                    $distance = distance_ ($leg) / $div;
                    $distance_totale += $distance;
                    print XG "$alon $alat\n";
                    $json .= "{\"lat\":".sprintf("%.8f",$alat).",\"lon\":".sprintf("%.8f",$alon);
                    $json .= ",\"alt\":".sprintf("%d",$aalt);
                    $json .= ",\"icao\":\"$icao\"";
                    $json .=",\"name\":\"$name\",\"type\":\"APT\",\n"; # ,\"info\":\"".join(" ",@{$raa})."\",\n";
                    $json .= "\"runways\":[".get_rw_list($ap)."],\n";
                    $json .= "\"coms\":[".get_coms_list($ap)."]";
                    push(@air2air,${$ap}[3]);   # array of airport items
                    # TODO Add any ILS for this airport
                    $json .= "},\n";
                    $wp_count++;
                    print XG "NEXT\n";
                    print XG "# re-fuel at airport $name\n";
                    print XG "anno $alon $alat APT: ".join(" ",@{$raa})." h=$heading d=$distance $unit\n";
                    print XG "$alon $alat\n";   # start next leg
                    $leg      = [$alat,$alon,$lat,$lon];
                    $heading  = round (llll2dir_ ($leg));
                    $distance = distance_ ($leg) / $div;
                }
                $dist_cum = 0;
            }
            $dist_cum += $distance;
            $distance_totale += $distance;
            # for display only
            $distance = round ($distance);
        } else {
            $heading = '';
            $distance = '';
        }
        # next waypoint
        $name = $route[$index]->[6 + $version].' '.$route[$index]->[7 + $version];
       $waypointtype = "APT";
       $wpinfo = "none";
       if ($typ == 2) {
           $wpinfo = "f=".$route[$index]->[4];
           $waypointtype = "NDB";
       }
       elsif ($typ == 3) { 
           $wpinfo = "f=".$route[$index]->[4];
           $waypointtype = "VOR"; 
       }
       elsif ($typ == 65){ 
           $waypointtype = "FIX"; 
       }
       elsif ($typ == 66){ 
           $waypointtype = "GPS"; 
       }
        if (length($heading)) {
            $wpinfo .= " b=$heading d=$distance $unit.";
        }
        if ($index == 0) { $wpinfo .= " BEGIN OF ROUTE"; }
       if ($index == ($max - 1)) { $wpinfo .= " END OF ROUTE"; }
   
        printf XG "anno %f %f %s\n", $lon, $lat, "$name $waypointtype $wpinfo";
        printf XG "%f %f\n", $lon, $lat;
        # get bounding box
        $max_lat = $lat if ($lat > $max_lat);
        $max_lon = $lon if ($lon > $max_lon);
        $min_lat = $lat if ($lat < $min_lat);
        $min_lon = $lon if ($lon < $min_lon);
        $json .= "{\"lat\":".sprintf("%.8f",$lat).",\"lon\":".sprintf("%.8f",$lon).",\"name\":\"$name\",\"type\":\"$waypointtype\",\"info\":\"$wpinfo\"";
       if ($waypointtype eq "APT") {
            #add runways and coms, and ils if any
            $raa = $route[$index]->[4];
            $aalt = ${$raa}[0][1];
            $icao = ${$raa}[0][4];
            $json .= ",\"alt\":".sprintf("%d",$aalt);
            $json .= ",\"icao\":\"$icao\"";
            $json .= ",\n\"runways\":[".get_rw_list2($raa)."],\n";
            $json .= "\"coms\":[".get_coms_list2($raa)."]";
            push(@air2air,$raa);
            # TODO Add any ILS for this airport
        } elsif (($typ == 2)||($typ == 3)) {
            # would be nice to add FREQUENT ALTITUDE RANGE to json
            # 0   1 (lat)   2 (lon)     3(alt) 4(feq)  5(rng)     6    7  8++
            # 2   38.087769 -077.324919  284     396    25     0.000 APH  A P Hill NDB
            # 3   57.103719  009.995578   57   11670   100     1.000 AAL  Aalborg VORTAC
            $nalt = $route[$index]->[3];
            $nfrq = $route[$index]->[4];
            $nrng = $route[$index]->[5];
            if ($typ == 3) {
                $nfrq = sprintf("%.2f",$nfrq);
            } else {
                $nfrq = sprintf("%d",$nfrq);
            }
            $json .= ",\"alt_ft\":".sprintf("%d",$nalt).",\"freq\":$nfrq,\"rng_nm\":".sprintf("%d", int($nrng));
        }
        $json .= "},\n";
        $wp_count++;
        $lat2 = $lat;
        $lon2 = $lon;
    }

    print XG "NEXT\n";

    print XG "# bbox=$min_lon,$min_lat,$max_lon,$max_lat\n";
    print XG "# route dist ".round($distance_totale)." $unit.\n";

    # add an airport to airport track
    if (@air2air) {
        print XG "color red\n";
        foreach $raa (@air2air) {
            ($alat,$alon) = get_apt_pos($raa);
            print XG "$alon $alat\n";
        }   
        print XG "NEXT\n";
    }

    # on ferme le fichier
    close (XG);
    prt("# bbox=$min_lon,$min_lat,$max_lon,$max_lat\n");
    prt("XG output written to $XGFILE.\n");
    my $jfile = $XGFILE.".json";
    if (open(XG,">$jfile")) {
        if ($wp_count) {
            $json =~ s/,\n$/\n/g;
        }
        $json .= "],\"count\":$wp_count}\n";
        print XG $json;
        close XG;
        prt("JSON output written to $jfile.\n");
    }
}


sub fichier_wp () {
    $WPFILE = $depart[1]."-".$arrivee[1];
    my $WPFILE_test = $WPFILE;
    my $count = 1;
    if (! -d "$FGROOT/Routes") {
        prt("Error: Directory $FGROOT/Routes does NOT exist! Create it first...\n");
    }
    while ( -e "$FGROOT/Routes/$WPFILE_test.xml" ) {
       $WPFILE_test = $WPFILE;
       $WPFILE_test .= $count;
       $count++;
    }
    $WPFILE = $WPFILE_test;

    # ouverture du fichier
    if (!open (WP, ">$FGROOT/Routes/$WPFILE.xml")) {
        prt("Error: Failed to open $FGROOT/Routes/$WPFILE.xml!\n");
        return;
    }

    my $template = <<EOT;
 <Waypoint n="%s">
  <ID type="string">%s</ID>
  <name type="string">%s</name>
  <latitude-deg type="double">%s</latitude-deg>
  <longitude-deg type="double">%s</longitude-deg>
  <altitude-ft type="double">0</altitude-ft>
  <waypoint-type type="string">%s</waypoint-type>
  <desc type="string">%s</desc>
 </Waypoint>\n
EOT
    print WP "<PropertyList>\n";
    # on écrit le contenu
    for (my $index = 1; $index < @route; $index++) {
   my $waypointtype = "APT";
   my $wpinfo = "none";
   if ($route[$index]->[0] == 2) {
       $wpinfo = "FREQ: ".$route[$index]->[4];
       $waypointtype = "NDB";
   }
   elsif ($route[$index]->[0] == 3) { 
       $wpinfo = "FREQ: ".$route[$index]->[4];
       $waypointtype = "VOR"; 
   }
   elsif ($route[$index]->[0] == 65){ 
       $waypointtype = "FIX"; 
   }
   elsif ($route[$index]->[0] == 66){ 
       $waypointtype = "GPS"; 
   }

   if ($index == @route - 1) { $wpinfo .= " END OF ROUTE"; }
   
        printf WP $template, 
          $index - 1,
          $route[$index]->[6 + $version], 
          $route[$index]->[7 + $version],
          $route[$index]->[1], 
          $route[$index]->[2],
          $waypointtype,
          $wpinfo;
    }
    print WP "</PropertyList>";

    # fermeture du fichier
    close (WP);
    prt("File $FGROOT/Routes/$WPFILE.xml written.\n");
}

sub sortie_standard () { # cette procédure est de la bouillie pour les chats et pour les chiens!
    my $div = ($km) ? 1 : $NM2KM;   # 1.852;
    my ($leg, $distance, $distance_totale, $heading);

    $sous_fonction = sub {
        print "\033[30;1m" if $add_couleur;
        prt("$_[0]\n");
        print "\033[m" if $add_couleur;
    };

    if ($com_dep) {
        &$sous_fonction ("Fréquences utiles pour le départ");
        foreach (@{$depart[4]}) { 
            prt(sprintf("$_->[@{$_}-1]: %s\n", $_->[1]/100))
                if ($_->[0] >= 50 && $_->[0] <= 59 && $_->[@{$_}-1] ne 'APP');
        }
    }

    prt( "procédure SID  : $depart[0]\n" ) if $depart[0];
    prt( "procédure STAR : $arrivee[0]\n" ) if $arrivee[0];

    &$sous_fonction ("\nCode - Nom complet");
    prt(sprintf( "\t| Frequences | Heading | Course/RNW | Distance en %s\n", ($km)? 'km':'nm'));

    &$sous_fonction ("$depart[4]->[0]->[4] - $depart[4]->[0]->[5]");
    prt(sprintf("%s", ($depart[0] =~ /\RW(...)\s+/)? "décollage piste $1\n" : ''));
    for (my $index = 1; $index < @route; $index++) {
        $leg      = [@{$route[$index-1]}[1],@{$route[$index-1]}[2],@{$route[$index]}[1],@{$route[$index]}[2]];
        $heading  = round (llll2dir_ ($leg));
        $distance = distance_ ($leg) / $div;
        $distance_totale += $distance;
        $distance = round ($distance);

        ETAPE : {
            if (@{$route[$index]}[0] == 2) {    # étape ndb
                if ($version
                &&  $distance * $div > @{$route[$index]}[5]
                &&  (@{$route[$index-1]}[0] == 2 || @{$route[$index-1]}[0] == 3)) {
                    $distance -= round (@{$route[$index]}[5] / $div);
                    prt(sprintf( "\t| ADF %-7s| %-6s  |     --     | $distance\n",
                        @{$route[$index-1]}[4], $heading ))
                        if @{$route[$index-1]}[0] == 2;
                    prt(sprintf( "\t| NAV %-7s| %-6s  | %-10s | $distance\n",
                        @{$route[$index-1]}[4], $heading, round ($heading - @{$route[$index-1]}[5+$version])))
                        if @{$route[$index-1]}[0] == 3;
                    $distance = round (@{$route[$index]}[5] / $div);
                }
                prt(sprintf( "\t| ADF %-7s| %-6s  |     --     | $distance\n",
                    @{$route[$index]}[4], $heading ));
                &$sous_fonction ("@{$route[$index]}[6 + $version] - @{$route[$index]}[7 + $version]");
                last ETAPE;
            }
            if (@{$route[$index]}[0] == 3) {    # étape vor
                @{$route[$index]}[4] /= 100;
                if ($version
                &&  $distance * $div> (@{$route[$index]}[5]-5)
                &&  (@{$route[$index-1]}[0] == 2 || @{$route[$index-1]}[0] == 3)) {
                    $distance -= round (@{$route[$index]}[5] / $div);
                    prt(sprintf( "\t| ADF %-7s| %-6s  |     --     | $distance\n",
                        @{$route[$index-1]}[4], $heading ))
                        if @{$route[$index-1]}[0] == 2;
                    prt(sprintf( "\t| NAV %-7s| %-6s  | %-10s | $distance\n",
                        @{$route[$index-1]}[4], $heading, round ($heading - @{$route[$index-1]}[5+$version])))
                        if @{$route[$index-1]}[0] == 3;
                    $distance = round (@{$route[$index]}[5] / $div);
                }
                prt(sprintf("\t| NAV %-7s| %-6s  | %-10s | $distance\n", @{$route[$index]}[4], $heading, round ($heading - @{$route[$index]}[5+$version])));
                &$sous_fonction ("@{$route[$index]}[6 + $version] - @{$route[$index]}[7 + $version]");
                last ETAPE;
            }
            if (@{$route[$index]}[0] == 65) {   # étape fix
                prt(sprintf( "\t| FIX        | %-6s  |     --     | $distance\n", $heading));
                &$sous_fonction ("@{$route[$index]}[6 + $version]");
                last ETAPE;
            }
            if (@{$route[$index]}[0] == 66) {   # étape gps
                prt(sprintf( "\t| GPS        | %-6s  |   --   | $distance\n", $heading ));
                &$sous_fonction ("GPS - [@{$route[$index]}[1] , @{$route[$index]}[2]]");
                last ETAPE;
            }
            if (@{$route[$index]}[0] == 1) {    # aéroport de d'arrivée
                my ($localizer, $piste);
                if ($arrivee[0] =~ /\[RW(...)\s*/) {
                    $piste = $1;
                    $localizer = "RW $piste";
                    foreach (@{$arrivee[4]}) {
                        $localizer = "ILS $_->[2]" if (($_->[0] == 4 || $_->[0] == 5) && $_->[1] eq $piste);
                    }
                    prt(sprintf( "\t| %-10s | %-6s  | %-10s | $distance\n", $localizer, $heading, "RW $piste"));
                } else {
                    foreach (@{$arrivee[4]}) {
                        if ($_->[0] == 10) {
                            $piste = "RW $_->[3]" ;
                            prt(sprintf( "\t| %-10s | %-6s  | %-10s | $distance\n", $piste, $heading, $piste));
                        }
                        elsif ($_->[0] == 4 || $_->[0] == 5) {
                            ($localizer, $piste) = ("ILS $_->[2]", "RW $_->[1]");
                            prt(sprintf( "\t| %-10s | %-6s  | %-10s | $distance\n", $localizer, $heading, $piste));
                        }
                    }
                }
                &$sous_fonction ("$arrivee[4]->[0]->[4] - $arrivee[4]->[0]->[5]");
                last ETAPE;
            }
        }
    }
    $leg = [$depart[2], $depart[3], $arrivee[2], $arrivee[3]];
    prt(sprintf( "\ndistance totale parcourue: %s %s (vol direct: %s)\n\n", round ($distance_totale), ($km)? 'km':'nm', round (distance_ ($leg) / $div)));

    if ($com_app) {
        &$sous_fonction ("Fréquences utiles pour l'approche");
        foreach (@{$arrivee[4]}) { 
            prt(sprintf ("$_->[@{$_}-1]: %s\n", $_->[1]/100))
                if ($_->[0] >= 50 && $_->[0] <= 59 && $_->[@{$_}-1] ne 'DEP');
        }
    }
}

#######################
# FONCTION PRINCIPALE #
#######################
sub main () {
    # exit 1;
    # si aucune option n'est demandée ou si l'option -h est appelée, on affiche l'aide et on quitte
    if (!$options || $help) {
        prt( $texte_aide );
        exit;
    }

    prt("Plan de vol ".$depart[1]." to ".$arrivee[1]."... moment...\n");

    if ($wpt ) {
        if (! -d "$FGROOT/Routes") {
            prt("Error: Directory $FGROOT/Routes does NOT exist! Create it first...\n");
            exit 1;
        }
    }

    # si le départ et l'arrivée ont été trouvés on commence la construction
    # du plan de vol, sinon on affiche un message d'erreur.
    (configure_extremite (\@depart, \$sid, \$sidx ) &&
     configure_extremite (\@arrivee,\$star,\$starx)) ? plan_de_vol : prt(sprintf($erreur));

    # redirection du résultat selon les choix
    sortie_standard if (!$no_stdout );
    fichier_csv if ($CSVFILE    );
    fichier_wp  if ($wpt );
    fichier_xg  if ($XGFILE );
} main;

# eof

index -|- top

checked by tidy  Valid HTML 4.01 Transitional