Open up communications with Reality-XP

I’m following up with my question about the autopilot system.

To recap, I was explaining:

Let me stress this is not a trap question either. I’ve been asking myself this for quite some time.

So here is the question again:

considering nearly all autopilot systems boil down in the end to just the same 4 controllers I’ve listed above, why is it 3rd party vendors have to rewrite their own autopilot system?

There has been different and interesting comments and none are about what I was thinking about. This has been my job for 20 years among 15 different simulator version over 2 major simulator platforms and I guess this explains why I’m looking at these questions in a radically different angle. But I’m also dealing with these SDK questions everyday in order to deliver the most authentic experience to my professional customers.

Your comments:

First and foremost I believe it is essential differentiating the functional part and the logical part. The functional part for this discussion is the internal 4 blocks controlling the surfaces and the throttle. The logical part is the operating modes or the state machine. Let’s just keep it simple for now like this.

@N6722C is saying the issue might be due to lack of SDK documentation about the data sources which are used in the autopilot system. He is also mentioning the 100ft fixed increment.

Both are somewhat related to me and it is possible this is one of the reason it is making it harder for 3rd party to use and why they are better off reimplementing theirs.

I’d venture to say what matters the most to me in your example, is not so much knowing which Baro source is feeding the autopilot, or which fixed value the increment is. In my opinion what matters is how 3rd party could override the Baro source data and the increment value so that the autopilot could be made functioning like the one they want. For example if an aircraft can have 2 different Baro source data for the autopilot, it would be best the SDK allows setting the autopilot Baro data directly. This can be in the form of the source, or the data value directly, and here there are 2 schools of thoughts but I’ll come back to this later.

@CristiNeagu is saying the issue might be due to the lack of direct access to the PIDs.

The PIDs are accessible and actually are configurable in the aircraft config files. Now if the question is to adjust the PIDs at runtime to make them behaving differently it might be interesting indeed to be able to also override their initial settings, and to reset them at runtime, via the SDK. I don’t think this is the most important factor preventing using them in any complex aircraft but this would be added bonus if needed and honestly, I don’t think this is highly complex to implement as an API. These PIDs instances are well known at runtime and there are only a handful of parameters to set them up.

@Bishop398 is offering a different point of view which I find interesting as well.

What you’re describing Matt, if I’m not mistaken, is actually how you’ve implemented it in the CJ4. We came to the same conclusions and workaround 20 years ago when we implemented our first Appolo GPS and shortly after with our first Garmin GNS simulation back in 2003. However we quickly realized it was not the right way nor the one delivering on the promises and we changed our approach in adding into the core simulator the code which is missing.

The solution you’re actively working on, offering access to these simvars, is making sense to me from a middleware perspective to flight simulator and the SDK. However from my experience as 3rd party vendor dealing with such questions, I believe this is missing the point and the root cause and how to deal with it from an SDK perspective. I’m not saying you’re wrong in your approach though, I’ll try explaining below.

So here we are, and my take on this question.

It is important to understand what I’m saying below is about flight simulator prior FS2020. I don’t know whether they changed much the implementation but from the look of the SDK and the bugs and problems other 3rd party vendors are sharing with me, I’d guess this is more or less the same in essence. But regardless, the main point is not there, not entirely at least!

The FS autopilot is based on a certain model of autopilot, historically the Bendik King. And the SDK is based on a certain view of the 3rd party vendors needs, which consists in offering access to ready-to-display values and to ready-to-process events.

I might loose some of you reading this because it takes dealing in gauge development to know what this means, but in short, the SDK is giving back the value you’d display in the autopilot readout, and the event corresponding to pressing a button on the autopilot to change modes. In other words, it was meant and designed to eliminate the need to programming the autopilot from a visible and interactive gauge standpoint: just read simvars and display, and just intercept mouse and send event.

And this is where it gets interesting to me.

