From f92a2217de1b9f8e0b7cb2417bd11e93e1ffefc1 Mon Sep 17 00:00:00 2001 From: Anton Gomez Alvedro Date: Sat, 29 Nov 2014 11:53:26 +0100 Subject: [PATCH 1/4] Factoring out the soaring-sdk's update loop into a generic update-loop.nas that can be reused by other modules. --- .../Generic/soaring-instrumentation-sdk.nas | 107 ++++--------- Aircraft/Generic/update_loop.nas | 147 ++++++++++++++++++ .../glider/vario/ilec-sc7/ilec-sc7.nas | 4 +- 3 files changed, 176 insertions(+), 82 deletions(-) create mode 100644 Aircraft/Generic/update_loop.nas diff --git a/Aircraft/Generic/soaring-instrumentation-sdk.nas b/Aircraft/Generic/soaring-instrumentation-sdk.nas index de085bc6a..95b8cd77e 100644 --- a/Aircraft/Generic/soaring-instrumentation-sdk.nas +++ b/Aircraft/Generic/soaring-instrumentation-sdk.nas @@ -1,6 +1,20 @@ # Glider Instrumentation Toolkit -# Author: Anton Gomez Alvedro (galvedro) -# Licensed under GNU GPL +# +# Copyright (C) 2013-2014 Anton Gomez Alvedro +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # Features: # + Total energy compensated variometer @@ -14,16 +28,22 @@ # - add wind correction to speed-to-fly # - final glide computer +io.include("update_loop.nas"); + var MPS2KPH = 3.6; var sqr = func(x) {x * x} var InstrumentComponent = { + parents: [Component], output: 0, - init: func { me.output = 0 }, - update: func(dt) { }, + reset: func { me.output = 0 }, }; +# This alias is for keeping backwards compatibility. +# TODO: Refactor aircrafts that use it and remove this. +var Instrument = UpdateLoop; + # update_prop(property) # Helper generator for updating the given property on every element update # @@ -141,7 +161,7 @@ var TotalEnergyProbe = { }; }, - init: func { + reset: func { me.airspeed = getprop("/velocities/airspeed-kt") * KT2MPS; me.altitude = getprop("/position/altitude-ft") * FT2M; me.output = 0; @@ -215,11 +235,11 @@ var Averager = { m.sum = m.wp = 0; m.buffer = setsize([], buffer_size); - m.init(); + m.reset(); return m; }, - init: func { + reset: func { me.sum = me.wp = me.output = 0; forindex (var i; me.buffer) me.buffer[i] = 0; @@ -384,76 +404,3 @@ var SpeedCmdVario = { if (me.on_update != nil) me.on_update(me.output); } }; - -# Instrument -# Wraps a set of components and updates them periodically. -# Takes care of critical sim signals (reinit, fdm-initialized, speed-up). -# -# var instrument = Instrument.new( -# components: List of components to update in the fast loop. -# update_period: (optional) Time in seconds between updates (fast components). -# enable: (optional) Enable instrument after creation. - -var Instrument = { - - new: func(components, update_period = 0, enable = 1) { - - var m = { parents: [me] }; - m.initialized = 0; - m.enabled = enable; - m.update_period = update_period; - m.time_last = 0; - m.sim_speed = 1; - m.components = (components != nil)? components : []; - - m.timer = maketimer(update_period, - func { call(me.update, [], m) }); - - setlistener("/sim/speed-up", - func(n) { m.sim_speed = n.getValue() }, 1, 0); - - setlistener("sim/signals/reinit", func { - m.timer.stop(); - m.initialized = 0; - }); - - setlistener("sim/signals/fdm-initialized", func { - if (m.timer.isRunning) m.timer.stop(); - call(me.init, [], m); - if (m.enabled) m.timer.start(); - }); - - return m; - }, - - init: func { - me.time_last = getprop("/sim/time/elapsed-sec"); - - foreach (var component; me.components) - component.init(); - - me.initialized = 1; - }, - - update: func { - var time_now = getprop("/sim/time/elapsed-sec"); - var dt = (time_now - me.time_last) * me.sim_speed; - if (dt == 0) return; - - me.time_last = time_now; - - foreach (var component; me.components) - component.update(dt); - }, - - enable: func { - if (me.initialized) me.timer.start(); - me.enabled = 1; - }, - - disable: func { - me.timer.stop(); - me.enabled = 0; - } -}; - diff --git a/Aircraft/Generic/update_loop.nas b/Aircraft/Generic/update_loop.nas new file mode 100644 index 000000000..b552f0df9 --- /dev/null +++ b/Aircraft/Generic/update_loop.nas @@ -0,0 +1,147 @@ +# update_loop.nas - Generic Nasal update loop for implementing sytems, +# instruments or physics simulation. +# +# Copyright (C) 2014 Anton Gomez Alvedro +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Component +# +# The update loop accepts a vector of Components that it will update at regular +# intervals. In order to make custom components, you should add Component to +# the parents vector of your object: +# +# var CustomComponent = { +# parents: [Component], +# ... +# }; +# +# Your component shall then implement an "update" and a "reset" methods that +# the UpdateLoop will call when needed. + +var Component = { + reset: func { }, + update: func(dt) { }, +}; + +## +# Helper generator for updating a property on every element update +# +# Example: +# +# var needle = Dampener.new( +# input: probe, +# dampening: 2.8, +# on_update: update_prop("/instrumentation/variometer/te-reading-mps")); +# +# See Aircraft/Instruments-3d/glider/vario/ilec-sc7.nas and +# Aircraft/Generic/soaring-instrumentation-sdk.nas for usage examples. +# You can also refer to the soaring sdk wiki page. + +var update_prop = func(property) { + func(value) { setprop(property, value) } +}; + +# UpdateLoop +# Wraps a set of components and updates them periodically. +# Takes care of critical sim signals (reinit, fdm-initialized, speed-up). + +var UpdateLoop = { + + # UpdateLoop.new(components, [update_period], [enable]) + # + # components: Vector of components to update on every iteration. + # update_period: Time in seconds between updates (optional). + # enable: Enable the loop immediately after creation (optional). + + new: func(components, update_period = 0, enable = 1) { + + var m = { parents: [UpdateLoop] }; + m.initialized = 0; + m.enabled = enable; + m.update_period = update_period; + m.time_last = 0; + m.sim_speed = 1; + m.components = (components != nil)? components : []; + + m.timer = maketimer(update_period, + func { call(me._update, [], m) }); + + m.lst = []; + + append(m.lst, setlistener("/sim/speed-up", + func(n) { m.sim_speed = n.getValue() }, 1, 0)); + + append(m.lst, setlistener("sim/signals/reinit", func { + m.timer.stop(); + m.initialized = 0; + })); + + append(m.lst, setlistener("sim/signals/fdm-initialized", func { + if (m.timer.isRunning) m.timer.stop(); + call(me.reset, [], m); + if (m.enabled) m.timer.start(); + })); + + return m; + }, + + del: func { + foreach (var l; me.lst) removelistener(l); + }, + + ## + # Resets internal state for all components controlled by the loop. + # It is of course responsibility of every component to clean their internal + # state appropriately. + + reset: func { + me.time_last = getprop("/sim/time/elapsed-sec"); + + foreach (var component; me.components) + component.reset(); + + me.initialized = 1; + }, + + ## + # Enables the loop if it was disabled, i.e. the loop will start updating + # the components under control + + enable: func { + if (me.initialized) me.timer.start(); + me.enabled = 1; + }, + + ## + # Disables the loop if it was enabled, i.e. the loop will freeze and its + # components will not get updates until enabled again + + disable: func { + me.timer.stop(); + me.enabled = 0; + }, + + _update: func { + var time_now = getprop("/sim/time/elapsed-sec"); + var dt = (time_now - me.time_last) * me.sim_speed; + if (dt == 0) return; + + me.time_last = time_now; + + foreach (var component; me.components) + component.update(dt); + }, +}; diff --git a/Aircraft/Instruments-3d/glider/vario/ilec-sc7/ilec-sc7.nas b/Aircraft/Instruments-3d/glider/vario/ilec-sc7/ilec-sc7.nas index 1c71b14f3..b9474f51b 100644 --- a/Aircraft/Instruments-3d/glider/vario/ilec-sc7/ilec-sc7.nas +++ b/Aircraft/Instruments-3d/glider/vario/ilec-sc7/ilec-sc7.nas @@ -62,12 +62,12 @@ setlistener("instrumentation/ilec-sc7/sensitivity", func(n) { sc7_needle.dampening = n.getValue() }, 0, 0); # Wrap everything together into an instrument -var fast_instruments = Instrument.new( +var fast_instruments = UpdateLoop.new( update_period: 0, components: [probe, sc7_needle, extra_needle], enable: 1); -var slow_instruments = Instrument.new( +var slow_instruments = UpdateLoop.new( update_period: 1, components: [averager, temperature, lcd_controller], enable: 1); From 4edcf7c7a0c143524d15caf551308fb8512d26e6 Mon Sep 17 00:00:00 2001 From: Anton Gomez Alvedro Date: Sun, 30 Nov 2014 10:46:26 +0100 Subject: [PATCH 2/4] Renaming the "Component" interface to "Updatable". "Component" is just too generic and inspecific to be used as a public interface. Also, making sure the looper is disabled when destruction is requested. --- .../Generic/soaring-instrumentation-sdk.nas | 2 +- Aircraft/Generic/update_loop.nas | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Aircraft/Generic/soaring-instrumentation-sdk.nas b/Aircraft/Generic/soaring-instrumentation-sdk.nas index 95b8cd77e..c82bb2d79 100644 --- a/Aircraft/Generic/soaring-instrumentation-sdk.nas +++ b/Aircraft/Generic/soaring-instrumentation-sdk.nas @@ -35,7 +35,7 @@ var sqr = func(x) {x * x} var InstrumentComponent = { - parents: [Component], + parents: [Updatable], output: 0, reset: func { me.output = 0 }, }; diff --git a/Aircraft/Generic/update_loop.nas b/Aircraft/Generic/update_loop.nas index b552f0df9..55de150de 100644 --- a/Aircraft/Generic/update_loop.nas +++ b/Aircraft/Generic/update_loop.nas @@ -17,21 +17,21 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# Component +# Updatable # -# The update loop accepts a vector of Components that it will update at regular -# intervals. In order to make custom components, you should add Component to -# the parents vector of your object: +# The update loop accepts a vector of Updatable elements that it will update at +# regular intervals. In order to make custom elements, you should add Updatable +# to the parents vector of your object: # -# var CustomComponent = { -# parents: [Component], +# var MyElement = { +# parents: [Updatable], # ... # }; # -# Your component shall then implement an "update" and a "reset" methods that +# Your component shall then implement an "update" and "reset" methods that # the UpdateLoop will call when needed. -var Component = { +var Updatable = { reset: func { }, update: func(dt) { }, }; @@ -55,7 +55,7 @@ var update_prop = func(property) { }; # UpdateLoop -# Wraps a set of components and updates them periodically. +# Wraps a set of user provided objects and updates them periodically. # Takes care of critical sim signals (reinit, fdm-initialized, speed-up). var UpdateLoop = { @@ -99,6 +99,7 @@ var UpdateLoop = { }, del: func { + me.disable(); foreach (var l; me.lst) removelistener(l); }, From 60d806be6b2341f497e620dedffe61120f385ce1 Mon Sep 17 00:00:00 2001 From: Anton Gomez Alvedro Date: Thu, 4 Dec 2014 21:14:22 +0100 Subject: [PATCH 3/4] Rename to updateloop.nas --- Aircraft/Generic/{update_loop.nas => updateloop.nas} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Aircraft/Generic/{update_loop.nas => updateloop.nas} (100%) diff --git a/Aircraft/Generic/update_loop.nas b/Aircraft/Generic/updateloop.nas similarity index 100% rename from Aircraft/Generic/update_loop.nas rename to Aircraft/Generic/updateloop.nas From 1bae213eb929ab4f20e34ea8a04f20d19486f7fa Mon Sep 17 00:00:00 2001 From: Anton Gomez Alvedro Date: Fri, 12 Dec 2014 21:50:35 +0100 Subject: [PATCH 4/4] Reinit revisited, added an option to ignore fdm-initialized and cleaned up the doc. --- .../Generic/soaring-instrumentation-sdk.nas | 11 +- Aircraft/Generic/updateloop.nas | 106 ++++++++++-------- 2 files changed, 69 insertions(+), 48 deletions(-) diff --git a/Aircraft/Generic/soaring-instrumentation-sdk.nas b/Aircraft/Generic/soaring-instrumentation-sdk.nas index c82bb2d79..fcce506d5 100644 --- a/Aircraft/Generic/soaring-instrumentation-sdk.nas +++ b/Aircraft/Generic/soaring-instrumentation-sdk.nas @@ -28,7 +28,7 @@ # - add wind correction to speed-to-fly # - final glide computer -io.include("update_loop.nas"); +io.include("updateloop.nas"); var MPS2KPH = 3.6; var sqr = func(x) {x * x} @@ -44,14 +44,19 @@ var InstrumentComponent = { # TODO: Refactor aircrafts that use it and remove this. var Instrument = UpdateLoop; -# update_prop(property) -# Helper generator for updating the given property on every element update +## +# Helper generator for updating a property on every element update # # Example: +# # var needle = Dampener.new( # input: probe, # dampening: 2.8, # on_update: update_prop("/instrumentation/variometer/te-reading-mps")); +# +# See Aircraft/Instruments-3d/glider/vario/ilec-sc7.nas and +# Aircraft/Generic/soaring-instrumentation-sdk.nas for usage examples. +# You can also refer to the soaring sdk wiki page. var update_prop = func(property) { func(value) { setprop(property, value) } diff --git a/Aircraft/Generic/updateloop.nas b/Aircraft/Generic/updateloop.nas index 55de150de..d2fd000df 100644 --- a/Aircraft/Generic/updateloop.nas +++ b/Aircraft/Generic/updateloop.nas @@ -1,4 +1,4 @@ -# update_loop.nas - Generic Nasal update loop for implementing sytems, +# updateloop.nas - Generic Nasal update loop for implementing sytems, # instruments or physics simulation. # # Copyright (C) 2014 Anton Gomez Alvedro @@ -17,45 +17,44 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# Updatable + +## +# The UpdateLoop accepts a vector of Updatable components that it will call +# at regular intervals. In order to define custom objects to be controlled by +# the loop, the Updatable interface must be implemented: # -# The update loop accepts a vector of Updatable elements that it will update at -# regular intervals. In order to make custom elements, you should add Updatable -# to the parents vector of your object: -# -# var MyElement = { +# var MyComponent = { # parents: [Updatable], +# +# reset: func { +# ... +# }, +# update: func(dt) { +# ... +# }, # ... # }; -# -# Your component shall then implement an "update" and "reset" methods that -# the UpdateLoop will call when needed. var Updatable = { + + ## + # When reset() is called, the component shall clean all internal state and + # reinitialize itself as if starting from scratch. + # + # Reset will be called automatically after a teleport, and then on demand + # when UpdateLoop.reset() is explicitly called. + reset: func { }, + + ## + # Called at regular intervals from the UdateLoop. + # dt: Elapsed time in seconds since the last call. + update: func(dt) { }, }; ## -# Helper generator for updating a property on every element update -# -# Example: -# -# var needle = Dampener.new( -# input: probe, -# dampening: 2.8, -# on_update: update_prop("/instrumentation/variometer/te-reading-mps")); -# -# See Aircraft/Instruments-3d/glider/vario/ilec-sc7.nas and -# Aircraft/Generic/soaring-instrumentation-sdk.nas for usage examples. -# You can also refer to the soaring sdk wiki page. - -var update_prop = func(property) { - func(value) { setprop(property, value) } -}; - -# UpdateLoop -# Wraps a set of user provided objects and updates them periodically. +# Wraps a set of user provided Updatable objects and calls them periodically. # Takes care of critical sim signals (reinit, fdm-initialized, speed-up). var UpdateLoop = { @@ -63,14 +62,16 @@ var UpdateLoop = { # UpdateLoop.new(components, [update_period], [enable]) # # components: Vector of components to update on every iteration. - # update_period: Time in seconds between updates (optional). - # enable: Enable the loop immediately after creation (optional). + # update_period: Time in seconds between updates. + # enable: Enable the loop immediately after creation. + # ignore_fdm: Do not wait for the fdm to be ready for starting the loop. - new: func(components, update_period = 0, enable = 1) { + new: func(components, update_period = 0, enable = 1, ignore_fdm = 0) { var m = { parents: [UpdateLoop] }; m.initialized = 0; m.enabled = enable; + m.ignore_fdm = ignore_fdm; m.update_period = update_period; m.time_last = 0; m.sim_speed = 1; @@ -84,16 +85,18 @@ var UpdateLoop = { append(m.lst, setlistener("/sim/speed-up", func(n) { m.sim_speed = n.getValue() }, 1, 0)); - append(m.lst, setlistener("sim/signals/reinit", func { - m.timer.stop(); - m.initialized = 0; - })); + append(m.lst, setlistener("sim/signals/reinit", + func(n) { m._on_teleport(n) })); - append(m.lst, setlistener("sim/signals/fdm-initialized", func { - if (m.timer.isRunning) m.timer.stop(); - call(me.reset, [], m); - if (m.enabled) m.timer.start(); - })); + if (ignore_fdm or getprop("sim/signals/fdm-initialized")) { + m.reset(); + enable and m.timer.start(); + } + + if (!ignore_fdm) { + append(m.lst, setlistener("sim/signals/fdm-initialized", + func(n) { m._on_teleport(n) })); + } return m; }, @@ -118,8 +121,7 @@ var UpdateLoop = { }, ## - # Enables the loop if it was disabled, i.e. the loop will start updating - # the components under control + # The loop will start updating the components under its control. enable: func { if (me.initialized) me.timer.start(); @@ -127,8 +129,8 @@ var UpdateLoop = { }, ## - # Disables the loop if it was enabled, i.e. the loop will freeze and its - # components will not get updates until enabled again + # The loop will freeze and its components will not get updates until + # enabled again. disable: func { me.timer.stop(); @@ -145,4 +147,18 @@ var UpdateLoop = { foreach (var component; me.components) component.update(dt); }, + + _on_teleport: func(n) { + var signal = n.getName(); + + if (signal == "reinit" and n.getValue()) { + me.timer.stop(); + me.initialized = 0; + } + elsif (me.ignore_fdm or signal == "fdm-initialized") { + me.timer.isRunning and me.timer.stop(); + me.reset(); + me.enabled and me.timer.start(); + } + } };