From 6ca37b3a427f2cfa13267196834fb8ffd7d5c193 Mon Sep 17 00:00:00 2001 From: helijah Date: Thu, 11 Jun 2009 22:55:36 +0000 Subject: [PATCH] *** empty log message *** --- Aircraft/Generic/helicopter-fcs.nas | 701 ++++++++++++++++++++++++++++ 1 file changed, 701 insertions(+) create mode 100644 Aircraft/Generic/helicopter-fcs.nas diff --git a/Aircraft/Generic/helicopter-fcs.nas b/Aircraft/Generic/helicopter-fcs.nas new file mode 100644 index 000000000..cb0ef271d --- /dev/null +++ b/Aircraft/Generic/helicopter-fcs.nas @@ -0,0 +1,701 @@ +# +# Flight Control System for Helicopters by Tatsuhiro Nishioka +# $Id$ +# + +var enableDebug = func() { + setprop("/controls/flight/fcs/switches/debug", 1); +} + +var debugEnabled = func() { + var debugStatus = getprop("/controls/flight/fcs/switches/debug"); + if (debugStatus == 1) { + return 1; + } else { + return 0; + } +} + +var FCSFilter = { + new : func(input_path, output_path) { + var obj = { parents : [FCSFilter], + input_path : input_path, + output_path : output_path }; + obj.axis_conv = {'roll' : 'aileron', 'pitch' : 'elevator', 'yaw' : 'rudder' }; + obj.body_conv = {'roll' : 'v', 'pitch' : 'u' }; + obj.last_body_fps = {'roll' : 0.0, 'pitch' : 0.0 }; + obj.last_pos = {'roll' : 0.0, 'pitch' : 0.0, 'yaw' : 0.0}; + return obj; + }, + + updateSensitivities : func() { + me.sensitivities = props.globals.getNode("/controls/flight/fcs/gains/sensitivities").getValues(); + }, + + # read input command for a given axis + read : func(axis) { + if (me.input_path == nil or me.input_path == "") { + return getprop("/controls/flight/" ~ me.axis_conv[axis]); + } else { + var value = getprop(me.input_path ~ "/" ~ axis); + value = int(value * 1000) / 1000.0; + } + }, + + # write output command for a given axis + # this will be the output of an next command filter (like SAS) + write : func(axis, value) { + if (me.output_path == nil or me.output_path == '') { + setprop("/controls/flight/fcs/" ~ axis, me.limit(value, 1.0)); + } else { + setprop(me.output_path ~ "/" ~ axis, me.limit(value, 1.0)); + } + }, + + toggleFilterStatus : func(name) { + var messages = ["disengaged", "engaged"]; + var path = "/controls/flight/fcs/switches/" ~ name; + var status = getprop(path); + setprop(path, 1 - status); + screen.log.write(name ~ " " ~ messages[1 - status]); + }, + + getStatus : func(name) { + var path = "/controls/flight/fcs/switches/" ~ name; + return getprop(path); + }, + + limit : func(value, range) { + if (value > range) { + return range; + } elsif (value < -range) { + return - range; + } + return value; + }, + + max : func(val1, val2) { + return (val1 > val2) ? val1 : val2; + }, + + min : func(val1, val2) { + return (val1 > val2) ? val2 : val1; + }, + + calcCounterBodyFPS : func(axis, input, offset_deg) { + var position = getprop("/orientation/" ~ axis ~ "-deg"); + var body_fps = 0; + var last_body_fps = me.last_body_fps[axis]; + var reaction_gain = 0; + var heading = getprop("/orientation/heading-deg"); + var wind_speed_fps = getprop("/environment/wind-speed-kt") * 1.6878099; + var wind_direction = getprop("/environment/wind-from-heading-deg"); + var wind_direction -= heading; + var rate = getprop("/orientation/" ~ axis ~ "-rate-degps"); + var gear_pos = getprop("/gear/gear[0]/compression-norm") + getprop("/gear/gear[1]/compression-norm"); + var counter_fps = 0; + var fps_axis = me.body_conv[axis]; # convert from {roll, pitch} to {u, v} + var target_pos = offset_deg; + var brake_deg = 0; + + body_fps = getprop("/velocities/" ~ fps_axis ~ "Body-fps"); + if (axis == 'roll') { + var wind_fps = math.sin(wind_direction / 180 * math.pi) * wind_speed_fps; + } else { + var wind_fps = math.cos(wind_direction / 180 * math.pi) * wind_speed_fps; + } + var brake_freq = getprop("/controls/flight/fcs/gains/afcs/fps-" ~ axis ~ "-brake-freq"); + var brake_gain = getprop("/controls/flight/fcs/gains/afcs/fps-brake-gain-" ~ axis); + body_fps -= wind_fps; + var dfps = body_fps - me.last_body_fps[axis]; + var fps_coeff = getprop("/controls/flight/fcs/gains/afcs/fps-" ~ axis ~ "-coeff"); + target_pos -= int(body_fps * 100) / 100 * fps_coeff; + if (axis == 'roll' and gear_pos > 0.0 and position > 0) { + target_pos -= position * gear_pos / 5; + } + reaction_gain = getprop("/controls/flight/fcs/gains/afcs/fps-reaction-gain-" ~ axis); + var brake_sensitivity = (axis == 'roll') ? 1 : 1; + if (math.abs(position + rate / brake_freq * brake_sensitivity) > math.abs(target_pos)) { + if (math.abs(dfps) > 1) { + dfps = 1; + } + var error_deg = target_pos - position; + brake_deg = (error_deg - rate / brake_freq) * math.abs(dfps * 10) * brake_gain; + if (target_pos > 0) { + brake_deg = me.min(brake_deg, 0); + } else { + brake_deg = me.max(brake_deg, 0); + } + } + counter_fps = me.limit((target_pos + brake_deg) * reaction_gain, 1.0); + if (debugEnabled() == 1) { + setprop("/controls/flight/fcs/afcs/status/ah-" ~ fps_axis ~ "body-fps", body_fps); + setprop("/controls/flight/fcs/afcs/status/ah-" ~ fps_axis ~ "body-wind-fps", wind_fps); + setprop("/controls/flight/fcs/afcs/status/ah-" ~ axis ~ "-target-deg", target_pos); + setprop("/controls/flight/fcs/afcs/status/ah-" ~ axis ~ "-rate", rate); + setprop("/controls/flight/fcs/afcs/status/ah-delta-" ~ fps_axis ~ "body-fps", dfps); + setprop("/controls/flight/fcs/afcs/status/ah-" ~ axis ~ "-brake-deg", brake_deg); + setprop("/controls/flight/fcs/afcs/status/counter-fps-" ~ axis, counter_fps); + } + + me.last_pos[axis] = position; + me.last_body_fps[axis] = body_fps; + return me.limit(counter_fps + input * 0.2, 1.0); + }, + +}; + +# +# AFCS : Automatic Flight Control System +# +var AFCS = { + new : func(input_path, output_path) { + var obj = FCSFilter.new(input_path, output_path); + obj.parents = [FCSFilter, AFCS]; + return obj; + }, + + toggleAutoHover : func() { + me.toggleFilterStatus("auto-hover"); + }, + + toggleAirSpeedLock : func() { + me.toggleFilterStatus("air-speed-lock"); + }, + + toggleHeadingLock : func() { + me.toggleFilterStatus("heading-lock"); + }, + + toggleAltitudeLock : func() { + me.toggleFilterStatus("altitude-lock"); + }, + + # + # auto hover : locks vBody_fps and uBody_fps regardless of wind speed/direction + # + autoHover : func(axis, input) { + if (axis == 'yaw') { + return input; + } else { + var offset_deg = getprop("/controls/flight/fcs/gains/afcs/fps-" ~ axis ~ "-offset-deg"); + return me.calcCounterBodyFPS(axis, input, offset_deg); + } + }, + + altitudeLock : func(axis, input) { + # not implemented yet + return input; + }, + + headingLock : func(axis, input) { + # not implementet yet + return input; + }, + + apply : func(axis) { + var input = me.read(axis); + var hover_status = me.getStatus("auto-hover"); + if (hover_status == 0) { + me.write(axis, input); + return; + } + me.write(axis, me.autoHover(axis, input)); + } +}; + +# +# SAS : Stability Augmentation System - a rate damper +# +var SAS = { + # + # new + # authority_limit: shows how much SAS can take over control + # 0 means no stability control, 1.0 means SAS fully takes over pilot control + # input_path: is a base path to input axis; nil for using raw input from KB/JS + # output_path: is a base path to output axis; nis for using /controls/flight/fcs + # + # with input_path / output_path, you can connect SAS, CAS, and more control filters + # + new : func(input_path, output_path) { + var obj = FCSFilter.new(input_path, output_path); + obj.parents = [FCSFilter, SAS]; + return obj; + }, + + toggleEnable : func() { + me.toggleFilterStatus("sas"); + }, + + # + # calcGain - get gain for each axis based on air speed and dynamic pressure + # axis: one of 'roll', 'pitch', or 'yaw' + # + calcGain : func(axis) { + var mach = getprop("/velocities/mach"); + var initial_gain = getprop("/controls/flight/fcs/gains/sas/" ~ axis); + var gain = initial_gain - 0.1 * mach * mach; + if (math.abs(gain) < math.abs(initial_gain) * 0.01 or gain * initial_gain < 0) { + gain = initial_gain * 0.01; + } + return gain; + }, + + calcAuthorityLimit : func() { + var mach = getprop("/velocities/mach"); + var min_mach = 0.038; + me.authority_limit = getprop("/controls/flight/fcs/gains/sas/authority-limit"); + var limit = me.authority_limit; + if (math.abs(mach < min_mach)) { + limit += (min_mach - math.abs(mach)) / min_mach * (1 - me.authority_limit) * 0.95; + } + if (debugEnabled() == 1) { + setprop("/controls/flight/fcs/sas/status/authority-limit", limit); + } + return limit; + }, + + # + # apply - apply SAS damper to a given input axis + # axis: one of 'roll', 'pitch', or 'yaw' + # + apply : func(axis) { + me.updateSensitivities(); + var status = me.getStatus("sas"); + var input = me.read(axis); + if (status == 0) { + me.write(axis, input); + return; + } + var mach = getprop("/velocities/mach"); + var value = 0; + var rate = getprop("/orientation/" ~ axis ~ "-rate-degps"); + var gain = me.calcGain(axis); + var limit = me.calcAuthorityLimit(); + if (math.abs(rate) >= me.sensitivities[axis]) { + value = - gain * rate; + if (value > limit) { + value = limit; + } elsif (value < - limit) { + value = - limit; + } + } + me.write(axis, value + input); + } +}; + +# +# CAS : Control Augmentation System - makes your aircraft more meneuverable +# +var CAS = { + new : func(input_path, output_path) { + var obj = FCSFilter.new(input_path, output_path); + obj.parents = [FCSFilter, CAS]; + setprop("/autopilot/locks/altitude", ''); + setprop("/autopilot/locks/heading", ''); + return obj; + }, + + calcRollRateAdjustment : func { + var position = getprop("/orientation/roll-deg"); + return math.abs(math.sin(position / 180 * math.pi)) / 6; + }, + + calcHeadingAdjustment : func { + if (getprop("/controls/flight/fcs/switches/heading-adjuster") == 1) { + var gain = getprop("/controls/flight/fcs/gains/cas/output/heading-adjuster-gain"); + var yaw_rate = getprop("/orientation/yaw-rate-degps"); + var limit = getprop("/controls/flight/fcs/gains/cas/output/heading-adjuster-limit"); + var adjuster = yaw_rate * gain; + return me.limit(adjuster, limit); + } else { + return 0; + } + }, + + calcSideSlipAdjustment : func { + if (getprop("/controls/flight/fcs/switches/sideslip-adjuster") == 0) { + return 0; + } + var mach = getprop("/velocities/mach"); + var slip = getprop("/orientation/side-slip-deg"); + var min_speed_threshold = getprop("/controls/flight/fcs/gains/cas/input/anti-side-slip-min-speed"); + if (mach < min_speed_threshold) { # works only if air speed > min_speed_threshold + slip = 0; + } + var anti_slip_gain = getprop("/controls/flight/fcs/gains/cas/output/anti-side-slip-gain"); + var roll_deg = getprop("/orientation/roll-deg"); + var gain_adjuster = me.min(math.abs(mach) / 0.060, 1) * me.limit(0.2 + math.sqrt(math.abs(roll_deg)/10), 3); + anti_slip_gain *= gain_adjuster; + if (debugEnabled() == 1) { + setprop("/controls/flight/fcs/cas/status/anti-side-slip", slip * anti_slip_gain); + } + return slip * anti_slip_gain; + }, + + isInverted : func() { + var roll_deg = getprop("/orientation/roll-deg"); + if (roll_deg > 90 or roll_deg < -90) + return 1; + else + return 0; + }, + + # FIXME: command for CAS is just a temporal one + calcCommand: func (axis, input) { + var output = 0; + var mach = getprop("/velocities/mach"); + var input_gain = me.calcGain(axis); + var output_gain = getprop("/controls/flight/fcs/gains/cas/output/" ~ axis); + var target_rate = input * input_gain; + var rate = getprop("/orientation/" ~ axis ~ "-rate-degps"); + var drate = target_rate - rate; + if (axis == 'pitch' and me.isInverted() == 1) { + drate = - drate; + } + var attitudeControlThreshold = getprop("/controls/flight/fcs/gains/cas/input/attitude-control-threshold"); + var rateControlThreshold = getprop("/controls/flight/fcs/gains/cas/input/rate-control-threshold"); + var locks = {'pitch' : getprop("/autopilot/locks/altitude"), + 'roll' : getprop("/autopilot/locks/heading")}; + setprop("/controls/flight/fcs/cas/target_" ~ axis ~ "rate", target_rate); + setprop("/controls/flight/fcs/cas/delta_" ~ axis, drate); + + if (axis == 'roll' or axis == 'pitch') { + if (math.abs(input) > rateControlThreshold) { + return input; + } elsif (math.abs(input) > attitudeControlThreshold or locks[axis] != '') { + output = drate * output_gain; + } else { + output = me.calcAttitudeCommand(axis); + } + if (axis == 'roll' and math.abs(mach) < 0.035) { + # FIXME: I don't know if OH-1 has this one + output += me.calcCounterBodyFPS(axis, input, -0.8); + } + } elsif (axis == 'yaw') { + if (getprop("/controls/flight/fcs/switches/tail-rotor-adjuster") == 0) { + output = input; + } else { + output = drate * output_gain + me.calcSideSlipAdjustment(); + } + } else { + output = drate * output_gain; + } + return output; + }, + + toggleEnable : func() { + me.toggleFilterStatus("cas"); + }, + + calcAttitudeCommand : func(axis) { + var input_gain = getprop("/controls/flight/fcs/gains/cas/input/attitude-" ~ axis); + var output_gain = getprop("/controls/flight/fcs/gains/cas/output/" ~ axis); + var brake_freq = getprop("/controls/flight/fcs/gains/cas/output/" ~ axis ~ "-brake-freq"); + var brake_gain = getprop("/controls/flight/fcs/gains/cas/output/" ~ axis ~ "-brake"); + var trim = getprop("/controls/flight/" ~ me.axis_conv[axis] ~ "-trim"); + + var current_deg = getprop("/orientation/" ~ axis ~ "-deg"); + var rate = getprop("/orientation/" ~ axis ~ "-rate-degps"); + var target_deg = (me.read(axis) + trim) * input_gain; + if (axis == 'roll' and math.abs(target_deg) < 0.1) { + # rolls a bit to counteract the heading changes only if target roll rate = 0 + target_deg += me.calcHeadingAdjustment(); + } + var command_deg = 0; + if (target_deg != 0) { + command_deg = (0.094 * math.ln(math.abs(target_deg)) + 0.53) * target_deg; + } + + var error_deg = command_deg - current_deg; + if (axis == 'pitch' and me.isInverted() == 1) { + error_deg = - error_deg; + } + var brake_deg = (error_deg - rate / brake_freq) * math.abs(error_deg) * brake_gain; + + if (command_deg > 0) { + brake_deg = me.min(brake_deg, 0); + } else { + brake_deg = me.max(brake_deg, 0); + } + + if (debugEnabled() == 1) { + var monitor_prefix = me.output_path ~ "/status/" ~ axis; + setprop(monitor_prefix ~ "-target_deg", target_deg); + setprop(monitor_prefix ~ "-error_deg", error_deg); + setprop(monitor_prefix ~ "-brake_deg", brake_deg); + setprop(monitor_prefix ~ "-deg", current_deg); + setprop(monitor_prefix ~ "-rate", -rate); + } + + return (error_deg + brake_deg) * output_gain; + }, + + # FixMe: gain should be calculated using both speed and dynamic pressure + calcGain : func(axis) { + var mach = getprop("/velocities/mach"); + var input_gain = getprop("/controls/flight/fcs/gains/cas/input/" ~ axis); + var gain = input_gain; + if (axis == 'pitch') { + gain += 0.1 * mach * mach; + } elsif (axis== 'yaw') { + gain *= ((1 - mach) * (1 - mach)); + } + if (gain * input_gain < 0.0 ) { + gain = 0; + } + if (debugEnabled() == 1) { + setprop("/controls/flight/fcs/cas/gain-" ~ axis, gain); + } + return gain; + }, + + apply : func(axis) { + me.updateSensitivities(); + var input = me.read(axis); + var status = me.getStatus("cas"); + var cas_command = 0; + # FIXME : hmm, a bit nasty. CAS should be enabled even with auto-hover.... + if (status == 0 or (me.getStatus("auto-hover") == 1 and axis != 'yaw')) { + me.write(axis, input); + return; + } + cas_command = me.calcCommand(axis, input); + me.write(axis, cas_command); + } +}; + +# +# Tail hstab, "stabilator," for stabilize the nose +# +var Stabilator = { + new : func() { + var obj = { parents : [Stabilator] }; + me.gainTable = props.globals.getNode("/controls/flight/fcs/gains/stabilator").getChildren('gain-table'); + return obj; + }, + + toggleManual : func { + var status = getprop("/controls/flight/fcs/switches/auto-stabilator"); + getprop("/controls/flight/fcs/switches/auto-stabilator", 1 - status); + }, + +# apply : func(delta) { +# setprop("/controls/flight/fcs/switches/auto-stabilator", 0); +# var value = getprop("/controls/flight/fcs/stabilator"); +# getprop("/controls/flight/fcs/stabilator", value + delta); +# }, + + calcPosition : func() { + var speed = getprop("/velocities/mach") / 0.001497219; # in knot + var index = int(math.abs(speed) / 10); + if (index >= size(me.gainTable) - 1) { + index = size(me.gainTable) - 2; + } + var gain = me.gainTable[index].getValue(); + var gainAmb = me.gainTable[index-1].getValue(); + var mod = math.mod(int(math.abs(speed)), 10); + var position = gain * ((10 - mod) / 10) + gainAmb * mod / 10; + if (speed < -20) { + position = - position; + } + return position; + }, + + apply : func() { + var status = getprop("/controls/flight/fcs/switches/auto-stabilator"); + if (status == 0) { + return; + } + var gain = getprop("/controls/flight/fcs/gains/stabilator/stabilator-gain"); + var mach = getprop("/velocities/mach"); + var throttle = getprop("/controls/flight/throttle"); + var stabilator_norm = 0; + + stabilator_norm = me.calcPosition(); + setprop("/controls/flight/fcs/stabilator", stabilator_norm); + } +}; + +# +# Automatic tail rotor adjuster depending on collective/throttle status +# +var TailRotorCollective = { + new : func() { + var obj = FCSFilter.new("/controls/engines/engine[1]", "/controls/flight/fcs/tail-rotor"); + obj.parents = [FCSFilter, TailRotorCollective]; + obj.adjuster = 0.0; + return obj; + }, + + apply : func() { + var throttle = me.read("throttle"); + var pedal_pos_deg = getprop("/controls/flight/fcs/yaw"); + var cas_input = cas.read('yaw'); + var cas_input_gain = cas.calcGain('yaw'); + var target_rate = cas_input * cas_input_gain; + var rate = getprop("/orientation/yaw-rate-degps"); + var error_rate = getprop("/controls/flight/fcs/cas/delta_yaw"); + var error_adjuster_gain = getprop("/controls/flight/fcs/gains/tail-rotor/error-adjuster-gain"); + + var minimum = getprop("/controls/flight/fcs/gains/tail-rotor/src-minimum"); + var maximum = getprop("/controls/flight/fcs/gains/tail-rotor/src-maximum"); + var low_limit = getprop("/controls/flight/fcs/gains/tail-rotor/low-limit"); + var high_limit = getprop("/controls/flight/fcs/gains/tail-rotor/high-limit"); + var authority_limit = getprop("/controls/flight/fcs/gains/tail-rotor/authority-limit"); + var output = 0; + var range = maximum - minimum; + + if (throttle < minimum) { + output = low_limit; + } elsif (throttle > maximum) { + output = high_limit; + } else { + output = low_limit + (throttle - minimum) / range * (high_limit - low_limit); + } + + # CAS driven tail rotor thrust adjuster + me.adjuster = error_rate * error_adjuster_gain; + me.adjuster = me.limit(me.adjuster, authority_limit); + output += me.adjuster; + + setprop("/controls/flight/fcs/tail-rotor/error-rate", error_rate); + setprop("/controls/flight/fcs/tail-rotor/adjuster", me.adjuster); + + me.write("throttle", output); + } +}; + +var sas = nil; +var cas = nil; +var afcs = nil; +var stabilator = nil; +var tail = nil; +var count = 0; + +var update = func { + count += 1; + # AFCS, CAS, and SAS run at 60Hz + rpm = getprop("/rotors/main/rpm"); + # AFCS, CAS, and SAS run at 60Hz only when engine rpm > 1 + # this rpm filter prevents CAS/SAS work when engine is not running, + # which may cause Nasal runtime error + if (math.mod(count, 2) == 0 or rpm < 10) { + return; + } + + cas.apply('roll'); + cas.apply('pitch'); + cas.apply('yaw'); + + afcs.apply('roll'); + afcs.apply('pitch'); + afcs.apply('yaw'); + + sas.apply('roll'); + sas.apply('pitch'); + sas.apply('yaw'); + stabilator.apply(); + tail.apply(); +} + +# Factory default configuration values +# DO NOT CHANGE THESE VALUES.! +# You can change some of these values in per-aircraft nasal file. +# See Aircraft/OH-1/Nasal/OH1.nas for more detail +# +var default_fcs_params = { + 'gains' : { + 'afcs' : { + # Auto Hover parameters + 'fps-brake-gain-pitch' : 1.8, + 'fps-brake-gain-roll' : 0.8, + 'fps-pitch-brake-freq' : 3, + 'fps-pitch-coeff' : -0.95, + 'fps-pitch-offset-deg' : 0.9, + 'fps-reaction-gain-pitch' : -0.8, + 'fps-reaction-gain-roll' : 0.3436, + 'fps-roll-brake-freq' : 8, + 'fps-roll-coeff' : 0.8, + 'fps-roll-offset-deg' : -0.8 + }, + 'cas' : { + 'input' : { # Input gains for CAS + 'roll' : 30, + 'pitch' : -60, + 'yaw' : 30, + 'attitude-roll' : 80, + 'attitude-pitch' : -80, + 'attitude-control-threshold' : 0.7, # input threshold that CAS changes attitude-base control to rate-base control + 'rate-control-threshold' : 0.95, # input threshold that CAS changes rate-base control to doing nothing + 'anti-side-slip-min-speed' : 0.015 + }, + 'output' : { # Output gains for CAS + 'roll' : 0.06, + 'pitch' : -0.1, + 'yaw' : 0.5, + 'roll-brake-freq' : 10, + 'pitch-brake-freq' : 3, + 'roll-brake' : 0.4, + 'pitch-brake' : 6, + 'anti-side-slip-gain' : -4.5, + 'heading-adjuster-gain' : -5, + 'heading-adjuster-limit' : 5, + } + }, + 'sas' : { # gains for SAS + 'roll' : 0.02, + 'pitch' : -0.10, + 'yaw' : 0.04, + 'authority-limit' : 0.15 # How much SAS will take over pilot's control. 0.15 means 15% + }, + 'sensitivities' : { + 'roll' : 1.0, + 'pitch' : 1.0, + 'yaw' : 3.0 + }, + 'tail-rotor' : { # parameters for tail rotor control based on throttle / collective + 'src-minimum' : 0.10, # throttle value that outputs low-limit + 'src-maximum' : 1.00, # throttle value that outputs high-limit + 'low-limit' : 0.00011, + 'high-limit' : 0.0035, + 'error-adjuster-gain' : -0.5, # gain that how much CAS adjust yaw rate + 'authority-limit' : 0.3 + }, + 'stabilator' : { # gain tables for adjusting either incidence or flap angle of hstab + # index is the speed (Kt) devided by 10 + # 0 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160, 170, 180, ..... + 'gain-table' : [-0.9, -0.8, 0.1, -0.5, 0.0, 0.7, 0.8, 0.9, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.9, 0.8, 0.6, 0.4, 0.2, -1.0] + } + }, + 'switches' : { # master switches for AFCS, can be controlled by cockpit panel or keys + 'auto-hover' : 0, + 'cas' : 1, + 'sas' : 1, + 'auto-stabilator' : 1, + 'sideslip-adjuster' : 1, + 'tail-rotor-adjuster' : 1, + 'heading-adjuster' : 0, + 'air-speed-lock' : 0, + 'heading-lock' : 0, + 'altitude-lock' : 0, + } +}; + +var initialize = func { + cas = CAS.new(nil, "/controls/flight/fcs/cas"); + afcs = AFCS.new("/controls/flight/fcs/cas", "/controls/flight/fcs/afcs"); + sas = SAS.new("/controls/flight/fcs/afcs", "/controls/flight/fcs"); + stabilator = Stabilator.new(); + tail = TailRotorCollective.new(); + setlistener("/rotors/main/cone-deg", update); +} + +# Stores default AFCS parameters +var confNode = props.globals.getNode("/controls/flight/fcs", 1); +confNode.setValues(default_fcs_params); + +# fcs-initialized signal must be set by per-aircraft nasal script +# to show that FCS configuration parameters are set +_setlistener("/sim/signals/fcs-initialized", initialize); +