The FS autopilot system is internally coded no differently than a state machine and a set of controllers (PIDs). The events are changing the state, and the controllers are updated depending on the active state(s). If you dig deeper, you realize the core autopilot update loop/callback is actually selectively changing the state machine based on conditions (for example, ALT CAPTURE is armed and diff alt is less than 100ft, it switches to ALT HOLD). What is even more interesting is that the actual FS implementation in code is really well decoupled: on one hand the state machine changes modes, on the other hand the modes which are engaged are updating the PIDs.

So what does this has anything to do with 3rd party having to do their own autopilots?

I’ll spare you the question of double loops and other similar complexities which are a factor. But in the end, any custom autopilot will be built atop at least the same 4 PIDs and controllers and the same state machine principle. The problem here, is that FS SDK is not exposing the autopilot core components (the implementation), it is instead exposing only the logical view of a Bendix King.

To illustrate, here is some of the actual internal FS code (redacted):

Helper function to update the autopilot “sensor” data:

void Autopilot::UpdateInput()
{
  this->input.vs = ...
  this->input.alt = ...
  this->input.ptch = pSimObject->GetPitch(this->params->attitudeIndicator);
  this->input.bank = pSimObject->GetRoll(this->params->attitudeIndicator);
  this->input.speed = ...
  this->input.mach = ...
  [...]
}

Handling autopilot internal state modes changes:

