API Question VESSEL::GetGlobalVel() and/or simdt oscillation

indy91

Addon Developer
Addon Developer
Joined
Oct 26, 2011
Messages
1,226
Reaction score
591
Points
128
Next to this potential bug in Orbiter 2016 there is another problem with the implementation of accelerometers in NASSP.

Part of the acceleration calculation is taking the global velocity each timestep and use the difference to the previous timestep to get the global velocity difference per second.

First Timestep:

Code:
sat->GetGlobalVel(vel);
lastGlobalVel = vel;

All subsequent timesteps:

Code:
sat->GetGlobalVel(vel);
VECTOR3 dvel = (vel - lastGlobalVel) / simdt;
lastGlobalVel = vel;

This is only the few lines of code concerning the velocity. The problem is "dvel" seems to have a kind of oscillation in Orbiter 2016. It's not a steady change as in Orbiter 2010 and when the global velocity is changing a lot, like in Earth orbit, then this oscillation is severe enough to cause us some issues. Somewhere in cislunar space the global velocity doesn't change all that much per timestep and the effect is much weaker.

The code above is called in clbkPostStep and the "simdt" is from that function. Is simdt maybe not the exact time difference between the two velocities? In that case an oscillating framerate between e.g. 59.9 fps and 60.1 fps could already cause the issues.

The overall acceleration measurement seems to be fine. So the velocity change during a time period of let's say a minute is not really affected by the oscillation. But a few spacecraft systems have problems when there isn't a steadily measured acceleration.
 

jarmonik

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 28, 2008
Messages
2,666
Reaction score
795
Points
128
I had the same code used in IMFD to evaluate applied thrust in autoburn. It worked fine in 2010-P1 but failed in Orbiter 2016. It just couldn't get the remaining dV low enough and kept the vessel rotating aimlessly. Then I replaced the code with this one from LTMFD and it seems to be working fine. But why the original code failed in 2016 ? I have no idea.

PHP:
	theVessel->GetThrustVector(_NewThrust);

	theVessel->GlobalRot(_NewThrust, _NewThrust);
	
	_dVS += (_NewThrust + _OldThrust) * SimDT / (OldMass+NewMass);
 

indy91

Addon Developer
Addon Developer
Joined
Oct 26, 2011
Messages
1,226
Reaction score
591
Points
128
Thanks for the reply! What you are describing is the same behavior that I have observed. The small acceleration oscillation is enough to confuse our Entry Monitoring System, which needs a steady 0.05G or more for 1 second until it recognizes the 0.05G event. But that doesn't really work until the acceleration is very high, because it will go below 0.05G every other timestep or so, because of this issue.

I have tested your implementation and it seems to work well. Now, the GetThrustVector() won't recognize atmospheric forces, which we also need to measure. But I guess adding GetDragVector() and GetLiftVector() to the force vector could work?

EDIT: No, the weight vector needs to be measured also, because the systems are already used on the launchpad. So simply adding lift, drag and thrust won't work.
 
Last edited:

martins

Orbiter Founder
Orbiter Founder
Joined
Mar 31, 2008
Messages
2,448
Reaction score
462
Points
83
Website
orbit.medphys.ucl.ac.uk
Next to this potential bug in Orbiter 2016 there is another problem with the implementation of accelerometers in NASSP.

Part of the acceleration calculation is taking the global velocity each timestep and use the difference to the previous timestep to get the global velocity difference per second.

First Timestep:

Code:
sat->GetGlobalVel(vel);
lastGlobalVel = vel;

All subsequent timesteps:

Code:
sat->GetGlobalVel(vel);
VECTOR3 dvel = (vel - lastGlobalVel) / simdt;
lastGlobalVel = vel;

This is only the few lines of code concerning the velocity. The problem is "dvel" seems to have a kind of oscillation in Orbiter 2016. It's not a steady change as in Orbiter 2010 and when the global velocity is changing a lot, like in Earth orbit, then this oscillation is severe enough to cause us some issues. Somewhere in cislunar space the global velocity doesn't change all that much per timestep and the effect is much weaker.

The code above is called in clbkPostStep and the "simdt" is from that function. Is simdt maybe not the exact time difference between the two velocities? In that case an oscillating framerate between e.g. 59.9 fps and 60.1 fps could already cause the issues.

The overall acceleration measurement seems to be fine. So the velocity change during a time period of let's say a minute is not really affected by the oscillation. But a few spacecraft systems have problems when there isn't a steadily measured acceleration.

Does the oscillation still happen if you use the previous time interval, i.e.
Code:
// first step
sat->GetGlobalVel(vel);
lastGlobalVel = vel;
last_dt = simdt;

// All subsequent timesteps:

sat->GetGlobalVel(vel);
VECTOR3 dvel = (vel - lastGlobalVel) / last_dt;
lastGlobalVel = vel;
last_dt = simdt;

The thing is that the time step update point is located after the call to clbkPostStep. Let's say, at timestep n, clbkPreStep reports [math]\mathrm{simt} = t_n[/math], [math]\mathrm{simdt} = t_{n+1}-t_n[/math], and a call to GetGlobalVel returns [math]v(t_n)[/math].
The subsequent clbkPostStep reports [math]\mathrm{simt} = t_{n+1}[/math], [math]\mathrm{simdt} = t_{n+1}-t_n[/math], and a call to GetGlobalVel still returns [math]v(t_n)[/math]. So in your code the time interval is one step ahead of the speed difference.

