PFD (Primary Flight Display) pour Flightgear - ESP32

Horizon artificiel +Compas, Altitude, Vitesse, réglages autopilot, ILS pour Flightgear, simulateur de vol Open Source. Puissance ESP32.
Table des matières :
1 - Un aperçu en vidéo (LFMT)
2 - Vue d'ensemble de cette réalisation :
3 - Le schéma
4 - Le programme pour l'ESP32 à compiler avec l'IDE Arduino
5 - Le principe
6 - L'afficheur utilisé :
7 - Dans la jungle des afficheurs
8 - Utilité du panel ESP32
9 - Evolution
10 - Circuit imprimé
11 - Le boitier imprimé en 3D
12 - Disposition des éléments sur le circuit imprimé
13 - Captures d'écran sur SDcard
14 - Evolution 2
15 - l'ILS est fonctionnel
16 - Affichage de l'orientation de la piste
17 - Fonction Autoland (Atterrissage automatique)
18 - Deuxième instrument : ND (Navigation Display)
19 - Vers un troisième instrument ?
20 - Code source de l'instrument ND (Navigateur Display)
21 - Planche de bord
22 - La voici la planche de bord
23 - Utilisation
24 - Un troisième ESP32 gérant des boutons
25 - Le panel des boutons
26 - Les boutons sont dans la boite
27 - Code source du panel des boutons (SW)
28 - Sérigraphie
29 - Un pas de plus vers une planche de bord
30 - Nouvelle version, nouvelles fonctions...
31 - Ecrans de la version 17.3
32 - Version 18.0
33 - Schéma de la version 18.0 du module ND
34 - Version 19.0
35 - Version 21.0
36 - version 24.0
37 - Version 25.0 - Plan des installations
38 - version 28.0
39 - Un nouveau module ! le MCDU
40 - Détail du MCDU
41 - Code source du MCDU
42 - Le fichier d'entête MCDU.h
43 - Documents et code source complet :
44 - Utiliser Geany comme éditeur
45 - Voici un exemple de l'édition du code source avec Geany
46 - Vidéo - Décollage LFMT
47 - Le Lac du Salagou

