1 #############################################################################
2 # 777 Autopilot Flight Director System
5 # speed modes: THR,THR REF, IDLE,HOLD,SPD;
6 # roll modes : TO/GA,HDG SEL,HDG HOLD, LNAV,LOC,ROLLOUT,TRK SEL, TRK HOLD,ATT;
7 # pitch modes: TO/GA,ALT,V/S,VNAV PTH,VNAV SPD,VNAV ALT,G/S,FLARE,FLCH SPD,FPA;
8 # FPA range : -9.9 ~ 9.9 degrees
9 # VS range : -8000 ~ 6000
10 # ALT range : 0 ~ 50,000
11 # KIAS range : 100 ~ 399
12 # MACH range : 0.40 ~ 0.95
14 #############################################################################
16 #Usage : var afds = AFDS.new();
18 var copilot = func(msg) { setprop("/sim/messages/copilot",msg);}
22 var m = {parents:[AFDS]};
24 m.spd_list=["","THR","THR REF","HOLD","IDLE","SPD"];
26 m.roll_list=["","HDG SEL","HDG HOLD","LNAV","LOC","ROLLOUT",
27 "TRK SEL","TRK HOLD","ATT","TO/GA"];
29 m.pitch_list=["","ALT","V/S","VNAV PTH","VNAV SPD",
30 "VNAV ALT","G/S","FLARE","FLCH SPD","FPA","TO/GA"];
35 m.remaining_distance_log_last = 36000;
36 m.heading_change_rate = 0;
39 m.AFDS_node = props.globals.getNode("instrumentation/afds",1);
40 m.AFDS_inputs = m.AFDS_node.getNode("inputs",1);
41 m.AFDS_apmodes = m.AFDS_node.getNode("ap-modes",1);
42 m.AFDS_settings = m.AFDS_node.getNode("settings",1);
43 m.AP_settings = props.globals.getNode("autopilot/settings",1);
45 m.AP = m.AFDS_inputs.initNode("AP",0,"BOOL");
46 m.AP_disengaged = m.AFDS_inputs.initNode("AP-disengage",0,"BOOL");
47 m.AP_passive = props.globals.initNode("autopilot/locks/passive-mode",1,"BOOL");
48 m.AP_pitch_engaged = props.globals.initNode("autopilot/locks/pitch-engaged",1,"BOOL");
49 m.AP_roll_engaged = props.globals.initNode("autopilot/locks/roll-engaged",1,"BOOL");
51 m.FD = m.AFDS_inputs.initNode("FD",0,"BOOL");
52 m.at1 = m.AFDS_inputs.initNode("at-armed[0]",0,"BOOL");
53 m.at2 = m.AFDS_inputs.initNode("at-armed[1]",0,"BOOL");
54 m.alt_knob = m.AFDS_inputs.initNode("alt-knob",0,"BOOL");
55 m.autothrottle_mode = m.AFDS_inputs.initNode("autothrottle-index",0,"INT");
56 m.lateral_mode = m.AFDS_inputs.initNode("lateral-index",0,"INT");
57 m.vertical_mode = m.AFDS_inputs.initNode("vertical-index",0,"INT");
58 m.gs_armed = m.AFDS_inputs.initNode("gs-armed",0,"BOOL");
59 m.loc_armed = m.AFDS_inputs.initNode("loc-armed",0,"BOOL");
60 m.vor_armed = m.AFDS_inputs.initNode("vor-armed",0,"BOOL");
61 m.lnav_armed = m.AFDS_inputs.initNode("lnav-armed",0,"BOOL");
62 m.vnav_armed = m.AFDS_inputs.initNode("vnav-armed",0,"BOOL");
63 m.rollout_armed = m.AFDS_inputs.initNode("rollout-armed",0,"BOOL");
64 m.flare_armed = m.AFDS_inputs.initNode("flare-armed",0,"BOOL");
65 m.ias_mach_selected = m.AFDS_inputs.initNode("ias-mach-selected",0,"BOOL");
66 m.hdg_trk_selected = m.AFDS_inputs.initNode("hdg-trk-selected",0,"BOOL");
67 m.vs_fpa_selected = m.AFDS_inputs.initNode("vs-fpa-selected",0,"BOOL");
68 m.bank_switch = m.AFDS_inputs.initNode("bank-limit-switch",0,"INT");
69 m.vnav_path_mode = m.AFDS_inputs.initNode("vnav-path-mode",0,"INT");
70 m.vnav_mcp_reset = m.AFDS_inputs.initNode("vnav-mcp-reset",0,"BOOL");
71 m.vnav_descent = m.AFDS_inputs.initNode("vnav-descent",0,"BOOL");
72 m.climb_continuous = m.AFDS_inputs.initNode("climb-continuous",0,"BOOL");
73 m.indicated_vs_fpm = m.AFDS_inputs.initNode("indicated-vs-fpm",0,"DOUBLE");
74 m.estimated_time_arrival = m.AFDS_inputs.initNode("estimated-time-arrival",0,"INT");
75 m.remaining_distance = m.AFDS_inputs.initNode("remaining-distance",0,"DOUBLE");;
77 m.ias_setting = m.AP_settings.initNode("target-speed-kt",200);# 100 - 399 #
78 m.mach_setting = m.AP_settings.initNode("target-speed-mach",0.40);# 0.40 - 0.95 #
79 m.vs_setting = m.AP_settings.initNode("vertical-speed-fpm",0); # -8000 to +6000 #
80 m.hdg_setting = m.AP_settings.initNode("heading-bug-deg",360,"INT"); # 1 to 360
81 m.hdg_setting_last = m.AP_settings.initNode("heading-bug-last",360,"INT"); # 1 to 360
82 m.hdg_setting_active = m.AP_settings.initNode("heading-bug-active",0,"BOOL");
83 m.trk_setting = m.AP_settings.initNode("track-bug-deg",360,"INT"); # 1 to 360
84 m.fpa_setting = m.AP_settings.initNode("flight-path-angle",0); # -9.9 to 9.9 #
85 m.alt_setting = m.AP_settings.initNode("counter-set-altitude-ft",10000,"DOUBLE");
86 m.target_alt = m.AP_settings.initNode("actual-target-altitude-ft",10000,"DOUBLE");
87 m.auto_brake_setting = m.AP_settings.initNode("autobrake",0.000,"DOUBLE");
88 m.flare_constant_setting = m.AP_settings.initNode("flare-constant",0.000,"DOUBLE");
89 m.thrust_lmt = m.AP_settings.initNode("thrust-lmt",1,"DOUBLE");
90 m.flight_idle = m.AP_settings.initNode("flight-idle",0,"DOUBLE");
92 m.vs_display = m.AFDS_settings.initNode("vs-display",0);
93 m.fpa_display = m.AFDS_settings.initNode("fpa-display",0);
94 m.bank_min = m.AFDS_settings.initNode("bank-min",-25);
95 m.bank_max = m.AFDS_settings.initNode("bank-max",25);
96 m.pitch_min = m.AFDS_settings.initNode("pitch-min",-10);
97 m.pitch_max = m.AFDS_settings.initNode("pitch-max",15);
98 m.vnav_alt = m.AFDS_settings.initNode("vnav-alt",35000);
99 m.auto_popup = m.AFDS_settings.initNode("auto-pop-up",0,"BOOL");
100 m.heading_magnetic = m.AFDS_settings.getNode("heading-magnetic",1);
101 m.manual_intervention = m.AFDS_settings.initNode("manual-intervention",0,"BOOL");
103 m.AP_roll_mode = m.AFDS_apmodes.initNode("roll-mode","TO/GA");
104 m.AP_roll_arm = m.AFDS_apmodes.initNode("roll-mode-arm"," ");
105 m.AP_pitch_mode = m.AFDS_apmodes.initNode("pitch-mode","TO/GA");
106 m.AP_pitch_arm = m.AFDS_apmodes.initNode("pitch-mode-arm"," ");
107 m.AP_speed_mode = m.AFDS_apmodes.initNode("speed-mode","");
108 m.AP_annun = m.AFDS_apmodes.initNode("mode-annunciator"," ");
110 m.APl = setlistener(m.AP, func m.setAP(),0,0);
111 m.APdisl = setlistener(m.AP_disengaged, func m.setAP(),0,0);
112 m.Lbank = setlistener(m.bank_switch, func m.setbank(),0,0);
113 m.LTMode = setlistener(m.autothrottle_mode, func m.updateATMode(),0,0);
114 m.WpChanged = setlistener(props.globals.getNode("/autopilot/route-manager/wp/id",1), func m.wpChanged(),0,0);
115 m.RmDisabled = setlistener(props.globals.getNode("/autopilot/route-manager/active",1), func m.wpChanged(),0,0);
121 input : func(mode,btn)
123 if(getprop("/systems/electrical/outputs/avionics"))
125 var current_alt = getprop("instrumentation/altimeter/indicated-altitude-ft");
128 # horizontal AP controls
129 if(btn == 1) # Heading Sel button
131 if(getprop("position/gear-agl-ft") < 50)
133 btn = me.lateral_mode.getValue();
136 elsif(btn == 2) # Heading Hold button
138 # set target to current magnetic heading
139 var tgtHdg = int(me.heading_magnetic.getValue() + 0.50);
140 me.hdg_setting.setValue(tgtHdg);
141 me.trk_setting.setValue(tgtHdg);
142 if(getprop("position/gear-agl-ft") < 50)
144 btn = me.lateral_mode.getValue();
148 btn = 1; # Heading sel
151 elsif(btn==3) # LNAV button
153 if ((!getprop("/autopilot/route-manager/active"))or
154 (getprop("/autopilot/route-manager/current-wp")<0)or
155 (getprop("/autopilot/route-manager/wp/id")==""))
157 # Oops, route manager isn't active. Keep current mode.
158 btn = me.lateral_mode.getValue();
159 copilot("Captain, LNAV doesn't engage. We forgot to program or activate the route manager!");
163 if(me.lateral_mode.getValue() == 3) # Current mode is LNAV
165 # set target to current magnetic heading
166 var tgtHdg = int(me.heading_magnetic.getValue() + 0.50);
167 me.hdg_setting.setValue(tgtHdg);
168 me.trk_setting.setValue(tgtHdg);
169 btn = 1; # Heading sel
171 elsif(me.lnav_armed.getValue())
172 { # LNAV armed then disarm
173 me.lnav_armed.setValue(0);
174 btn = me.lateral_mode.getValue();
178 me.lnav_armed.setValue(1);
179 btn = me.lateral_mode.getValue();
183 me.lateral_mode.setValue(btn);
187 # vertical AP controls
190 # hold current altitude
191 if(me.AP.getValue() or me.FD.getValue())
193 var alt = int((current_alt+50)/100)*100;
194 me.target_alt.setValue(alt);
195 me.autothrottle_mode.setValue(5); # A/T SPD
204 # hold current vertical speed
205 var vs = getprop("instrumentation/inst-vertical-speed-indicator/indicated-speed-fpm");
206 vs = int(vs/100)*100;
207 if (vs<-8000) vs = -8000;
208 if (vs>6000) vs = 6000;
209 me.vs_setting.setValue(vs);
212 me.target_alt.setValue(current_alt);
216 me.target_alt.setValue(me.alt_setting.getValue());
218 me.autothrottle_mode.setValue(5); # A/T SPD
222 if(me.vs_setting.getValue() == 0)
224 me.target_alt.setValue(current_alt);
228 me.target_alt.setValue(me.alt_setting.getValue());
234 if ((!getprop("/autopilot/route-manager/active"))or
235 (getprop("/autopilot/route-manager/current-wp")<0)or
236 (getprop("/autopilot/route-manager/wp/id")==""))
238 # Oops, route manager isn't active. Keep current mode.
239 btn = me.vertical_mode.getValue();
240 copilot("Captain, VNAV doesn't engage. We forgot to program or activate the route manager!");
244 var vnav_mode = me.vertical_mode.getValue();
245 if(vnav_mode == 3) # Current mode is VNAV PTH
248 elsif(vnav_mode == 4) # Current mode is VNAV SPD
251 elsif(vnav_mode == 5) # Current mode is VNAV ALT
254 elsif(me.vnav_armed.getValue())
255 { # VNAV armed then disarm
256 me.vnav_armed.setValue(0);
261 me.vnav_armed.setValue(1);
262 me.vnav_path_mode.setValue(0);
263 me.vnav_mcp_reset.setValue(0);
264 me.vnav_descent.setValue(0);
267 me.manual_intervention.setValue(0);
271 if(btn==8) # FLCH SPD
273 # change flight level
275 - getprop("autopilot/internal/airport-height")) < 400)
276 or (me.at1.getValue() == 0) or (me.at2.getValue() == 0))
280 elsif(current_alt < me.alt_setting.getValue())
282 me.autothrottle_mode.setValue(1); # A/T THR
286 me.autothrottle_mode.setValue(4); # A/T IDLE
288 setprop("autopilot/internal/current-pitch-deg", getprop("orientation/pitch-deg"));
289 var alt = me.alt_setting.getValue();
290 me.target_alt.setValue(alt);
292 me.vertical_mode.setValue(btn);
296 # throttle AP controls
297 if(me.autothrottle_mode.getValue() != 0
298 or (me.at1.getValue() == 0) or (me.at2.getValue() == 0))
302 elsif(btn == 2) # TOGA
304 if((getprop("instrumentation/airspeed-indicator/indicated-speed-kt") > 50)
305 or (getprop("/controls/flight/flaps") == 0))
309 me.auto_popup.setValue(1);
312 - getprop("autopilot/internal/airport-height")) < 400)
315 copilot("Captain, auto-throttle won't engage below 400ft.");
317 me.autothrottle_mode.setValue(btn);
319 elsif(mode==3) #FD, LOC or G/S button
321 if(btn == 2) # FD button toggle
325 if(getprop("gear/gear[1]/wow"))
327 me.lateral_mode.setValue(9); # TOGA
328 me.vertical_mode.setValue(10); # TOGA
333 if(!me.AP.getValue())
335 me.lateral_mode.setValue(0); # Clear
336 me.vertical_mode.setValue(0); # Clear
340 elsif(btn == 3) # AP button toggle
342 if(!me.AP.getValue())
344 me.rollout_armed.setValue(0);
345 me.flare_armed.setValue(0);
346 me.loc_armed.setValue(0); # Disarm
347 me.gs_armed.setValue(0); # Disarm
348 if(!me.FD.getValue())
350 me.lateral_mode.setValue(0); # NO MODE
351 me.vertical_mode.setValue(0); # NO MODE
355 me.lateral_mode.setValue(2); # HDG HOLD
356 me.vertical_mode.setValue(1); # ALT
362 and !me.lnav_armed.getValue()
363 and (me.lateral_mode.getValue() != 3))
365 var current_bank = getprop("orientation/roll-deg");
368 setprop("autopilot/internal/target-roll-deg", current_bank);
369 me.lateral_mode.setValue(8); # ATT
373 # set target to current magnetic heading
374 var tgtHdg = int(me.heading_magnetic.getValue() + 0.50);
375 me.hdg_setting.setValue(tgtHdg);
376 me.trk_setting.setValue(tgtHdg);
377 me.lateral_mode.setValue(2); # HDG HOLD
380 if(!me.vnav_armed.getValue()
381 and (me.vertical_mode.getValue() == 0))
383 # hold current vertical speed
384 var vs = getprop("instrumentation/inst-vertical-speed-indicator/indicated-speed-fpm");
385 vs = int(vs/100)*100;
386 if (vs<-8000) vs = -8000;
387 if (vs>6000) vs = 6000;
388 me.vs_setting.setValue(vs);
391 me.target_alt.setValue(current_alt);
395 me.target_alt.setValue(me.alt_setting.getValue());
397 me.vertical_mode.setValue(2); # V/S
403 if (btn==1) #APP button
405 var lgsmode = me.vertical_mode.getValue();
406 if(lgsmode == 6) # Already in G/S mode
408 me.vertical_mode.setValue(1); # Keep current altitude
409 me.gs_armed.setValue(0); # Disarm
411 elsif(me.gs_armed.getValue()) # G/S armed but not captured yet
413 me.gs_armed.setValue(0); # Disarm
417 me.gs_armed.setValue(1); # G/S arm
420 var llocmode = me.lateral_mode.getValue();
421 if(llocmode == 4) # Alrady in LOC mode
423 # set target to current magnetic heading
424 var tgtHdg = int(me.heading_magnetic.getValue() + 0.50);
425 me.hdg_setting.setValue(tgtHdg);
426 me.trk_setting.setValue(tgtHdg);
427 me.lateral_mode.setValue(2); # Keep current headding
428 me.loc_armed.setValue(0); # Disarm
430 elsif(me.loc_armed.getValue()) # LOC armed but not captured yet
432 if(!me.gs_armed.getValue()) # G/S not armed
434 me.loc_armed.setValue(0); # Disarm
439 me.loc_armed.setValue(1); # LOC arm
440 me.tiller_status = getprop("/controls/gear/tiller-enabled");
448 var output = 1-me.AP.getValue();
449 var disabled = me.AP_disengaged.getValue();
450 if((output==0)and(getprop("position/gear-agl-ft")<200))
453 copilot("Captain, autopilot won't engage below 200ft.");
455 if((disabled)and(output==0)){output = 1;me.AP.setValue(0);}
461 if (abs(getprop("controls/flight/rudder-trim")) > 0.04) msg = "rudder";
462 if (abs(getprop("controls/flight/elevator-trim")) > 0.04) msg2 = "pitch";
463 if (abs(getprop("controls/flight/aileron-trim")) > 0.04) msg3 = "aileron";
464 if (msg ~ msg2 ~ msg3 != "")
466 if ((msg != "")and(msg2!=""))
467 msg = msg ~ ", " ~ msg2;
470 if ((msg != "")and(msg3!=""))
471 msg = msg ~ " and " ~ msg3;
474 copilot("Captain, autopilot disengaged. Careful, check " ~ msg ~ " trim!");
478 if(me.lateral_mode.getValue() != 3) me.input(0,1);
479 setprop("autopilot/internal/target-pitch-deg",0);
480 setprop("autopilot/internal/target-roll-deg",0);
481 me.AP_passive.setValue(output);
485 var banklimit = me.bank_switch.getValue();
487 if(banklimit>0) {lmt = banklimit * 5};
488 me.bank_max.setValue(lmt);
490 me.bank_min.setValue(lmt);
493 updateATMode : func()
495 var idx = me.autothrottle_mode.getValue();
496 me.AP_speed_mode.setValue(me.spd_list[idx]);
500 if (((getprop("/autopilot/route-manager/wp/id")=="")or
501 (!getprop("/autopilot/route-manager/active")))and
502 (me.lateral_mode.getValue() == 3)and
505 # LNAV active, but route manager is disabled now => switch to HDG HOLD (current heading)
511 var current_alt = getprop("instrumentation/altimeter/indicated-altitude-ft");
512 var VS = getprop("velocities/vertical-speed-fps");
513 var TAS = getprop("velocities/uBody-fps");
514 me.indicated_vs_fpm.setValue(int((abs(VS) * 60 + 50) / 100) * 100);
515 if(TAS < 10) TAS = 10;
516 if(VS < -200) VS=-200;
519 var FPangle = math.asin(VS/TAS);
521 setprop("autopilot/internal/fpa",FPangle);
531 if(me.rollout_armed.getValue())
536 me.AP_annun.setValue(msg);
537 var tmp = abs(me.vs_setting.getValue());
538 me.vs_display.setValue(tmp);
539 tmp = abs(me.fpa_setting.getValue());
540 me.fpa_display.setValue(tmp);
543 if(me.hdg_trk_selected.getValue())
545 hdgoffset = me.trk_setting.getValue()-getprop("orientation/heading-magnetic-deg");
549 hdgoffset = me.hdg_setting.getValue()-getprop("orientation/heading-magnetic-deg");
551 if(hdgoffset < -180) hdgoffset +=360;
552 if(hdgoffset > 180) hdgoffset +=-360;
553 setprop("autopilot/internal/fdm-heading-bug-error-deg",hdgoffset);
555 if(me.step==0){ ### glideslope armed ?###
557 if(me.gs_armed.getValue())
560 var gsdefl = getprop("instrumentation/nav/gs-needle-deflection");
561 var gsrange = getprop("instrumentation/nav/gs-in-range");
562 if ((gsdefl< 0.5 and gsdefl>-0.5)and
565 me.vertical_mode.setValue(6);
566 me.gs_armed.setValue(0);
569 elsif(me.flare_armed.getValue())
573 elsif(me.vnav_armed.getValue())
577 me.AP_pitch_arm.setValue(msg);
579 }elsif(me.step==1){ ### localizer armed ? ###
581 if(me.loc_armed.getValue())
584 if (getprop("instrumentation/nav/in-range"))
587 if(!getprop("instrumentation/nav/nav-loc"))
589 var vheading = getprop("instrumentation/nav/radials/selected-deg");
590 var vvor = getprop("instrumentation/nav/heading-deg");
591 var vdist = getprop("instrumentation/nav/nav-distance");
592 var vorient = getprop("environment/magnetic-variation-deg");
593 var vmag = getprop("orientation/heading-magnetic-deg");
594 var vspeed = getprop("/instrumentation/airspeed-indicator/indicated-mach");
595 var deg_to_rad = math.pi / 180;
596 var vdiff = abs(vheading - vvor + vorient);
597 vdiff = abs(vdist * math.sin(vdiff * deg_to_rad));
598 var vlim = vspeed / 0.3 * 1300 * abs(vheading - vmag) / 45 ;
601 me.lateral_mode.setValue(4);
602 me.loc_armed.setValue(0);
607 var hddefl = getprop("instrumentation/nav/heading-needle-deflection");
608 if(abs(hddefl) < 9.9)
610 me.lateral_mode.setValue(4);
611 me.loc_armed.setValue(0);
612 var vradials = getprop("instrumentation/nav[0]/radials/target-radial-deg")
613 - getprop("environment/magnetic-variation-deg") + 0.5;
614 if(vradials < 0.5) vradials += 360;
615 elsif(vradials >= 360.5) vradials -= 360;
616 me.hdg_setting.setValue(vradials);
621 elsif(me.lnav_armed.getValue())
623 if(getprop("position/gear-agl-ft") > 50)
626 me.lnav_armed.setValue(0); # Clear
627 me.lateral_mode.setValue(3); # LNAV
634 elsif(me.rollout_armed.getValue())
638 me.AP_roll_arm.setValue(msg);
640 }elsif(me.step == 2){ ### check lateral modes ###
641 var vheading = getprop("orientation/heading-magnetic-deg");
646 me.heading_magnetic.setValue(vheading);
647 var vsethdg = me.hdg_setting.getValue();
648 if(me.hdg_setting_last.getValue() != vsethdg)
650 me.hdg_setting_last.setValue(vsethdg);
651 me.hdg_setting_active.setValue(1);
655 if(me.hdg_setting_active.getValue() == 1)
659 if(me.hdg_setting_last.getValue() == vsethdg)
661 me.hdg_setting_active.setValue(0);
667 var idx = me.lateral_mode.getValue();
668 if ((idx == 1) or (idx == 2))
670 if(getprop("position/gear-agl-ft") > 50)
672 # switch between HDG SEL to HDG HOLD
673 if (abs(getprop("orientation/heading-magnetic-deg")-me.hdg_setting.getValue())<2)
679 elsif(idx == 4) # LOC
681 if((me.rollout_armed.getValue())
682 and (getprop("position/gear-agl-ft") < 5))
684 me.rollout_armed.setValue(0);
686 setprop("/controls/gear/tiller-enabled", 1);
689 elsif(idx == 5) # ROLLOUT
691 if(getprop("velocities/groundspeed-kt") < 50)
693 setprop("/controls/gear/tiller-enabled", me.tiller_status);
694 me.AP.setValue(0); # Autopilot off
695 setprop("controls/flight/rudder", 0); # Rudder set neutral
696 if(!me.FD.getValue())
706 me.lateral_mode.setValue(idx);
707 me.AP_roll_mode.setValue(me.roll_list[idx]);
708 me.AP_roll_engaged.setBoolValue(idx > 0);
710 }elsif(me.step==3){ ### check vertical modes ###
711 if(getprop("instrumentation/airspeed-indicator/indicated-speed-kt") < 100)
713 setprop("autopilot/internal/airport-height", current_alt);
715 var idx = me.vertical_mode.getValue();
716 var test_fpa = me.vs_fpa_selected.getValue();
717 if(idx==2 and test_fpa)idx=9;
718 if(idx==9 and !test_fpa)idx=2;
719 if ((idx==8)or(idx==1)or(idx==2)or(idx==9)
720 or ((idx == 3) and (me.vnav_path_mode.getValue() == 2)))
722 var offset = (abs(getprop("instrumentation/inst-vertical-speed-indicator/indicated-speed-fpm")) / 8);
727 # flight level change mode
728 if (abs(current_alt - me.alt_setting.getValue()) < offset)
730 # within MCP altitude: switch to ALT HOLD mode
735 if(me.autothrottle_mode.getValue() != 0)
737 me.autothrottle_mode.setValue(5); # A/T SPD
739 me.vs_setting.setValue(0);
740 me.vnav_path_mode.setValue(0);
741 me.descent_step += 1;
743 if((me.mach_setting.getValue() >= 0.840)
744 and (me.ias_mach_selected.getValue() == 0)
745 and (current_alt < me.target_alt.getValue()))
747 me.ias_mach_selected.setValue(1);
748 me.mach_setting.setValue(0.840);
750 elsif((me.ias_setting.getValue() >= 320)
751 and (me.ias_mach_selected.getValue() == 1)
752 and (current_alt > me.target_alt.getValue()))
754 me.ias_mach_selected.setValue(0);
755 me.ias_setting.setValue(320);
757 # This is not official setting. Until VNAV DECENT is implemented
758 elsif(current_alt > me.target_alt.getValue())
760 if((current_alt < 12000)
761 and (me.ias_setting.getValue() > 250))
763 me.ias_setting.setValue(250);
767 elsif(idx == 3) # VNAV PATH
769 if(me.vnav_descent.getValue())
771 if(me.descent_step == 0)
773 if(me.ias_mach_selected.getValue() == 1)
775 me.mach_setting.setValue(0.780);
779 me.ias_setting.setValue(280);
781 me.descent_step += 1;
783 elsif(me.descent_step == 1)
785 if(me.ias_mach_selected.getValue() == 1)
787 if(getprop("/instrumentation/airspeed-indicator/indicated-mach") < 0.785)
789 me.vnav_path_mode.setValue(1);
790 me.target_alt.setValue(me.alt_setting.getValue());
791 me.vs_setting.setValue(-2000);
792 me.descent_step += 1;
797 if(getprop("/instrumentation/airspeed-indicator/indicated-speed-kt") < 285)
799 me.vnav_path_mode.setValue(1);
800 me.target_alt.setValue(me.alt_setting.getValue());
801 me.vs_setting.setValue(-2000);
802 me.descent_step += 1;
806 elsif(me.descent_step == 2)
808 if(me.ias_mach_selected.getValue() == 1)
810 if(getprop("instrumentation/airspeed-indicator/indicated-speed-kt") >= 280)
812 me.ias_mach_selected.setValue(0);
813 me.ias_setting.setValue(280);
814 me.descent_step += 1;
819 me.descent_step += 1;
822 elsif(me.descent_step == 3)
824 if(current_alt < 29000)
826 me.vnav_path_mode.setValue(2);
827 me.descent_step += 1;
831 elsif((me.mach_setting.getValue() >= 0.840)
832 and (me.ias_mach_selected.getValue() == 0))
834 me.ias_mach_selected.setValue(1);
835 me.mach_setting.setValue(0.840);
837 elsif((me.ias_setting.getValue() >= 320)
838 and (me.ias_mach_selected.getValue() == 1))
840 me.ias_mach_selected.setValue(0);
841 me.ias_setting.setValue(320);
843 elsif(me.ias_mach_selected.getValue() == 0)
845 if(current_alt < 10000)
847 me.ias_setting.setValue(250);
851 me.ias_setting.setValue(320);
854 if(me.remaining_distance.getValue() > 200)
856 var optimal_alt = ((getprop("consumables/fuel/total-fuel-lbs") + getprop("/sim/weight[0]/weight-lb") + getprop("/sim/weight[1]/weight-lb"))
857 / getprop("sim/max-payload"));
858 if(optimal_alt > 0.95) optimal_alt = 29000;
859 elsif(optimal_alt > 0.83) optimal_alt = 31000;
860 elsif(optimal_alt > 0.65) optimal_alt = 33000;
861 elsif(optimal_alt > 0.53) optimal_alt = 35000;
862 elsif(optimal_alt > 0.41) optimal_alt = 37000;
863 elsif(optimal_alt > 0.29) optimal_alt = 39000;
864 elsif(optimal_alt > 0.16) optimal_alt = 41000;
865 else optimal_alt = 43000;
867 - me.alt_setting.getValue()) > 1000)
869 if(getprop("autopilot/route-manager/cruise/altitude-ft") >= optimal_alt)
871 me.alt_setting.setValue(optimal_alt);
873 setprop("autopilot/internal/current-pitch-deg", getprop("orientation/pitch-deg"));
875 elsif(getprop("autopilot/route-manager/cruise/altitude-ft") > (me.alt_setting.getValue() + 500))
878 setprop("autopilot/internal/current-pitch-deg", getprop("orientation/pitch-deg"));
883 elsif(idx == 4) # VNAV SPD
885 if(getprop("/controls/flight/flaps") > 0) # flaps down
887 me.ias_setting.setValue(getprop("instrumentation/weu/state/target-speed"));
889 elsif(current_alt < 10000)
891 me.ias_setting.setValue(250);
895 if((me.mach_setting.getValue() >= 0.840)
896 and (me.ias_mach_selected.getValue() == 0)
897 and (current_alt < me.target_alt.getValue()))
899 me.ias_mach_selected.setValue(1);
900 me.mach_setting.setValue(0.840);
902 elsif((me.ias_setting.getValue() >= 320)
903 and (me.ias_mach_selected.getValue() == 1)
904 and (current_alt > me.target_alt.getValue()))
906 me.ias_mach_selected.setValue(0);
907 me.ias_setting.setValue(320);
909 elsif(me.ias_mach_selected.getValue() == 0)
911 if(current_alt < 10000)
913 me.ias_setting.setValue(250);
917 me.ias_setting.setValue(320);
921 var optimal_alt = ((getprop("consumables/fuel/total-fuel-lbs") + getprop("/sim/weight[0]/weight-lb") + getprop("/sim/weight[1]/weight-lb"))
922 / getprop("sim/max-payload"));
923 if(optimal_alt > 0.95) optimal_alt = 29000;
924 elsif(optimal_alt > 0.83) optimal_alt = 31000;
925 elsif(optimal_alt > 0.65) optimal_alt = 33000;
926 elsif(optimal_alt > 0.53) optimal_alt = 35000;
927 elsif(optimal_alt > 0.41) optimal_alt = 37000;
928 elsif(optimal_alt > 0.29) optimal_alt = 39000;
929 elsif(optimal_alt > 0.16) optimal_alt = 41000;
930 else optimal_alt = 43000;
931 if(getprop("autopilot/route-manager/cruise/altitude-ft") >= optimal_alt)
933 me.alt_setting.setValue(optimal_alt);
937 me.alt_setting.setValue(getprop("autopilot/route-manager/cruise/altitude-ft"));
939 me.target_alt.setValue(me.alt_setting.getValue());
940 var offset = (abs(getprop("instrumentation/inst-vertical-speed-indicator/indicated-speed-fpm")) / 8);
946 - me.alt_setting.getValue()) < offset)
948 # within target altitude: switch to VANV PTH mode
950 if(me.autothrottle_mode.getValue() != 0)
952 me.autothrottle_mode.setValue(5); # A/T SPD
954 me.vs_setting.setValue(0);
957 elsif(idx == 6) # G/S
959 var f_angle = getprop("autopilot/constant/flare-base") * 135 / getprop("instrumentation/airspeed-indicator/indicated-speed-kt");
960 me.flare_constant_setting.setValue(f_angle);
961 if((getprop("position/gear-agl-ft") < 50)
962 and (me.flare_armed.getValue()))
964 me.flare_armed.setValue(0);
967 elsif(me.AP.getValue() and (getprop("position/gear-agl-ft") < 1500))
969 me.rollout_armed.setValue(1); # ROLLOUT
970 me.flare_armed.setValue(1); # FLARE
971 setprop("autopilot/settings/flare-speed-fps", 0);
974 elsif(idx == 7) # FLARE
977 me.flare_constant_setting.setValue(f_angle);
978 if(me.autothrottle_mode.getValue())
980 if(getprop("position/gear-agl-ft") < 25)
982 me.autothrottle_mode.setValue(4); # A/T IDLE
985 if(getprop("velocities/groundspeed-kt") < 30)
987 if(!me.FD.getValue())
998 - getprop("autopilot/internal/airport-height")) > 400) # Take off mode and above baro 400 ft
1000 if(me.vnav_armed.getValue())
1002 if(me.alt_setting.getValue() == int(current_alt))
1010 me.vnav_armed.setValue(0);
1013 me.vertical_mode.setValue(idx);
1014 me.AP_pitch_mode.setValue(me.pitch_list[idx]);
1015 me.AP_pitch_engaged.setBoolValue(idx>0);
1018 elsif(me.step == 4) ### Auto Throttle mode control ###
1020 # Thrust reference rate calculation. This should be provided by FMC
1021 var grossweight = getprop("consumables/fuel/total-fuel-lbs") + getprop("/sim/weight[0]/weight-lb") + getprop("/sim/weight[1]/weight-lb");
1022 var derate = 0.3 - grossweight * 0.00000083;
1023 if(me.ias_setting.getValue() < 251)
1025 var vflight_idle = (getprop("autopilot/constant/descent-profile-low-base")
1026 + (getprop("autopilot/constant/descent-profile-low-rate") * grossweight / 1000));
1030 var vflight_idle = (getprop("autopilot/constant/descent-profile-high-base")
1031 + (getprop("autopilot/constant/descent-profile-high-rate") * grossweight / 1000));
1033 if(vflight_idle < 0.00) vflight_idle = 0.00;
1034 me.flight_idle.setValue(vflight_idle);
1035 # Thurst limit varis on altitude
1036 var thrust_lmt = 0.96;
1037 if(current_alt < 25000)
1039 thrust_lmt = derate / 25000 * abs(current_alt) + (0.95 - derate);
1041 me.thrust_lmt.setValue(thrust_lmt);
1042 # IAS and MACH number update in back ground
1044 if(me.ias_mach_selected.getValue() == 1)
1046 temp = int(getprop("instrumentation/airspeed-indicator/indicated-speed-kt") + 0.5);
1047 me.ias_setting.setValue(temp);
1051 temp = (int(getprop("instrumentation/airspeed-indicator/indicated-mach") * 1000 + 0.5) / 1000);
1052 me.mach_setting.setValue(temp);
1054 # Auto throttle arm switch is offed
1055 if((me.at1.getValue() == 0) or (me.at2.getValue() == 0))
1057 me.autothrottle_mode.setValue(0);
1059 # auto-throttle disengaged when reverser is enabled
1060 elsif (getprop("controls/engines/engine/reverser"))
1062 me.autothrottle_mode.setValue(0);
1064 elsif(me.autothrottle_mode.getValue() == 2) # THR REF
1066 if((getprop("instrumentation/airspeed-indicator/indicated-speed-kt") > 80)
1067 and ((current_alt - getprop("autopilot/internal/airport-height")) < 400))
1069 me.autothrottle_mode.setValue(3); # HOLD
1071 elsif((me.vertical_mode.getValue() != 3) # not VNAV PTH
1072 and (me.vertical_mode.getValue() != 5)) # not VNAV ALT
1074 if((getprop("/controls/flight/flaps") == 0) # FLAPs up
1075 and (me.vertical_mode.getValue() != 4)) # not VNAV SPD
1077 me.autothrottle_mode.setValue(5); # SPD
1081 setprop("/controls/engines/engine[0]/throttle", thrust_lmt);
1082 setprop("/controls/engines/engine[1]/throttle", thrust_lmt);
1086 elsif((me.autothrottle_mode.getValue() == 4) # Auto throttle mode IDLE
1087 and ((me.vertical_mode.getValue() == 8) # FLCH SPD mode
1088 or (me.vertical_mode.getValue() == 3)) # VNAV PTH mode
1089 and (me.flight_idle.getValue() == getprop("/controls/engines/engine[0]/throttle")) # #1Thrust is actual flight idle
1090 and (me.flight_idle.getValue() == getprop("/controls/engines/engine[1]/throttle"))) # #2Thrust is actual flight idle
1092 me.autothrottle_mode.setValue(3); # HOLD
1094 # Take off mode and above baro 400 ft
1095 elsif((current_alt - getprop("autopilot/internal/airport-height")) > 400)
1097 if(me.autothrottle_mode.getValue() == 1) # THR
1099 setprop("/controls/engines/engine[0]/throttle", thrust_lmt);
1100 setprop("/controls/engines/engine[1]/throttle", thrust_lmt);
1102 elsif((me.vertical_mode.getValue() == 10) # TO/GA
1103 or ((me.autothrottle_mode.getValue() == 3) # HOLD
1104 and (me.vertical_mode.getValue() != 8) # not FLCH
1105 and (me.vertical_mode.getValue() != 3))) # not VNAV PTH
1107 if(getprop("/controls/flight/flaps") == 0)
1109 me.autothrottle_mode.setValue(5); # SPD
1113 me.autothrottle_mode.setValue(2); # THR REF
1116 elsif(me.vertical_mode.getValue() == 4) # VNAV SPD
1118 me.autothrottle_mode.setValue(2); # THR REF
1120 elsif(me.vertical_mode.getValue() == 3) # VNAV PATH
1122 if(me.vnav_path_mode.getValue() == 2)
1124 if(me.autothrottle_mode.getValue() != 3)
1126 me.autothrottle_mode.setValue(4); # IDLE
1131 me.autothrottle_mode.setValue(5); # SPD
1135 elsif((getprop("position/gear-agl-ft") > 100) # Approach mode and above 100 ft
1136 and (me.vertical_mode.getValue() == 6))
1138 me.autothrottle_mode.setValue(5); # SPD
1140 idx = me.autothrottle_mode.getValue();
1141 me.AP_speed_mode.setValue(me.spd_list[idx]);
1145 if (getprop("/autopilot/route-manager/active")){
1146 var max_wpt = getprop("/autopilot/route-manager/route/num");
1147 var atm_wpt = getprop("/autopilot/route-manager/current-wp");
1148 var destination_elevation = getprop("/autopilot/route-manager/destination/field-elevation-ft");
1149 var total_distance = getprop("/autopilot/route-manager/total-distance");
1150 if(me.lateral_mode.getValue() == 3) # Current mode is LNAV
1152 if(atm_wpt < (max_wpt - 1))
1154 me.remaining_distance.setValue(getprop("/autopilot/route-manager/wp/remaining-distance-nm")
1155 + getprop("autopilot/route-manager/wp/dist"));
1156 var next_course = getprop("/autopilot/route-manager/wp[1]/bearing-deg");
1160 me.remaining_distance.setValue(getprop("autopilot/route-manager/wp/dist"));
1162 if(me.vnav_descent.getValue() == 0) # Calculation of Top Of Descent distance
1164 var top_of_descent = 16;
1165 if(current_alt > 10000)
1167 top_of_descent += 21;
1168 if(current_alt > 29000)
1170 top_of_descent += 41.8;
1171 if(current_alt > 36000)
1173 top_of_descent += 28;
1174 top_of_descent += (current_alt - 36000) / 1000 * 3.8;
1178 top_of_descent += (current_alt - 29000) / 1000 * 4;
1183 top_of_descent += (current_alt - 10000) / 1000 * 2.2;
1185 top_of_descent += 6.7;
1189 top_of_descent += (current_alt - 3000) / 1000 * 3;
1191 top_of_descent -= (destination_elevation / 1000 * 3);
1192 if((me.alt_setting.getValue() > 24000)
1193 and (me.alt_setting.getValue() >= current_alt))
1195 if(me.remaining_distance.getValue() < (top_of_descent + 10))
1197 me.vnav_mcp_reset.setValue(1);
1198 copilot("Reset MCP ALT");
1203 if(me.remaining_distance.getValue() < top_of_descent)
1205 me.vnav_descent.setValue(1);
1210 if(getprop("/autopilot/route-manager/active"))
1212 var wpt_distance = getprop("autopilot/route-manager/wp/dist");
1213 var groundspeed = getprop("/velocities/groundspeed-kt");
1214 if(wpt_distance != nil)
1216 var wpt_eta = (wpt_distance / groundspeed * 3600);
1217 var gmt = getprop("instrumentation/clock/indicated-sec");
1218 if((getprop("gear/gear[1]/wow") == 0) and (getprop("gear/gear[2]/wow") == 0))
1220 gmt += (wpt_eta + 30);
1222 var gmt_hour = int(gmt / 3600);
1223 me.estimated_time_arrival.setValue(gmt_hour * 100 + int((gmt - gmt_hour * 3600) / 60));
1224 var change_wp = abs(getprop("/autopilot/route-manager/wp[1]/bearing-deg") - me.heading_magnetic.getValue());
1225 if(change_wp > 180) change_wp = (360 - change_wp);
1226 if(((me.heading_change_rate * change_wp) > wpt_eta)
1227 or (wpt_distance < 0.2)
1228 or ((me.remaining_distance_log_last < wpt_distance) and (change_wp < 90)))
1230 if (getprop("/autopilot/route-manager/current-wp") < max_wpt)
1233 props.globals.getNode("/autopilot/route-manager/current-wp").setValue(atm_wpt);
1234 me.remaining_distance_log_last = 36000;
1239 me.remaining_distance_log_last = wpt_distance;
1246 if(getprop("/controls/flight/flaps") == 0)
1248 me.auto_popup.setValue(0);
1250 var ma_spd = getprop("/instrumentation/airspeed-indicator/indicated-mach");
1251 var banklimit = getprop("/instrumentation/afds/inputs/bank-limit-switch");
1255 me.heading_change_rate = 0;
1259 me.heading_change_rate = 4.9 * 0.7;
1261 elsif(ma_spd > 0.6666)
1264 me.heading_change_rate = 2.45 * 0.7;
1269 me.heading_change_rate = 1.125 * 0.7;
1271 elsif(ma_spd > 0.3333)
1274 me.heading_change_rate = 0.625 * 0.7;
1279 me.heading_change_rate = 0.55 * 0.7;
1281 props.globals.getNode("/instrumentation/afds/settings/bank-max").setValue(lim);
1283 props.globals.getNode("/instrumentation/afds/settings/bank-min").setValue(lim);
1288 if(me.step>6) me.step =0;
1291 #####################
1294 var afds = AFDS.new();
1296 var afds_init_listener = setlistener("/sim/signals/fdm-initialized", func {
1297 removelistener(afds_init_listener);
1298 settimer(update_afds,6);
1299 print("AFDS System ... check");
1302 var update_afds = func {
1304 settimer(update_afds, 0);