I am aware that this arrangement may be counterintuitive. The alternative would be to move the time synchronisation point ahead of clbkPostStep, such that a call to GetGlobalVel from clbkPostStep returns [math]v(t_{n+1})[/math]. But then clbkPostStep is essentially redundant, since it returns the same state as the clbkPreStep in the next frame. The idea of clbkPostStep was essentially to have a hook into the simulation after Orbiter has done its internal update but before the updates have been propagated to the next time step.
 

indy91

Addon Developer
Addon Developer
Joined
Oct 26, 2011
Messages
1,226
Reaction score
591
Points
128
Yeah, using the simdt from the previous timestep works. Awesome!

But I am not sure I follow your explanation. The global velocity returned by clbkPreStep and clbkPostStep of the same timestep is identical? It looks different in the Orbiter frame update loop figure from the API reference. If the "Render world at state s0" is updating GetGlobalVel, what is then the difference between clbkPreStep and clbkPostStep of the same timestep?

In NASSP we have explicitely been using clbkPostStep to access the updated states. So I guess we need to change a few things there, mostly the accelerometers. I hope using the previous simdt will fix most of these problems. So thanks for your answer!
 

martins

Orbiter Founder
Orbiter Founder
Joined
Mar 31, 2008
Messages
2,448
Reaction score
462
Points
83
Website
orbit.medphys.ucl.ac.uk
You are right, the behaviour doesn't correspond to that schematic. I remember that I fiddled around with the callback sequence quite a bit at some point, and that I settled on the current behaviour for some compelling reason which completely eludes me now :lol:

In some sense the documented behaviour seems more logical. I might see if I can go back to that (or at least remind myself why I changed it). In the worst case I could introduce a third per-frame callback function after the synchronisation point.
 

indy91

Addon Developer
Addon Developer
Joined
Oct 26, 2011
Messages
1,226
Reaction score
591
Points
128
In the worst case I could introduce a third per-frame callback function after the synchronisation point.

Yeah, I thought about that, too. clbkMidStep or something like that and then clbkPostStep could have the old behavior. :lol:

But our accelerometers work good again now, except on the launchpad. There this problem is occuring: http://orbiter-forum.com/project.php?issueid=1318 And once that isn't an issue anymore there aren't many obstacles for NASSP to run pretty well in Orbiter 2016. Just staging can't be in clbkPostStep anymore, if I have been reading some of the posts here correctly. Anyway, thanks again for the help!
 

martins

Orbiter Founder
Orbiter Founder
Joined
Mar 31, 2008
Messages
2,448
Reaction score
462
Points
83
Website
orbit.medphys.ucl.ac.uk
Ok, I think I more or less remember the reason behind this change now.

In a nutshell, it was to allow vessel state "Set" functions to be executed in clbkPostStep without introducing race conditions.

Let me explain:

Each vessel maintains its current state. This state is updated for all vessels simultaneously at the synchronisation point. Importantly, this is the _only_ point where this public state changes. Otherwise, the state should be considered read-only.

Prior to the synchronisation point, each vessel updates its state (applies forces, propagates the state across the current time step), by making a copy of its public state, and operating on this copy. This "update state" is where all "Set" operations should be directed at. This copy is created just before clbkPreStep, and it is merged back at the synchronisation point just after clbkPostStep.

If clbkPostStep was moved past the synchronisation point, any "Set" type API calls would operate on the public state, and thus introduce race conditions:
Let's say vessel A modifies its state in clbkPostStep (e.g. SetGlobalPos). Vessel B, in its own clbkPostStep, queries vessel A's state and does something based on that. This means that the state received by vessel B depends on the order in which orbiter calls the vessels' clbkPostStep function, hence a race condition. This would negate the whole rationale of the synchronisation point, where the entire simulation state is updated at once.

If I were to move clbkPostStep back past the synchronisation point (or add a new callback function there), no Set functions would be allowed to be called from there.

It should also be noted that each vessel's update state is private. No vessel should have access to another vessel's update state, again to prevent race conditions. (Ultimately, all vessel updates should be able to be executed in parallel on separate threads without inter-vessel communication).

Note that this strict separation between public "current" state and private "update" state is not completely enforced yet. There are numerous vessel callback functions taking place outside the vessel update phase, and orbiter doesn't yet prevent state changes in those functions. But clbkPreStep and clbkPostStep are the most critical.

Does this make sense? Please feel free to discuss this. Suggestions welcome!
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
Synchronization makes a ton of sense, yes. I remember having many issues in OMP when using DefSetStateEx based on GetStatusEx willy-nilly in the receiver thread of clients. Random CTDs, jittering etc.

To compensate I've sent the state-vectors of all vessels in the scenario together with time-stamps to a cache on PostStep, so I have a global snapshot of each vessel that is kind of thread-safe. I've also experimented with double-buffering to implement the setter direction, but to my surprise it didn't matter whether it was there or not. I guess now I know why :) .
 
Top