From dabf3f300cbd047059d6da32f6e2d9d060079196 Mon Sep 17 00:00:00 2001 From: mfranz <mfranz> Date: Fri, 3 Nov 2006 17:38:32 +0000 Subject: [PATCH] - aircraft.nas: add variable-interval EWMA lowpass filter class - dynamic_view.nas: use aircraft.lowpass() class This makes the dynamic view independent of the frame rate. Currently, each filter instance reads out dt on its own, which is a bit inefficient. I'll change that to just one read for all instances later (when Nasal implemented predictable module loading order. :-) --- Nasal/aircraft.nas | 40 +++++++++++++++++++++++++++++++++++++ Nasal/dynamic_view.nas | 45 ++++++++---------------------------------- 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/Nasal/aircraft.nas b/Nasal/aircraft.nas index ecb65e1a0..429ace407 100644 --- a/Nasal/aircraft.nas +++ b/Nasal/aircraft.nas @@ -216,6 +216,46 @@ light = { +# lowpass +# ============================================================================== +# class that implements a variable-interval EWMA (Exponentially Weighted +# Moving Average) lowpass filter with characteristics independent of the +# frame rate. +# +# SYNOPSIS: +# lowpass.new(<coefficient>); +# +# EXAMPLE: +# var lp = lowpass(0.5); +# print(lp.filter(10)); +# print(lp.filter(0)); +# +lowpass = { + new : func(coeff) { + var m = { parents : [lowpass] }; + m.dtN = props.globals.getNode("/sim/time/delta-realtime-sec", 1); + m.value = nil; + m.coeff = abs(coeff); + return m; + }, + # filter(raw_value) -> push new value, returns filtered value + filter : func(v) { + me.filter = me._filter_; + me.value = v; + }, + # get() -> returns filtered value + get : func { + me.value; + }, + _filter_ : func(v) { + var dt = me.dtN.getValue(); + var c = dt / (me.coeff + dt); + me.value = v * c + me.value * (1 - c); + }, +}; + + + # HUD control class to handle both HUD implementations. # HUDControl = { diff --git a/Nasal/dynamic_view.nas b/Nasal/dynamic_view.nas index 551c55f83..89967d628 100644 --- a/Nasal/dynamic_view.nas +++ b/Nasal/dynamic_view.nas @@ -3,7 +3,6 @@ # acceleration. - sin = func(a) { math.sin(a * math.pi / 180.0) } cos = func(a) { math.cos(a * math.pi / 180.0) } sigmoid = func(x) { 1 / (1 + math.exp(-x)) } @@ -25,34 +24,6 @@ normdeg = func(a) { -# Class that implements EWMA (Exponentially Weighted Moving Average) -# lowpass filter. Initialize with coefficient, set new value when -# you fetch one. filter() pushes new value and returns filtered value, -# get() only returns filtered value. A filter coefficient of 0 means -# no filtering, coeff = 1 generates a factor 0.1, coeff = 2 a facdtor -# 0.01, coeff = 3 a factor 0.001, etc. -# -LowPass = { - new : func(coeff) { - var m = { parents : [LowPass] }; - m.value = nil; - m.coeff = 1.0 / pow(10, abs(coeff)); - return m; - }, - filter : func(v) { - me.filter = me._filter_; - me.value = v; - }, - _filter_ : func(v) { - me.value = v * me.coeff + me.value * (1 - me.coeff); - }, - get : func { - me.value; - }, -}; - - - # Class that reads a property value, applies factor & offset, clamps to min & max, # and optionally lowpass filters. # @@ -64,7 +35,7 @@ Input = { m.offset = offset; m.min = min; m.max = max; - m.lowpass = filter ? LowPass.new(filter) : nil; + m.lowpass = filter ? aircraft.lowpass.new(filter) : nil; return m; }, get : func { @@ -131,19 +102,19 @@ ViewManager = { m.roll_axis = ViewAxis.new("/sim/current-view/goal-roll-offset-deg"); # accerations are converted to G (one G is subtraced from z-accel) - m.ax = Input.new("/accelerations/pilot/x-accel-fps_sec", 0.03108095, 0, 1.1, 0); - m.ay = Input.new("/accelerations/pilot/y-accel-fps_sec", 0.03108095, 0, 1.3); - m.az = Input.new("/accelerations/pilot/z-accel-fps_sec", -0.03108095, -1, 1.0073); + m.ax = Input.new("/accelerations/pilot/x-accel-fps_sec", 0.03108095, 0, 0.58, 0); + m.ay = Input.new("/accelerations/pilot/y-accel-fps_sec", 0.03108095, 0, 0.95); + m.az = Input.new("/accelerations/pilot/z-accel-fps_sec", -0.03108095, -1, 0.46); # velocities are converted to knots - m.vx = Input.new("/velocities/uBody-fps", 0.5924838, 0, 1); + m.vx = Input.new("/velocities/uBody-fps", 0.5924838, 0, 0.45); m.vy = Input.new("/velocities/vBody-fps", 0.5924838, 0); m.vz = Input.new("/velocities/wBody-fps", 0.5924838, 0); # turn WoW bool into smooth values ranging from 0 to 1 - m.wow = Input.new("/gear/gear/wow", 1, 0, 1.2); - m.hdg_change = LowPass.new(1.3); - m.ubody = LowPass.new(1.3); + m.wow = Input.new("/gear/gear/wow", 1, 0, 0.74); + m.hdg_change = aircraft.lowpass.new(0.95); + m.ubody = aircraft.lowpass.new(0.95); m.last_heading = m.headingN.getValue(); m.size_factor = -getprop("/sim/chase-distance-m") / 25; m.reset();