Je fais de fréquentes mises à jour des pages. Ce bouton permet de raffraichir la version mise en cache dans votre navigateur (et rien d'autre !!!)

1 Un aperçu en vidéo (LFMT)

L'ESP32 se connecte au programme de simulation de vol FlightGear par liaison série USB et permet, outre l'affichage des paramètres de vol, de gérer le pilote automatique de Flightgear en temps réel, et de contrôler totalement l'approche et atterrissage la piste choisie.

Cette vidéo est un montage montrant, en incrustation, le fonctionnement de cette réalisation et le comportement de l'avion, ici le Citation X, lors d'un vol complet : Décollage de LFMT, mise en palier, engagement de l'autopilot de FG, puis tour de piste vent arrière, approche et posé ILS totalement automatique.


.

Les vidéos sont un peu "crénelées" afin d'éviter un fichier trop gros, mais bien entendu, sur le PC sous Linux, avec un écran FUll HD, l'image est bien plus belle. Vous avez quand même intérêt à visionner celle-ci en tout écran.

Concernant les paysages : il en est de bien plus beau (dans Flightgear j'entends) que celui-ci, je pense à la Corse (après mise à jour de la scène), ou Annecy, voire Tarbes, c'est à dire près des montagnes (pour ce qui concerne la France)...

2 Vue d'ensemble de cette réalisation :

L'intégration dans un boîtier maison (imprimé en 3D) est prévu, avec peut être d'autres instruments de bord.

3 Le schéma

A noter que les ports GPIO 34 et 35 ne sont pas pourvus de résistances de rappel à VCC (Pull-up) internes dans l'ESP32, d'où l'ajout des deux résistances externes de 10k.
La saisie des paramètres pour l'autopilot se fait avec deux classiques encodeurs rotatifs. Les quatre capas de 10nF sont en cms à souder au au plus près des encodeurs (hors implantation du board donc).

Il ne reste plus beaucoup de broches libres sur le module ESP32 !

4 Le programme pour l'ESP32 à compiler avec l'IDE Arduino

CODE SOURCE du PFD en C++
  1. //
  2. // ==================================
  3. String version="31.5";
  4. // ==================================
  5.  
  6. /*
  7. PFD.ino - Primary Flight Display pour Flightgear et ESP32 - version Afficheur TFT 480x320
  8. Ne concerne pas un avion réel ! (ni ULM...)
  9. Fonctionne avec simulateur FlightGear sous Linux et avec l'avion Citation X (mon préféré!)
  10. Les autres avions ont un autopilot différent et donc une "Property tree" différente, il faudrait adapter le programme
  11. - en particulier ne fonctionne pas tel quel avec les B7xx ni les A3xx
  12.  
  13. par Silicium628
  14.  
  15. */
  16. /**---------------------------------------------------------------------------------------
  17. Logiciel libre et gratuit : Pour les #includes issus de l'univers Arduino (que je ne fournis pas), il faut voir au cas par cas.
  18. (drivers d'affichage en particulier)
  19.  
  20. ---------------------------------------------------------------------------------------
  21. De petites images à placer sur la SDcard centrées sur les aérodromes proviennent de OpenStreetMap
  22.  
  23. OpenStreetMap® est un ensemble de données ouvertes,
  24. disponibles sous la licence libre Open Data Commons Open Database License (ODbL)
  25. accordée par la Fondation OpenStreetMap (OSMF).
  26.  
  27. Voir ce lien pour plus de détails :
  28. https://www.openstreetmap.org/copyright
  29. --------------------------------------------------------------------------------------**/
  30.  
  31. /*=====================================================================================================
  32. CONCERNANT L'AFFICHAGE TFT : connexion :
  33.  
  34. ( Pensez à configurer le fichier User_Setup.h de la bibliothèque ~/Arduino/libraries/TFT_eSPI/ )
  35.  
  36. les lignes qui suivent ne sont q'un commentaire pour vous indiquer la config à utiliser
  37. placée ici, elle ne sont pas fonctionnelles
  38. Il FAUT modifier le fichier User_Setup.h installé par le système Arduino dans ~/Arduino/libraries/TFT_eSPI/
  39.  
  40. // ESP32 pins used for the parallel interface TFT
  41. #define TFT_CS 27 // Chip select control pin
  42. #define TFT_DC 14 // Data Command control pin - must use a pin in the range 0-31
  43. #define TFT_RST 26 // Reset pin
  44.  
  45. #define TFT_WR 12 // Write strobe control pin - must use a pin in the range 0-31
  46. #define TFT_RD 13
  47.  
  48. #define TFT_D0 16 // Must use pins in the range 0-31 for the data bus
  49. #define TFT_D1 4 // so a single register write sets/clears all bits
  50. #define TFT_D2 2 // 23
  51. #define TFT_D3 22
  52. #define TFT_D4 21
  53. #define TFT_D5 15 // 19
  54. #define TFT_D6 25 // 18
  55. #define TFT_D7 17
  56. =====================================================================================================
  57.  
  58. Notes :
  59. - Si les data de FG ne sont pas reçues, il faut vérifier que le PFD est bien connecté sur le port USB0 (et pas USB1 ou autre...)
  60. - Le module PFD doit impérativement être lancé et connecté à l'USB_0 avant de lancer Flightgear et non l'inverse
  61. sinon le dialogue USB avec l'ordinateur ne se rera pas.
  62. */
  63.  
  64. // v14 la fonction Autoland() utilise le glide pour la descente et non plus une altitude calculée théorique
  65. // v15.2 modif du fichier hardware4.xml (nav-distance)
  66. // v15.3 revu autoland - possibilité de poser auto si localizer seul (aérodrome non équipé de glide de glide).
  67. // v15.6 nombreuses modifs dans tous les fichiers, dont fichier FG_data.h
  68. // v16.0 prise en charge du module "SW" (boutons poussoirs) par WiFi
  69. // v16.2 prise en charge des 2 nouveaux boutons (target_speed) + le fichier "hardware4.xml" a été modifié en conséquence
  70. // v20.0 autoland possible (par GPS au lieu de l'ILS) même pour aérodromes non équipé ILS
  71. // v21.0 autoland toujours calculé par GPS, avec prise en compte de l'altitude de l'aérodrome
  72. // v22.0 modifs fonction autolang & fichier 'hardware4.xml' (prise en charge du trim de profondeur, voir 'desengage_autoland()'
  73. // v24.2 tous les calculs de position et de direction se font directement avec les coordonnées GPS,
  74. // sans passer par système LAMBERT
  75. // v26.0 prise en compte de la longueur de la piste pour l'autolanding
  76. // v26.2 asservissement (facultatif) de la gouverne de direction et de la roue avant au décollage et à l'atterrissage
  77. // v30.3 le module MCDU associé à ce PFD et au module ND permettent :
  78. // -un décollage entièrement automatique, avec engagement automatique de l'autopilot et la mise en palier
  79. // -une route automatique vers un point d'entrée en finale situé à 10NM d'une autre piste choisie pendant le vol
  80. // -la gestion de la finale avec pente à 5%, l'arrondi, le touché de roues sur train principal et le guidage en lacet sur la piste
  81. // Un aérodrome m'a posé quelques soucis : LFLB Chambéry. La finale se fait avec un pente nettement plus accentuée,
  82. // au dessus du lac, en évitant d'abimer la montagne ! Le point d'entrée en finale se situe alors
  83. // à 8 NM de la piste depuis une hauteur + importante (voir le fichier FG_data.h)
  84. // v30.9 entré-sortie auto du train d'atterrissage (et modif dans le fichie "hardware4.xml")
  85. // v31.0 gestion des freins (freine automatiquement à l'atterrissage)
  86.  
  87.  
  88. /* le numéro de version doit être identique à celui du ND (Navigation Display) et des autres fichiers sources */
  89.  
  90.  
  91. /** un peu de théorie : ****************************
  92.  
  93. ALTITUDE vs HAUTEUR
  94.  
  95. Une hauteur est la distance verticale entre un aéronef et la surface qu'il survole (terre ou eau).
  96.  
  97. Pour exprimer une hauteur, il est défini les hauteurs AGL (Above Ground Level) ou ASFC (Above Surface).
  98. Il s'agit de la hauteur entre l'avion et le sol juste en dessous de sa position. Elle suit donc le relief.
  99.  
  100. Pour exprimer une hauteur au dessus de l'aérodrome, il est défini la hauteur AAL (Above Aerodrome Level).
  101. Il s'agit de la hauteur entre l'avion et le point de référence de l'aérodrome comme s'il était en dessous de la
  102. position de l'appareil (même s'il n'y est pas). Cette hauteur ne suit pas le relief.
  103.  
  104. source : https://aeroclub-narbonne.com/download/2017/04/BASE_ALT.pdf
  105. IVAO TM © ELH FLA septembre 2014
  106. je conseille de lire et relire l'ensemble de ce PDF.
  107.  
  108. *****************************************************/
  109.  
  110.  
  111. #include "PFD.h"
  112. #include <stdint.h>
  113. #include <TFT_eSPI.h> // Hardware-specific library
  114. #include "Free_Fonts.h"
  115.  
  116. #include "FS.h"
  117. #include "SD.h"
  118. #include "SPI.h"
  119.  
  120. #include "FG_data.h"
  121. #include "Fonctions1.h"
  122.  
  123. #include <WiFi.h> // Ce PFD est un serveur WiFi
  124. #include "ESPAsyncWebServer.h"
  125.  
  126.  
  127. const char* ssid = "PFD_srv";
  128. const char* password = "72r4TsJ28";
  129.  
  130. AsyncWebServer server(80); // Create AsyncWebServer object on port 80
  131.  
  132. String argument_recu1;
  133. String argument_recu2;
  134. String argument_recu3;
  135.  
  136. TFT_eSprite SPR_E = TFT_eSprite(&TFT480); // Declare Sprite object "SPR_11" with pointer to "TFT" object
  137. TFT_eSprite SPR_N = TFT_eSprite(&TFT480);
  138. TFT_eSprite SPR_O = TFT_eSprite(&TFT480);
  139. TFT_eSprite SPR_S = TFT_eSprite(&TFT480);
  140.  
  141. TFT_eSprite SPR_trajectoire = TFT_eSprite(&TFT480);
  142.  
  143. VOYANT voyant_L; // Localizer (azimut)
  144. VOYANT voyant_G; // Glide (hauteur)
  145. VOYANT voyant_APP; // Approche auto (= attéro ILS)
  146. VOYANT voyant_route;
  147. VOYANT voyant_RD; // Auto rudder (asservissement de la gouverne de direction (lacet) et de la roulettes de nez au sol)
  148. VOYANT voyant_ATT; // atterrissage en cours
  149.  
  150. Led Led1;
  151. Led Led2;
  152. Led Led3;
  153. Led Led4;
  154. Led Led5;
  155.  
  156.  
  157. uint16_t hauteur_mini_autopilot = 10; // ou 100 ou 300 ou 500 à tester...
  158.  
  159.  
  160. int16_t Ax_actu, Ay_actu;
  161. int16_t Bx_actu, By_actu;
  162.  
  163. //position et dimensions de l'horizon artificiel
  164. #define HA_x0 210
  165. #define HA_y0 130
  166. #define HA_w 120 // demi largeur
  167. #define HA_h 100 // demi hauteur
  168.  
  169. #define x_autopilot 320
  170.  
  171. // Width and height of sprite
  172. #define SPR_W 14
  173. #define SPR_H 14
  174.  
  175.  
  176. /*
  177. #define taille1 100000 //480*320*2
  178. uint8_t data_ecran[taille1];
  179. */
  180.  
  181. uint32_t memo_micros = 0;
  182. uint32_t temps_ecoule;
  183. uint16_t nb_secondes=0;
  184. uint8_t nb_acqui;
  185.  
  186. String parametre; //garde en mémoire les données reçues par USB entre les passages dans la fonction "void acquisitions()"
  187.  
  188. uint8_t landing_light1=0;
  189. uint8_t landing_light2=0;
  190.  
  191. float roulis;
  192. float tangage;
  193.  
  194.  
  195. float altitude_GPS_float;
  196. int32_t altitude_GPS; // accepte les valeurs négatives (par exemple si QNH mal réglé avant décollage)
  197. int32_t hauteur_AAL; // (Above Aerodrome Level)
  198.  
  199. #define ASL 0
  200. #define AAL 1
  201.  
  202. uint8_t mode_affi_hauteur = ASL;
  203.  
  204. int32_t gnd_elv; // feet
  205. int32_t vitesse; // kts
  206. int32_t memo_vitesse;
  207. int16_t target_speed =180; // consigne de vitesse pour l'autopilot
  208. int16_t dV;
  209. int16_t acceleration;
  210. int16_t vspeed; // vitesse verticale
  211.  
  212. float cap; // en degrés d'angle; direction actuelle du nez de l'avion
  213.  
  214. int16_t hdg1 = 150; // en degrés d'angle; consigne cap = Heading (HDG) Bug
  215. int16_t memo_hdg1;
  216. uint8_t flag_refresh_hdg=0;
  217. uint8_t flag_traiter_SW=0;
  218. uint8_t flag_traiter_MCDU=0;
  219.  
  220.  
  221. float lat_avion; // WGS84
  222. float lon_avion; // WGS84
  223.  
  224. float px_par_km;
  225. float px_par_NM;
  226.  
  227.  
  228. //Les points ptAA et ptBB sont les points d'insertion en finale, situés à 10NM de chaque côté dans l'axe de la piste
  229. //Leurs coordonnées sont calculées en fonction de celles de la piste
  230. float lon_ptAA;
  231. float lat_ptAA;
  232.  
  233. float lon_ptBB;
  234. float lat_ptBB;
  235.  
  236. float lon_pti;
  237. float lat_pti;
  238.  
  239. float GPS_distance_piste; // en NM
  240. float memo_GPS_distance_piste; // servira à savoir si on se rapproche du centre de la piste ou si on s'en éloigne après
  241. // l'avoir dépassé (lors du décollage)
  242.  
  243. float GPS_distance_ptAA; // point situé à 10 ou 15 NM dans l'axe de la piste pour amorcer l'approche
  244. float GPS_distance_ptBB; // point situé à 10 ou 15 NM dans l'axe de la piste, dans l'autre sens
  245. float GPS_distance_pti; // point quelconque
  246.  
  247. float erreur_axe=0;
  248.  
  249.  
  250. #define sens_AB 0
  251. #define sens_BA 1
  252. uint8_t sens_app_effectif; // effectif pour l'attero (ne concerne pas le décollage)
  253.  
  254. float lat_centre_pst;
  255. float lon_centre_pst;
  256. float longueur_piste;
  257.  
  258. float orient_pisteAB;
  259. float orient_pisteBA;
  260.  
  261. float GPS_azimut_piste;
  262. float GPS_azimut_ptAA;
  263. float GPS_azimut_ptBB;
  264. float GPS_azimut_pti;
  265.  
  266.  
  267.  
  268. char extremite_pst ='X'; // le bout le plus éloigné lors de l'approche, = 'A' ou 'B' sert aussi au décollage (au roulage)
  269. // ce paramètre est calculé en fonction de la position réelle de l'avion lors de la prise de décision
  270.  
  271. uint8_t choix_aleatoire;
  272.  
  273.  
  274. int16_t asel1 = 30; // consigne altitude ('niveau de vol' en centaines de pieds) 30 -> 3000ft (ASL)
  275. float climb_rate=0; // taux de montée (négatif pour descendre - sert pour attérissage automatique)
  276.  
  277. float joystick1; // valeur reçue de Flightgear par la liaison USB (lue dans le properties tree de FG)
  278. float trim_elevator;
  279. float elevator; // valeur à envoyer à FG, qui fixera la position de la gouverne de profondeur (val <0 pour monter)
  280. float throttle;
  281. int8_t flaps=0; // 0..4
  282. float speedbrake=0; // 0 = rentré, 1 = sorti
  283. bool gear_down=1;
  284. float brake_left;
  285. float brake_right;
  286.  
  287. float rudder=0;
  288. float rudder_manuel; // fonction directe du potentiomètre
  289.  
  290.  
  291. uint8_t view_number=0;
  292.  
  293. String locks_type; // "ALT" ou "VS"
  294. String AP_status; // "" ou "AP" permet d'engager ou de désengager l'autopilot de FlightGear
  295. bool speed_ctrl;
  296.  
  297. int16_t num_bali=0;
  298. int16_t memo_num_bali=0;
  299.  
  300. uint8_t flag_SDcardOk=0;
  301. uint32_t data_ok=0; // ce n'est pas un flag
  302. //uint8_t gs_ok=0;
  303. uint8_t QNH_ok=0;
  304.  
  305. uint8_t flag_1er_passage =1;
  306. uint8_t attente_data=1;
  307. uint8_t inc_num_pt1_autorisee=1;
  308.  
  309. int16_t loc=0; // localizer
  310. int16_t memo_loc=0;
  311.  
  312. float memo_R2;
  313. int16_t memo_y0;
  314.  
  315. uint16_t memo_x_avion=0; // pour fonction "affi_approche()"
  316. uint16_t memo_y_avion=0;
  317.  
  318. const int bouton1 = 36; // attention: le GPIO 36 n'a pas de R de pullup interne, il faut en câbler une (10k) au +3V3
  319. bool bouton1_etat;
  320. bool memo_bouton1_etat;
  321.  
  322. const int bouton2 = 39; // attention: le GPIO 39 n'a pas de R de pullup interne, il faut en câbler une (10k) au +3V3
  323. bool bouton2_etat;
  324. bool memo_bouton2_etat;
  325.  
  326. String switches; // boutons connectés au 3eme ESP32 (SW), reçus par WiFi
  327. uint16_t v_switches=0;
  328. uint16_t memo_v_switches=0;
  329.  
  330. String switches_ND; // boutons connectés au 2eme ESP32 (ND), reçus par WiFi
  331. uint16_t v_switches_ND=0;
  332. uint16_t memo_v_switches_ND=0;
  333.  
  334. String bt_MCDU; // boutons connectés au 4eme ESP32 (MCDU), reçus par WiFi
  335. uint16_t v_bt_MCDU=0;
  336. uint16_t memo_v_bt_MCDU=0;
  337.  
  338. uint8_t options_route=0;
  339. uint8_t num_pti=1;
  340.  
  341. String msg_to_send = "null";
  342.  
  343. String potar1;
  344. int16_t v_potar1=0; // peut être négatif
  345.  
  346. // deux encodeurs rotatifs pas à pas
  347. const int rot1a = 32; // GPIO32 -> câbler une R au +3V3
  348. const int rot1b = 33; // GPIO33 -> câbler une R au +3V3
  349.  
  350. const int rot2a = 35; // GPIO35 -> câbler une R au +3V3
  351. const int rot2b = 34; // GPIO34 -> câbler une R au +3V3
  352.  
  353.  
  354. //const int led1 = 25; // GPIO15
  355.  
  356.  
  357. #define TEMPO 5 // tempo anti-rebond pour l'acquisition des encodeurs rotatifs
  358. volatile uint32_t timer1 = 0;
  359. volatile uint32_t timer2 = 0;
  360.  
  361. uint16_t compteur1;
  362.  
  363. uint8_t heures=0;
  364. uint8_t minutes=0;
  365. uint8_t secondes=0;
  366.  
  367. float v_test1=-1.0;
  368.  
  369.  
  370. void RAZ_variables()
  371. {
  372. roulis=0;
  373. tangage=0;
  374. altitude_GPS=0;
  375. gnd_elv=0;
  376. vitesse=0;
  377. vspeed=0;
  378. cap=0;
  379. memo_hdg1=0;
  380.  
  381. loc=0;
  382. memo_loc=0;
  383. }
  384.  
  385.  
  386.  
  387. /** ***********************************************************************************
  388. IMAGE.bmp
  389. ***************************************************************************************/
  390.  
  391. /**
  392. Rappel et décryptage de la fonction Color_To_565 : (elle se trouve dans le fichier LCDWIKI_KBV.cpp)
  393.  
  394. //Pass 8-bit (each) R,G,B, get back 16-bit packed color
  395.  
  396. uint16_t Color_To_565(uint8_t r, uint8_t g, uint8_t b)
  397. {
  398. return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((b & 0xF8) >> 3);
  399. }
  400.  
  401. 0xF8 = 11111000
  402. 0xFC = 11111100
  403.  
  404. (r & 0xF8) -> 5 bit de gauche de r (on ignore donc les 3 bits de poids faible)
  405. (g & 0xFC) -> 6 bit de gauche de g (on ignore donc les 2 bits de poids faible)
  406. (b & 0xF8) -> 5 bit de gauche de b (on ignore donc les 3 bits de poids faible)
  407.  
  408. rrrrr---
  409. gggggg--
  410. bbbbb---
  411.  
  412. après les décallages on obtient les 16 bits suivants:
  413.  
  414. rrrrr---========
  415.   gggggg--===
  416.   ===bbbbb
  417.  
  418. soit après le ou :
  419.  
  420. rrrrrggggggbbbbb
  421.  
  422. calcul de la Fonction inverse :
  423. RGB565_to_888
  424. **/
  425.  
  426.  
  427. void RGB565_to_888(uint16_t color565, uint8_t *R, uint8_t *G, uint8_t *B)
  428. {
  429. *R=(color565 & 0xFFFFF800) >> 8;
  430. *G=(color565 & 0x7E0) >> 3;
  431. *B=(color565 & 0x1F) << 3 ;
  432. }
  433.  
  434.  
  435.  
  436. /** -----------------------------------------------------------------------------------
  437. CAPTURE D'ECRAN vers SDcard
  438. /** ----------------------------------------------------------------------------------- */
  439.  
  440. void write_TFT_on_SDcard() // enregistre le fichier .bmp
  441. {
  442.  
  443. //TFT480.setTextColor(VERT, NOIR);
  444. //TFT480.drawString("CP", 450, 300);
  445.  
  446. if (flag_SDcardOk==0) {return;}
  447.  
  448. String s1;
  449. uint16_t ys=200;
  450. TFT480.setFreeFont(FF1);
  451. TFT480.setTextColor(JAUNE, NOIR);
  452.  
  453. uint16_t x, y;
  454. uint16_t color565;
  455. uint16_t bmp_color;
  456. uint8_t R, G, B;
  457.  
  458. if( ! SD.exists("/bmp/capture2.bmp"))
  459. {
  460. TFT480.fillRect(0, 0, 480, 320, NOIR); // efface
  461. TFT480.setTextColor(ROUGE, NOIR);
  462. TFT480.drawString("NO /bmp/capture2.bmp !", 100, ys);
  463. delay(300);
  464. TFT480.fillRect(100, ys, 220, 20, NOIR); // efface
  465. return;
  466. }
  467.  
  468.  
  469. File File1 = SD.open("/bmp/capture2.bmp", FILE_WRITE); // ouverture du fichier binaire (vierge) en écriture
  470. if (File1)
  471. {
  472. /*
  473. Les images en couleurs réelles BMP888 utilisent 24 bits par pixel:
  474. Il faut 3 octets pour coder chaque pixel, en respectant l'ordre de l'alternance bleu, vert et rouge.
  475. */
  476. uint16_t bmp_offset = 138;
  477. File1.seek(bmp_offset);
  478.  
  479.  
  480. TFT480.setTextColor(VERT, NOIR);;
  481.  
  482. for (y=320; y>0; y--)
  483. {
  484. for (x=0; x<480; x++)
  485. {
  486. color565=TFT480.readPixel(x, y);
  487.  
  488. RGB565_to_888(color565, &R, &G, &B);
  489.  
  490. File1.write(B); //G
  491. File1.write(G); //R
  492. File1.write(R); //B
  493. }
  494.  
  495. s1=(String) (y/10);
  496. TFT480.fillRect(450, 300, 20, 20, NOIR);
  497. TFT480.drawString(s1, 450, 300);// affi compte à rebour
  498. }
  499.  
  500. File1.close(); // referme le fichier
  501. TFT480.fillRect(450, 300, 20, 20, NOIR); // efface le compte à rebour
  502. }
  503. }
  504.  
  505. /** ----------------------------------------------------------------------------------- */
  506.  
  507.  
  508.  
  509. void Draw_arc_elliptique(uint16_t x0, uint16_t y0, int16_t dx, int16_t dy, float alpha1, float alpha2, uint16_t couleur)
  510. // alpha1 et alpha2 en radians
  511. {
  512. /*
  513. REMARQUES :
  514. -cette fonction permet également de dessiner un arc de cercle (si dx=dy), voire le cercle complet
  515. - dx et dy sont du type int (et pas uint) et peuvent êtres négafifs, ou nuls.
  516. -alpha1 et alpha2 sont les angles (en radians) des caps des extrémités de l'arc
  517. */
  518. uint16_t n;
  519. float i;
  520. float x,y;
  521.  
  522. i=alpha1;
  523. while(i<alpha2)
  524. {
  525. x=x0+dx*cos(i);
  526. y=y0+dy*cos(i+M_PI/2.0);
  527. TFT480.drawPixel(x,y, couleur);
  528. i+=0.01; // radians
  529. }
  530. }
  531.  
  532.  
  533. void affi_rayon1(uint16_t x0, uint16_t y0, uint16_t rayon, double angle, float pourcent, uint16_t couleur_i, bool gras)
  534. {
  535. // trace une portion de rayon de cercle depuis 100%...à pourcent du rayon du cercle
  536. // angle en radians - sens trigo
  537. float x1, x2;
  538. float y1, y2;
  539.  
  540. x1=x0+rayon* cos(angle);
  541. y1=y0-rayon* sin(angle);
  542.  
  543. x2=x0+pourcent*rayon* cos(angle);
  544. y2=y0-pourcent*rayon* sin(angle);
  545.  
  546. TFT480.drawLine(x1, y1, x2, y2, couleur_i);
  547. if (gras)
  548. {
  549. TFT480.drawLine(x1, y1-1, x2, y2-1, couleur_i);
  550. TFT480.drawLine(x1, y1+1, x2, y2+1, couleur_i);
  551. }
  552. }
  553.  
  554.  
  555.  
  556. void affi_rayon2(uint16_t x0, uint16_t y0, float r1, float r2, float angle_i, uint16_t couleur_i, bool gras)
  557. {
  558. // trace une portion de rayon de cercle entre les distances r1 et r2 du centre
  559. // angle_i en degrés décimaux - sens trigo
  560.  
  561. float angle =angle_i/57.3; // (57.3 ~ 180/pi)
  562. int16_t x1, x2;
  563. int16_t y1, y2;
  564.  
  565. x1=x0+int16_t(r1* cos(angle));
  566. y1=y0-int16_t(r1* sin(angle));
  567.  
  568. x2=x0+int16_t(r2* cos(angle));
  569. y2=y0-int16_t(r2* sin(angle));
  570.  
  571. if ((x1>0) && (x2>0) && (y1>0) && (y2>0) && (x1<480) && (x2<480) && (y1<320) && (y2<320) )
  572. {
  573. TFT480.drawLine(x1, y1, x2, y2, couleur_i);
  574.  
  575. if (gras)
  576. {
  577. TFT480.drawLine(x1, y1-1, x2, y2-1, couleur_i);
  578. TFT480.drawLine(x1, y1+1, x2, y2+1, couleur_i);
  579. }
  580. }
  581. //TFT480.fillCircle(x2, y2, 2, ROUGE);
  582. }
  583.  
  584.  
  585.  
  586.  
  587. void affi_tiret_H(uint16_t x0, uint16_t y0, uint16_t r, double angle_i, uint16_t couleur_i)
  588. {
  589. // trace un tiret perpendiculaire à un rayon de cercle de rayon r
  590. // angle_i en degrés décimaux
  591.  
  592. float angle =angle_i/57.3; // (57.3 ~ 180/pi)
  593. float x1, x2;
  594. float y1, y2;
  595.  
  596. x1=x0+(r)* cos(angle-1);
  597. y1=y0-(r)* sin(angle-1);
  598.  
  599. x2=x0+(r)* cos(angle+1);
  600. y2=y0-(r)* sin(angle+1);
  601.  
  602. TFT480.drawLine(x1, y1, x2, y2, couleur_i);
  603. }
  604.  
  605.  
  606.  
  607. void affi_pointe(uint16_t x0, uint16_t y0, uint16_t r, double angle_i, float taille, uint16_t couleur_i)
  608. {
  609. // trace une pointe de flèche sur un cercle de rayon r
  610. // angle_i en degrés décimaux - sens trigo
  611.  
  612. float angle =angle_i/57.3; // (57.3 ~ 180/pi)
  613. int16_t x1, x2, x3;
  614. int16_t y1, y2, y3;
  615.  
  616. x1=x0+r* cos(angle); // pointe
  617. y1=y0-r* sin(angle); // pointe
  618.  
  619. x2=x0+(r-7)* cos(angle-taille); // base A
  620. y2=y0-(r-7)* sin(angle-taille); // base A
  621.  
  622. x3=x0+(r-7)* cos(angle+taille); // base B
  623. y3=y0-(r-7)* sin(angle+taille); // base B
  624.  
  625. TFT480.fillTriangle(x1, y1, x2, y2, x3, y3, couleur_i);
  626. }
  627.  
  628.  
  629. void affi_rectangle_incline(uint16_t x0, uint16_t y0, uint16_t r, double angle_i, uint16_t couleur_i)
  630. {
  631. //rectangle inscrit dans le cerce de rayon r
  632. // angle_i en degrés décimaux - sens trigo
  633.  
  634. float angle =angle_i/57.3; // (57.3 ~ 180/pi)
  635. int16_t x1, x2, x3, x4;
  636. int16_t y1, y2, y3, y4;
  637. float d_alpha=0.08; // détermine la largeur du rectangle
  638.  
  639. // point 1
  640. x1=x0+r*cos(angle-d_alpha);
  641. y1=y0+r*sin(angle-d_alpha);
  642. // point 2
  643. x2=x0+r*cos(angle+d_alpha);
  644. y2=y0+r*sin(angle+d_alpha);
  645. // point 3
  646. x3=x0+r*cos(M_PI + angle-d_alpha);
  647. y3=y0+r*sin(M_PI + angle-d_alpha);
  648. // point 4
  649. x4=x0+r*cos(M_PI + angle+d_alpha);
  650. y4=y0+r*sin(M_PI + angle+d_alpha);
  651.  
  652. TFT480.drawLine(x1, y1, x2, y2, couleur_i);
  653. TFT480.drawLine(x2, y2, x3, y3, couleur_i);
  654. TFT480.drawLine(x3, y3, x4, y4, couleur_i);
  655. TFT480.drawLine(x4, y4, x1, y1, couleur_i);
  656.  
  657. }
  658.  
  659.  
  660.  
  661. void affi_indexH(uint16_t x, uint16_t y, int8_t sens, uint16_t couleur)
  662. {
  663. // petite pointe de flèche horizontale
  664. // sens = +1 ou -1 pour orienter la pointe vers la droite ou vers la gauche
  665.  
  666. TFT480.fillTriangle(x, y-4, x, y+4, x+8*sens, y, couleur);
  667. }
  668.  
  669.  
  670.  
  671. void affi_indexV(uint16_t x, uint16_t y, int8_t sens, uint16_t couleur)
  672. {
  673. // petite pointe de flèche verticale
  674. // sens = +1 ou -1 pour orienter la pointe vers le haut ou vers le bas
  675.  
  676. TFT480.fillTriangle(x-4, y, x+4, y, x, y+8*sens, couleur);
  677. }
  678.  
  679.  
  680.  
  681. float degTOrad(float angle)
  682. {
  683. return (angle * M_PI / 180.0);
  684. }
  685.  
  686.  
  687.  
  688. void init_affi_HA()
  689. {
  690. TFT480.fillRect(HA_x0-HA_w, HA_y0-HA_h-1, 2*HA_w, HA_h+1, HA_CIEL);
  691. TFT480.fillRect(HA_x0-HA_w, HA_y0-HA_h + HA_h, 2*HA_w, HA_h, HA_SOL);
  692.  
  693. }
  694.  
  695.  
  696.  
  697.  
  698. void dessine_avion() // sous forme d'équerres horizontales noires entourées de blanc
  699. {
  700. // aile gauche
  701. TFT480.fillRect(HA_x0-102, HA_y0-3, 60, 10, BLANC); //H contour en blanc
  702. TFT480.fillRect(HA_x0-42, HA_y0-3, 10, 19, BLANC); //V
  703.  
  704. TFT480.fillRect(HA_x0-100, HA_y0-1, 60, 5, NOIR); //H
  705. TFT480.fillRect(HA_x0-40, HA_y0-1, 5, 15, NOIR); //V
  706.  
  707.  
  708. // aile droite
  709. TFT480.fillRect(HA_x0+28, HA_y0-3, 64, 10, BLANC); //H contour en blanc
  710. TFT480.fillRect(HA_x0+28, HA_y0-3, 10, 19, BLANC); //V
  711.  
  712. TFT480.fillRect(HA_x0+30, HA_y0-1, 60, 5, NOIR); //H
  713. TFT480.fillRect(HA_x0+30, HA_y0-1, 5, 15, NOIR); //V
  714.  
  715. //carré blanc au centre
  716.  
  717. TFT480.fillRect(HA_x0-4, HA_y0-3, 8, 2, BLANC);
  718. TFT480.fillRect(HA_x0-4, HA_y0-3, 2, 8, BLANC);
  719.  
  720. TFT480.fillRect(HA_x0-4, HA_y0+3, 10, 2, BLANC);
  721. TFT480.fillRect(HA_x0+4, HA_y0-3, 2, 8, BLANC);
  722.  
  723. //affi_dst_NAV();
  724.  
  725. }
  726.  
  727.  
  728. void affiche_chrono()
  729. {
  730. uint16_t x0=200;
  731. uint16_t y0=0;
  732. TFT480.setFreeFont(FM9);
  733. TFT480.setTextColor(JAUNE);
  734. String s1;
  735. ////if(heures<10){s1+="0";}
  736. ////s1+=String(heures);
  737. ////s1+=":";
  738. if(minutes<10){s1+="0";}
  739. s1+=String(minutes);
  740. s1+=":";
  741. if(secondes<10){s1+="0";}
  742. s1+=String(secondes);
  743. TFT480.fillRect(x0, y0, 55, 15, BLEU); //efface
  744. TFT480.drawString(s1, x0, y0);
  745.  
  746. }
  747.  
  748.  
  749. void inc_chrono()
  750. {
  751. secondes++;
  752. if (secondes>59)
  753. {
  754. secondes=0;
  755. minutes++;
  756. if(minutes>59)
  757. {
  758. minutes=0;
  759. heures++;
  760. if (heures>23)
  761. heures=0;
  762. }
  763. }
  764. }
  765.  
  766.  
  767. void RAZ_chrono()
  768. {
  769. heures=0;
  770. minutes=0;
  771. secondes=0;
  772. }
  773.  
  774.  
  775.  
  776. void lign_sep(uint16_t Ax, uint16_t Ay, uint16_t Bx, uint16_t By)
  777. {
  778. // actualise la ligne de séparation ciel-sol
  779.  
  780. TFT480.drawLine(Ax, Ay-1, Bx, By-1, HA_CIEL);
  781. TFT480.drawLine(Ax, Ay, Bx, By, BLANC);
  782. TFT480.drawLine(Ax, Ay+1, Bx, By+1, HA_SOL);
  783. }
  784.  
  785.  
  786.  
  787. void arrondissement_coins()
  788. {
  789.  
  790. // fillTriangle(int32_t x1,int32_t y1, int32_t x2,int32_t y2, int32_t x3,int32_t y3, uint32_t color);
  791.  
  792. //HG
  793. TFT480.fillTriangle(
  794. HA_x0-HA_w, HA_y0-HA_h-1,
  795. HA_x0-HA_w, HA_y0-HA_h+20,
  796. HA_x0-HA_w+20, HA_y0-HA_h-1,
  797. NOIR);
  798.  
  799.  
  800. //----------------------------------------------
  801. //HD
  802. TFT480.fillTriangle(
  803. HA_x0+HA_w, HA_y0-HA_h-1,
  804. HA_x0+HA_w, HA_y0-HA_h+20,
  805. HA_x0+HA_w-20, HA_y0-HA_h-1,
  806. NOIR);
  807.  
  808.  
  809.  
  810. //----------------------------------------------
  811. //BG
  812. TFT480.fillTriangle(
  813. HA_x0-HA_w, HA_y0+HA_h+1,
  814. HA_x0-HA_w, HA_y0+HA_h-20,
  815. HA_x0-HA_w+20, HA_y0+HA_h+1,
  816. NOIR);
  817.  
  818. //----------------------------------------------
  819. //BD
  820. TFT480.fillTriangle(
  821. HA_x0+HA_w, HA_y0+HA_h+1,
  822. HA_x0+HA_w, HA_y0+HA_h-20,
  823. HA_x0+HA_w-20, HA_y0+HA_h+1,
  824. NOIR);
  825.  
  826. }
  827.  
  828.  
  829.  
  830.  
  831. void affi_HA() // Horizon Artificiel
  832. {
  833. String s1;
  834.  
  835. ////String s1=(String) roulis;
  836. ////TFT480.drawString(s1, 400, 20);
  837.  
  838.  
  839. // pivot
  840. int16_t x0=0;
  841. int16_t y0=0;
  842.  
  843. //points d'intersection avec le bord du carré
  844. int16_t Ax, Ay; // sur le bord gauche
  845. int16_t Bx, By; // sur le bord droit
  846. // Le dessin consistera à tracer des segments colorés entre les points A et B
  847.  
  848.  
  849. // roulis -> [-90..+90]
  850.  
  851. // normalisation de la valeur R2 -> toujours >0
  852. float R2 = -1*roulis;
  853. if (R2<0) {R2+=360;} // ce qui est un angle identique, de valeur positive (sens trigo)
  854.  
  855. // le pivot reste centré horizontalement mais se déplace verticalement en fonction du tangage
  856. y0 += 2*tangage;
  857.  
  858. //calcul & memorisation de ces deux facteurs, ce qui évitera 2 calculs de tangente à chaque passage dan la fonction
  859. float tgt_moins = tan(degTOrad(90-R2));
  860. float tgt_plus = tan(degTOrad(90+R2));
  861.  
  862. //-----------------------------------------------------------------------------
  863. // CALCUL COTE DROIT (point B)
  864.  
  865. // calcul du point B d'intersection
  866. Bx=HA_w;
  867. By=y0 + HA_w*tan(degTOrad(R2));
  868.  
  869. //test si le point d'intersection se trouve plus haut que le haut du carré :
  870.  
  871. if(By>HA_h)
  872. {
  873. By=HA_h;
  874. Bx = x0 + (HA_h-y0)*tgt_moins;
  875. }
  876.  
  877. if(By< -HA_h)
  878. {
  879. By= -HA_h;
  880. Bx = x0 + (HA_h+y0)*tgt_plus;
  881. }
  882. //-----------------------------------------------------------------------------
  883. // CALCUL COTE GAUCHE (point A)
  884.  
  885. Ax=-HA_w;
  886. Ay=y0 - HA_w*tan(degTOrad(R2));
  887.  
  888. if(Ay> HA_h)
  889. {
  890. Ay= HA_h;
  891. Ax = x0 + (HA_h-y0)*tgt_moins;
  892. }
  893.  
  894. if(Ay< -HA_h)
  895. {
  896. Ay= -HA_h;
  897. Ax = x0 + (HA_h+y0)*tgt_plus;
  898. }
  899.  
  900.  
  901. //-----------------------------------------------------------------------------
  902. // positionnement de l'ensemble sur l'écran
  903.  
  904. Ax += HA_x0;
  905. Ay += HA_y0;
  906.  
  907. Bx += HA_x0;
  908. By += HA_y0;
  909.  
  910. // pour éviter un tracé hors cadre au premier passage :
  911. if (flag_1er_passage == 1)
  912. {
  913. Ax_actu = Ax;
  914. Ay_actu = Ay;
  915.  
  916. Bx_actu = Bx;
  917. By_actu = By;
  918. flag_1er_passage=0;
  919. }
  920.  
  921.  
  922. //-----------------------------------------------------------------------------
  923. // ligne "verticale" d'inclinaison (tangage)
  924.  
  925. affi_rayon2(HA_x0, HA_y0, 85, -memo_y0, 90-memo_R2, HA_CIEL, false); // efface partie supérieure
  926. affi_rayon2(HA_x0, HA_y0, 85, -y0, 90-R2, BLANC, false); // retrace ligne partie supérieure
  927.  
  928. affi_rayon2(HA_x0, HA_y0, -85,-memo_y0, 90-memo_R2, HA_SOL, false); // efface partie inférieure
  929. affi_rayon2(HA_x0, HA_y0, -85,-y0, 90-R2, VERT, false); // retrace ligne partie inférieure
  930.  
  931. affi_pointe(HA_x0, HA_y0, 85, 90-memo_R2, 0.1, HA_CIEL); // efface
  932. affi_pointe(HA_x0, HA_y0, 85, 90-R2, 0.1, BLANC); // retrace
  933.  
  934.  
  935. //-----------------------------------------------------------------------------
  936. // graduation fixe
  937. TFT480.setFreeFont(FF1);
  938. TFT480.setTextColor(BLANC, GRIS_AF);
  939. TFT480.drawString("30", HA_x0-70, HA_y0-98);
  940. TFT480.drawString("60", HA_x0-120, HA_y0-55);
  941.  
  942. TFT480.drawString("30", HA_x0+60, HA_y0-98);
  943. TFT480.drawString("60", HA_x0+100, HA_y0-55);
  944.  
  945. //-----------------------------------------------------------------------------
  946. // animation de la ligne de séparation horizontale
  947.  
  948. while ((Bx_actu != Bx) || (By_actu != By) || (Ax_actu != Ax) || (Ay_actu != Ay))
  949. {
  950. // déplacements successifs de 1 pixel de chaque extrémités de la ligne
  951.  
  952. //TFT480.drawLine(Bx, By, x2, y2, BLANC);
  953.  
  954. if (Bx_actu < Bx) { Bx_actu++; lign_sep(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  955. if (Bx_actu > Bx) { Bx_actu--; lign_sep(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  956.  
  957. if (Ax_actu < Ax) { Ax_actu++; lign_sep(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  958. if (Ax_actu > Ax) { Ax_actu--; lign_sep(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  959.  
  960. if (By_actu < By) { By_actu++; lign_sep(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  961. if (By_actu > By) { By_actu--; lign_sep(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  962.  
  963. if (Ay_actu < Ay) { Ay_actu++; lign_sep(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  964. if (Ay_actu > Ay) { Ay_actu--; lign_sep(Ax_actu, Ay_actu, Bx_actu, By_actu); }
  965. }
  966.  
  967.  
  968. // graduation roulis qui se déplace angulairement avec la ligne de tangage
  969. for (int8_t n=0; n<4; n++)
  970. {
  971. Draw_arc_elliptique(HA_x0, HA_y0, 20*n, 20*n, degTOrad(80-memo_R2+n), degTOrad(100-memo_R2-n), HA_CIEL); // efface bas
  972. Draw_arc_elliptique(HA_x0, HA_y0, 20*n, 20*n, degTOrad(80-R2+n), degTOrad(100-R2-n), BLANC); // trace bas
  973.  
  974. Draw_arc_elliptique(HA_x0, HA_y0, 20*n, 20*n, degTOrad(260-memo_R2+n), degTOrad(280-memo_R2-n), HA_SOL); // efface haut
  975. Draw_arc_elliptique(HA_x0, HA_y0, 20*n, 20*n, degTOrad(260-R2+n), degTOrad(280-R2-n), BLANC); // trace haut
  976. }
  977.  
  978. memo_R2 = R2;
  979. memo_y0 = y0;
  980.  
  981. //-----------------------------------------------------------------------------
  982. arrondissement_coins();
  983.  
  984. if (read_bit(flags, bit_autoland) == 0)
  985. {
  986. affi_distance_piste();
  987. }
  988. }
  989.  
  990.  
  991.  
  992. void affi_acceleration()
  993. {
  994. // POUR TEST **********
  995. ////String s2= (String) acceleration;
  996. ////TFT480.fillRect(100, 50, 200, 20, TFT_BLACK);
  997. ////TFT480.setFreeFont(FF5);
  998. ////TFT480.setTextColor(BLANC, NOIR);
  999. ////TFT480.drawString("Acceleration=", 100, 50);
  1000. ////TFT480.drawString(s2, 250, 50);
  1001. // ********************
  1002.  
  1003. //barres verticales colorées juste à droite de la vitesse indiquant sa variation
  1004. uint16_t x0=60;
  1005. uint16_t Y_zero=162;
  1006.  
  1007. int16_t dy=0;
  1008.  
  1009. //"fleche" haute
  1010. TFT480.fillRect(x0, 40, 8, Y_zero, GRIS_TRES_FONCE); // efface haut
  1011. if (acceleration > 1)
  1012. {
  1013. dy= acceleration;
  1014.  
  1015. TFT480.fillRect(x0, Y_zero-dy, 8, dy, VERT); // fleche
  1016. }
  1017.  
  1018.  
  1019. //"fleche" basse
  1020. TFT480.fillRect(x0, Y_zero, 8, 150, GRIS_TRES_FONCE); // efface bas
  1021. if (acceleration < -1)
  1022. {
  1023. dy= -acceleration;
  1024.  
  1025. TFT480.fillRect(x0, Y_zero, 8, dy, JAUNE); // fleche
  1026. }
  1027.  
  1028. TFT480.fillRect(x0, Y_zero, 10, 2, BLANC); // tiret horizontal blanc
  1029.  
  1030. TFT480.fillRect(x0, 310, 8, 20, NOIR);
  1031.  
  1032. }
  1033.  
  1034.  
  1035. void bride(int16_t *valeur)
  1036. {
  1037. int16_t y_min =40;
  1038. int16_t y_max =310;
  1039. if (*valeur<y_min) {*valeur=y_min;}
  1040. if (*valeur>y_max) {*valeur=y_max;}
  1041. }
  1042.  
  1043.  
  1044. void affi_switches() // en haut à droite
  1045. {
  1046. TFT480.setTextFont(1);
  1047. TFT480.setTextColor(GRIS);
  1048.  
  1049. TFT480.fillRect(430, 0, 25, 10, NOIR); // efface le nombre précédemment affiché
  1050. TFT480.drawString(switches, 430, 0);
  1051.  
  1052. TFT480.fillRect(430, 10, 25, 10, NOIR); // efface le nombre précédemment affiché
  1053. TFT480.drawString(switches_ND, 430, 10);
  1054. }
  1055.  
  1056.  
  1057.  
  1058. void affi_elevator()
  1059. {
  1060. bargraph_V_float(elevator, 340, 130, JAUNE);
  1061. }
  1062.  
  1063.  
  1064.  
  1065. void affi_rudder()
  1066. {
  1067. TFT480.setTextFont(1);
  1068. TFT480.fillRect(430, 20, 25, 8, NOIR); // efface le nombre précédemment affiché
  1069. TFT480.setTextColor(ORANGE);
  1070. TFT480.drawString(potar1, 430, 20); // nombre orange en haut à droite
  1071.  
  1072. float v1 = rudder;
  1073. bargraph_H_float(v1, 210, 235, JAUNE); // barre horizontale sous l'horizon artificiel
  1074. }
  1075.  
  1076.  
  1077. void affi_flags() // nombre jaune en haut à droite
  1078. {
  1079. TFT480.fillRect(430, 30, 25, 8, NOIR); // efface le nombre précédemment affiché
  1080. TFT480.setTextFont(1);
  1081. TFT480.setTextColor(JAUNE);
  1082. String s1 = String(flags);
  1083. TFT480.drawString(s1, 430, 30);
  1084.  
  1085. }
  1086.  
  1087.  
  1088. void affi_etats_bt_MCDU() // nombre vert en haut à droite
  1089. {
  1090. TFT480.fillRect(430, 40, 25, 8, NOIR); // efface le nombre précédemment affiché
  1091. TFT480.setTextFont(1);
  1092. TFT480.setTextColor(VERT);
  1093. String s1 = String(v_bt_MCDU);
  1094. TFT480.drawString(s1, 430, 38);
  1095. }
  1096.  
  1097.  
  1098. void affi_extremite() // en haut à droite
  1099. {
  1100. TFT480.fillRect(430, 50, 25, 6, NOIR); // efface le nombre précédemment affiché
  1101. TFT480.setTextFont(1);
  1102. TFT480.setTextColor(BLEU_CLAIR);
  1103. String s1 = String(extremite_pst); // 'A' ou 'B' (la plus éloignée de l'avion)
  1104. TFT480.drawString(s1, 430, 45);
  1105. }
  1106.  
  1107.  
  1108. void affi_sens_APP() // en haut à droite
  1109. {
  1110. TFT480.fillRect(445, 50, 35, 6, NOIR); // efface le nombre précédemment affiché
  1111. TFT480.setTextFont(1);
  1112. TFT480.setTextColor(VERT);
  1113. String s1;
  1114. if (sens_app_effectif == sens_AB) {s1 = "A->B";} else {s1 = "B->A";}
  1115. TFT480.drawString(s1, 445, 48);
  1116. }
  1117.  
  1118.  
  1119. void affi_vitesse()
  1120. {
  1121. uint16_t x1;
  1122. String s1;
  1123.  
  1124. int16_t y_min =40;
  1125. int16_t y_max =300;
  1126.  
  1127. TFT480.setTextColor(BLANC); // Background is not defined so it is transparent
  1128.  
  1129. //---------------------------------------------------------------------------------------
  1130. //bande verticale multicolore
  1131.  
  1132. #define vitesse_sol 40
  1133. int16_t vitesse_mini1 =90;
  1134. int16_t vitesse_mini2 =130;
  1135. int16_t vitesse_maxi1 =200;
  1136. int16_t vitesse_maxi2 =280;
  1137.  
  1138.  
  1139. //calcul de la position des limites entre les différentes couleurs verticales
  1140.  
  1141. int16_t d1, d2, d3, d4, d5;
  1142.  
  1143. d1=(int16_t)(100 + 3.2*((vitesse - vitesse_sol)));
  1144. d2=(int16_t)(100 + 3.2*((vitesse - vitesse_mini1)));
  1145. d3=(int16_t)(100 + 3.2*((vitesse - vitesse_mini2)));
  1146. d4=(int16_t)(100 + 3.2*((vitesse - vitesse_maxi1)));
  1147. d5=(int16_t)(100 + 3.2*((vitesse - vitesse_maxi2)));
  1148.  
  1149. bride(&d1);
  1150. bride(&d2);
  1151. bride(&d3);
  1152. bride(&d4);
  1153. bride(&d5);
  1154.  
  1155. int16_t h1, h2, h3, h4, h5;
  1156.  
  1157. h1 = y_max-(int16_t)d1;
  1158. h2 = d1-d2;
  1159. h3 = d2-d3;
  1160. h4 = d3-d4;
  1161. h5 = d4-d5;
  1162.  
  1163. TFT480.fillRect(50, 40, 6, (int16_t)d5, ORANGE);
  1164. TFT480.fillRect(50, d5, 6, h5, JAUNE);
  1165. TFT480.fillRect(50, d4, 6, h4, VERT);
  1166. TFT480.fillRect(50, d3, 6, h3, ORANGE);
  1167. TFT480.fillRect(50, d2, 6, h2, ROUGE);
  1168. TFT480.fillRect(50, d1, 6, 300-(int16_t)d1, GRIS);
  1169.  
  1170. TFT480.fillRect(50, 300, 6, 20, NOIR);
  1171.  
  1172. //---------------------------------------------------------------------------------------
  1173. //échelle verticale graduée glissante
  1174.  
  1175. uint16_t y0;
  1176.  
  1177. int16_t vit1;
  1178. float d6;
  1179.  
  1180. TFT480.setFreeFont(FF1);
  1181.  
  1182. y0=3.2*(vitesse%10);
  1183.  
  1184. TFT480.fillRect(0, y_min, 50, y_max-30, GRIS_AF); // bande verticale à gauche
  1185. for(int n=0; n<10; n++)
  1186. {
  1187. d6 =2+y0+32.0*n; // 24 pixels verticalement entre chaque trait -> 10*24 = 240px (hauteur de l'affiur)
  1188. {
  1189. if ( (d6>y_min) && (d6<y_max-10) && (vit1>=0) && (vit1<1000) )
  1190. {
  1191. TFT480.fillRect(45, (int16_t)d6, 10, 2, BLANC); // petits tirets horizontaux
  1192. }
  1193.  
  1194. vit1 = vitesse -10*(n-5);
  1195. vit1 /= 10;
  1196. vit1 *= 10;
  1197. s1=(String) vit1;
  1198.  
  1199. if ( (d6>y_min) && (d6<y_max-10) && (vit1>=0) && (vit1<1000) )
  1200. {
  1201. TFT480.setTextColor(BLANC, GRIS_AF);
  1202. //TFT480.drawString(" ", 9, d6);
  1203. x1=0;
  1204. if(vit1<100){x1=7;} // pour affichage centré
  1205. if(vit1<10){x1=14;}
  1206. if (vit1>=10)
  1207. {
  1208. TFT480.drawString(s1, x1, (uint16_t)d6-5); // Graduation (tous les 20kts)
  1209. }
  1210. }
  1211. }
  1212. }
  1213. TFT480.fillRect(0, 38, 68, 2, NOIR); // efface ; BLEU pour test
  1214.  
  1215. //---------------------------------------------------------------------------------------
  1216. // affichage de la valeur principale
  1217.  
  1218. uint16_t VP_y0 = 150;
  1219.  
  1220. TFT480.setTextColor(BLANC, NOIR);
  1221. TFT480.setFreeFont(FF18);
  1222. s1=(String)vitesse;
  1223.  
  1224. TFT480.fillRect(3, VP_y0, 42, 26, NOIR); //efface le nombre précédemment affiché (pour le cas où on passe de 3 à 2 chiffres)
  1225.  
  1226. if ((vitesse>=0) && (vitesse <1000))
  1227. {
  1228. x1=3;
  1229. if(vitesse<100){x1=10;} // pour affichage centré
  1230. if(vitesse<10){x1=20;}
  1231. TFT480.drawString(s1, x1, VP_y0+3); // affi le nombre
  1232. } // affi en gros à mi-hauteur de l'écran
  1233. else
  1234. { TFT480.fillRect(3, VP_y0, 42, 26, GRIS); }
  1235.  
  1236. TFT480.drawRoundRect(1, VP_y0-1, 45, 28, 5, BLANC); // encadrement de la valeur centrale affichée
  1237.  
  1238. TFT480.fillTriangle(45, VP_y0+7, 45, VP_y0+17, 55, VP_y0+12, NOIR); // petit triangle (curseur) noir
  1239. }
  1240.  
  1241.  
  1242.  
  1243. void affi_asel(int16_t asel_i)
  1244. {
  1245. // consigne ALTITUDE de l'autopilot (en rose en haut à droite)
  1246. uint16_t x1 =360;
  1247. TFT480.setFreeFont(FF5);
  1248.  
  1249. if(asel_i >=0)
  1250. {
  1251. // ( chiffres en roses en haut à droite)
  1252. String s2 =(String)(asel_i);
  1253. TFT480.setTextColor(ROSE, NOIR);
  1254.  
  1255. TFT480.fillRect(x1, 0, 77, 20, NOIR); // efface
  1256. if(asel_i<10000){x1+=7;}
  1257. if(asel_i<1000){x1+=7;} // pour affichage centré
  1258. if(asel_i<100){x1+=7;}
  1259. if(asel_i<10){x1+=7;}
  1260.  
  1261. TFT480.drawString(s2, x1, 5);
  1262. }
  1263.  
  1264. }
  1265.  
  1266.  
  1267. void affi_target_speed()
  1268. {
  1269. // consigne de vitesse de l'autopilot
  1270. // ( chiffres en rose en haut à gauche )
  1271.  
  1272. String s2 =(String)(target_speed);
  1273. TFT480.setTextColor(ROSE, NOIR);
  1274. TFT480.setFreeFont(FF5);
  1275. uint8_t x1=7;
  1276. TFT480.fillRect(x1, 20, 60, 15, NOIR); // efface
  1277. TFT480.drawString(s2, x1, 20);
  1278. }
  1279.  
  1280.  
  1281.  
  1282.  
  1283. void affi_vt_verticale()
  1284. {
  1285. // affichage analogique sur la droite de l'écran
  1286.  
  1287. uint16_t x0=435;
  1288. uint16_t y0=165;
  1289.  
  1290. float y1;
  1291.  
  1292. uint16_t x1;
  1293. String s1;
  1294.  
  1295. TFT480.fillRect(x0, y0-90, 45, 180, GRIS_AF); // barre grise
  1296. TFT480.fillRect(x0, y0, 25, 2, BLEU_CLAIR); // centre
  1297.  
  1298. // ------------------------
  1299. // graduations sur un arc vertical
  1300. TFT480.setFreeFont(FF1);
  1301.  
  1302. TFT480.setTextColor(BLANC, NOIR);
  1303. TFT480.drawString("ft/mn", x0-8, y0+125);
  1304.  
  1305. TFT480.setTextColor(BLANC, GRIS_AF);
  1306.  
  1307. float angle;
  1308. for(uint8_t n=0; n<7; n++ )
  1309. {
  1310. angle =135+ n*15; // 1 tiret tous les 15 degrés
  1311. affi_rayon1(HA_x0+340, y0, 110, degTOrad(angle), 0.9, BLANC, false); // tirets de graduation
  1312. }
  1313.  
  1314. TFT480.drawString("3", x0+9, y0-90);
  1315. TFT480.drawString("2", x0-3, y0-65);
  1316. TFT480.drawString("1", x0-8, y0-35);
  1317. TFT480.drawString("0", x0-3, y0-5 + 0);
  1318. TFT480.drawString("1", x0-8, y0+25);
  1319. TFT480.drawString("2", x0-3, y0+50);
  1320. TFT480.drawString("3", x0+9, y0+75);
  1321.  
  1322. // ------------------------
  1323. // aiguille à droite de l'écran
  1324. float angle2;
  1325.  
  1326. TFT480.setFreeFont(FF1);
  1327. s1=(String) (vspeed*60);
  1328.  
  1329. angle2 = 180.0 - vspeed *0.92;
  1330.  
  1331. TFT480.fillRect(x0-10, y0-110, 55, 15, GRIS_TRES_FONCE); // efface haut
  1332. TFT480.fillRect(x0-10, y0+105, 55, 15, GRIS_TRES_FONCE); // efface bas
  1333.  
  1334. if ((vspeed > -50) && (vspeed < 50))
  1335. {
  1336. affi_rayon1(HA_x0+330, y0, 100, degTOrad(angle2), 0.7, JAUNE, true);
  1337.  
  1338. TFT480.setTextColor(JAUNE, NOIR);
  1339. }
  1340. else if (vspeed > 50)
  1341. {
  1342. affi_rayon1(HA_x0+330, y0, 110, degTOrad(132), 0.7, JAUNE, true);
  1343. TFT480.setTextColor(JAUNE, NOIR);
  1344.  
  1345. TFT480.drawString(s1, x0-10, y0-110);
  1346. }
  1347. else if (vspeed < -50)
  1348. {
  1349. affi_rayon1(HA_x0+330, y0, 110, degTOrad(228), 0.7, JAUNE, true);
  1350. TFT480.setTextColor(JAUNE, NOIR);
  1351.  
  1352. TFT480.drawString(s1, x0-10, y0+105);
  1353. }
  1354.  
  1355.  
  1356. // affichage digital de la valeur
  1357.  
  1358.  
  1359. /*
  1360. // = vitesse ascensionnelle, sous forme de barres verticales vertes, à droite, près de l'echelle d'altitude
  1361. uint16_t x0=405;
  1362. uint16_t y0=40;
  1363.  
  1364. int16_t dy=0;
  1365.  
  1366. //fleche haute
  1367. TFT480.fillRect(x0, 0, 10, 140, GRIS_FONCE); // efface haut
  1368. if (vspeed > 1)
  1369. {
  1370. dy= vspeed;
  1371.  
  1372. TFT480.fillRect(x0, y0+100-dy, 10, dy, VERT); // fleche
  1373. }
  1374.  
  1375.  
  1376. //fleche basse
  1377. TFT480.fillRect(x0, y0+150, 10, 135, GRIS_FONCE); // efface bas
  1378. if (vspeed < -1)
  1379. {
  1380. dy= -vspeed;
  1381.  
  1382. TFT480.fillRect(x0, y0+150, 10, dy, VERT); // fleche
  1383. }
  1384. */
  1385.  
  1386. }
  1387.  
  1388.  
  1389.  
  1390.  
  1391. void affi_cap()
  1392. {
  1393. // cercle tournant de CAP gradué en bas au centre de l'écran
  1394. // Les lettres 'N' 'S' 'E' 'O' pour Nord Sud Est Ouset sont initialisées sous forme de sprites dans la fonction setup()
  1395.  
  1396. uint16_t x02 = 200;
  1397. uint16_t y02 = 350;
  1398. float angle; // en radians
  1399. //float cap_RD; // en radians (le cap fourni par FG étant en degrés d'angle)
  1400. uint16_t x_spr;
  1401. uint16_t y_spr;
  1402.  
  1403. uint16_t x_hdg;
  1404. uint16_t y_hdg;
  1405.  
  1406. uint8_t R =70;
  1407. uint8_t R2 =R-6;
  1408. /**
  1409. 360° =2 pi rad
  1410. 1° = 2 pi/360 rad = pi/180 rad
  1411. **/
  1412.  
  1413. TFT480.fillCircle(x02,y02, R, GRIS_AF);
  1414.  
  1415. for(uint8_t n=0; n<24; n++ )
  1416. {
  1417. angle = (int16_t)cap+15 + n*15; // 1 tiret tous les 15 degrés
  1418. affi_rayon1(x02, y02, (R-5), degTOrad(angle), 0.9, BLANC, false); // tirets de graduation
  1419. }
  1420. x_hdg = x02 + R2*cos(degTOrad(hdg1-90-cap));
  1421. y_hdg = y02 + R2*sin(degTOrad(hdg1-90-cap));
  1422.  
  1423. TFT480.drawLine(x02, y02, x_hdg, y_hdg, VERT);
  1424. TFT480.drawCircle(x_hdg, y_hdg, 5, VERT); // rond vert sur le cercle = consigne de cap de l'autopilot
  1425.  
  1426. x_spr = x02+R2 * cos(degTOrad(angle));
  1427. y_spr = y02-R2 * sin(degTOrad(angle));
  1428. TFT480.setPivot(x_spr, y_spr);
  1429. SPR_E.pushRotated(-cap+90, TFT_BLACK); // Plot rotated Sprite, black = transparent
  1430.  
  1431. x_spr = x02+R2* cos(degTOrad(angle+90));
  1432. y_spr = y02-R2 * sin(degTOrad(angle+90));
  1433. TFT480.setPivot(x_spr, y_spr);
  1434. SPR_N.pushRotated(-cap, TFT_BLACK);
  1435.  
  1436. x_spr = x02+R2 * cos(degTOrad(angle+180));
  1437. y_spr = y02-R2 * sin(degTOrad(angle+180));
  1438. TFT480.setPivot(x_spr, y_spr);
  1439. SPR_O.pushRotated(-cap-90, TFT_BLACK);
  1440.  
  1441. x_spr = x02+R2 * cos(degTOrad(angle-90));
  1442. y_spr = y02-R2 * sin(degTOrad(angle-90));
  1443. TFT480.setPivot(x_spr, y_spr);
  1444. SPR_S.pushRotated(-cap, TFT_BLACK);
  1445.  
  1446.  
  1447. // petite "maison" dans le cercle (valeur du cap)
  1448.  
  1449. #define a 170 // x général
  1450. #define b a+30
  1451. #define c b+30
  1452. #define d 288 // y général
  1453. #define e d+10
  1454. #define f e+20
  1455.  
  1456. TFT480.drawLine(a, f, c, f, BLANC); // sol
  1457. TFT480.drawLine(a, f, a, e, BLANC); // mur de gauche
  1458. TFT480.drawLine(c, f, c, e, BLANC); // mur de droite
  1459. TFT480.drawLine(a, e, b, d, BLANC); // toit pente gauche
  1460. TFT480.drawLine(c, e, b, d, BLANC); // toit pente droite
  1461.  
  1462. // affi la valeur
  1463. String s1;
  1464. uint16_t x0 = a+1;
  1465. uint16_t y0 = e;
  1466.  
  1467. uint16_t x1= x0;
  1468. if(cap<100){x1+=5;} // pour affichage centré
  1469. if(cap<10){x1+=5;}
  1470.  
  1471. s1=String (cap, 1);
  1472.  
  1473. TFT480.fillRect(x0, y0, 57, 20, NOIR); // efface le nombre précédemment affiché
  1474. TFT480.setTextColor(BLANC, NOIR);
  1475. TFT480.setFreeFont(FM9);
  1476. TFT480.drawString(s1, x1, y0);
  1477. }
  1478.  
  1479.  
  1480.  
  1481. void affi_hauteur()
  1482. {
  1483. /**
  1484. Pour exprimer une hauteur au dessus de l'aérodrome, on défini la hauteur AAL (Above Aerodrome Level). Il
  1485. s'agit de la hauteur entre l'avion et le point de référence de l'aérodrome comme s'il était en dessous de la
  1486. position de l'appareil (même s'il n'y est pas). Cette hauteur ne suit pas le relief.
  1487. On la calculera ici en retranchant [l'altitude de l'aéroport sélectionné] à [l'altitude GPS].
  1488.  
  1489. En conséquense, il faut impérativement penser à sélectionner dans le module SD le bon aérodrome, celui d'où l'on décolle,
  1490. puis en cas de voyage, celui où l'on va se poser (se qui renseignera son altitude) sinon l'affichage sera faux.
  1491.  
  1492. par exemple si l'on choisit "Montpellier" en étant à Clermont-Ferrand, l'erreur sera de 1089 ft
  1493.  
  1494. Les altitudes des aérodromes sont enregistées ici dans le fichier FG_data.h
  1495. */
  1496.  
  1497. String s1;
  1498. uint16_t x0 =365;
  1499. //---------------------------------------------------------------------------------------
  1500. //échelle verticale graduée glissante
  1501.  
  1502. uint16_t x1;
  1503. uint16_t y0;
  1504. uint16_t hauteur;
  1505. int16_t alt1;
  1506. float d5;
  1507.  
  1508. if (mode_affi_hauteur == AAL) {hauteur = hauteur_AAL;}
  1509. if (mode_affi_hauteur == ASL) {hauteur = altitude_GPS;}
  1510.  
  1511. TFT480.setFreeFont(FF1);
  1512.  
  1513.  
  1514. y0=3.2*(hauteur_AAL%10);
  1515.  
  1516. TFT480.fillRect(x0, 20, 60, 319, GRIS_AF); //efface bande verticale à droite
  1517.  
  1518.  
  1519. for(int n=0; n<10; n++)
  1520. {
  1521. d5 =0+y0+32.0*n; // pixels verticalement entre chaque trait -> 10*24 = 240px (hauteur de l'affi)
  1522. {
  1523. if (d5>=20) // marge en haut
  1524. {
  1525. TFT480.fillRect(x0, (int16_t)d5+5, 5, 2, BLANC); // petits tirets horizontaux
  1526.  
  1527. alt1 = hauteur -10*(n-5);
  1528. alt1 /= 10;
  1529. alt1 *= 10;
  1530. s1=(String) alt1;
  1531.  
  1532. if(alt1>=0)
  1533. {
  1534. TFT480.setTextColor(BLANC, GRIS_AF);
  1535. //TFT480.drawString(" ", 9, d5);
  1536. x1=x0;
  1537. if(alt1<10000){x1+=7;} // pour affichage centré
  1538. if(alt1<1000){x1+=7;}
  1539. if(alt1<100){x1+=7;}
  1540. if(alt1<10){x1+=7;}
  1541. TFT480.drawString(s1, x1, (uint16_t)d5); // Graduation (tous les 20kts)
  1542. }
  1543. }
  1544. }
  1545. }
  1546.  
  1547. //---------------------------------------------------------------------------------------
  1548. // affichage de la valeur principale
  1549.  
  1550. uint16_t x2;
  1551. uint16_t y0b = 155;
  1552. TFT480.fillRect(x0-20, y0b, 80, 25, NOIR); // efface le nombre précédemment affiché
  1553. TFT480.setTextColor(BLANC, NOIR);
  1554. TFT480.setFreeFont(FF18);
  1555.  
  1556. if ((1) && (hauteur < 60000))
  1557. {
  1558. s1=(String) hauteur;
  1559. }
  1560. else {s1="----";}
  1561. x2=x0-20;
  1562. if(hauteur<10000){x2+=10;} // pour affichage centré
  1563. if(hauteur<1000){x2+=10;}
  1564. if(hauteur<100){x2+=10;}
  1565. if(hauteur<10){x2+=10;}
  1566.  
  1567. if(hauteur<0)
  1568. {
  1569. TFT480.setTextColor(ROUGE);
  1570. x2=x0-20; // si valeur négative affichée avec signe "-"
  1571. }
  1572.  
  1573. TFT480.drawString(s1, x2, y0b);
  1574. uint16_t couleur1=GRIS;
  1575. if (mode_affi_hauteur == ASL) {couleur1=BLEU;}
  1576. if (mode_affi_hauteur == AAL) {couleur1=VERT;}
  1577.  
  1578. TFT480.drawRoundRect(x0-20, y0b-3, 75, 28, 5, couleur1); // encadrement de la valeur centrale affichée
  1579. }
  1580.  
  1581.  
  1582.  
  1583.  
  1584. void affi_distance_piste()
  1585. {
  1586. String s1;
  1587. uint16_t x0=190;
  1588. uint16_t y0=255;
  1589. float nav_nm;
  1590. // rappel: 1 mile marin (NM nautical mile) = 1852m
  1591. //ils_nm = (float)ils_dst / 1852.0;
  1592. //if (ils_nm >99) {ils_nm=0;}
  1593.  
  1594. TFT480.drawRect(x0-47, y0-15, 190, 35, GRIS_FONCE); //encadrement
  1595.  
  1596. TFT480.setTextFont(1);
  1597. TFT480.setTextColor(BLANC, NOIR);
  1598. TFT480.drawString("distance", x0, y0-12);
  1599.  
  1600. TFT480.setFreeFont(FM9);
  1601. TFT480.setTextColor(JAUNE, NOIR);
  1602. TFT480.drawString("RWY", x0-45, y0-12);
  1603.  
  1604. TFT480.setTextColor(BLANC, NOIR);
  1605. uint8_t nb_decimales;
  1606. if(GPS_distance_piste>99) {nb_decimales =0;} else {nb_decimales =1;}
  1607. s1 = String(GPS_distance_piste, nb_decimales);
  1608. if (data_ok == 0) {s1=" --";}
  1609. TFT480.fillRect(x0, y0, 52, 18, NOIR); // efface
  1610. TFT480.setFreeFont(FM9);
  1611. TFT480.drawString(s1, x0, y0);
  1612. TFT480.drawRoundRect(x0, y0-2, 50, 18, 5, GRIS_FONCE); // encadrement de la valeur affichée
  1613.  
  1614. //affi_float_test(GPS_distance_piste_new, 100, 3, VERT, NOIR);
  1615.  
  1616. TFT480.setTextColor(JAUNE, NOIR);
  1617. TFT480.drawString("NM", x0+55, y0);
  1618.  
  1619. //affi_direction_piste // direction de la piste vue de l'avion
  1620. TFT480.setTextFont(1);
  1621. TFT480.setTextColor(BLANC, NOIR);
  1622. TFT480.drawString("direction", x0+80, y0-12);
  1623.  
  1624. TFT480.setTextColor(BLANC, NOIR);
  1625. s1 = String(GPS_azimut_piste, 0); // 0 -> 0 décimales
  1626. if (data_ok == 0) {s1=" --";}
  1627. TFT480.fillRect(x0+90, y0, 52, 18, NOIR); // efface
  1628. TFT480.setFreeFont(FM9);
  1629. TFT480.drawString(s1, x0+90, y0);
  1630. TFT480.drawRoundRect(x0+90, y0-2, 40, 18, 5, GRIS_FONCE); // encadrement de la valeur affichée
  1631.  
  1632. TFT480.drawCircle(x0+135, y0, 2, JAUNE); // caractère 'degré'
  1633. }
  1634.  
  1635.  
  1636.  
  1637. void affi_distance_ptAA()
  1638. {
  1639. String s1;
  1640. uint16_t x0=260;
  1641. uint16_t y0=280;
  1642.  
  1643. TFT480.setTextFont(1);
  1644. TFT480.setTextColor(BLANC, NOIR);
  1645. TFT480.drawString("AA", x0, y0);
  1646.  
  1647. s1 = String(GPS_distance_ptAA, 1);
  1648. TFT480.fillRect(x0, y0+10, 45, 12, NOIR); // efface
  1649. TFT480.setFreeFont(FM9);
  1650. TFT480.setTextColor(VERT, NOIR);
  1651. TFT480.drawString(s1, x0, y0+10);
  1652.  
  1653. TFT480.drawRoundRect(x0-2, y0+8, 52, 18, 5, GRIS_FONCE);
  1654.  
  1655. TFT480.setTextColor(JAUNE, NOIR);
  1656. TFT480.drawString("NM", x0+55, y0+10);
  1657. }
  1658.  
  1659.  
  1660. void affi_distance_ptBB()
  1661. {
  1662. String s1;
  1663. uint16_t x0=260;
  1664. uint16_t y0=280;
  1665.  
  1666. TFT480.setTextFont(1);
  1667. TFT480.setTextColor(BLANC, NOIR);
  1668. TFT480.drawString("BB", x0, y0);
  1669.  
  1670. s1 = String(GPS_distance_ptBB, 1);
  1671. TFT480.fillRect(x0, y0+10, 45, 12, NOIR); // efface
  1672. TFT480.setFreeFont(FM9);
  1673. TFT480.setTextColor(VERT, NOIR);
  1674. TFT480.drawString(s1, x0, y0+10);
  1675.  
  1676. TFT480.drawRoundRect(x0-2, y0+8, 52, 18, 5, GRIS_FONCE);
  1677.  
  1678. TFT480.setTextColor(JAUNE, NOIR);
  1679. TFT480.drawString("NM", x0+55, y0+10);
  1680. }
  1681.  
  1682.  
  1683. void affi_distance_pti()
  1684. {
  1685. String s1;
  1686. uint16_t x0=260;
  1687. uint16_t y0=280;
  1688.  
  1689. TFT480.setTextFont(1);
  1690. TFT480.setTextColor(BLANC, NOIR);
  1691.  
  1692. s1 = "to Pt ";
  1693. s1 += String(num_pti);
  1694.  
  1695. TFT480.drawString(s1, x0, y0);
  1696.  
  1697. s1 = String(GPS_distance_pti, 1);
  1698. TFT480.fillRect(x0, y0+10, 45, 12, NOIR); // efface
  1699. TFT480.setFreeFont(FM9);
  1700. TFT480.setTextColor(VERT, NOIR);
  1701. TFT480.drawString(s1, x0, y0+10);
  1702.  
  1703. TFT480.drawRoundRect(x0-2, y0+8, 52, 18, 5, GRIS_FONCE);
  1704.  
  1705. TFT480.setTextColor(JAUNE, NOIR);
  1706. TFT480.drawString("NM", x0+55, y0+10);
  1707. }
  1708.  
  1709.  
  1710. void efface_cadre_bas(uint16_t couleur)
  1711. {
  1712. TFT480.fillRect(70, 232, 292, 84, NOIR);
  1713. TFT480.drawRect(70, 232, 292, 84, GRIS_FONCE);
  1714. efface_sprite_trajectoire();
  1715. }
  1716.  
  1717.  
  1718.  
  1719. void affi_Airport()
  1720. {
  1721. uint16_t n;
  1722. float v1;
  1723. String s1;
  1724.  
  1725.  
  1726. TFT480.fillRect(255, 280, 108, 20, NOIR); // efface - BLEU pour test
  1727. TFT480.setTextFont(1);
  1728.  
  1729. TFT480.setTextColor(BLEU_CLAIR, NOIR);
  1730. s1= liste_bali[num_bali].ID_OACI;
  1731. TFT480.drawString(s1, 255, 280);
  1732.  
  1733. s1= (String)liste_bali[num_bali].altitude;
  1734. s1 +=" ft";
  1735. TFT480.setTextColor(VIOLET2, NOIR);
  1736. TFT480.drawString(s1, 300, 280);
  1737.  
  1738. TFT480.fillRect(270, 300, 60, 30, NOIR); // efface - GRIS pour test
  1739. s1= liste_bali[num_bali].nom;
  1740. TFT480.setTextColor(BLEU_CLAIR, NOIR);
  1741. TFT480.drawString(s1, 255, 290);
  1742. }
  1743.  
  1744.  
  1745. void affi_mode_affi_hauteur()
  1746. {
  1747. if (mode_affi_hauteur == AAL)
  1748. {
  1749. TFT480.setFreeFont(FF1);
  1750. TFT480.setTextColor(VERT, GRIS_AF); // Autolanding en cours, ok
  1751. TFT480.drawString("AAL", 290, 0);
  1752. }
  1753. if (mode_affi_hauteur == ASL)
  1754. {
  1755. TFT480.setFreeFont(FF1);
  1756. TFT480.setTextColor(BLEU_CLAIR, GRIS_AF); // Autolanding en cours, ok
  1757. TFT480.drawString("ASL", 290, 0);
  1758. }
  1759. }
  1760.  
  1761.  
  1762. void calcul_ptA_ptB(float dst) // situés à dst NM de la piste, dans l'axe. (dst = 10NM en principe, sauf cas particuliers)
  1763. {
  1764. float lat_A = liste_bali[num_bali].lat_A; // extrémité de la piste
  1765. float lon_A = liste_bali[num_bali].lon_A; // extrémité de la piste
  1766.  
  1767. float lat_B = liste_bali[num_bali].lat_B; // extrémité de la piste
  1768. float lon_B = liste_bali[num_bali].lon_B; // extrémité de la piste
  1769.  
  1770. float d_lat = lat_A - lat_B;
  1771. float d_lon = lon_A - lon_B;
  1772.  
  1773. lat_ptAA = lat_centre_pst + (1852 * dst /longueur_piste) * d_lat;
  1774. lon_ptAA = lon_centre_pst + (1852 * dst /longueur_piste) * d_lon;
  1775.  
  1776. //affi_float_test(lat_ptAA, 120, 2, VERT, NOIR);
  1777. //affi_float_test(lon_ptAA, 120, 3, JAUNE, NOIR);
  1778.  
  1779. lat_ptBB = lat_centre_pst - (1852 * dst /longueur_piste) * d_lat;
  1780. lon_ptBB = lon_centre_pst - (1852 * dst /longueur_piste) * d_lon;
  1781.  
  1782. //affi_float_test(lat_ptBB, 120, 4, VERT, NOIR);
  1783. //affi_float_test(lon_ptBB, 120, 5, JAUNE, NOIR);
  1784. }
  1785.  
  1786.  
  1787.  
  1788. void calculs_piste() // lors du choix de l'Airport
  1789. {
  1790.  
  1791. float lat_A = liste_bali[num_bali].lat_A;
  1792. float lon_A = liste_bali[num_bali].lon_A;
  1793.  
  1794. float lat_B = liste_bali[num_bali].lat_B;
  1795. float lon_B = liste_bali[num_bali].lon_B;
  1796.  
  1797. longueur_piste = 1000.0* distance_AB(lat_A, lon_A, lat_B, lon_B); // en m
  1798.  
  1799. orient_pisteAB = azimut_AB(lat_A, lon_A, lat_B, lon_B);
  1800.  
  1801. orient_pisteBA = orient_pisteAB + 180.0;
  1802. if (orient_pisteBA > 360.0) {orient_pisteBA -= 360.0;}
  1803.  
  1804. lat_centre_pst=(lat_A +lat_B)/2.0;
  1805. lon_centre_pst=(lon_A +lon_B)/2.0;
  1806. }
  1807.  
  1808.  
  1809.  
  1810. void calculs_GPS() // temps réel
  1811. {
  1812.  
  1813. // calculs de la position de l'avion / piste (distance et direction)
  1814.  
  1815. // DISTANCE (variable globale)
  1816. // voir la fonction "distance_AB()" dans le fichier "Fonctions1.h"
  1817. GPS_distance_piste = distance_AB(lat_avion, lon_avion, lat_centre_pst, lon_centre_pst) / 1.852; // du centre de la piste, en NM
  1818. GPS_distance_ptAA = distance_AB(lat_avion, lon_avion, lat_ptAA, lon_ptAA) / 1.852;
  1819. GPS_distance_ptBB = distance_AB(lat_avion, lon_avion, lat_ptBB, lon_ptBB) / 1.852;
  1820. GPS_distance_pti = distance_AB(lat_avion, lon_avion, lat_pti, lon_pti) / 1.852;
  1821.  
  1822.  
  1823. // DIRECTION (variable globale)
  1824. GPS_azimut_piste = azimut_AB(lat_avion, lon_avion, lat_centre_pst, lon_centre_pst);// latitudes et longitudes en degrés décimaux
  1825. GPS_azimut_ptAA = azimut_AB(lat_avion, lon_avion, lat_ptAA, lon_ptAA);
  1826. GPS_azimut_ptBB = azimut_AB(lat_avion, lon_avion, lat_ptBB, lon_ptBB);
  1827. GPS_azimut_pti = azimut_AB(lat_avion, lon_avion, lat_pti, lon_pti);
  1828. }
  1829.  
  1830.  
  1831. void calcul_pti(float azimut_i, float distance_i, float *latitude, float *longitude)
  1832. {
  1833. /*
  1834. calcul des coordonnées GPS d'un point quelquonque PROCHE de la piste (en vue de faire des "hyppodromes" biens maitrisés)
  1835.  
  1836. données:
  1837. -azimut et distance du point concerné par rapport au, et vu du, centre de la piste (paramètres: azimut_i et distance_i)
  1838. -coordonnées GPS des points extrémités de la piste (lus dans le fichier FG_data.h)
  1839. -coordonnées GPS du centre, et l'orientation de la piste (voir fonction "void calculs_GPS()" )
  1840. on va alors ajouter la valeur de l'azimut_i à l'orientation de la piste pour faire le calcul
  1841. */
  1842.  
  1843. // calcul de l'orientation (relevé) du point (azimut par rapport au nord)
  1844. float orientation_point = -1.0 * orient_pisteAB + azimut_i; // azimut_i étant l'angle entre la piste et la direction du point
  1845.  
  1846. // 1 minute d'angle sur un méridien => 1 NM de latitude
  1847. // 60mn d'angle (1deg) => 60 NM
  1848. // 1 NM => 1/60 de degré -> 0.0166 degrés
  1849.  
  1850. *latitude = lat_centre_pst + (distance_i /60.0 * sin(raddeg * (orientation_point - 90.0)));
  1851.  
  1852. // pour la longitude, il faut tenir compte que la longueur d'un parallèle dépend de la latitude
  1853. // (max à l'équateur, nulle au pôle) suivant une loi en cos.
  1854. *longitude= lon_centre_pst + (distance_i /(60.0 * cos(raddeg * lat_centre_pst)) * cos(raddeg * (orientation_point - 90.0)));
  1855.  
  1856. }
  1857.  
  1858.  
  1859.  
  1860. void find_sens_approche() // en fonction de la position réelle de l'avion
  1861. {
  1862. //détermination du sens de l'approche pour l'autoland (en vol)
  1863.  
  1864. ////float delta_1 = orient_pisteBA - GPS_azimut_piste;
  1865. ////if (delta_1<0) {delta_1+=360.0;}
  1866. ////if (delta_1>360) {delta_1-=360.0;}
  1867.  
  1868. ////if ((delta_1 >90.0) && (delta_1 <270.0)) {sens_app_effectif = sens_AB;} else {sens_app_effectif = sens_BA;}
  1869. find_END_RWY_dst();
  1870.  
  1871. if(extremite_pst=='A') {sens_app_effectif = sens_BA;}
  1872. if(extremite_pst=='B') {sens_app_effectif = sens_AB;}
  1873. }
  1874.  
  1875.  
  1876.  
  1877.  
  1878. void find_END_RWY_dst() //le pt le plus ELOIGNE en face de nous, en bout de piste (= A ou B )
  1879. {
  1880.  
  1881. // calcul basé sur les distances
  1882. // en vue de guider (en lacet) l'avion au roulage lors de l'atterrissage
  1883. // on visera le point le plus éloigné
  1884. // attention: lors d'un touch and go, si l'avion a dépassé le centre de la piste lors de la remise des gaz, le sens sera FAUX !
  1885.  
  1886. float lat_A=liste_bali[num_bali].lat_A;
  1887. float lon_A=liste_bali[num_bali].lon_A;
  1888. float lat_B=liste_bali[num_bali].lat_B;
  1889. float lon_B=liste_bali[num_bali].lon_B;
  1890.  
  1891. float dst_A = distance_AB(lat_avion, lon_avion, lat_A, lon_A);
  1892. float dst_B = distance_AB(lat_avion, lon_avion, lat_B, lon_B);
  1893.  
  1894. if((dst_A) > (dst_B)) {extremite_pst='A';} else {extremite_pst='B';}
  1895. }
  1896.  
  1897.  
  1898. void find_END_RWY_angl() //le pt le plus ELOIGNE en face de nous, en bout de piste (= A ou B )
  1899. {
  1900. // calcul par les angles
  1901. // en vue de guider (en lacet) l'avion au roulage lors du décollage
  1902. // on visera le point le plus éloigné
  1903.  
  1904.  
  1905. float delta = cap - orient_pisteAB;
  1906.  
  1907. if (delta < -180) {delta += 360;}
  1908. if (delta > 180) {delta -= 360;}
  1909.  
  1910. if(abs(delta) > 90) {extremite_pst='A';} else {extremite_pst='B';}
  1911.  
  1912. //affi_string_test((String)extremite_pst, 130, 4, BLANC, NOIR);
  1913. }
  1914.  
  1915.  
  1916.  
  1917.  
  1918. void auto_rudder_deco() // on passera en boucle dans cette fonction
  1919. {
  1920. // losrqu'on est bien positionné sur la piste, on doit voir l'extrémité de la piste, en face, au loin
  1921. // dans la même direction que l'orientation physique de la piste
  1922.  
  1923. float d_alpha;
  1924. float lat_i, lon_i;
  1925.  
  1926. affi_extremite(); // l'extrémité concernée est déterminée par la fonction "find_END_RWY_angl()"
  1927.  
  1928. if (extremite_pst == 'A')
  1929. {
  1930. lat_i=liste_bali[num_bali].lat_A;
  1931. lon_i=liste_bali[num_bali].lon_A;
  1932. }
  1933.  
  1934. if (extremite_pst == 'B')
  1935. {
  1936. lat_i=liste_bali[num_bali].lat_B;
  1937. lon_i=liste_bali[num_bali].lon_B;
  1938. }
  1939.  
  1940. float az1 = azimut_AB(lat_avion, lon_avion, lat_i, lon_i); // direction dans laquelle on voit le bout de la piste au loin...
  1941.  
  1942. d_alpha = az1 - cap;
  1943. borne_in (&d_alpha, -5.0, 5.0);
  1944. if (vitesse < 20) { rudder =0;}
  1945.  
  1946.  
  1947. /**
  1948. //else if (is_in(vitesse, 20, 50)) {rudder = d_alpha /60.0;}
  1949. //else if (is_in(vitesse, 50, 120)) {rudder = d_alpha / 40.0;}
  1950. //else if (is_in(vitesse, 120, 170)) {rudder = d_alpha / 20.0;}
  1951.  
  1952. CALCUL: système 2 équations à 2 inconnues :
  1953. y=ax+b
  1954.  
  1955. 60=20a+b
  1956. 20=170a+b
  1957.  
  1958. 40=(20-170)a
  1959. 40=-150a
  1960. a= -40/150 = -0.26
  1961.  
  1962. b = 60-20a
  1963. = 60+5.2
  1964. = 65.2
  1965.  
  1966. y = -0.26 x + 70 environ
  1967. **/
  1968.  
  1969. float facteur = -0.26 * vitesse +65.2;
  1970.  
  1971. // en fait ce n'est pas aussi simple: le débattement doit augmenter fortement lorsque la roue avant ne touche plus le sol
  1972. // et que seule la dérive a une action (aérodynamique) (vers 80 kts)
  1973.  
  1974. if (is_in(vitesse, 80, 175)) {facteur = 15;} // on augmente fortement l'amplitude de la correction
  1975.  
  1976. rudder = /*rudder_manuel + */ d_alpha / facteur;
  1977.  
  1978. if (vitesse > 175) { rudder =0;}
  1979.  
  1980.  
  1981. ////calcul_erreur_position();
  1982.  
  1983. }
  1984.  
  1985.  
  1986.  
  1987. void auto_rudder_attero() // on passera en boucle dans cette fonction
  1988. {
  1989. // losrqu'on est bien positionné sur la piste, on doit voir le l'extrémité de la piste, en face, au loin
  1990. // dans la même direction que l'orientation physique de la piste
  1991.  
  1992. float d_alpha;
  1993. float lat_i, lon_i;
  1994.  
  1995. affi_extremite(); // déterninée en une seule fois lors de la fin de la phase d'autoland
  1996. // voir dans la fonction "void auto_landing()"
  1997. // ne plus la re-déterminer par la suite parce du'une fois dépassé le centre de la piste, le résultat serait faux !
  1998.  
  1999. if (extremite_pst == 'A')
  2000. {
  2001. lat_i=liste_bali[num_bali].lat_A;
  2002. lon_i=liste_bali[num_bali].lon_A;
  2003. }
  2004.  
  2005. if (extremite_pst == 'B')
  2006. {
  2007. lat_i=liste_bali[num_bali].lat_B;
  2008. lon_i=liste_bali[num_bali].lon_B;
  2009. }
  2010.  
  2011. float az1 = azimut_AB(lat_avion, lon_avion, lat_i, lon_i); // direction dans laquelle on voit le bout de la piste au loin...
  2012.  
  2013. d_alpha = az1 - cap;
  2014.  
  2015. borne_in (&d_alpha, -3.0, 3.0); // -3 3
  2016.  
  2017.  
  2018.  
  2019. ////if (is_in(vitesse, 100, 140)) {rudder = d_alpha / 20.0;}
  2020. ////else if (is_in(vitesse, 80, 100)) {rudder = d_alpha / 25.0;} //30
  2021. ////else if (is_in(vitesse, 50, 80)) {rudder = d_alpha / 30.0;} //40
  2022. ////else if (is_in(vitesse, 20, 50)) {rudder = d_alpha / 50.0;} //80
  2023.  
  2024. float facteur = -0.26 * vitesse +65.2;
  2025.  
  2026. // le débattement doit être important lorsque la roue avant ne touche pas le sol
  2027. // et que seule la dérive a une action (aérodynamique) (> 80 kts)
  2028.  
  2029. if (is_in(vitesse, 80, 175)) {facteur = 15;} // on augmente fortement l'amplitude de la correction
  2030.  
  2031. rudder = d_alpha / facteur;
  2032.  
  2033. if (vitesse > 175) { rudder = 0;}
  2034. if (vitesse < 10) { rudder = 0;}
  2035.  
  2036. //raz_bit(&flags, bit_rudder_attero); // afin de pouvoir manoeuvrer sur les taxiways
  2037.  
  2038. // OK, garde l'axe de la piste, heu... lorsque la roue avant touche le sol...
  2039. // lorsque seul le train principal touche et la vitesse est faible et donc la gouverne de direction peu efficace... pas top !
  2040. // on pourrait jouer en différentiel sur les freins gauche-droite, mais ça complique pas mal l'affaire !
  2041. // toutefois si on freine rapidement (dans la seconde qui suit le toucher initial) la roue avant touche à son tour, et c'est OK
  2042.  
  2043. }
  2044.  
  2045.  
  2046. void nav_to_centre_piste()
  2047. {
  2048. voyant_APP.affiche(BLANC, BLEU);
  2049. hdg1 = round(GPS_azimut_piste);
  2050. if(GPS_distance_piste < 2) // on désengage tout, il faut un appui sur touche pour décider de la suite du vol
  2051. {
  2052. raz_bit(&flags, bit_nav_to_piste);// ce qui signe la fin des appel de cette fonction
  2053. raz_bit(&flags, bit_nav_to_ptAA);
  2054. raz_bit(&flags, bit_nav_to_ptBB);
  2055. raz_bit(&flags, bit_route);
  2056. raz_bit(&flags, bit_autoland);
  2057. raz_bit(&flags, bit_atterrissage);
  2058. raz_bit(&flags, bit_au_sol);
  2059. raz_bit(&flags, bit_decollage);
  2060.  
  2061. for (int n=0; n<4; n++)
  2062. {
  2063. TFT480.setFreeFont(FF6);
  2064. affi_message("verticale RWY", 130, 200, 200, 1000, BLEU_CLAIR, HA_SOL, 1); // ici
  2065. }
  2066.  
  2067. // rien de plus, on repasse en auto-pilotage manuel
  2068.  
  2069. set_bit(&flags, bit_FG_AP);
  2070. }
  2071. }
  2072.  
  2073.  
  2074. void nav_to_ptAA() // on passera en boucle dans cette fonction
  2075. {
  2076. // point situé à 12NM dans l'axe de la piste (d'un côté)
  2077.  
  2078. voyant_APP.affiche(BLANC, VIOLET1);
  2079.  
  2080. // CAP
  2081. hdg1 = round(GPS_azimut_ptAA);
  2082.  
  2083. // ALTITUDE (à discuter avec un pilote ou du personnel ATC,
  2084. // je me suis fié à ce que je constate sur le site flightradar24.com...)
  2085. if (is_in(GPS_distance_ptAA, 15, 50) && (asel1 > 40)) {asel1--;} // local
  2086. if (is_in(GPS_distance_ptAA, 50, 100) && (asel1 > 100)) {asel1--;} // proche
  2087.  
  2088. uint16_t asel_mini = liste_bali[num_bali].niveau_de_vol_mini;
  2089. if ((GPS_distance_ptAA < 30) && (asel1 < asel_mini)) {asel1 = asel_mini;}
  2090. // force à garder une hauteur minimale de sécurité le cas échéant (relief...)
  2091.  
  2092. affi_distance_ptAA();
  2093.  
  2094. if(GPS_distance_ptAA < 3.0)
  2095. {
  2096. raz_bit(&flags, bit_nav_to_piste);
  2097. raz_bit(&flags, bit_nav_to_ptAA); // ce qui signe la fin des appel de cette fonction
  2098. raz_bit(&flags, bit_nav_to_ptBB);
  2099. raz_bit(&flags, bit_route);
  2100. //efface_cadre_bas(NOIR);
  2101.  
  2102. uint16_t asel_mini = liste_bali[num_bali].niveau_de_vol_mini;
  2103. if (asel1 > asel_mini) {asel1 = asel_mini;}
  2104.  
  2105. TFT480.setFreeFont(FF6);
  2106. affi_message("proche ptA", 130, 200, 200, 500, BLEU_CLAIR, HA_SOL, 1); // ici
  2107.  
  2108. TFT480.setFreeFont(FF6);
  2109. affi_message("Finale ILS", 130, 200, 200, 500, BLEU_CLAIR, HA_SOL, 1); // ici
  2110.  
  2111. efface_cadre_bas(NOIR);
  2112. find_END_RWY_dst(); // la plus éloignée (= 'A' ou = 'B')
  2113. set_bit(&flags, bit_autoland); // on passe en finale
  2114. //set_bit(&flags, bit_rudder_attero);
  2115. }
  2116.  
  2117. // vitesse
  2118.  
  2119. if ( (GPS_distance_piste < 30.0) && (target_speed>160) ) {target_speed =160;}
  2120. }
  2121.  
  2122.  
  2123.  
  2124. void nav_to_ptBB() // on passera en boucle dans cette fonction
  2125. {
  2126. // point situé à 12NM dans l'axe de la piste (de l'autre côté)
  2127.  
  2128. voyant_APP.affiche(BLANC, VIOLET2);
  2129.  
  2130. // CAP
  2131. hdg1 = round(GPS_azimut_ptBB);
  2132.  
  2133. // ALTITUDE (à discuter avec un pilote ou du personnel ATC,
  2134. // je me suis fié à ce que je constate sur le site flightradar24.com...)
  2135. if (is_in(GPS_distance_ptBB, 15, 50) && (asel1 > 40)) {asel1--;} // local
  2136. if (is_in(GPS_distance_ptBB, 50, 100) && (asel1 > 100)) {asel1--;} // proche
  2137.  
  2138. uint16_t asel_mini = liste_bali[num_bali].niveau_de_vol_mini;
  2139. if ((GPS_distance_ptBB < 30) && (asel1 < asel_mini)) {asel1 = asel_mini;}
  2140. // force à garder une hauteur minimale de sécurité le cas échéant (relief...)
  2141.  
  2142. affi_distance_ptBB();
  2143.  
  2144. if(GPS_distance_ptBB < 3.0)
  2145. {
  2146. raz_bit(&flags, bit_nav_to_piste);
  2147. raz_bit(&flags, bit_nav_to_ptAA);
  2148. raz_bit(&flags, bit_nav_to_ptBB); // ce qui signe la fin des appel de cette fonction
  2149. raz_bit(&flags, bit_nav_to_pti);
  2150. raz_bit(&flags, bit_circling);
  2151. raz_bit(&flags, bit_route);
  2152.  
  2153. //efface_cadre_bas(NOIR);
  2154.  
  2155. if (asel1 > 30) {asel1 = 30;}
  2156.  
  2157. TFT480.setFreeFont(FF6);
  2158. affi_message("proche ptB", 130, 200, 200, 500, BLEU_CLAIR, HA_SOL, 1); // ici
  2159.  
  2160.  
  2161. TFT480.setFreeFont(FF6);
  2162. affi_message("Finale ILS", 130, 200, 200, 500, BLEU_CLAIR, HA_SOL, 1); // ici
  2163.  
  2164. efface_cadre_bas(NOIR);
  2165. find_END_RWY_dst(); // la plus éloignée (= 'A' ou = 'B')
  2166.  
  2167.  
  2168. set_bit(&flags, bit_autoland); // on passe en finale
  2169. //set_bit(&flags, bit_rudder_attero);
  2170. }
  2171. // vitesse
  2172.  
  2173. if ( (GPS_distance_piste < 30.0) && (target_speed>160) ) {target_speed =160;}
  2174. }
  2175.  
  2176.  
  2177. void nav_to_pti()
  2178. {
  2179. String s1;
  2180. // point quelconque
  2181. voyant_APP.affiche(BLANC, VERT_FONCE);
  2182.  
  2183. // CAP
  2184. hdg1 = round(GPS_azimut_pti);
  2185.  
  2186.  
  2187. affi_distance_pti();
  2188.  
  2189. if(GPS_distance_pti > 1.5) {inc_num_pt1_autorisee=1;}
  2190.  
  2191.  
  2192. if(GPS_distance_pti < 1.0)
  2193. {
  2194. TFT480.setFreeFont(FF6);
  2195. //s1 ="PT ";
  2196. s1 = "to PT " + String(num_pti);
  2197. msg_to_send = s1;
  2198. TFT480.setFreeFont(FF6);
  2199. affi_message(s1, 130, 200, 200, 300, VERT, HA_SOL, 1); // ici
  2200.  
  2201. if (inc_num_pt1_autorisee==1)
  2202. {
  2203. num_pti ++; // pour naviguer vers le point suivant
  2204. if (num_pti >10)
  2205. {
  2206. num_pti =1;
  2207. }
  2208.  
  2209. asel1 = 30; //à priori. niveau de vol (en ft/100)
  2210.  
  2211. if (num_pti==1){asel1 = 3; flaps=3; gear_down = 1;} // 300ft -> 100m
  2212. if (num_pti==2){asel1 = 15; flaps=2; gear_down = 0;} // 1500ft -> 500m
  2213. if (num_pti==3){asel1 = 30; flaps=0; gear_down = 0;} // 3000ft -> 1000m
  2214. if (num_pti==4){asel1 = 30; flaps=0; gear_down = 0;}
  2215. if (num_pti==5){asel1 = 30; flaps=0; gear_down = 0;}
  2216. if (num_pti==6){asel1 = 30; flaps=0; gear_down = 0;}
  2217. if (num_pti==7){asel1 = 30; flaps=0; gear_down = 0;}
  2218. if (num_pti==8){asel1 = 30; flaps=0; gear_down = 0;}
  2219. if (num_pti==9){asel1 = 20; flaps=2; gear_down = 0;}
  2220. if (num_pti==10){asel1 =10; flaps=3; gear_down = 1;}
  2221.  
  2222. inc_num_pt1_autorisee =0; // pour éviter d'incrémenter plusieurs fois lorsqu'on est proche du point
  2223. }
  2224. }
  2225. }
  2226.  
  2227.  
  2228.  
  2229. void tour_de_piste()
  2230. {
  2231. // cheminement entre points dont la position est définie par un vecteur partant du centre de la piste (angle & distance)
  2232. // num_pti est incrémenté dans la fonction 'nav_to_pti()' lorsque le point en cours est atteint
  2233.  
  2234.  
  2235. float dst;
  2236. float alpha;
  2237. uint8_t n2=0;
  2238.  
  2239. if (extremite_pst == 'A') {n2 = num_pti;}
  2240. if (extremite_pst == 'B') {n2 = 11-num_pti;} //même trajectoire parcourue en sens inverse
  2241.  
  2242. if (n2 ==1) {alpha= 0; dst = 1.0;}
  2243. if (n2 ==2) {alpha= 0; dst = 4.0;}
  2244. if (n2 ==3) {alpha= 16; dst = 5.1;}
  2245. if (n2 ==4) {alpha= 46; dst = 4.0;}
  2246. if (n2 ==5) {alpha= 72.3; dst = 3.1;}
  2247. if (n2 ==6) {alpha= 109; dst = 3.1;}
  2248. if (n2 ==7) {alpha= 135; dst = 4.0;}
  2249. if (n2 ==8) {alpha= 163; dst = 5.1;}
  2250. if (n2 ==9) {alpha= 180; dst = 4.0;}
  2251. if (n2 ==10){alpha= 180; dst = 1.0;}
  2252.  
  2253. dst *= 1.5; // taille de la figure
  2254.  
  2255.  
  2256. if (read_bit(flags, bit_sens_circling) == 1) {alpha = 360-alpha;} // trajectoire miroir
  2257.  
  2258. calcul_pti(alpha, dst, &lat_pti, &lon_pti);
  2259.  
  2260. // variante :
  2261. //calcul_pti(30.0*num_pti, 10.0, &lat_pti, &lon_pti); // points disposés en cercle, à 10NM
  2262.  
  2263. }
  2264.  
  2265.  
  2266.  
  2267.  
  2268.  
  2269. void calcul_erreur_position() // pour savoir si l'avion se trouve exactement dans l'axe de la piste
  2270. {
  2271.  
  2272. float x, y;
  2273. float x1, x2;
  2274. float y1, y2;
  2275. float p;
  2276. float s;
  2277. //float erreur;
  2278.  
  2279. x=lon_avion;
  2280. y=lat_avion;
  2281.  
  2282. x1 = liste_bali[num_bali].lon_A; x2 = liste_bali[num_bali].lon_B;
  2283. y1 = liste_bali[num_bali].lat_A; y2 = liste_bali[num_bali].lat_B;
  2284.  
  2285. p = (y2-y1) / (x2-x1); // pente de la droite A-B
  2286.  
  2287. s= y1+ p * (x-x1);
  2288.  
  2289. erreur_axe = y-s;
  2290.  
  2291. //affi_float_test(erreur_axe, 110, 2, BLANC, BLEU); // pour test
  2292.  
  2293.  
  2294.  
  2295. }
  2296.  
  2297.  
  2298.  
  2299. void desengage_autoland()
  2300. {
  2301. raz_bit(&flags, bit_autoland);
  2302. efface_cadre_bas(NOIR);
  2303. //init_affi_HA();
  2304.  
  2305. voyant_L.affiche(BLANC, GRIS_FONCE);
  2306. voyant_G.affiche(BLANC, GRIS_FONCE);
  2307. /*
  2308. target_speed =180;
  2309. locks_type = "ALT";
  2310. asel1 = 30; // consigne altitude 30 -> 3000ft
  2311. climb_rate=0; // taux de montée (négatif pour descendre - sert pour attérissage automatique)
  2312. hdg1 = cap;
  2313. RAZ_chrono();
  2314. */
  2315. }
  2316.  
  2317.  
  2318. void affiche_etats_flags() // certains "voyants" en haut à gauche
  2319. {
  2320. if (read_bit(flags, bit_rudder_decol) == 1) { voyant_RD.affiche(NOIR, VERT);}
  2321. else if (read_bit(flags, bit_rudder_attero) == 1){ voyant_RD.affiche(NOIR, JAUNE); }
  2322. else { voyant_RD.affiche( BLANC, GRIS_TRES_FONCE); }
  2323.  
  2324. if (read_bit(flags, bit_nav_to_piste) == 1) {voyant_route.affiche(BLANC, BLEU);}
  2325. else if (read_bit(flags, bit_nav_to_ptAA) == 1) {voyant_route.affiche(NOIR, JAUNE);}
  2326. else if (read_bit(flags, bit_nav_to_ptBB) == 1) {voyant_route.affiche(NOIR, JAUNE);}
  2327.  
  2328. else {voyant_route.affiche(BLANC, GRIS_TRES_FONCE);}
  2329.  
  2330. if (read_bit(flags, bit_atterrissage)==1){voyant_ATT.affiche(NOIR, VERT);}
  2331. else {voyant_ATT.affiche(BLANC, GRIS_TRES_FONCE);}
  2332. }
  2333.  
  2334.  
  2335.  
  2336.  
  2337. void auto_landing()
  2338. {
  2339. // on passera en boucle dans cette fonction
  2340. /**
  2341.  voir: https://en.wikipedia.org/wiki/Autoland
  2342.  
  2343.  Approche automatique
  2344.  CAPTURE l'avion et le POSE !
  2345.  
  2346.  LES CONSEILS QUI SUIVENT ne concernent que l'utilisation du simulateur de vol FlightGear connecté aux ESP32
  2347.  et le choix du Citation X comme avion.
  2348.  c'est à dire qu'ils ne doivent en aucun cas servir pour le pilotage d'un avion réel.
  2349.  
  2350.  
  2351.  -vitesse conseillée : 140kts, 160kts max
  2352.  -distance conseillée : entre 20 et 10 nautiques
  2353.  -avec une trajectoire qui recoupe l'axe de la piste, < 90°
  2354.  (si capture à moins de 10 NM, la trajectoire sera difficilement corrigée -> remise des gaz ou crash au choix !)
  2355.  -hauteur conseillée : 3000ft à 10NM (= niveau 30)
  2356.  
  2357.  -volets sortis 2 puis 3
  2358.  à priori pas d'AF si vitesse correcte
  2359.  
  2360.  -sortir le train !
  2361.  -allumer feux d'atterrissage
  2362.  
  2363.  notes: l'autopilot se désengage automatiquement (par FlightGear) sous 100ft de hauteur
  2364.  (réglable, voir la variable 'hauteur_mini_autopilot' au début de ce programme)
  2365.  
  2366. ce qui suit est actuellement devenu automatique dans les nouvelles versions :
  2367.  
  2368.  (Donc garder le manche en main pour l'arrondi et le touché final, en surveillant
  2369.  - la hauteur
  2370.  - la vitesse
  2371.  - le pitch
  2372.  - position des volets
  2373.  - éventuellement petit coup d'AF (aérofreins -> CTRL + B au clavier)
  2374.  - si piste très courte, inverseurs de poussée (au sol) + gaz (touche 'suppr')
  2375.  - toutefois si approche visiblement trop courte ou trop longue, pas d'attéro kamikaze ! -> remise des gaz !!
  2376.  - si système visuel "papi" présent, le respecter !!
  2377.  
  2378.  TOUTEFOIS :
  2379.  - on peut obtenir un posé 100% auto en anticipant un cabrage de l'avion pour faire l'arrondi dans les règles de l'art
  2380.  avec posé du train principal en premier, puis ensuite la roulette de nez. Pour cela :
  2381.  - en fin de finale, à une hauteur de 100 feet, dès le désengagement de l'autopilot de FlightGear:
  2382.  -gaz au mini et sortir les AF. La vitesse diminue, et l'avion se cabre un peu
  2383.  pour ne pas plonger... et il finit par poser le train principal.
  2384.  - dès que ce touché est fait, freiner légèrement -> la roue avant va alors à son tour toucher la piste, ce qui permet
  2385.  à l'auto-rudder de guider la trajectoire suivant l'axe de la piste.
  2386.  ( Tant que la roue avant ne touche pas, l'auto-rudder, qui n'agit alors qu'aérodynamiquement sur la gouverne de direction,
  2387.  n'est pas assezefficace).
  2388.  
  2389.  Le tout suivi d'un freinage, on pose avec arrêt complet sur 850m (sans faire craquer la structure...).
  2390.  Avec les inverseurs de poussée, on doit pouvoir faire bien mieux encore. Quant au porte-avion, il est normalement
  2391.  équipé d'un câble de retenue qu'on accroche avec une perche (sur un jet militaire, sans doute pas avec notre Cessna Citation X)
  2392.  
  2393. **/
  2394.  
  2395. String s1;
  2396. float alti1;
  2397. float GPS_distance_seuil_piste;
  2398.  
  2399. TFT480.setFreeFont(FSS9);
  2400.  
  2401. TFT480.setTextColor(GRIS_FONCE, NOIR);
  2402. //TFT480.fillRect(180, 0, 20, 16, JAUNE); // JAUNE pour test. efface 1/2 le bandeau d'information en haut
  2403.  
  2404. voyant_L.affiche(BLANC, GRIS_TRES_FONCE);
  2405.  
  2406. voyant_G.affiche(BLANC, GRIS_TRES_FONCE);
  2407.  
  2408. voyant_APP.affiche(BLANC, GRIS_TRES_FONCE);
  2409.  
  2410. uint8_t AZ =0; // flag azimut OK
  2411. uint8_t GL =0; // flag glide OK
  2412.  
  2413. //voyant_descente_GPS.affiche(BLANC, GRIS_TRES_FONCE);
  2414.  
  2415.  
  2416. //--------------------- (si autoland engagé, sinon on ne fait rien de plus)--------------
  2417.  
  2418. //TFT480.fillRect(HA_x0-40, HA_y0+80, 87, 15, HA_SOL); // efface "APP"
  2419.  
  2420. if (read_bit(flags, bit_autoland) == 1)
  2421. {
  2422. if (GPS_distance_piste > 25.0)
  2423. {
  2424. return; // rien de plus
  2425. }
  2426. else
  2427. {
  2428. //affi_float_test(liste_bali[num_bali].orient_pisteAB,110, 2, BLANC, BLEU); // pour test
  2429. //affi_float_test(GPS_azimut_piste,110, 3, BLANC, GRIS_FONCE); // pour test
  2430.  
  2431. voyant_APP.affiche(NOIR, JAUNE);
  2432.  
  2433.  
  2434. // -------------------- AZIMUT -----------------------------------------------------------------
  2435.  
  2436. float op1;
  2437. find_sens_approche();
  2438. affi_sens_APP(); // en haut à droite
  2439.  
  2440. if (sens_app_effectif == sens_AB) {op1 = orient_pisteAB;}
  2441. if (sens_app_effectif == sens_BA) {op1 = orient_pisteBA;}
  2442. //s1 = String(op1,1);
  2443. //TFT480.drawString(s1, 0, 250);
  2444.  
  2445.  
  2446. float delta_AZM = op1 -GPS_azimut_piste;
  2447.  
  2448. //affi_float_test(delta_AZM,110, 2, VERT, GRIS_FONCE); // pour test
  2449.  
  2450. delta_AZM *= 20.0;
  2451. borne_in(&delta_AZM, -35.0, 35.0);
  2452.  
  2453. //if((delta_AZM >-10.0)&&(delta_AZM <10.0))
  2454. if (is_in(delta_AZM, -10.0, 10.0)==1)
  2455. {
  2456. voyant_L.affiche(NOIR, VERT);
  2457. AZ=1;
  2458. }
  2459.  
  2460. affi_localizer(delta_AZM * -2.5);
  2461.  
  2462. hdg1 = round(op1 - delta_AZM);
  2463.  
  2464.  
  2465. //affi_float_test(hdg1,110, 5, VERT, GRIS_FONCE); // pour test
  2466.  
  2467. // -------------------- VITESSE -----------------------------------------------------------------
  2468.  
  2469. if ( (GPS_distance_piste < 20.0) && (target_speed>160) ) {target_speed =160;}
  2470. if ( (GPS_distance_piste < 10.0) && (target_speed>150) ) {target_speed =150;}
  2471. if ( (GPS_distance_piste < 5.0) && (target_speed>140) ) {target_speed =140;}
  2472. if ( (GPS_distance_piste < 2.0) && (target_speed>130) ) {target_speed =130;}
  2473. if ( (GPS_distance_piste < 1.0) && (target_speed>120) ) {target_speed =120;}
  2474.  
  2475. if ((vitesse - target_speed) > 5 ) {speedbrake = 1.0;} else {speedbrake = 0;}
  2476.  
  2477. // -------------------- HAUTEUR -----------------------------------------------------------------
  2478.  
  2479. //voyant_descente_GPS.affiche(NOIR, JAUNE);
  2480. float longueur_piste_NM = longueur_piste / 1852.0;
  2481. GPS_distance_seuil_piste = GPS_distance_piste - (longueur_piste_NM/2.0) + 0.1;
  2482. // Rappel : "GPS_distance_piste" est la distance au point CENTRAL de la piste
  2483.  
  2484. TFT480.setFreeFont(FM12);
  2485. TFT480.setTextColor(BLANC);
  2486. s1=String(GPS_distance_seuil_piste, 1);
  2487. s1+= " NM";
  2488. TFT480.fillRect(150, 300, 100, 25, GRIS_AF); // efface
  2489. TFT480.drawString(s1, 150, 300);
  2490.  
  2491.  
  2492. //affi_float_test(GPS_distance_seuil_piste, 110, 2, BLANC, BLEU); // pour test
  2493.  
  2494. float alti_correcte = liste_bali[num_bali].altitude + 300.0 * GPS_distance_seuil_piste;
  2495. if (alti_correcte > 3000) {alti_correcte = 3000;}
  2496.  
  2497. affi_asel(alti_correcte);
  2498.  
  2499. // soit 3000ft pour 10 nautiques -> pente 5%
  2500. //sachant que la ref de position est située au milieu de la longueur de la piste
  2501.  
  2502. //affi_float_test(alti_correcte, 110, 2, BLANC, BLEU); // pour test
  2503.  
  2504. float erreur_altitude = altitude_GPS - alti_correcte;
  2505. //affi_float_test(erreur_altitude, 110, 3, BLANC, BLEU); // pour test
  2506.  
  2507. if((erreur_altitude > -20)&& (erreur_altitude < 20))
  2508. {
  2509. //voyant_G.affiche(NOIR, VERT);
  2510. }
  2511.  
  2512. affi_index_lateral( - erreur_altitude / 3.0); // affiche les triangles roses latéraux
  2513.  
  2514. /**Rappels :
  2515. 1 NM (nautical mile ou 'nautique') = 1852 m
  2516. 1 feet = 0,3048 m
  2517. 1 NM = 1852/0.3048 = 6076.12 feet
  2518. 1 noeud (nd) = 1NM/h = 1852/3600 = 0.51444 m/s
  2519.  
  2520. début de descente (5%) vers la piste : 3000ft à 10NM, vitesse 150 nd (par exemple)
  2521. v=150*0.51444 = 77.17m/s
  2522. temps pour parcourir la distance : v=d/t
  2523. t=d/v = 10*1852m / 77.17 = 240 s (soit 4 minutes)
  2524.  
  2525. taux de descente = 3000ft/240s = 12.5 fps
  2526. **/
  2527. if ((GPS_distance_piste < 10) && (hauteur_AAL > 1500)) // && (hauteur_AAL <= 6000)
  2528. {
  2529. // initialisation de l'approche auto (palier puis descente)
  2530. //voyant_descente_GPS.affiche(NOIR, VERT);
  2531.  
  2532. locks_type = "VS"; // bascule le pilote auto de FG en mode vertical speed
  2533.  
  2534. //climb_rate = -5.0;
  2535.  
  2536. }
  2537.  
  2538. //if ((GPS_distance_piste < 30) && (hauteur_AAL < (liste_bali[num_bali].niveau_de_vol_mini * 100) ) )
  2539. if ((GPS_distance_piste < 10) && (hauteur_AAL < 8000) )
  2540. {
  2541. // descente
  2542. // correction du taux de descente (climb_rate) pour respecter la pente à 3° (=5%)
  2543. voyant_G.affiche(NOIR, VERT);
  2544. GL=1;
  2545.  
  2546. //if (erreur_altitude > 4) {climb_rate -= 5; }
  2547. //if (erreur_altitude < -4) {climb_rate += 5; }
  2548.  
  2549. climb_rate = erreur_altitude * -1.5;
  2550. if (climb_rate > +30) {climb_rate = +30;}
  2551. if (climb_rate < -50) {climb_rate = -50;}
  2552.  
  2553. //affi_float_test( erreur_altitude, 110, 4, NOIR, JAUNE); // pour test
  2554. }
  2555.  
  2556. // -------------------- FLAPS -----------------------------------------------------------------
  2557.  
  2558. if (GPS_distance_piste < 8)
  2559. {
  2560. flaps = 2;
  2561. landing_light1=1;
  2562. landing_light2=1;
  2563. }
  2564.  
  2565.  
  2566. if (GPS_distance_piste < 4)
  2567. {
  2568. flaps = 3; // participe grandement au freinage de l'avion en fin de finale
  2569. gear_down = 1;
  2570. }
  2571.  
  2572.  
  2573. if (GPS_distance_piste < 2)
  2574. {
  2575. flaps = 4;
  2576. }
  2577.  
  2578.  
  2579. // -------------------- FIN -----------------------------------------------------------------
  2580.  
  2581. if (hauteur_AAL < hauteur_mini_autopilot) // signe la fin des appels de cette fonction
  2582. {
  2583.  
  2584. raz_bit(&flags, bit_rudder_decol);
  2585. raz_bit(&flags, bit_nav_to_pti);
  2586.  
  2587. set_bit(&flags, bit_rudder_attero);// la trajectoire près du sol -> par gouverne de direction
  2588.  
  2589.  
  2590. throttle = 1.0; // gaz au minimum
  2591. target_speed = 0;
  2592. //trim_elevator = 0.0;
  2593.  
  2594. raz_bit(&flags, bit_FG_AP);
  2595. desengage_autoland(); // donc on ne repassera plus dans la fonction (ici)
  2596.  
  2597. brake_left =0;
  2598. brake_right =0;
  2599. set_bit(&flags, bit_atterrissage); // on passera dorénavant en boucle dans la fonction "atterrissage()"
  2600.  
  2601. affiche_etats_flags();
  2602. }
  2603.  
  2604. if( (AZ==1) && (GL==1) ) {voyant_APP.affiche(NOIR, VERT);}
  2605.  
  2606. /**
  2607. alti1 = 3.0*GPS_distance_piste + gnd_elv/100.0 -1;
  2608. if (alti1 < asel1) //empêche de (re)monter lors de la capture ILS, reste en palier le cas échéant
  2609. {
  2610. asel1 = alti1;
  2611. }
  2612. **/
  2613. }
  2614.  
  2615. // =============================================================================================================================
  2616. }
  2617.  
  2618. //------------------------------------------------------------------------------------------------------------------------------
  2619.  
  2620. }
  2621.  
  2622.  
  2623.  
  2624. void prepare_decollage()
  2625. {
  2626. raz_bit(&flags, bit_atterrissage);
  2627. raz_bit(&flags, bit_FG_AP);
  2628. raz_bit(&flags, bit_autoland);
  2629. raz_bit(&flags, bit_route);
  2630.  
  2631. set_bit(&flags, bit_nav_to_piste); // par défaut
  2632.  
  2633. raz_bit(&flags, bit_nav_to_pti);
  2634. raz_bit(&flags, bit_nav_to_ptAA);
  2635. raz_bit(&flags, bit_nav_to_ptBB);
  2636.  
  2637. raz_bit(&flags, bit_rudder_attero);
  2638.  
  2639. set_bit(&flags, bit_au_sol);
  2640.  
  2641. locks_type = "ALT";
  2642. asel1 = 30;
  2643. target_speed = 180;
  2644.  
  2645. find_END_RWY_angl();
  2646.  
  2647. set_bit(&flags, bit_rudder_decol);
  2648.  
  2649. set_bit(&flags, bit_decollage);
  2650. flaps = 3;
  2651.  
  2652. msg_to_send="OK to take-off";
  2653.  
  2654. }
  2655.  
  2656.  
  2657.  
  2658.  
  2659. void decollage()
  2660. // on passera en boucle dans cette fonction ; voir aussi la fonction "prepare_decollage()"
  2661. {
  2662.  
  2663. asel1 = liste_bali[num_bali].niveau_de_vol_mini;
  2664.  
  2665. speedbrake =0;
  2666. throttle = -1.0;
  2667.  
  2668. brake_left =0;
  2669. brake_right =0;
  2670. landing_light1=1;
  2671. landing_light2=1;
  2672.  
  2673. trim_elevator = -0.3; // bonne valeur pour décoller
  2674. //if (hauteur_AAL >10){trim_elevator=-0.25;} // pour ne pas grimper aux arbres
  2675. //if (hauteur_AAL >15){trim_elevator=-0.2;}
  2676.  
  2677. if (vitesse > 140){trim_elevator=-0.25;} // pour ne pas grimper aux arbres
  2678. if (vitesse > 150){trim_elevator=-0.2;}
  2679.  
  2680. affi_elevator();
  2681.  
  2682. if (hauteur_AAL > 10)
  2683. {
  2684. flaps = 2;
  2685. }
  2686.  
  2687. if (hauteur_AAL > 20)
  2688. {
  2689. flaps = 0;
  2690. set_bit(&flags, bit_FG_AP); // engage Autopilot de FlightGear
  2691. speed_ctrl=true;
  2692.  
  2693. msg_to_send = "engage AP";
  2694. TFT480.setFreeFont(FF6);
  2695. affi_message (msg_to_send, 130, 200, 200, 100, VERT, HA_SOL, 1); // ici
  2696.  
  2697. raz_bit(&flags, bit_rudder_decol);
  2698. rudder=0;
  2699. gear_down = 0;
  2700. landing_light1=0;
  2701. landing_light2=0;
  2702. raz_bit(&flags, bit_decollage); // fin des appels de cette fonction
  2703.  
  2704. }
  2705. }
  2706.  
  2707.  
  2708. void atterrissage()
  2709. // on passera en boucle dans cette fonction
  2710. {
  2711. // premier terme = léger cabré pour l'arrondi
  2712. // deuxième terme diminue ce cabré tant que la hauteur est grande
  2713. // troisième terme = asservissement de l'angle de tangage de façon à stabiliser l'ensemble
  2714. //trim_elevator = -0.5+ (float)hauteur_AAL/1500.0 + (tangage / 20.0);
  2715. //trim_elevator = -0.3 + (tangage / 20.0);
  2716.  
  2717. trim_elevator = -0.3 + (hauteur_AAL / 20.0) + (tangage / 20.0);
  2718.  
  2719. throttle = 1.0; // gaz au mini
  2720.  
  2721. if(hauteur_AAL < 20) {speedbrake = 1.0;}
  2722.  
  2723. set_bit(&flags, bit_rudder_attero);
  2724. locks_type = "VS"; // bascule le pilote auto de FG en mode vertical speed
  2725. //climb_rate = -40;
  2726.  
  2727.  
  2728.  
  2729. if(is_in(vitesse, 130, 120) == 1) { brake_left =0.6;};
  2730. if(vitesse < 120) { brake_left =1.0;};
  2731.  
  2732. brake_right = brake_left;
  2733.  
  2734. }
  2735.  
  2736.  
  2737.  
  2738. void roulage()
  2739. // sur taxiways
  2740. // on passera en boucle dans cette fonction
  2741. {
  2742. // throttle = 0.80; // 1.0 -> ralentit; 0 -> mi-gaz ; -1.0 -> plein gaz
  2743.  
  2744. brake_left =0;
  2745. brake_right =0;
  2746. flaps = 0;
  2747. if (vitesse < 15) {throttle -= 0.01;}
  2748. if (vitesse > 15) {throttle += 0.01;}
  2749.  
  2750. borne_in(&throttle, 0.5, 1.0);
  2751.  
  2752. calcul_erreur_position();
  2753. }
  2754.  
  2755.  
  2756.  
  2757. void affi_localizer(float valeur)
  2758. {
  2759. //ILS (maintenant GPS) dans le plan horizontal; affiche l'erreur de position par rapport à l'axe de la piste
  2760.  
  2761. uint16_t y1 = HA_y0-HA_h-14;
  2762.  
  2763. uint16_t couleur1 = ROSE;
  2764.  
  2765. loc = HA_x0 + valeur;
  2766.  
  2767. if ( loc < (HA_x0-HA_w+5)) {loc = HA_x0-HA_w+5; couleur1 = GRIS;}
  2768. if ( loc > (HA_x0+HA_w-5)) {loc= HA_x0+HA_w-5; couleur1 = GRIS;}
  2769.  
  2770.  
  2771. TFT480.fillRect(HA_x0-HA_w, y1, 2*HA_w, 9, GRIS_TRES_FONCE);
  2772. TFT480.drawLine(HA_x0, y1-5, HA_x0, y1+5, BLANC);
  2773.  
  2774. affi_indexV(loc, y1, 1, couleur1); // petit triangle rose en haut, se déplaçant horizontalement
  2775.  
  2776. memo_loc=loc;
  2777. }
  2778.  
  2779.  
  2780.  
  2781. void affi_index_lateral(uint16_t position_i)
  2782. {
  2783. // petits triangles roses de chaque côtés du PFD
  2784. // (à mi-hauteur du PFD si =0)
  2785.  
  2786. uint16_t x1 = 75;
  2787. uint16_t x2 = 332;
  2788.  
  2789. uint16_t position_V = HA_y0 - position_i;
  2790.  
  2791. TFT480.fillRect(x1, 30, 9, 2*HA_h, GRIS_TRES_FONCE); // efface
  2792. TFT480.fillRect(x2, 30, 9, 2*HA_h, GRIS_TRES_FONCE); // efface
  2793.  
  2794. TFT480.drawRect(x1, HA_y0, 12, 5, BLANC);
  2795. TFT480.drawRect(x2, HA_y0, 12, 5, BLANC);
  2796.  
  2797. uint16_t couleur1 = ROSE;
  2798. if ( position_V < (HA_y0-HA_h+5)) {position_V = HA_y0-HA_h+5; couleur1 = GRIS;}
  2799. if ( position_V > (HA_y0+HA_h-5)) {position_V = HA_y0+HA_h-5; couleur1 = GRIS;}
  2800.  
  2801. affi_indexH(x1, position_V, 1, couleur1);
  2802. affi_indexH(x2+8, position_V, -1, couleur1);
  2803. }
  2804.  
  2805.  
  2806.  
  2807. void trace_arc_gradu()
  2808. {
  2809. //arc gradué en haut au centre, indiquant la valeur de l'inclinaison
  2810.  
  2811. float angle;
  2812. //Draw_arc_elliptique(HA_x0, 120, 120, 80, 0.6, 2.6, BLANC);
  2813.  
  2814.  
  2815. for(uint8_t n=0; n<9; n++ )
  2816. {
  2817. angle =30+ n*15; // 1 tiret tous les 15 degrés
  2818. float pourcent = 0.9;
  2819. if (((n+2)%2) == 0) {pourcent = 0.8;}
  2820.  
  2821. affi_rayon1(HA_x0, HA_y0+10, 110, degTOrad(angle), pourcent, BLANC, false); // tirets de graduation
  2822. }
  2823. }
  2824.  
  2825.  
  2826.  
  2827. void rotation1()
  2828. {
  2829. // consigne de cap
  2830. // acquisition de l'encodeur pas à pas (1)
  2831. if ( millis() - TEMPO >= timer1 )
  2832. {
  2833. timer1 = millis();
  2834. bool etat = digitalRead(rot1b);
  2835. if(etat == 0) { hdg1+=1;} else { hdg1-=1;}
  2836. if (hdg1<0){hdg1=359;}
  2837.  
  2838. if (hdg1>359){hdg1=0;}
  2839. }
  2840. }
  2841.  
  2842.  
  2843.  
  2844. void rotation2()
  2845. {
  2846. // consigne d'altitude
  2847. // acquisition de l'encodeur pas à pas (2)
  2848. if ( millis() - TEMPO >= timer2 )
  2849. {
  2850. timer2 = millis();
  2851. bool etat = digitalRead(rot2b);
  2852. if(etat == 0) { asel1+=1; } else { asel1-=1; }
  2853. if (asel1<1){asel1=1;} // 100 pieds -> 30m
  2854. if (asel1>600){asel1=600;}
  2855. }
  2856. }
  2857.  
  2858.  
  2859.  
  2860. void init_SDcard()
  2861. {
  2862. String s1;
  2863.  
  2864. TFT480.fillRect(0, 0, 480, 320, NOIR); // efface
  2865. TFT480.setTextColor(BLANC, NOIR);
  2866. TFT480.setFreeFont(FF1);
  2867.  
  2868. uint16_t y=0;
  2869.  
  2870. TFT480.drawString("PRIMARY FLIGHT DISPLAY", 0, y);
  2871. y+=20;
  2872.  
  2873. s1="version " + version;
  2874. TFT480.drawString(s1, 0, y);
  2875.  
  2876. y+=40;
  2877. TFT480.setTextColor(VERT, NOIR);
  2878. TFT480.drawString("Init SDcard", 0, y);
  2879. y+=20;
  2880.  
  2881.  
  2882. if(!SD.begin())
  2883. {
  2884. TFT480.drawString("Card Mount Failed", 0, y);
  2885. delay (2000);
  2886. TFT480.fillRect(0, 0, 480, 320, NOIR); // efface
  2887. return;
  2888. }
  2889.  
  2890.  
  2891. uint8_t cardType = SD.cardType();
  2892.  
  2893. if(cardType == CARD_NONE)
  2894. {
  2895. TFT480.drawString("No SDcard", 0, y);
  2896. delay (2000);
  2897. TFT480.fillRect(0, 0, 480, 320, NOIR); // efface
  2898. return;
  2899. }
  2900.  
  2901. flag_SDcardOk=1;
  2902.  
  2903. TFT480.drawString("SDcard Type: ", 0, y);
  2904. if(cardType == CARD_SD) {TFT480.drawString("SDSC", 150, y);}
  2905. else if(cardType == CARD_SDHC) {TFT480.drawString("SDHC", 150, y);}
  2906.  
  2907. y+=20;
  2908.  
  2909. uint32_t cardSize = SD.cardSize() / (1024 * 1024);
  2910. s1=(String)cardSize + " GB";
  2911. TFT480.drawString("SDcard size: ", 0, y);
  2912. TFT480.drawString(s1, 150, y);
  2913.  
  2914. // listDir(SD, "/", 0);
  2915.  
  2916. //Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024));
  2917. //Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024));
  2918.  
  2919. delay (1000);
  2920. TFT480.fillRect(0, 0, 480, 320, NOIR); // efface
  2921.  
  2922. }
  2923.  
  2924.  
  2925. void efface_sprite_trajectoire()
  2926. {
  2927. SPR_trajectoire.fillSprite(TFT_BLACK);
  2928. SPR_trajectoire.drawString("pente 5%", 170, 1 );
  2929. }
  2930.  
  2931.  
  2932.  
  2933.  
  2934. void init_sprites()
  2935. {
  2936. // sprites représentant les lettres 'N' 'S' 'E' 'O' qui seront affichées sur un cercle, inclinées donc.
  2937.  
  2938. SPR_E.setFreeFont(FF1);
  2939. SPR_E.setTextColor(JAUNE);
  2940. SPR_E.createSprite(SPR_W, SPR_H);
  2941. SPR_E.setPivot(SPR_W/2, SPR_H/2); // Set pivot relative to top left corner of Sprite
  2942. SPR_E.fillSprite(GRIS_TRES_FONCE);
  2943. SPR_E.drawString("E", 2, 1 );
  2944.  
  2945. SPR_N.setFreeFont(FF1);
  2946. SPR_N.setTextColor(JAUNE);
  2947. SPR_N.createSprite(SPR_W, SPR_H);
  2948. SPR_N.setPivot(SPR_W/2, SPR_H/2);
  2949. SPR_N.fillSprite(GRIS_TRES_FONCE);
  2950. SPR_N.drawString("N", 2, 1 );
  2951.  
  2952. SPR_O.setFreeFont(FF1);
  2953. SPR_O.setTextColor(JAUNE);
  2954. SPR_O.createSprite(SPR_W, SPR_H);
  2955. SPR_O.setPivot(SPR_W/2, SPR_H/2);
  2956. SPR_O.fillSprite(GRIS_TRES_FONCE);
  2957. SPR_O.drawString("W", 2, 1 );
  2958.  
  2959. SPR_S.setFreeFont(FF1);
  2960. SPR_S.setTextColor(JAUNE);
  2961. SPR_S.createSprite(SPR_W, SPR_H);
  2962. SPR_S.setPivot(SPR_W/2, SPR_H/2);
  2963. SPR_S.fillSprite(GRIS_TRES_FONCE);
  2964. SPR_S.drawString("S", 2, 1 );
  2965.  
  2966. SPR_trajectoire.setFreeFont(FF1);
  2967. SPR_trajectoire.setTextColor(JAUNE);
  2968. SPR_trajectoire.createSprite(292, 88);
  2969. efface_sprite_trajectoire();
  2970. }
  2971.  
  2972.  
  2973.  
  2974. void init_Leds() // pour l'affichage des données, voir la fonction "affi_data_piste()"
  2975. {
  2976. uint16_t x0 = 464;
  2977. uint16_t y0 = 0;
  2978. uint16_t xi=x0;
  2979. uint16_t yi=y0;
  2980.  
  2981.  
  2982. Led1.init(xi,yi, 10, 10);
  2983. Led1.set_couleur(ROUGE);
  2984. Led1.allume();
  2985.  
  2986. yi+=10;
  2987.  
  2988. Led2.init(xi,yi, 10, 10);
  2989. Led2.set_couleur(JAUNE);
  2990. Led2.allume();
  2991.  
  2992. yi+=10;
  2993.  
  2994. Led3.init(xi,yi, 10, 10);
  2995. Led3.set_couleur(VERT);
  2996. Led3.allume();
  2997.  
  2998. yi+=10;
  2999.  
  3000. Led4.init(xi,yi, 10, 10);
  3001. Led4.set_couleur(BLEU);
  3002. Led4.allume();
  3003.  
  3004. yi+=10;
  3005.  
  3006. Led5.init(xi,yi, 10, 10);
  3007. Led5.set_couleur(VIOLET1);
  3008. Led5.allume();
  3009.  
  3010. delay(100);
  3011.  
  3012. }
  3013.  
  3014.  
  3015.  
  3016. void int16_to_array(int16_t valeur_i)
  3017. {
  3018. // prépare la chaine de caract à zéro terminal pour l'envoi
  3019. // Remarque : 2^16 -1 = 65535 -> 5 caractères)
  3020.  
  3021. String s1= (String) valeur_i;
  3022. uint8_t len1 = s1.length();
  3023. for(int n=0; n<len1; n++)
  3024. {
  3025. var_array16[n]=s1[n];
  3026. }
  3027. var_array16[len1]=0; // zéro terminal -> chaine C
  3028. }
  3029.  
  3030.  
  3031.  
  3032. void int32_to_array(int32_t valeur_i)
  3033. {
  3034. // prépare la chaine de caract à zéro terminal pour l'envoi
  3035. // Remarque : 2^32 -1 = 4294967295 -> 10 caractères
  3036.  
  3037. String s1= (String) valeur_i;
  3038. uint8_t len1 = s1.length();
  3039. for(int n=0; n<len1; n++)
  3040. {
  3041. var_array32[n]=s1[n];
  3042. }
  3043. var_array32[len1]=0; // zéro terminal -> chaine C
  3044. }
  3045.  
  3046.  
  3047. void string_to_array(String str_i)
  3048. {
  3049. // prépare la chaine de caract à zéro terminal pour l'envoi
  3050.  
  3051. uint8_t len1 = str_i.length();
  3052. for(int n=0; n<len1; n++)
  3053. {
  3054. var_array32[n]=str_i[n];
  3055. }
  3056. var_array32[len1]=0; // zéro terminal -> chaine C
  3057. }
  3058.  
  3059.  
  3060. void annule_tout()
  3061. {
  3062. msg_to_send = "RAZ";
  3063. raz_bit(&flags, bit_decollage);
  3064. raz_bit(&flags, bit_atterrissage);
  3065. raz_bit(&flags, bit_FG_AP);
  3066. raz_bit(&flags, bit_autoland);
  3067. raz_bit(&flags, bit_route);
  3068. raz_bit(&flags, bit_nav_to_piste);
  3069. raz_bit(&flags, bit_nav_to_pti);
  3070. raz_bit(&flags, bit_circling);
  3071. raz_bit(&flags, bit_nav_to_ptAA);
  3072. raz_bit(&flags, bit_nav_to_ptBB);
  3073. raz_bit(&flags, bit_rudder_decol);
  3074. raz_bit(&flags, bit_roulage);
  3075. gear_down = 1;
  3076. //raz_bit(&flags, bit_rudder_attero);
  3077. locks_type = "ALT";
  3078. speed_ctrl=false;
  3079. trim_elevator=0;
  3080. throttle = 1.0;
  3081. speedbrake=0;
  3082. landing_light1=0;
  3083. landing_light2=0;
  3084.  
  3085. }
  3086.  
  3087.  
  3088.  
  3089.  
  3090. void setup()
  3091. {
  3092. Serial.begin(38400); // 19200
  3093.  
  3094. locks_type ="ALT";
  3095. raz_bit(&flags, bit_FG_AP); // pas d'engagement de l'autopilot de FlightGear à ce stade
  3096. WiFi.persistent(false);
  3097. WiFi.softAP(ssid, password); // Crée un réseau WiFi en mode privé (indépendant de celui de la box internet...)
  3098. IPAddress IP = WiFi.softAPIP();
  3099.  
  3100.  
  3101. server.on("/switch", HTTP_GET, [](AsyncWebServerRequest *request) // lecture des boutons de l'ESP du module SW
  3102. {
  3103. // attention: ce code est appelé par une interruption WiFi qui intervient hors timing. Donc pas d'affichage ici !!
  3104.  
  3105. argument_recu1 = request->arg("sw1"); // réception de l'argument n°1 de la requête
  3106. switches=argument_recu1; // réception des boutons du module SW
  3107. v_switches=switches.toInt();
  3108. flag_traiter_SW=1; //positionne ce drapeau afin que le traitement se fasse dans le timming général, pas ici !
  3109.  
  3110. int16_to_array(0);
  3111.  
  3112. argument_recu2 = request->arg("pot1"); // réception de l'argument n°2 de la requête
  3113. potar1=argument_recu2; // = "0".."255"
  3114. v_potar1 = -128 + potar1.toInt(); // centre autour de 0
  3115.  
  3116. float valeur1 = v_potar1 * v_potar1; // au carré pour avoir une bonne précision aux faibles débattements,
  3117. //et une bonne réponse à fond (taxi au sol)
  3118.  
  3119. //if (abs(v_potar1) > 100) {raz_bit(&flags, bit_rudder_attero);} // afin de pouvoir manoeuvrer sur les taxiways}
  3120.  
  3121. if (v_potar1<0) {valeur1 = -valeur1;} // because un carré est toujours positif, or on veut conserver le signe
  3122. rudder_manuel = valeur1 / 20000.0; // 10000.0 détermine la sensibilité de la gouverne de direction (lacet)
  3123.  
  3124. //cet array because la fonction "request->send_P()" n'accèpte pas directement le string
  3125. //rappel :
  3126. //void send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
  3127. //void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);
  3128.  
  3129. request->send_P(200, "text/plain", var_array16); // envoie comme réponse au client
  3130. });
  3131.  
  3132.  
  3133. server.on("/hdg", HTTP_GET, [](AsyncWebServerRequest *request) // consigne de cap
  3134. {
  3135. // attention: ce code est appelé par une interruption WiFi qui intervient hors timing. Donc pas d'affichage ici !!
  3136.  
  3137. argument_recu1 = request->arg("a1"); // reception de l'argument n°1 de la requête
  3138. num_bali=argument_recu1.toInt();
  3139.  
  3140. argument_recu2 = request->arg("swND");
  3141. flag_traiter_SW=1; //positionne ce drapeau afin que le traitement se fasse dans le timming général, pas ici !
  3142.  
  3143. v_switches_ND = argument_recu2.toInt();
  3144. switches_ND = String(v_switches_ND);
  3145.  
  3146.  
  3147. int16_to_array(hdg1);
  3148.  
  3149. //cet array because la fonction "request->send_P()" n'accèpte pas directement le string
  3150. //rappel :
  3151. //void send_P(int code, const String& contentType, const uint8_t * content, size_t len, AwsTemplateProcessor callback=nullptr);
  3152. //void send_P(int code, const String& contentType, PGM_P content, AwsTemplateProcessor callback=nullptr);
  3153.  
  3154. request->send_P(200, "text/plain", var_array16); // envoie hdg1 comme réponse au client
  3155. });
  3156.  
  3157. // réponses aux requêtes :
  3158. // VOIR la fonction "void interroge_WiFi()" dans le code du ND (l'affichage de la carte...)
  3159. // pour la réception des données qui suivent
  3160. server.on("/cap", HTTP_GET, [](AsyncWebServerRequest *request)
  3161. {
  3162. int16_to_array(cap); // prépare la chaine de caract à zéro terminal pour l'envoi
  3163. request->send_P(200, "text/plain", var_array16); // envoie réponse au client
  3164. });
  3165.  
  3166.  
  3167. server.on("/latitude", HTTP_GET, [](AsyncWebServerRequest *request) // latitude de l'avion
  3168. {
  3169. int32_t lati1 = (int32_t) (lat_avion * 10000.0);
  3170.  
  3171. int32_to_array(lati1);
  3172. request->send_P(200, "text/plain", var_array32); // envoie réponse au client
  3173. });
  3174.  
  3175.  
  3176. server.on("/longitude", HTTP_GET, [](AsyncWebServerRequest *request) // longitude de l'avion
  3177. {
  3178. int32_t longi1 = (int32_t) (lon_avion * 10000.0);
  3179.  
  3180. int32_to_array(longi1);
  3181. request->send_P(200, "text/plain", var_array32); // envoie réponse au client
  3182. });
  3183.  
  3184.  
  3185. server.on("/hauteur", HTTP_GET, [](AsyncWebServerRequest *request) // hauteur de l'avion / sol
  3186. {
  3187. int32_t haut1 = (int32_t) (hauteur_AAL * 10.0);
  3188.  
  3189. int32_to_array(haut1);
  3190. request->send_P(200, "text/plain", var_array32); // envoie réponse au client
  3191. });
  3192.  
  3193.  
  3194. server.on("/flags", HTTP_GET, [](AsyncWebServerRequest *request) // paramètres divers PFD -> ND & MCDU
  3195. {
  3196. argument_recu3 = request->arg("btMCDU"); // valeur reçue du module MCDU en tant qu'argument
  3197.  
  3198. v_bt_MCDU = argument_recu3.toInt();
  3199. flag_traiter_MCDU=1;
  3200.  
  3201. int32_to_array(flags); // valeur à envoyer
  3202. request->send_P