• Which the release of FS2020 we see an explosition of activity on the forun and of course we are very happy to see this. But having all questions about FS2020 in one forum becomes a bit messy. So therefore we would like to ask you all to use the following guidelines when posting your questions:

    • Tag FS2020 specific questions with the MSFS2020 tag.
    • Questions about making 3D assets can be posted in the 3D asset design forum. Either post them in the subforum of the modelling tool you use or in the general forum if they are general.
    • Questions about aircraft design can be posted in the Aircraft design forum
    • Questions about airport design can be posted in the FS2020 airport design forum. Once airport development tools have been updated for FS2020 you can post tool speciifc questions in the subforums of those tools as well of course.
    • Questions about terrain design can be posted in the FS2020 terrain design forum.
    • Questions about SimConnect can be posted in the SimConnect forum.

    Any other question that is not specific to an aspect of development or tool can be posted in the General chat forum.

    By following these guidelines we make sure that the forums remain easy to read for everybody and also that the right people can find your post to answer it.

MSFS Delta time, normalizing rates to frame-time & detecting pause

Messages
32
Country
australia
Hey guys,

In my C++ gauge I'm trying to 'animate' a variable by having it chase a target number at various speeds. This is something I've done many times in different sims, and ordinarily I use delta_time to normalise my rates for frame time (ie multiply the desired rate per second by the timestep). The function looks like this:

C++:
float animate(float tgt, float currentVal, float dt = 0.2, float speed = 1.0, float offset = 1.2)
{
    if (currentVal > tgt + offset)
    {
        return currentVal - (speed * dt);
    }
    else if (currentVal < tgt - offset)
    {
        return currentVal + (speed * dt);
    }
    else
    {
        return tgt;
    }
}

Reading up as well as my own tests shows the update loop runs at about 18hz (0.055s); so in my tests I've just passed in 0.055 as dt. With a speed set to 1, I expect the variable to 'animate' at a rate of 1unit/second (or close to it since I'm passing in dt as a const). It simply doesn't though; what should take 5 mins happens in about 15-20 seconds.

The relevant code flow in the update loop:
1. Read my lvar current value
2.Perform calculations etc
3.Animate the lvar via function above
4.Set lvar to new value from step 3

My questions:
1. What is the standard way to get & store dt as a var in C++? Should I just read the difference between ABSOLUTE TIME simvar each update call?
2. Can anyone explain what could be causing the animation rate to be so off? Or if not; can anyone explain a tried & true method to normalize rates for frame-time?

EDIT: On a related note, how can I detect when the sim is paused? I'm using SimConnect currently but it only detects dev-mode pause and does not react to the esc key menu state (ie the 'pause' feature that will be used by the user 99.9% of the time)

Thanks
 
Last edited:
Messages
122
Country
unitedkingdom
Not sure if your issue is related but note unlike FSX the gauge update loop is an out-of-sync separate thread from the simvar updates so between a successive pair of updates you can't be sure where in the cycle you got the actual simvar values, and the time delta between the simvar values will vary as your 18Hz gauge update window slides across the discrete simvar updates.

Also I think requesting e.g airspeed and absolute time in the update loop does *not* necessarily imply that airspeed was relevant for that particular time.

E.g this means the calculations I do in every other sim to compute 'total energy' changes as the plane flies (involves using deltas of airspeed, altitude and time but those have to be related) have massive jitter in MSFS.

The best solution I came up with was to move my time-sensitive calculation into the '/model/aircraft.xml' RPN XML code, updating a local variable in there and subscribing to that in my gauge code (which is actually html/js but I suspect WASM is the same).

Maybe nothing to do with your issue but it might provide clues.

Asobo html/js gauges are often subclassed from something that calls OnUpdate(_deltaTime) rather than the BaseInstrument Update() which sounds like a dream solution but it's not - the _deltaTime comes from a JS Date() timestamp delta so the issue isn't really changed.

Another workaround (unlikely in your case) is there is a 'derivative' simvar from which you can calculate (or approximate) the function you need, e.g. the MSFS 'speed' simvar is usable even though your calculation of (distance A minus distance B) / (time A minus time B) will have jitter in MSFS. In my requirement there are 'acceleration' simvars which help.
 
Messages
32
Country
australia
Thanks mate, very helpful information; I will try moving into XML code and passing it through as an lvar.

On a related note, my calculations continue in a paused state (ie in the esc menu). I'm using SimConnect to read SimState & Pause states, but they only react to dev-mode pausing and not the in-game menu. Have you or anyone figured out a way to reliably detect when the game is paused/frozen/in-menu?

Thanks again
 
Messages
122
Country
unitedkingdom
Ah, the joy of active pause...

Here's my "are we paused?" code (js), which updates a 'global' this.pause_mode true/false.