void Autopilot::SetModes(OTO_MODE_ENUM mode, int enable) {
   
    UpdateInput();
   
    [...]
	
    else if ( mode > OTO_MODE_GLIDESLOPE_HOLD )
    {
      if ( mode > OTO_MODE_VS_HOLD )
      {
        if ( mode == OTO_MODE_HEADINDG_LOCK )
        {
          if ( enable )
          {
            SetModes(OTO_MODE_ON, 1);
            is_backcourse = 0;
			modes &= ~ (OTO_MODE_BANK_HOLD | OTO_MODE_ATTITUDE_HOLD | OTO_MODE_WING_LEVELER | OTO_MODE_NAV_HOLD);
            modes |= OTO_MODE_HEADINDG_LOCK;
          }
          else
          {
            modes &= ~OTO_MODE_HEADINDG_LOCK;
          }
        }
        else if ( mode == OTO_MODE_NAV_HOLD )
        {
          if ( enable )
          {
            SetModes(OTO_MODE_ON, 1);
			modes &= ~(OTO_MODE_BANK_HOLD | OTO_MODE_ATTITUDE_HOLD | OTO_MODE_WING_LEVELER | OTO_MODE_NAV | OTO_MODE_HEADING_LOCK);
            modes |= OTO_MODE_NAV_HOLD;
          }
          else
          {
            modes &= ~OTO_MODE_NAV_HOLD;
          }
        }
      }

      [...]
	
}

Handling autopilot SDK Events (logical autopilot events as if it is a Bendix King):

void Autopilot::HandleEvent(unsigned int event, int v1, int v2) {
 [...]
 
      switch ( event )
      {
        case KEY_AP_PANEL_ALTITUDE_ON:
          SetModes(OTO_MODE_ALTITUDE_CAPT, 1);
          return;
		  
        case KEY_AP_PANEL_ALTITUDE_OFF:
          SetModes(OTO_MODE_ALTITUDE_CAPT, 0);
          SetModes(OTO_MODE_ALTITUDE_HOLD, 0);
          return;
		  
        case KEY_AP_PANEL_ALTITUDE_SET:
          SetModes(OTO_MODE_ALTITUDE_CAPT, v2);
          return;
		  
        case KEY_AP_PANEL_HEADING_ON:
          SetModes(OTO_MODE_HEADINDG_LOCK, 1);
          return;
 
        [...]
 
        case KEY_AP_NAV1_HOLD:
          if ( !(modes & OTO_MODE_NAV) || is_approach )
            HandleEvent(KEY_AP_NAV1_HOLD_ON, v1, v2);
          else
            HandleEvent(KEY_AP_NAV1_HOLD_OFF, v1, v2);
          return;
		
        case KEY_AP_NAV1_HOLD_ON:
          SetModes(OTO_MODE_NAV_CAPT, 1);
          is_approach = 0;
          is_backcourse = 0;
		  return;
		
        [...]
		
}

And the last piece is the main autopilot update function:

  • It is basically updating pitch, bank, cws, yaw damper, throttles.
  • It is updating the controllers depending on the actual internal state modes (see SetModes)
  • The PIDs are updated with the difference between the target and the actual value (delta error)
  • It is also updating the state machine, mostly for handling arm → engaged transitions.

And here we are back to the initial question, and why Matt I’m saying the middleware solution might not be the right one. As you can see, the FS code is already perfectly capable of handling the basic 4 controllers exactly like any other 3rd party vendor would implement their own.

However there are a number of different problems with this and the SDK.

  1. the internal state machine and the mode changes is based on a Bendix King.
    .
    For example, in HandleEvent() above, KEY_AP_NAV1_HOLD_ON is actually disabling is_approach and is_backcourse. But what if another 3rd party autopilot, say hypothetically a STEC 55X shouldn’t disarm/disable these specific alternate logic flags when setting NAV_HOLD_ON?

  2. the internal state machine is not directly accessible through the SDK.
    .
    The only way to “arm” or “engage” a mode is to follow the Bendik King logic which is hard coded in the HandleEvent() function. You can’t selectively arm or engage any mode by your own logic.

  3. the autopilot input is based on the simvars
    .
    There is no way to set the autopilot source data than what is hard coded in the simulator. In practice these are not just the actual simvars, some of the source data come from other internal data.

  4. the autopilot NAV mode is hard coded for the internal radio based nav signal or the internal gps data.
    .
    there is no way to set other nav source data you might need to simulate whatever operating mode a 3rd party autopilot would require. Furthermore, when the source is the GPS, it is hard coded to ignore the vertical deviation because the stock GPS doesn’t support vertical guidance.

NB: this is about all the other FS versions, not FS2020, but I suspect a lot of the above is nearly as-is in FS2020 like I said at the beginning.

Therefore what am I suggesting?

This autopilot question and the description I’ve given above is just a small glimpse of the state of the SDK.

Most things in the SDK are thought and implemented in nearly the same way:

  • the simulator was developed with a certain number of features
  • the SDK is just a bridge to accessing these features from a logical standpoint.

However this is not what 3rd party vendors need, not always. In this particular case of the autopilot, what is needed in my opinion is not giving additional write access to some of the hard-coded simvars. Instead 3rd party developers need the SDK which permits overriding the state machine, and the source data.

The code is already there: in giving access to tell FS to no update the state machine, and in letting 3rd party set/clear the individual state machine bits (OTO_MODE_XXX) like in SetModes(), and keep the rest as-is, the autopilot update code would just pick up any arm and engaged bits which are set, and would just update the stock autopilot PID controllers accordingly.

I believe if 3rd party would have had access to the autopilot internal state machine like this, most wouldn’t need to build their own custom autopilots. Be it a STEC, a Bendik King, a Garmin or whatever, it is just a matter of updating the state machine arm/engaged modes according to this or that specific autopilot user facing logical view.

What is paramount in any autopilot is a robust PID update code and controllers actuating the simulated control surfaces, and since the internal autopilot update is done in sync with the simulator update loop, it is a better place to prevent any form of lag or oscillation than any 3rd party code running out of sync.

More complex autopilots with dual loops or anything exotic could still be using this core autopilot component as the last line of controllers to the control surfaces. A wanted addition would be of course the capability to also lock the control surfaces in order to simulate for example servo failures, but if you have access to the individual bits of the state machine you can easily disable the mode which would control the surface as well.

Now what I said above might be completely unintelligible to some of you reading it, but what is important with all this to me is that there are a number of problems which can’t be solved from the outside-in, and only a few 3rd party developers know these deep implementation details, the others never had to deal with them directly but are often suffering from the SDK not giving access to these details.

In addition I shall add the X-Plane SDK gives 3rd party devs access to these internal state machine bits and this makes a whole lot difference in the capability to implementing a custom autopilot in practice.

Hope this helps!

13 Likes