# vim: set foldmethod=marker foldmarker={{{,}}} : var APClass = { new : func { var m = { parents: [ APClass ] }; m.system = 'none'; var ap_systems = { # described AP systems to search, if it returns true system is set STEC55X: func contains(stec55x, 'ITAF'), GFC700: func props.globals.getNode('/autopilot/GFC700/FSM/lateral').getPath(), KAP140: func contains(kap140, 'apButton'), }; foreach (var s; sort(keys(ap_systems), func(a,b) cmp(a,b))) { call(ap_systems[s], [], nil, nil, var errors = []); if (!size(errors)) { msg('found autopilot system: ' ~ s); m.system = s; break; } } if (m.system == 'none') msg('no autopilot system found'); m.engaged = 0; if (! contains(data.timers, 'updateAP')) { data.timers.updateAP = maketimer(1, m, m.systems[m.system].updateDisplay); data.timers.updateAP.start(); } var ap_annun = [ 'LATMOD-Armed-text', 'LATMOD-Active-text', 'AP-Status-text', 'YD-Status-text', 'VERMOD-Active-text', 'VERMOD-Reference-text', 'VERMOD-Armed-text' ]; var terminal_count = { PFD: 0, MFD: 0 }; m.terminals = []; foreach (var name; keys(flightdeck)) { terminal_count[flightdeck[name].role] += 1; if (flightdeck[name].role == 'PFD') append(m.terminals, name); } if (terminal_count.PFD == 0 and terminal_count.MFD > 0) { foreach (var name; keys(flightdeck)) { flightdeck[name].display.loadGroup({show: 'PFD-navbox'}); if (flightdeck[name].role == 'MFD') { append(m.terminals, name); flightdeck[name].display.loadGroup({text: ap_annun}); } } } foreach (var terminal; m.terminals) { foreach (var elem; ap_annun) { var color = (elem == 'LATMOD-Armed-text' or elem == 'VERMOD-Armed-text') ? 'white' : 'green'; flightdeck[terminal].display.screenElements[elem] .setColor(flightdeck[terminal].display.colors[color]) .setVisible(0); } foreach (var ap; [ 'AP', 'YD' ]) flightdeck[terminal].display.screenElements[ap ~ '-Status-text'] .setDrawMode(canvas.Text.TEXT + canvas.Text.FILLEDBOUNDINGBOX) .setColorFill(flightdeck[terminal].display.colors.black) .setText(ap); } ap_annun = nil; ap_systems = nil; terminal_count = nil; # delete unused systems foreach (var e; keys(m.systems)) if (e != m.system) delete(m.systems, e); if (contains(m.systems[m.system], 'hook') and typeof(m.systems[m.system].hook) == 'func') m.systems[m.system].hook(); return m; }, softkey: func (side, row, a) { if (a) return; call(me.systems[me.system][side][row], [], autopilot.parents[0].systems[me.system]); }, systems : { # L: AP FD NAV ALT VS FLC # R: YD HDG APR VNV UP DN none: { updateDisplay: func, hook: func, L: [ func, func, func, func, func, func ], R: [ func, func, func, func, func, func ], }, GFC700: { # {{{ # Many thanks to the great work on the FG1000 _blink_count: 0, updateDisplay: func { var annunciator = props.globals.getNode('/autopilot/annunciator'); var ap_enabled = annunciator.getValue('autopilot-enabled'); var latmod = annunciator.getValue('lateral-mode'); var latmod_armed = annunciator.getValue('lateral-mode-armed'); var vertmod = annunciator.getValue('vertical-mode'); var vertmod_armed = annunciator.getValue('vertical-mode-armed'); var vertmod_target = annunciator.getValue('vertical-mode-target'); if (vertmod_target != nil) { vertmod_target = string.replace(vertmod_target, '+', utf8.chstr(9650)); vertmod_target = string.replace(vertmod_target, '-', utf8.chstr(9660)); } foreach (var terminal; me.terminals) { var se = flightdeck[terminal].display.screenElements; se['LATMOD-Active-text'].setVisible(latmod != nil and ap_enabled).setText(latmod); se['LATMOD-Armed-text'].setVisible(latmod_armed != nil and ap_enabled).setText(latmod_armed); se['VERMOD-Active-text'].setVisible(vertmod != nil and ap_enabled).setText(vertmod); se['VERMOD-Reference-text'].setVisible(vertmod_target != nil and ap_enabled).setText(vertmod_target); se['VERMOD-Armed-text'].setVisible(vertmod_armed != nil and ap_enabled).setText(vertmod_armed); if (se['AP-Status-text'].getVisible() and !ap_enabled) { if (math.mod(me._blink_count,2)) se['AP-Status-text'] .setDrawMode(canvas.Text.TEXT + canvas.Text.FILLEDBOUNDINGBOX) .setColorFill(flightdeck[terminal].display.colors.yellow) .setColor(flightdeck[terminal].display.colors.black); else se['AP-Status-text'] .setDrawMode(canvas.Text.TEXT + canvas.Text.FILLEDBOUNDINGBOX) .setColorFill(flightdeck[terminal].display.colors.black) .setColor(flightdeck[terminal].display.colors.yellow); me._blink_count += 1; if (me._blink_count == 5) { se['AP-Status-text'] .setColor(flightdeck[terminal].display.colors.green) .setVisible(0); me._blink_count = 0; } return; } else { se['AP-Status-text'].setVisible(ap_enabled); me._blink_count = 0; } } }, hook: func { me._vertical_mode = globals.props.getNode("/autopilot/annunciator/vertical-mode", 1); me._pitch_setting = globals.props.getNode("/autopilot/settings/target-pitch-deg", 1); me._climb_setting = globals.props.getNode("/autopilot/settings/vertical-speed-fpm", 1); me._speed_setting = globals.props.getNode("/autopilot/settings/target-speed-kt", 1); me._vertical_mode_button = globals.props.getNode("/autopilot/vertical-mode-button", 1); me._lateral_mode_button = globals.props.getNode("/autopilot/lateral-mode-button", 1); me._ap_mode_button = globals.props.getNode("/autopilot/AP-mode-button", 1); me._ap_enabled = globals.props.getNode("/autopilot/annunciator/autopilot-enabled", 1);; me._fd_enabled = globals.props.getNode("/autopilot/annunciator/flight-director-enabled", 1);; }, sendModeChange: func (value) { me._vertical_mode_button.setValue(value); me._lateral_mode_button.setValue(value); if (value == "AP") { me._ap_mode_button.setValue(value); } }, handleNoseUpDown : func(value) { var vertical_mode = me._vertical_mode.getValue(); if (vertical_mode == "PIT") me._pitch_setting.setValue(me._pitch_setting.getValue() + (value * 1)); if (vertical_mode == "VS") { me._climb_setting.setValue(me._climb_setting.getValue() + (value * 100)); setprop("/autopilot/annunciator/vertical-mode-target", sprintf("%+ifpm", me._climb_setting.getValue()) ); } if (vertical_mode == "FLC") { me._speed_setting.setValue(me._speed_setting.getValue() - (value * 1)); setprop("/autopilot/annunciator/vertical-mode-target", sprintf("%i kt", me._speed_setting.getValue()) ); } }, L: [ func { me.sendModeChange('AP'); }, func { me.sendModeChange('FD'); }, func { setprop('/autopilot/settings/nav-mode-source', cdi.getValue('source')); me.sendModeChange('NAV'); }, func { me.sendModeChange('ALT'); }, func { me.sendModeChange('VS'); }, func { me.sendModeChange('FLC'); }, ], R: [ func { me.sendModeChange('YD'); }, func { me.sendModeChange('HDG'); }, func { me.sendModeChange('APR'); }, func { me.sendModeChange('VNV'); }, func { me.handleNoseUpDown(1); }, func { me.handleNoseUpDown(-1); }, ], }, # }}} STEC55X: { # {{{ _aliases: { hdg: afcs.getNode('heading-bug-deg'), NAVCourse: cdi.getNode('course'), OBSNAVNeedle: cdi.getNode('course-deflection'), }, ap_annun_color: { rdy: "white", fail: "red", }, hook : func { me.trimTarget = 0; foreach (var a; keys(me._aliases)) stec55x[a].alias(me._aliases[a]); setprop('/it-stec55x/input/ap-master-sw', 1); }, updateDisplay: func { var vert_armed = ''; var vert_active = ''; var vert_reference = ''; if (stec55x.ALT_annun.getBoolValue()) { vert_active = 'ALT'; vert_reference = sprintf('%.2f inHg', stec55x.alt.getValue()); vert_armed = sprintf('%5d ft', abs(data.alt - afcs.getValue('selected-alt-ft')) < 150 ? afcs.getValue('selected-alt-ft') : math.round(data.alt, 10)); } elsif (stec55x.VS_annun.getBoolValue()) { vert_active = 'VS'; vert_reference = sprintf('%s%4d fpm', utf8.chstr(stec55x.vs.getValue() > 0 ? 9650 : 9660), math.abs(math.round(stec55x.vs.getValue(), 10))); } elsif (stec55x.GSArmed.getBoolValue()) { vert_armed = 'GS'; vert_active = 'VPATH'; } var ap_state = []; if (stec55x.RDY_annun.getBoolValue()) ap_state = ["RDY", "white", "black"]; elsif (stec55x.FAIL_annun.getBoolValue()) ap_state = ["FAIL", "black", "red"]; elsif (stec55x.systemAlive.getBoolValue()) ap_state = ["AP", "black", "green"]; else ap_state = ["", "black", "black"]; var lat_active = ''; foreach (var m; ['HDG', 'NAV', 'APR']) if (stec55x[m ~ "_annun"].getBoolValue()) { lat_active = m; break; } foreach (var terminal; me.terminals) { var se = flightdeck[terminal].display.screenElements; se['VERMOD-Active-text'].setVisible(size(vert_active)).setText(vert_active); se['VERMOD-Armed-text'].setVisible(size(vert_armed)).setText(vert_armed); se['VERMOD-Reference-text'].setVisible(size(vert_reference)).setText(vert_reference); se['AP-Status-text'].setColorFill(flightdeck[terminal].display.colors[ap_state[2]]) .setColor(ap_state[1]) .setVisible(size(ap_state[0])) .setText(ap_state[0]); se['YD-Status-text'].setVisible(stec55x.yaw.getValue() != -1); se['LATMOD-Active-text'].setVisible(size(lat_active)).setText(lat_active); } }, L: [ func { var ap_master_sw = '/it-stec55x/input/ap-master-sw'; setprop(ap_master_sw, !getprop(ap_master_sw)); }, func { var apfd_master_sw = '/it-stec55x/input/apfd-master-sw'; setprop(apfd_master_sw, !getprop(apfd_master_sw)); }, func { call(stec55x.button.NAV, [], nil, stec55x); }, func { call(stec55x.button.ALT, [], nil, stec55x); }, func { stec55x.vs.setValue(math.round(data.vsi, 100)); call(stec55x.button.VS, [], nil, stec55x); }, func, ], R: [ func { var yaw_dumper_sw = '/it-stec55x/input/yaw-damper-sw'; setprop(yaw_dumper_sw, !getprop(yaw_dumper_sw)); }, func { if (stec55x.roll.getValue() == 0) stec55x.roll.setValue(-1); else call(stec55x.button.HDG, [], nil, stec55x); }, func { call(stec55x.button.APR, [], nil, stec55x); }, func, func { # UP (trim) call(stec55x.button.Knob, [1], nil, stec55x); }, func { # DN (trim) call(func{stec55x.button.Knob(-1)}, [], nil, stec55x); }, ], }, # }}} KAP140: { # {{{ _blink_count: 0, updateDisplay: func { var latmod = latmod_armed = vertmod = vertmod_armed = ""; var vertmod_target = 0; var ap_enabled = kap140.lockRollMode.getValue() != kap140.rollModes["OFF"] or kap140.lockPitchMode.getValue() != kap140.pitchModes["OFF"]; if (ap_enabled) { foreach (var mode; keys(kap140.rollModes)) if (kap140.lockRollMode.getValue() == kap140.rollModes[mode]) { latmod = mode; break; } foreach (var mode; keys(kap140.rollArmModes)) if (kap140.lockRollArm.getValue() == kap140.rollArmModes[mode]) { latmod_armed = mode; break; } foreach (var mode; keys(kap140.pitchModes)) if (kap140.lockPitchMode.getValue() == kap140.pitchModes[mode]) { vertmod = mode; break; } foreach (var mode; keys(kap140.pitchArmModes)) if (kap140.lockPitchArm.getValue() == kap140.pitchArmModes[mode]) { vertmod_armed = mode; break; } vertmod_target = kap140.settingTargetPressureRate.getValue() * 58000; if (kap140.lockPitchMode.getValue() == kap140.pitchModes["VS"] or kap140.lockPitchArm.getValue() == kap140.pitchArmModes["VS"]) vertmod_target = sprintf('%s%4d fpm', utf8.chstr(vertmod_target > 0 ? 9650 : 9660), math.abs(math.round(vertmod_target, 10))); } foreach (var terminal; me.terminals) { var se = flightdeck[terminal].display.screenElements; if (se['AP-Status-text'].getVisible() and !ap_enabled) { if (math.mod(me._blink_count,2)) se['AP-Status-text'] .setDrawMode(canvas.Text.TEXT + canvas.Text.FILLEDBOUNDINGBOX) .setColorFill(flightdeck[terminal].display.colors.yellow) .setColor(flightdeck[terminal].display.colors.black); else se['AP-Status-text'] .setDrawMode(canvas.Text.TEXT + canvas.Text.FILLEDBOUNDINGBOX) .setColorFill(flightdeck[terminal].display.colors.black) .setColor(flightdeck[terminal].display.colors.yellow); me._blink_count += 1; if (me._blink_count == 5) { se['AP-Status-text'] .setColor(flightdeck[terminal].display.colors.green) .setVisible(0); me._blink_count = 0; } return; } else { se['AP-Status-text'].setVisible(ap_enabled); me._blink_count = 0; se['LATMOD-Active-text'].setVisible(ap_enabled).setText(latmod); se['LATMOD-Armed-text'].setVisible(ap_enabled).setText(latmod_armed); se['VERMOD-Active-text'].setVisible(ap_enabled).setText(vertmod); se['VERMOD-Reference-text'].setVisible(ap_enabled).setText(vertmod_target); se['VERMOD-Armed-text'].setVisible(ap_enabled).setText(vertmod_armed); } } }, L: [ func { kap140.apButton(); }, func, func { kap140.navButton(); }, func { kap140.altButton(); }, func, func, ], R: [ func, func { kap140.hdgButton(); }, func { kap140.aprButton(); }, func, func { kap140.upButton(); }, func { kap140.downButton(); } ], }, } # }}} };