My test (as you'll see) is that the 'sim frame of reference' velocity isn't changing AT ALL - I used those rather than aeronautical simvars as they're more basic. Note during active pause, although the plane is no longer moving, the simvars (such as velocity) are also paused, i.e. not set to zero. Originally I thought I'd need the floating point test to be 'within some tiny margin' to avoid any rounding errors, and in that case worried a bit about the test being duped by some orientation of the aircraft, but in practice the exact equality always works.

My (soaring) requirement needs to compute time-varying things accurately IN THE AIR (raw simvar computation can mistake Active Pause for incredibly fast vertical movement of the airmass, enough to keep the plane floating there... - obviously that would look daft but you still need a test whether to believe the simvars or not and that's what this function is for).

You might not want to include my "ON_GROUND" test though (that's just set from the usual simvar) but then you'll to choose other test simvars to look for them being frozen (because the VELOCITY WORLD one's I chose will be zero when parked on the ground and the test as-is would flag that as a pause.

Code:
    // update this.pause_mode - called each gauge update cycle
    global_vars_pause() {
        // Initialize previous speed
        if (this.pause_mode_previous_speed2 == null) { // note on startup this 'var' will be null
            this.pause_mode_previous_speed2 = -99; // set to any numeric value to ensure delta on first pass.
        }
        // These VELOCITY vars freeze during pause (speed2 is speed squared)
        let speed2 = SimVar.GetSimVarValue("VELOCITY WORLD Z","feet per second")**2 +
                     SimVar.GetSimVarValue("VELOCITY WORLD X","feet per second")**2;
        this.pause_mode = !this.ON_GROUND && (speed2 == this.pause_mode_previous_speed2); // AIRBORNE and speed EXACTLY fixed.
        this.pause_mode_previous_speed2 = speed2; // remember current speed squared for next Update()
    }
 
Messages
32
Country
australia
Thanks mate, you're a gold mine of helpful information. I will try this approach in JS & pass the resulting pause state to an Lvar to be read elsewhere.

Really appreciate the help
 
Messages
122
Country
unitedkingdom
haha - I'm maybe a couple of months ahead of you with this stuff, but I have at least 10000 lines of JS in the AS33 glider I'm working on. I think if you're set on developing the gauge in C++ / WASM then you could implement a similar pause detect function in C++. Personally I've drunk the cool-aid and moved over to html/js for my MSFS gauges.
 
Messages
32
Country
australia
haha - I'm maybe a couple of months ahead of you with this stuff, but I have at least 10000 lines of JS in the AS33 glider I'm working on. I think if you're set on developing the gauge in C++ / WASM then you could implement a similar pause detect function in C++. Personally I've drunk the cool-aid and moved over to html/js for my MSFS gauges.
Saw the pics in the other thread - looking great. I work in C++ for my work in other sims so I'm trying to stick to that; up until now reading that other thread about event handlers I didn't even know JS could do that? What capabilities does JS have with events, both sim events & custom events?

In any case, using your advice I managed to implement a C++ pause detector & delta_time function. I'm essentially just passing through the E:ABSOLUTE SECONDS var through an Lvar from XML, reading it in my WASM gauge & comparing it to the last value. If the delta_time is zero, it's considered a pause state and my calculations aren't run. Otherwise delta_time is passed into my calculations for time normalization. What I discovered is that the panel pre_update is called 3-4 times before the absolute time simvar is updated, so my calculations are only run on 1/3 calls. This explains why previously my animation code was running so much faster than it should have; all sorted now.

Many thanks for your help
 
Messages
122
Country
unitedkingdom
What capabilities does JS have with events, both sim events & custom events?
Here's a simple answer, but HTML/JS is a whole new (undocumented) system for interacting with the sim so the code isn't 1:1 for what you'd have done in C++, but:

(1) 'custom events' are mainstream in the Asobo HTML/JS, using a new "(&gt;H:my_custom_event)" in modeldef XML code that triggers the onInteractiveEvent(_args) callback in a JS BaseInstrument (or any child that replaces the method, i.e. just about every gauge)

(2) There are plenty of examples of Asobo gauges sending 'legacy' key events via SimVar.SetSimVarValue e.g. SimVar.SetSimVarValue("K:KOHLSMAN_INC", "number", 1);

(3) I've not found any example of Asobo code where they *subscribe* to 'legacy' key events, and FWIW I'm not doing that in my JS code either. As far as I can tell Asobo maintain the 'state' of things of interest (like whether electricity is available or not) and trigger an HTML/JS event when they detect that changing. TBH events & callbacks are so fundamental to JS (e.g. including touch events) that all the Asobo "complex" instruments (like PFD's) are written as layers of JS classes with events and callbacks rippling down the hierarchy and that kinda dwarfs the issues with "K:KOHLSMAN_INC".
 
Messages
32
Country
australia
Here's a simple answer, but HTML/JS is a whole new (undocumented) system for interacting with the sim so the code isn't 1:1 for what you'd have done in C++, but:

(1) 'custom events' are mainstream in the Asobo HTML/JS, using a new "(&gt;H:my_custom_event)" in modeldef XML code that triggers the onInteractiveEvent(_args) callback in a JS BaseInstrument (or any child that replaces the method, i.e. just about every gauge)

(2) There are plenty of examples of Asobo gauges sending 'legacy' key events via SimVar.SetSimVarValue e.g. SimVar.SetSimVarValue("K:KOHLSMAN_INC", "number", 1);

(3) I've not found any example of Asobo code where they *subscribe* to 'legacy' key events, and FWIW I'm not doing that in my JS code either. As far as I can tell Asobo maintain the 'state' of things of interest (like whether electricity is available or not) and trigger an HTML/JS event when they detect that changing. TBH events & callbacks are so fundamental to JS (e.g. including touch events) that all the Asobo "complex" instruments (like PFD's) are written as layers of JS classes with events and callbacks rippling down the hierarchy and that kinda dwarfs the issues with "K:KOHLSMAN_INC".
Thank you for the detailed reply & information; I never thought to trigger events through the SetSimVarValue function
 
Top