1 # (c) Melchior FRANZ < mfranz # flightgear : org > Thanks for it- currently there is no better solutionout there!
4 ______ _____ __ ____ ___ ____ _ _
5 | ____| / ____| /_ | |___ \ / _ \ | _ \ | || |
6 | |__ | | | | __) | | | | | | |_)| | || |_
7 | __| | | | | |__ < | | | | | _< |__ _|
8 | |____| |____ | | ___) | | |_| | | |_) | | |
9 |______|\_____| |_||____/ \___/ |____/ |_|
13 if (!contains(globals, "cprint"))
14 var cprint = func nil;
16 var devel = !!getprop("devel");
17 var quickstart = !!getprop("quickstart");
19 var sin = func(a) math.sin(a * D2R);
20 var cos = func(a) math.cos(a * D2R);
21 var pow = func(v, w) math.exp(math.ln(v) * w);
22 var npow = func(v, w) v ? math.exp(math.ln(abs(v)) * w) * (v < 0 ? -1 : 1) : 0;
23 var clamp = func(v, min = 0, max = 1) v < min ? min : v > max ? max : v;
24 var normatan = func(x, slope = 1) math.atan2(x, slope) * 2 / math.pi;
25 var bell = func(x, spread = 2) pow(math.e, -(x * x) / spread);
26 var max = func(a, b) a > b ? a : b;
27 var min = func(a, b) a < b ? a : b;
29 # liveries =========================================================
30 aircraft.livery.init("Aircraft/ec130/Models/liveries");
32 # timers ============================================================
33 aircraft.timer.new("/sim/time/hobbs/helicopter", nil).start();
35 # strobes ===========================================================
36 var strobe_switch = props.globals.initNode("controls/lighting/strobe", 1, "BOOL");
37 aircraft.light.new("sim/model/ec130/lighting/strobe-top", [0.05, 1.00], strobe_switch);
38 aircraft.light.new("sim/model/ec130/lighting/strobe-bottom", [0.05, 1.03], strobe_switch);
40 # beacons ===========================================================
41 var beacon_switch = props.globals.initNode("controls/lighting/beacon", 1, "BOOL");
42 aircraft.light.new("sim/model/ec130/lighting/beacon-top", [0.62, 0.62], beacon_switch);
43 aircraft.light.new("sim/model/ec130/lighting/beacon-bottom", [0.63, 0.63], beacon_switch);
46 # nav lights ========================================================
47 var nav_light_switch = props.globals.initNode("controls/lighting/nav-lights", 1, "BOOL");
48 var visibility = props.globals.getNode("environment/visibility-m", 1);
49 var sun_angle = props.globals.getNode("sim/time/sun-angle-rad", 1);
50 var nav_lights = props.globals.getNode("sim/model/ec130/lighting/nav-lights", 1);
52 #var nav_light_loop = func {
53 # if (nav_light_switch.getValue())
54 # nav_lights.setValue(visibility.getValue() < 5000 or sun_angle.getValue() > 1.4);
56 # nav_lights.setValue(0);
58 # settimer(nav_light_loop, 3);
67 # fuel ==============================================================
69 # density = 6.682 lb/gal [Flight Manual Section 9.2]
71 var FUEL_DENSITY = getprop("/consumables/fuel/tank/density-ppg"); # pound per gallon
72 var GAL2LB = FUEL_DENSITY;
73 var LB2GAL = 1 / GAL2LB;
74 var KG2GAL = KG2LB * LB2GAL;
75 var GAL2KG = 1 / KG2GAL;
81 var m = { parents: [Tank] };
82 m.capacity = n.getNode("capacity-gal_us").getValue();
83 m.level_galN = n.initNode("level-gal_us", m.capacity);
84 m.level_lbN = n.getNode("level-lbs");
89 return me.level_galN.getValue();
91 consume: func(amount) { # US gal (neg. values for feeding)
92 var level = me.level();
96 if (level > me.capacity)
98 me.level_galN.setDoubleValue(level);
99 me.level_lbN.setDoubleValue(level * GAL2LB);
108 var fuel = props.globals.getNode("/consumables/fuel");
109 me.pump_capacity = 6.6 * L2GAL / 60; # same pumps for transfer and supply; from ec135: 6.6 l/min
110 me.total_galN = fuel.getNode("total-fuel-gals", 1);
111 me.total_lbN = fuel.getNode("total-fuel-lbs", 1);
112 me.total_normN = fuel.getNode("total-fuel-norm", 1);
113 #me.supply = Tank.new(fuel.getNode("tank[1]"));
114 me.main = Tank.new(fuel.getNode("tank[0]"));
116 #var sw = props.globals.getNode("/controls/switches");
117 #setlistener(sw.initNode("fuel/transfer-pump[0]", 1, "BOOL"), func(n) me.trans1 = n.getValue(), 1);
118 #setlistener(sw.initNode("fuel/transfer-pump[1]", 1, "BOOL"), func(n) me.trans2 = n.getValue(), 1);
119 setlistener("/sim/freeze/fuel", func(n) me.freeze = n.getBoolValue(), 1);
120 me.capacity = me.main.capacity;
125 # transfer pumps (feed supply from main)
126 #var free = me.supply.capacity - me.supply.level();
128 # var trans_flow = (me.trans1 + me.trans2) * me.pump_capacity;
129 # me.supply.consume(-me.main.consume(min(trans_flow * dt, free)));
132 # low fuel warning [POH "General Description" 0.28a]
133 #var time = elapsedN.getValue();
134 #if (time > me.warntime and me.supply.level() * GAL2KG < 60) {
135 # screen.log.write("LOW FUEL WARNING", 1, 0, 0);
136 # me.warntime = time + screen.log.autoscroll * 2;
139 var level = me.main.level();
140 me.total_galN.setDoubleValue(level);
141 me.total_lbN.setDoubleValue(level * GAL2LB);
142 me.total_normN.setDoubleValue(level / me.capacity);
145 return me.main.level();
147 consume: func(amount) {
148 return me.freeze ? 0 : me.main.consume(amount);
154 # engines/rotor =====================================================
155 var rotor_rpm = props.globals.getNode("rotors/main/rpm");
156 var torque = props.globals.getNode("rotors/gear/total-torque", 1);
157 var collective = props.globals.getNode("controls/engines/engine[0]/throttle");
158 var turbine = props.globals.getNode("sim/model/ec130/turbine-rpm-pct", 1);
159 var torque_pct = props.globals.getNode("sim/model/ec130/torque-pct", 1);
160 var target_rel_rpm = props.globals.getNode("controls/rotor/reltarget", 1);
161 var max_rel_torque = props.globals.getNode("controls/rotor/maxreltorque", 1);
167 var m = { parents: [Engine] };
168 m.in = props.globals.getNode("controls/engines", 1).getChild("engine", n, 1);
169 m.out = props.globals.getNode("engines", 1).getChild("engine", n, 1);
170 m.airtempN = props.globals.getNode("/environment/temperature-degc");
173 m.ignitionN = m.in.initNode("ignition", 0, "BOOL");
174 m.starterN = m.in.initNode("starter", 0, "BOOL");
175 m.powerN = m.in.initNode("power", 0);
176 m.magnetoN = m.in.initNode("magnetos", 1, "INT");
179 m.runningN = m.out.initNode("running", 0, "BOOL");
180 m.n1_pctN = m.out.initNode("n1-pct", 0);
181 m.n2_pctN = m.out.initNode("n2-pct", 0);
182 m.n1N = m.out.initNode("n1-rpm", 0);
183 m.n2N = m.out.initNode("n2-rpm", 0);
184 m.totN = m.out.initNode("tot-degc", m.airtempN.getValue());
186 m.starterLP = aircraft.lowpass.new(3);
187 m.n1LP = aircraft.lowpass.new(4);
188 m.n2LP = aircraft.lowpass.new(4);
189 setlistener("/sim/signals/reinit", func(n) n.getValue() or m.reset(), 1);
190 m.timer = aircraft.timer.new("/sim/time/hobbs/turbines[" ~ n ~ "]", 10);
198 me.magnetoN.setIntValue(1);
199 me.ignitionN.setBoolValue(0);
200 me.starterN.setBoolValue(0);
201 me.powerN.setDoubleValue(0);
202 me.runningN.setBoolValue(me.running = 0);
204 me.n1LP.set(me.n1 = 0);
205 me.n2LP.set(me.n2 = 0);
207 update: func(dt, trim = 0) {
208 var starter = me.starterLP.filter(me.starterN.getValue() * 0.19); # starter 15-20% N1max
209 me.powerN.setValue(me.power = clamp(me.powerN.getValue()));
210 var power = me.power * 0.97 + trim; # 97% = N2% in flight position
213 power += (1 - collective.getValue()) * 0.03; # droop compensator
215 power = 1.12; # overspeed restrictor
219 if (me.n1 > 0.05 and power > 0.05 and me.ignitionN.getValue()) {
220 me.runningN.setBoolValue(me.running = 1);
224 } elsif (power < 0.05 or !fuel.level()) {
225 me.runningN.setBoolValue(me.running = 0);
233 me.n1 = me.n1LP.filter(max(me.fuelflow, starter));
234 me.n2 = me.n2LP.filter(me.n1);
235 me.up = me.n1 - lastn1;
238 if (me.fuelflow > me.pos.idle)
239 var target = 440 + (779 - 440) * (0.03 + me.fuelflow - me.pos.idle) / (me.pos.flight - me.pos.idle);
241 var target = 440 * (0.03 + me.fuelflow) / me.pos.idle;
243 if (me.n1 < 0.4 and me.fuelflow - me.n1 > 0.001) {
244 target += (me.fuelflow - me.n1) * 7000;
249 var airtemp = me.airtempN.getValue();
250 if (target < airtemp)
253 var decay = (me.up > 0 ? 10 : me.n1 > 0.02 ? 0.01 : 0.001) * dt;
254 me.totN.setValue((me.totN.getValue() + decay * target) / (1 + decay));
256 # constant 130 kg/h for now (one turbines)
257 fuel.consume(65 * KG2GAL * me.fuelflow * dt / 3600);
259 # derived gauge values
260 me.n1_pctN.setDoubleValue(me.n1 * 100);
261 me.n2_pctN.setDoubleValue(me.n2 * 100);
262 me.n1N.setDoubleValue(me.n1 * 50970);
263 me.n2N.setDoubleValue(me.n2 * 33290);
266 var target = (int((me.power + 0.15) * 3) + v) / 3;
267 var time = abs(me.power - target) * 4;
268 interpolate(me.powerN, target, time);
270 adjust_power: func(delta, mode = 0) {
272 var power = me.powerN.getValue();
273 if (me.power_min == nil) {
275 if (power < me.pos.idle) {
276 me.power_min = me.pos.idle;
277 me.power_max = me.pos.flight;
279 me.power_min = me.pos.idle;
280 me.power_max = me.pos.flight;
283 if (power > me.pos.idle) {
284 me.power_max = me.pos.flight;
285 me.power_min = me.pos.idle;
287 me.power_max = me.pos.flight;
288 me.power_min = me.pos.idle;
292 me.powerN.setValue(power = clamp(power + delta, me.power_min, me.power_max));
295 me.power_min = me.power_max = nil;
298 pos: { cutoff: 0.0, idle: 0.63, flight: 1 },
305 me.engine = [Engine.new(0), Engine.new(1)];
306 me.trimN = props.globals.initNode("/controls/engines/power-trim");
307 me.balanceN = props.globals.initNode("/controls/engines/power-balance");
308 me.commonrpmN = props.globals.initNode("/engines/engine/rpm");
311 me.engine[0].reset();
312 me.engine[1].reset();
317 var trim = me.trimN.getValue() * 0.1;
318 var balance = me.balanceN.getValue() * 0.1;
319 me.engine[0].update(dt, trim - balance);
320 me.engine[1].update(dt, trim + balance);
323 var n2relrpm =me.engine[0].n2;
324 var n2max =me.engine[0].n2;
325 target_rel_rpm.setValue(n2relrpm);
326 max_rel_torque.setValue(n2max);
330 me.commonrpmN.setValue(n2max * 33290); # attitude indicator needs pressure
334 adjust_power: func(delta, mode = 0) {
336 engines.engine[0].adjust_power(0, mode);
337 engines.engine[1].adjust_power(0, mode);
340 for (var i = 0; i < 2; i += 1)
341 if (controls.engines[i].selected.getValue())
342 p[i] = engines.engine[i].adjust_power(delta);
343 gui.popupTip(sprintf("power lever %d%%", 100 * max(p[0], p[1])));
346 quickstart: func { # development only
347 me.engine[0].n1LP.set(1);
348 me.engine[0].n2LP.set(1);
358 var vert_speed_fpm = props.globals.initNode("/velocities/vertical-speed-fpm");
361 setprop("/instrumentation/altimeter/setting-inhg", getprop("/environment/pressure-inhg"));
363 setlistener("/sim/signals/fdm-initialized", func {
365 screen.property_display.x = 760;
366 screen.property_display.y = 200;
367 screen.property_display.format = "%.3g";
368 screen.property_display.add(
373 "/controls/engines/power-trim",
374 "/controls/engines/power-balance",
375 "/consumables/fuel/total-fuel-gals",
377 engines.engine[0].runningN,
378 engines.engine[0].ignitionN,
379 "/controls/engines/engine[0]/power",
380 engines.engine[0].n1_pctN,
381 engines.engine[0].n2_pctN,
382 engines.engine[0].totN,
383 #engines.engine[0].n1N,
384 #engines.engine[0].n2N,
387 "/sim/model/gross-weight-kg",
388 "/position/altitude-ft",
389 "/position/altitude-agl-ft",
390 "/instrumentation/altimeter/indicated-altitude-ft",
391 "/environment/temperature-degc",
393 "/velocities/airspeed-kt",
406 setlistener("/sim/startup/xsize", func(n) me.centerx = int(n.getValue() / 2), 1);
407 setlistener("/sim/startup/ysize", func(n) me.centery = int(n.getValue() / 2), 1);
408 setlistener("/devices/status/mice/mouse/mode", func(n) me.mode = n.getValue(), 1);
409 setlistener("/devices/status/mice/mouse/button[1]", func(n) {
410 me.mmb = n.getValue();
414 engines.adjust_power(0.0, 1);
417 gui.setCursor(me.centerx, me.centery, "none");
419 gui.setCursor(me.savex, me.savey, "pointer");
422 setlistener("/devices/status/mice/mouse/x", func(n) me.x = n.getValue(), 1);
423 setlistener("/devices/status/mice/mouse/y", func(n) me.update(me.y = n.getValue()), 1);
426 if (me.mode or !me.mmb)
429 if (var dy = -me.y + me.centery)
430 engines.adjust_power(dy * 0.005);
432 gui.setCursor(me.centerx, me.centery);
438 var power = func(v) {
439 if (controls.engines[0].selected.getValue())
440 engines.engine[0].setpower(v);
447 if (procedure.stage < 0) {
454 var shutdown = func {
455 if (procedure.stage > 0) {
473 next: func(delay = 0) {
476 if (me.stage < 0 and me.step > 0 or me.stage > 0 and me.step < 0)
479 settimer(func { me.stage += me.step; me.process(me.loopid) }, delay * !quickstart);
482 id == me.loopid or return;
485 cprint("", "1: press start button #1 -> spool up turbine #1 to N1 8.6--15%");
486 engines.engine[0].ignitionN.setValue(1);
487 engines.engine[0].starterN.setValue(1);
490 } elsif (me.stage == 2) {
492 cprint("", "2: move power lever #1 forward -> fuel injection");
493 engines.engine[0].powerN.setValue(0.13);
496 } elsif (me.stage == 3) {
497 cprint("", "3: turbine #1 ignition (wait for EGT stabilization)");
500 } elsif (me.stage == 4) {
501 cprint("", "4: move power lever #1 to idle position -> engine #1 spools up to N1 63%");
502 engines.engine[0].powerN.setValue(0.63);
505 } elsif (me.stage == 5) {
506 cprint("", "5: release start button #1\n");
507 engines.engine[0].starterN.setValue(0);
508 engines.engine[0].ignitionN.setValue(0);
513 } elsif (me.stage == -1) {
514 cprint("", "-1: engines shut down");
515 engines.engine[0].starterN.setValue(0);
517 engines.engine[0].ignitionN.setValue(0);
519 engines.engine[0].powerN.setValue(0);
531 torque.setDoubleValue(0);
533 var update_torque = func(dt) {
534 var f = dt / (0.2 + dt);
535 torque_val = torque.getValue() * f + torque_val * (1 - f);
536 torque_pct.setDoubleValue(torque_val / 5300);
541 # blade vibration absorber pendulum
542 var pendulum = props.globals.getNode("/sim/model/ec130/absorber-angle-deg", 1);
543 var update_absorber = func {
544 pendulum.setDoubleValue(90 * clamp(abs(rotor_rpm.getValue()) / 90));
549 var vibration = { # and noise ...
551 me.lonN = props.globals.initNode("/rotors/main/vibration/longitudinal");
552 me.latN = props.globals.initNode("/rotors/main/vibration/lateral");
553 me.soundN = props.globals.initNode("/sim/sound/vibration");
554 me.airspeedN = props.globals.getNode("/velocities/airspeed-kt");
555 me.vertspeedN = props.globals.getNode("/velocities/vertical-speed-fps");
557 me.groundspeedN = props.globals.getNode("/velocities/groundspeed-kt");
558 me.speeddownN = props.globals.getNode("/velocities/speed-down-fps");
559 me.angleN = props.globals.initNode("/velocities/descent-angle-deg");
563 var airspeed = me.airspeedN.getValue();
564 if (airspeed > 160) { # overspeed vibration
565 var frequency = 2000 + 500 * rand();
566 var v = 0.49 + 0.5 * normatan(airspeed - 160, 10);
568 var noise = v * internal;
570 } elsif (airspeed > 30) { # Blade Vortex Interaction (BVI) 8 deg, 65 kts max?
571 var frequency = rotor_rpm.getValue() * 4 * 60;
572 var down = me.speeddownN.getValue() * FT2M;
573 var level = me.groundspeedN.getValue() * NM2M / 3600;
574 me.angleN.setDoubleValue(var angle = math.atan2(down, level) * R2D);
575 var speed = math.sqrt(level * level + down * down) * MPS2KT;
576 angle = bell(angle - 9, 13);
577 speed = bell(speed - 65, 450);
578 var v = angle * speed;
579 var intensity = v * 0.10;
580 var noise = v * (1 - internal * 0.4);
583 var rpm = rotor_rpm.getValue();
584 var frequency = rpm * 4 * 60;
585 var coll = bell(collective.getValue(), 0.5);
586 var ias = bell(airspeed, 600);
587 var vert = bell(me.vertspeedN.getValue() * 0.5, 400);
588 var rpm = 0.477 + 0.5 * normatan(rpm - 350, 30) * 1.025;
589 var v = coll * ias * vert * rpm;
590 var intensity = v * 0.10;
591 var noise = v * (1 - internal * 0.4);
594 me.dir += dt * frequency;
595 me.lonN.setValue(cos(me.dir) * intensity);
596 me.latN.setValue(sin(me.dir) * intensity);
597 me.soundN.setValue(noise);
604 # sound =============================================================
607 var stall = props.globals.getNode("rotors/main/stall", 1);
608 var stall_filtered = props.globals.getNode("rotors/main/stall-filtered", 1);
611 stall.setDoubleValue(0);
613 var update_stall = func(dt) {
614 var s = stall.getValue();
616 var f = dt / (0.3 + dt);
617 stall_val = s * f + stall_val * (1 - f);
621 var c = collective.getValue();
622 stall_filtered.setDoubleValue(stall_val + 0.006 * (1 - c));
630 var m = { parents: [Skid] };
631 var soundN = props.globals.getNode("sim/model/ec130/sound", 1).getChild("slide", n, 1);
632 var gearN = props.globals.getNode("gear", 1).getChild("gear", n, 1);
634 m.compressionN = gearN.getNode("compression-norm", 1);
635 m.rollspeedN = gearN.getNode("rollspeed-ms", 1);
636 m.frictionN = gearN.getNode("ground-friction-factor", 1);
637 m.wowN = gearN.getNode("wow", 1);
638 m.volumeN = soundN.getNode("volume", 1);
639 m.pitchN = soundN.getNode("pitch", 1);
641 m.compressionN.setDoubleValue(0);
642 m.rollspeedN.setDoubleValue(0);
643 m.frictionN.setDoubleValue(0);
644 m.volumeN.setDoubleValue(0);
645 m.pitchN.setDoubleValue(0);
646 m.wowN.setBoolValue(1);
651 me.wow = me.wowN.getValue();
653 return me.volumeN.setDoubleValue(0);
655 var rollspeed = abs(me.rollspeedN.getValue());
656 me.pitchN.setDoubleValue(rollspeed * 0.6);
658 var s = normatan(20 * rollspeed);
659 var f = clamp((me.frictionN.getValue() - 0.5) * 2);
660 var c = clamp(me.compressionN.getValue() * 2);
662 me.volumeN.setDoubleValue(vol > 0.1 ? vol : 0);
664 # cprint("33;1", sprintf("S=%0.3f F=%0.3f C=%0.3f >> %0.3f", s, f, c, s * f * c));
670 for (var i = 0; i < 4; i += 1)
671 append(skids, Skid.new(i));
674 var update_slide = func {
675 foreach (var s; skids)
681 setlistener("sim/current-view/view-number", func {
682 internal = getprop("sim/current-view/internal");
686 var volume = props.globals.getNode("sim/model/ec130/sound/volume", 1);
688 # crash handler =====================================================
692 setprop("sim/model/ec130/tail-angle-deg", 35);
693 setprop("sim/model/ec130/shadow", 0);
694 setprop("rotors/main/rpm", 0);
695 setprop("rotors/main/blade[0]/flap-deg", -60);
696 setprop("rotors/main/blade[1]/flap-deg", -50);
697 setprop("rotors/main/blade[2]/flap-deg", -40);
698 #setprop("rotors/main/blade[3]/flap-deg", -30);
699 setprop("rotors/main/blade[0]/incidence-deg", -30);
700 setprop("rotors/main/blade[1]/incidence-deg", -20);
701 setprop("rotors/main/blade[2]/incidence-deg", -50);
702 #setprop("rotors/main/blade[3]/incidence-deg", -55);
703 setprop("rotors/tail/rpm", 0);
704 strobe_switch.setValue(0);
705 beacon_switch.setValue(0);
706 nav_light_switch.setValue(0);
707 engines.engine[0].n2_pctN.setValue(0);
708 #engines.engine[1].n2_pctN.setValue(0);
709 torque_pct.setValue(torque_val = 0);
710 stall_filtered.setValue(stall_val = 0);
713 # uncrash (for replay)
714 setprop("sim/model/ec130/tail-angle-deg", 0);
715 setprop("sim/model/ec130/shadow", 1);
717 setprop("rotors/tail/rpm", 2219);
718 setprop("rotors/main/rpm", 442);
719 for (i = 0; i < 4; i += 1) {
720 setprop("rotors/main/blade[" ~ i ~ "]/flap-deg", 0);
721 setprop("rotors/main/blade[" ~ i ~ "]/incidence-deg", 0);
723 strobe_switch.setValue(1);
724 beacon_switch.setValue(1);
725 engines.engine[0].n2_pct.setValue(100);
726 #engines.engine[1].n2_pct.setValue(100);
733 # "manual" rotor animation for flight data recorder replay ============
734 var rotor_step = props.globals.getNode("sim/model/ec130/rotor-step-deg");
735 var blade1_pos = props.globals.getNode("rotors/main/blade[0]/position-deg", 1);
736 var blade2_pos = props.globals.getNode("rotors/main/blade[1]/position-deg", 1);
737 var blade3_pos = props.globals.getNode("rotors/main/blade[2]/position-deg", 1);
738 var blade4_pos = props.globals.getNode("rotors/main/blade[3]/position-deg", 1);
741 var rotoranim_loop = func {
742 var i = rotor_step.getValue();
744 blade1_pos.setValue(rotorangle);
745 blade2_pos.setValue(rotorangle + 90);
746 blade3_pos.setValue(rotorangle + 180);
747 blade4_pos.setValue(rotorangle + 270);
749 settimer(rotoranim_loop, 0.1);
753 var init_rotoranim = func {
754 if (rotor_step.getValue() >= 0.0)
755 settimer(rotoranim_loop, 0.1);
765 # view management ===================================================
767 var elapsedN = props.globals.getNode("/sim/time/elapsed-sec", 1);
770 controls.flapsDown = func(v) {
773 down_time = elapsedN.getValue();
787 gui.popupTip("AUTOTRIM", 1e10);
788 aircraft.autotrim.start();
792 if (flap_mode == 1) {
793 if (elapsedN.getValue() < down_time + 0.2)
796 dynamic_view.resume();
797 } elsif (flap_mode == 2) {
798 aircraft.autotrim.stop();
806 # register function that may set me.heading_offset, me.pitch_offset, me.roll_offset,
807 # me.x_offset, me.y_offset, me.z_offset, and me.fov_offset
809 dynamic_view.register(func {
810 var lowspeed = 1 - normatan(me.speedN.getValue() / 50);
811 var r = sin(me.roll) * cos(me.pitch);
813 me.heading_offset = # heading change due to
814 (me.roll < 0 ? -50 : -30) * r * abs(r); # roll left/right
816 me.pitch_offset = # pitch change due to
817 (me.pitch < 0 ? -50 : -50) * sin(me.pitch) * lowspeed # pitch down/up
818 + 15 * sin(me.roll) * sin(me.roll); # roll
820 me.roll_offset = # roll change due to
821 -15 * r * lowspeed; # roll
825 var adjust_fov = func {
826 var w = getprop("/sim/startup/xsize");
827 var h = getprop("/sim/startup/ysize");
828 var ar = clamp(max(w, h) / min(w, h), 0, 2);
829 var fov = 60 + (ar - (4 / 3)) * 10 / (16 / 9 - 4 / 3);
830 setprop("/sim/view/config/default-field-of-view-deg", fov);
832 setprop("/sim/current-view/config/default-field-of-view-deg", fov);
835 setlistener("/sim/startup/xsize", adjust_fov);
836 setlistener("/sim/startup/ysize", adjust_fov, 1);
844 # main() ============================================================
845 var delta_time = props.globals.getNode("/sim/time/delta-sec", 1);
846 var hi_heading = props.globals.getNode("/instrumentation/heading-indicator/indicated-heading-deg", 1);
847 var vertspeed = props.globals.initNode("/velocities/vertical-speed-fps");
848 var gross_weight_lb = props.globals.initNode("/yasim/gross-weight-lbs");
849 var gross_weight_kg = props.globals.initNode("/sim/model/gross-weight-kg");
850 props.globals.getNode("/instrumentation/adf/rotation-deg", 1).alias(hi_heading);
853 var main_loop = func {
854 props.globals.removeChild("autopilot");
856 setprop("/position/gear-agl-m", getprop("/position/altitude-agl-ft") * 0.3 - 1.2);
857 vert_speed_fpm.setDoubleValue(vertspeed.getValue() * 60);
858 gross_weight_kg.setDoubleValue(gross_weight_lb.getValue() * LB2KG);
861 var dt = delta_time.getValue();
869 vibration.update(dt);
870 settimer(main_loop, 0);
878 #var config_dialog = gui.Dialog.new("/sim/gui/dialogs/ec130/config/dialog", "Aircraft/ec130/Dialogs/config.xml");
881 setlistener("/sim/signals/fdm-initialized", func {
882 gui.menuEnable("autopilot", 0);
892 collective.setDoubleValue(1);
894 setlistener("/sim/signals/reinit", func(n) {
895 n.getBoolValue() and return;
896 cprint("32;1", "reinit");
898 collective.setDoubleValue(1);
899 aircraft.livery.rescan();
904 setlistener("sim/crashed", func(n) {
905 cprint("31;1", "crashed ", n.getValue());
906 engines.engine[0].timer.stop();
907 engines.engine[1].timer.stop();
908 if (n.getBoolValue())
912 setlistener("/sim/freeze/replay-state", func(n) {
913 replay = n.getValue();
914 cprint("33;1", replay ? "replay" : "pause");
916 crash(!n.getBoolValue())
920 if (devel and quickstart)
921 engines.quickstart();