fcs.nas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. #
  2. # Flight Control System by Tatsuhiro Nishioka
  3. # $Id: fcs.nas,v 1.11 2008/08/28 02:41:04 tat Exp $
  4. #
  5. #This one simulates a jaw SAS
  6. var FCSFilter = {
  7. new : func(input_path, output_path) {
  8. var obj = { parents : [FCSFilter],
  9. input_path : input_path,
  10. output_path : output_path };
  11. obj.axis_conv = {'roll' : 'aileron', 'pitch' : 'elevator', 'yaw' : 'rudder' };
  12. obj.body_conv = {'roll' : 'v', 'pitch' : 'u' };
  13. obj.last_body_fps = {'roll' : 0.0, 'pitch' : 0.0 };
  14. obj.last_pos = {'roll' : 0.0, 'pitch' : 0.0, 'yaw' : 0.0};
  15. return obj;
  16. },
  17. # read input command for a given axis
  18. read : func(axis) {
  19. if (me.input_path == nil or me.input_path == "") {
  20. return getprop("/controls/flight/" ~ me.axis_conv[axis]);
  21. } else {
  22. var value = getprop(me.input_path ~ "/" ~ axis);
  23. value = int(value * 1000) / 1000.0;
  24. }
  25. },
  26. # write output command for a given axis
  27. # this will be the output of an next command filter (like SAS)
  28. write : func(axis, value) {
  29. if (me.output_path == nil or me.output_path == '') {
  30. setprop("/controls/flight/fcs/" ~ axis, me.limit(value, 1.0));
  31. } else {
  32. setprop(me.output_path ~ "/" ~ axis, me.limit(value, 1.0));
  33. }
  34. },
  35. toggleFilterStatus : func(name) {
  36. var messages = ["disengaged", "engaged"];
  37. var path = "/controls/flight/fcs/" ~ name ~ "-enabled";
  38. var status = getprop(path);
  39. setprop(path, 1 - status);
  40. screen.log.write(name ~ " " ~ messages[1 - status]);
  41. },
  42. getStatus : func(name) {
  43. var path = "/controls/flight/fcs/" ~ name ~ "-enabled";
  44. return getprop(path);
  45. },
  46. limit : func(value, range) {
  47. if (value > range) {
  48. return range;
  49. } elsif (value < -range) {
  50. return - range;
  51. }
  52. return value;
  53. },
  54. max : func(val1, val2) {
  55. return (val1 > val2) ? val1 : val2;
  56. },
  57. min : func(val1, val2) {
  58. return (val1 > val2) ? val2 : val1;
  59. },
  60. calcCounterBodyFPS : func(axis, input, offset_deg) {
  61. var position = getprop("/orientation/" ~ axis ~ "-deg");
  62. var body_fps = 0;
  63. var last_body_fps = me.last_body_fps[axis];
  64. var reaction_gain = 0;
  65. var heading = getprop("/orientation/heading-deg");
  66. var wind_speed_fps = getprop("/environment/wind-speed-kt") * 1.6878099;
  67. var wind_direction = getprop("/environment/wind-from-heading-deg");
  68. var wind_direction -= heading;
  69. var rate = getprop("/orientation/" ~ axis ~ "-rate-degps");
  70. var gear_pos = getprop("/gear/gear[0]/compression-norm") + getprop("/gear/gear[1]/compression-norm");
  71. var counter_fps = 0;
  72. var fps_axis = me.body_conv[axis]; # convert from {roll, pitch} to {u, v}
  73. var target_pos = offset_deg;
  74. var brake_deg = 0;
  75. body_fps = getprop("/velocities/" ~ fps_axis ~ "Body-fps");
  76. if (axis == 'roll') {
  77. var wind_fps = math.sin(wind_direction / 180 * math.pi) * wind_speed_fps;
  78. } else {
  79. var wind_fps = math.cos(wind_direction / 180 * math.pi) * wind_speed_fps;
  80. }
  81. var brake_freq = getprop("/controls/flight/fcs/gains/afcs/fps-" ~ axis ~ "-brake-freq");
  82. var brake_gain = getprop("/controls/flight/fcs/gains/afcs/fps-brake-gain-" ~ axis);
  83. body_fps -= wind_fps;
  84. var dfps = body_fps - me.last_body_fps[axis];
  85. var fps_coeff = getprop("/controls/flight/fcs/gains/afcs/fps-" ~ axis ~ "-coeff");
  86. target_pos -= int(body_fps * 100) / 100 * fps_coeff;
  87. if (axis == 'roll' and gear_pos > 0.0 and position > 0) {
  88. target_pos -= position * gear_pos / 5;
  89. }
  90. reaction_gain = getprop("/controls/flight/fcs/gains/afcs/fps-reaction-gain-" ~ axis);
  91. var brake_sensitivity = (axis == 'roll') ? 1 : 1;
  92. if (math.abs(position + rate / brake_freq * brake_sensitivity) > math.abs(target_pos)) {
  93. if (math.abs(dfps) > 1) {
  94. dfps = 1;
  95. }
  96. var error_deg = target_pos - position;
  97. brake_deg = (error_deg - rate / brake_freq) * math.abs(dfps * 10) * brake_gain;
  98. if (target_pos > 0) {
  99. brake_deg = me.min(brake_deg, 0);
  100. } else {
  101. brake_deg = me.max(brake_deg, 0);
  102. }
  103. }
  104. counter_fps = me.limit((target_pos + brake_deg) * reaction_gain, 1.0);
  105. setprop("/controls/flight/fcs/afcs/ah-" ~ fps_axis ~ "body-fps", body_fps);
  106. setprop("/controls/flight/fcs/afcs/ah-" ~ fps_axis ~ "body-wind-fps", wind_fps);
  107. setprop("/controls/flight/fcs/afcs/ah-" ~ axis ~ "-target-deg", target_pos);
  108. setprop("/controls/flight/fcs/afcs/ah-" ~ axis ~ "-rate", rate);
  109. setprop("/controls/flight/fcs/afcs/ah-delta-" ~ fps_axis ~ "body-fps", dfps);
  110. setprop("/controls/flight/fcs/afcs/ah-" ~ axis ~ "-brake-deg", brake_deg);
  111. setprop("/controls/flight/fcs/afcs/counter-fps-" ~ axis, counter_fps);
  112. me.last_pos[axis] = position;
  113. me.last_body_fps[axis] = body_fps;
  114. return me.limit(counter_fps + input * 0.2, 1.0);
  115. },
  116. };
  117. #
  118. # AFCS : Automatic Flight Control System
  119. #
  120. var AFCS = {
  121. new : func(input_path, output_path) {
  122. var obj = FCSFilter.new(input_path, output_path);
  123. obj.parents = [FCSFilter, AFCS];
  124. setprop("/controls/flight/fcs/auto-hover-enabled", 0);
  125. setprop("/controls/flight/fcs/gains/afcs/fps-brake-gain-pitch", 1.8);
  126. setprop("/controls/flight/fcs/gains/afcs/fps-brake-gain-roll", 0.8);
  127. setprop("/controls/flight/fcs/gains/afcs/fps-pitch-brake-freq", 3);
  128. setprop("/controls/flight/fcs/gains/afcs/fps-pitch-coeff", -0.95);
  129. setprop("/controls/flight/fcs/gains/afcs/fps-pitch-offset-deg", 0.9);
  130. setprop("/controls/flight/fcs/gains/afcs/fps-reaction-gain-pitch", -0.8);
  131. setprop("/controls/flight/fcs/gains/afcs/fps-reaction-gain-roll", 0.3436);
  132. setprop("/controls/flight/fcs/gains/afcs/fps-roll-brake-freq", 8);
  133. setprop("/controls/flight/fcs/gains/afcs/fps-roll-coeff", 0.8);
  134. setprop("/controls/flight/fcs/gains/afcs/fps-roll-offset-deg", -0.8);
  135. return obj;
  136. },
  137. toggleAutoHover : func() {
  138. me.toggleFilterStatus("auto-hover");
  139. },
  140. toggleAirSpeedLock : func() {
  141. me.toggleFilterStatus("air-speed-lock");
  142. },
  143. toggleHeadingLock : func() {
  144. me.toggleFilterStatus("heading-lock");
  145. },
  146. toggleAltitudeLock : func() {
  147. me.toggleFilterStatus("altitude-lock");
  148. },
  149. #
  150. # auto hover : locks vBody_fps and uBody_fps regardless of wind speed/direction
  151. #
  152. autoHover : func(axis, input) {
  153. if (axis == 'yaw') {
  154. return input;
  155. } else {
  156. var offset_deg = getprop("/controls/flight/fcs/gains/afcs/fps-" ~ axis ~ "-offset-deg");
  157. return me.calcCounterBodyFPS(axis, input, offset_deg);
  158. }
  159. },
  160. altitudeLock : func(axis, input) {
  161. # not implemented yet
  162. return input;
  163. },
  164. headingLock : func(axis, input) {
  165. # not implementet yet
  166. return input;
  167. },
  168. apply : func(axis) {
  169. var input = me.read(axis);
  170. var hover_status = me.getStatus("auto-hover");
  171. if (hover_status == 0) {
  172. me.write(axis, input);
  173. return;
  174. }
  175. me.write(axis, me.autoHover(axis, input));
  176. }
  177. };
  178. #
  179. # SAS : Stability Augmentation System - a rate damper
  180. #
  181. var SAS = {
  182. #
  183. # new
  184. # initial_gains: hash of initial gains for rate damping
  185. # sensitivities: hash of minimum rates (deg/sec) that enables rate damper
  186. # authority_limit: shows how much SAS can take over control
  187. # 0 means no stability control, 1.0 means SAS fully takes over pilot control
  188. # input_path: is a base path to input axis; nil for using raw input from KB/JS
  189. # output_path: is a base path to output axis; nis for using /controls/flight/fcs
  190. #
  191. # with input_path / output_path, you can connect SAS, CAS, and more control filters
  192. #
  193. new : func(initial_gains, sensitivities, authority_limit, input_path, output_path) {
  194. var obj = FCSFilter.new(input_path, output_path);
  195. obj.parents = [FCSFilter, SAS];
  196. obj.authority_limit = authority_limit;
  197. obj.sensitivities = sensitivities;
  198. obj.initial_gains = initial_gains;
  199. props.globals.getNode("/controls/flight/fcs/gains/sas", 1).setValues(obj.initial_gains);
  200. setprop("/controls/flight/fcs/sas-enabled", 1);
  201. return obj;
  202. },
  203. toggleEnable : func() {
  204. me.toggleFilterStatus("sas");
  205. },
  206. #
  207. # calcGain - get gain for each axis based on air speed and dynamic pressure
  208. # axis: one of 'roll', 'pitch', or 'yaw'
  209. #
  210. calcGain : func(axis) {
  211. var mach = getprop("/velocities/mach");
  212. var initial_gain = getprop("/controls/flight/fcs/gains/sas/" ~ axis);
  213. var gain = initial_gain - 0.1 * mach * mach;
  214. if (math.abs(gain) < math.abs(initial_gain) * 0.01 or gain * initial_gain < 0) {
  215. gain = initial_gain * 0.01;
  216. }
  217. return gain;
  218. },
  219. calcAuthorityLimit : func() {
  220. var mach = getprop("/velocities/mach");
  221. var min_mach = 0.038;
  222. var limit = me.authority_limit;
  223. if (math.abs(mach < min_mach)) {
  224. limit += (min_mach - math.abs(mach)) / min_mach * (1 - me.authority_limit) * 0.95;
  225. }
  226. setprop("/controls/flight/fcs/gains/sas/authority-limit", limit);
  227. return limit;
  228. },
  229. #
  230. # apply - apply SAS damper to a given input axis
  231. # axis: one of 'roll', 'pitch', or 'yaw'
  232. #
  233. apply : func(axis) {
  234. var status = me.getStatus("sas");
  235. var input = me.read(axis);
  236. if (status == 0) {
  237. me.write(axis, input);
  238. return;
  239. }
  240. var mach = getprop("/velocities/mach");
  241. var value = 0;
  242. var rate = getprop("/orientation/" ~ axis ~ "-rate-degps");
  243. var gain = me.calcGain(axis);
  244. var limit = me.calcAuthorityLimit();
  245. if (math.abs(rate) >= me.sensitivities[axis]) {
  246. value = - gain * rate;
  247. if (value > limit) {
  248. value = limit;
  249. } elsif (value < - limit) {
  250. value = - limit;
  251. }
  252. }
  253. me.write(axis, value + input);
  254. }
  255. };
  256. #
  257. # CAS : Control Augmentation System - makes your aircraft more meneuverable
  258. #
  259. var CAS = {
  260. new : func(input_gains, output_gains, sensitivities, input_path, output_path) {
  261. var obj = FCSFilter.new(input_path, output_path);
  262. obj.parents = [FCSFilter, CAS];
  263. obj.sensitivities = sensitivities;
  264. obj.input_gains = input_gains;
  265. obj.output_gains = output_gains;
  266. props.globals.getNode("/controls/flight/fcs/gains/cas/input", 1).setValues(obj.input_gains);
  267. props.globals.getNode("/controls/flight/fcs/gains/cas/output", 1).setValues(obj.output_gains);
  268. setprop("/autopilot/locks/altitude", '');
  269. setprop("/autopilot/locks/heading", '');
  270. setprop("/controls/flight/fcs/cas-enabled", 1);
  271. return obj;
  272. },
  273. calcRollRateAdjustment : func {
  274. var position = getprop("/orientation/roll-deg");
  275. return math.abs(math.sin(position / 180 * math.pi)) / 6;
  276. },
  277. calcSideSlipAdjustment : func {
  278. var mach = getprop("/velocities/mach");
  279. var slip = getprop("/orientation/side-slip-deg");
  280. if (mach < 0.015) { # works only if air speed > 10kt
  281. slip = 0;
  282. }
  283. var anti_slip_gain = getprop("/controls/flight/fcs/gains/cas/output/anti-side-slip-gain");
  284. var roll_deg = getprop("/orientation/roll-deg");
  285. var gain_adjuster = me.min(math.abs(mach) / 0.060, 1) * me.limit(0.2 + math.sqrt(math.abs(roll_deg)/10), 3);
  286. anti_slip_gain *= gain_adjuster;
  287. setprop("/controls/flight/fcs/cas/anti-side-slip", slip * anti_slip_gain);
  288. return slip * anti_slip_gain;
  289. },
  290. # FIXME: command for CAS is just a temporal one
  291. calcCommand: func (axis, input) {
  292. var output = 0;
  293. var mach = getprop("/velocities/mach");
  294. var input_gain = me.calcGain(axis);
  295. var output_gain = getprop("/controls/flight/fcs/gains/cas/output/" ~ axis);
  296. var target_rate = input * input_gain;
  297. var rate = getprop("/orientation/" ~ axis ~ "-rate-degps");
  298. var drate = target_rate - rate;
  299. var locks = {'pitch' : getprop("/autopilot/locks/altitude"),
  300. 'roll' : getprop("/autopilot/locks/heading")};
  301. setprop("/controls/flight/fcs/cas/target_" ~ axis ~ "rate", target_rate);
  302. setprop("/controls/flight/fcs/cas/delta_" ~ axis, drate);
  303. if (axis == 'roll' or axis == 'pitch') {
  304. if (math.abs(input > 0.7) or locks[axis] != '') {
  305. output = drate * output_gain;
  306. } else {
  307. output = me.calcAttitudeCommand(axis);
  308. }
  309. if (axis == 'roll' and math.abs(mach) < 0.035) {
  310. # FIXME: I don't know if OH-1 has this one
  311. output += me.calcCounterBodyFPS(axis, input, -0.8);
  312. }
  313. } elsif (axis == 'yaw') {
  314. output = drate * output_gain + me.calcSideSlipAdjustment();
  315. } else {
  316. output = drate * output_gain;
  317. }
  318. return output;
  319. },
  320. toggleEnable : func() {
  321. me.toggleFilterStatus("cas");
  322. },
  323. calcAttitudeCommand : func(axis) {
  324. var input_gain = getprop("/controls/flight/fcs/gains/cas/input/attitude-" ~ axis);
  325. var output_gain = getprop("/controls/flight/fcs/gains/cas/output/" ~ axis);
  326. var brake_freq = getprop("/controls/flight/fcs/gains/cas/output/" ~ axis ~ "-brake-freq");
  327. var brake_gain = getprop("/controls/flight/fcs/gains/cas/output/" ~ axis ~ "-brake");
  328. var trim = getprop("/controls/flight/" ~ me.axis_conv[axis] ~ "-trim");
  329. var current_deg = getprop("/orientation/" ~ axis ~ "-deg");
  330. var rate = getprop("/orientation/" ~ axis ~ "-rate-degps");
  331. var target_deg = (me.read(axis) + trim) * input_gain;
  332. var command_deg = 0;
  333. if (target_deg != 0) {
  334. command_deg = (0.094 * math.ln(math.abs(target_deg)) + 0.53) * target_deg;
  335. }
  336. var error_deg = command_deg - current_deg;
  337. var brake_deg = (error_deg - rate / brake_freq) * math.abs(error_deg) * brake_gain;
  338. if (command_deg > 0) {
  339. brake_deg = me.min(brake_deg, 0);
  340. } else {
  341. brake_deg = me.max(brake_deg, 0);
  342. }
  343. var monitor_prefix = me.output_path ~ "/" ~ axis;
  344. setprop(monitor_prefix ~ "-target_deg", target_deg);
  345. setprop(monitor_prefix ~ "-error_deg", error_deg);
  346. setprop(monitor_prefix ~ "-brake_deg", brake_deg);
  347. setprop(monitor_prefix ~ "-deg", current_deg);
  348. setprop(monitor_prefix ~ "-rate", -rate);
  349. return (error_deg + brake_deg) * output_gain;
  350. },
  351. # FixMe: gain should be calculated using both speed and dynamic pressure
  352. calcGain : func(axis) {
  353. var mach = getprop("/velocities/mach");
  354. var input_gain = getprop("/controls/flight/fcs/gains/cas/input/" ~ axis);
  355. var gain = input_gain;
  356. if (axis == 'pitch') {
  357. gain += 0.1 * mach * mach;
  358. } elsif (axis== 'yaw') {
  359. gain *= ((1 - mach) * (1 - mach));
  360. }
  361. if (gain * input_gain < 0.0 ) {
  362. gain = 0;
  363. }
  364. return gain;
  365. },
  366. apply : func(axis) {
  367. var input = me.read(axis);
  368. var status = me.getStatus("cas");
  369. var cas_command = 0;
  370. # FIXME : hmm, a bit nasty. CAS should be enabled even with auto-hover....
  371. if (status == 0 or (me.getStatus("auto-hover") == 1 and axis != 'yaw')) {
  372. me.write(axis, input);
  373. return;
  374. }
  375. cas_command = me.calcCommand(axis, input);
  376. me.write(axis, cas_command);
  377. }
  378. };
  379. #
  380. # Tail hstab, "stabilator," for stabilize the nose
  381. #
  382. var Stabilator = {
  383. new : func() {
  384. var obj = { parents : [Stabilator] };
  385. setprop("/controls/flight/fcs/gains/stabilator", -1.8);
  386. setprop("/controls/flight/fcs/auto-stabilator", 1);
  387. # 0 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160, 170, 180, .....
  388. me.gainTable = [-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];
  389. return obj;
  390. },
  391. toggleManual : func {
  392. var status = getprop("/controls/flight/fcs/auto-stabilator");
  393. getprop("/controls/flight/fcs/auto-stabilator", 1 - status);
  394. },
  395. apply : func(delta) {
  396. setprop("/controls/flight/fcs/auto-stabilator", 0);
  397. var value = getprop("/controls/flight/fcs/stabilator");
  398. getprop("/controls/flight/fcs/stabilator", value + delta);
  399. },
  400. calcPosition : func() {
  401. var speed = getprop("/velocities/mach") / 0.001497219; # in knot
  402. var index = int(math.abs(speed) / 10);
  403. if (index >= size(me.gainTable) - 1) {
  404. index = size(me.gainTable) - 2;
  405. }
  406. var mod = math.mod(int(math.abs(speed)), 10);
  407. var position = me.gainTable[index] * ((10 - mod) / 10) + me.gainTable[index-1] * (mod) / 10;
  408. if (speed < -20) {
  409. position = - position;
  410. }
  411. return position;
  412. },
  413. update : func() {
  414. var status = getprop("/controls/flight/fcs/auto-stabilator");
  415. if (status == 0) {
  416. return;
  417. }
  418. var gain = getprop("/controls/flight/fcs/gains/stabilator");
  419. var mach = getprop("/velocities/mach");
  420. var throttle = getprop("/controls/flight/throttle");
  421. var stabilator_norm = 0;
  422. stabilator_norm = me.calcPosition();
  423. setprop("/controls/flight/fcs/stabilator", stabilator_norm);
  424. }
  425. };
  426. var TailRotorCollective = {
  427. new : func(minimum=0.10, maximum=1.0, low_limit=0.00011, high_limit=0.0035) {
  428. var obj = FCSFilter.new("/controls/engines/engine[1]", "/controls/flight/fcs/tail-rotor");
  429. obj.parents = [FCSFilter, TailRotorCollective];
  430. obj.adjuster = 0.0;
  431. setprop("/controls/flight/fcs/tail-rotor/src-minimum", minimum);
  432. setprop("/controls/flight/fcs/tail-rotor/src-maximum", maximum);
  433. setprop("/controls/flight/fcs/tail-rotor/low-limit", low_limit);
  434. setprop("/controls/flight/fcs/tail-rotor/high-limit", high_limit);
  435. setprop("/controls/flight/fcs/gains/tail-rotor/error-adjuster-gain", -0.5);
  436. return obj;
  437. },
  438. update : func() {
  439. var throttle = me.read("throttle");
  440. var pedal_pos_deg = getprop("/controls/flight/fcs/yaw");
  441. var cas_input = cas.read('yaw');
  442. var cas_input_gain = cas.calcGain('yaw');
  443. var target_rate = cas_input * cas_input_gain;
  444. var rate = getprop("/orientation/yaw-rate-degps");
  445. var error_rate = getprop("/controls/flight/fcs/cas/delta_yaw");
  446. var error_adjuster_gain = getprop("/controls/flight/fcs/gains/tail-rotor/error-adjuster-gain");
  447. var minimum = getprop("/controls/flight/fcs/tail-rotor/src-minimum");
  448. var maximum = getprop("/controls/flight/fcs/tail-rotor/src-maximum");
  449. var low_limit = getprop("/controls/flight/fcs/tail-rotor/low-limit");
  450. var high_limit = getprop("/controls/flight/fcs/tail-rotor/high-limit");
  451. var output = 0;
  452. var range = maximum - minimum;
  453. if (throttle < minimum) {
  454. output = low_limit;
  455. } elsif (throttle > maximum) {
  456. output = high_limit;
  457. } else {
  458. output = low_limit + (throttle - minimum) / range * (high_limit - low_limit);
  459. }
  460. # CAS driven tail rotor thrust adjuster
  461. me.adjuster = error_rate * error_adjuster_gain;
  462. me.adjuster = me.limit(me.adjuster, 0.3);
  463. output += me.adjuster;
  464. setprop("/controls/flight/fcs/tail-rotor/error-rate", error_rate);
  465. setprop("/controls/flight/fcs/tail-rotor/adjuster", me.adjuster);
  466. me.write("throttle", output);
  467. }
  468. };
  469. var sas = nil;
  470. var cas = nil;
  471. var afcs = nil;
  472. var stabilator = nil;
  473. var tail = nil;
  474. var count = 0;
  475. var sensitivities = {'roll' : 0.0, 'pitch' : 0.0, 'yaw' : 1.125 };
  476. var sas_initial_gains = {'roll' : 0, 'pitch' : 0, 'yaw' : 0.008 };
  477. var cas_input_gains = {'roll' : 30, 'pitch' : -60, 'yaw' : 30,
  478. 'attitude-roll' : 80, 'attitude-pitch' : -80 };
  479. var cas_output_gains = {'roll' : 0.06, 'pitch' : -0.1, 'yaw' : 0.5,
  480. 'roll-brake-freq' : 10, 'pitch-brake-freq' : 3,
  481. 'roll-brake' : 0.4, 'pitch-brake' : 6,
  482. 'anti-side-slip-gain' : -4.5};
  483. var update = func {
  484. count += 1;
  485. # AFCS, CAS, and SAS run at 60Hz
  486. if (math.mod(count, 2) == 0) {
  487. return;
  488. }
  489. cas.apply('roll');
  490. cas.apply('pitch');
  491. cas.apply('yaw');
  492. afcs.apply('roll');
  493. afcs.apply('pitch');
  494. afcs.apply('yaw');
  495. sas.apply('roll');
  496. sas.apply('pitch');
  497. sas.apply('yaw');
  498. stabilator.update();
  499. tail.update();
  500. }
  501. var initialize = func {
  502. cas = CAS.new(cas_input_gains, cas_output_gains, sensitivities, nil, "/controls/flight/fcs/cas");
  503. afcs = AFCS.new("/controls/flight/fcs/cas", "/controls/flight/fcs/afcs");
  504. sas = SAS.new(sas_initial_gains, sensitivities, 3, "/controls/flight/fcs/afcs", "/controls/flight/fcs");
  505. stabilator = Stabilator.new();
  506. tail = TailRotorCollective.new();
  507. setlistener("/rotors/main/cone-deg", update);
  508. }
  509. _setlistener("/sim/signals/fdm-initialized", initialize);