zkv1000 / Nasal / afcs.nas /
d0decc8 a year ago
1 contributor
418 lines | 19.338kb
# 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 armed     = '';
                var active    = '';
                var reference = '';
                if (stec55x.ALT_annun.getBoolValue()) {
                    active    = 'ALT';
                    reference = sprintf('%5d ft', abs(data.alt - afcs.getValue('selected-alt-ft')) < 150 ? afcs.getValue('selected-alt-ft') : math.round(data.alt, 10));
                    armed     = sprintf('%.2f inHg', stec55x.alt.getValue());
                }
                elsif (stec55x.VS_annun.getBoolValue()) {
                    active    = 'VS';
                    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()) {
                    armed  = 'VPATH';
                    active = 'GS';
                }

                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"];

                foreach (var terminal; me.terminals) {
                    var se = flightdeck[terminal].display.screenElements;
                    se['VERMOD-Active-text'].setVisible(size(active)).setText(active);
                    se['VERMOD-Armed-text'].setVisible(size(armed)).setText(armed);
                    se['VERMOD-Reference-text'].setVisible(size(reference)).setText(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);
                    if (stec55x.rollMode  != -1) {
                        se['LATMOD-Active-text'].setVisible(1).setText('ROL');
                        var armed = '';
                        foreach (var m; [ 'NAV', 'CNAV', 'REV', 'CREV' ])
                            if (stec55x[m]) armed = m;
                        if (stec55x.roll.getValue() == 0) armed = 'HDG';
                        elsif (stec55x.roll.getValue() == 2) armed = 'GPS';
                        elsif (stec55x.APR_annun.getValue()) armed = 'LOC';
                        se['LATMOD-Armed-text']
                            .setVisible(size(armed))
                            .setText(armed);
                    }
                    else {
                        se['LATMOD-Active-text'].setVisible(0);
                        se['LATMOD-Armed-text'].setVisible(0);
                    }
                }

            },
            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(); }
            ],
        },
    }
# }}}
};