Has anyone gotten SimConnect to repeatedly get data?

Yes, I very much agree with that. Confusingly enough the simulation variables documentation does talk about all kinds of types, like Boolean, Flags, and sometimes even about „16bit values“.

I thing what that means: „In spirit that variable is a boolean, but as far as the concrete API is concerned the smallest possible value (in terms of size in bits) is an INT32“ (or possibly CHAR, I am using a mobile phone „with limited usability“ right now ;))

UPDATE: No, INT32 is indeed the smallest possible value.

This has of course to do with performance (address computation) and member alignment. Aligning struct members at a 1 bit (boolean) boundary wouldn‘t be very efficient :wink:

With that in mind, so not to dilute this thread by going off-topic, I’ve decided it’s about time we investigate using the API directly and start bypassing the SDK, by creating our own comms solutions.

This is what I am finding so far. I prefer it this way though because I am not actually saving any sim variables in a struct. Rather, in each simvar object I am saving a pointer to an element in array of double ( to ensure proper alignment, but the pointer can be cast to any size in case the size of the data returned is different from float64). That way regardless of the number of bytes needed to store the value, all I need to do is do pointer math to point the next index to however many bytes past the current index. It has the added advantage of being able to copy a big blob of data in one memcpy operation when the request ID is returned. All my variables are in one package definition.

I agree. That would probably be horrendous to try to send a ton of those values down the serial line to an Arduino. Which is why I come up with a bit packing technique to compress the variables into 2 bytes. It only deals with cases that I expect to encounter. Numbers less than 16384 can have a sign ( i.e. vertical speed can’t possibly be greater than 16000 right? If you have VS of -20,000, game over, you are probably screaming as your plane is plummeting to the ground :wink:); numbers below 256 can have 2 decimal places ( i.e. 108.55 Mhz ) but always positive; numbers above 16384 are stored as a square root with a decimal so squaring ( to decode back to float ) will lose some precision but when dealing with altitudes for example, the difference is not significant.

I am trying to make the configuration of the data definition to be demand driven. If an arduino requests a simvar, it gets activated and put in the data definition. If a simvar is not requested for a number of cycles, it gets deactivated and removed from the data definition. It could still work but now I have to redo the whole definition package instead of making tweaks.

I will now be working on the serial communication stuff. Any pointers from people who have gone down that road already? This will be for talking to an arduino via a comm port. I have read that it must be put on a separate thread at least.

I have found this:

Thanks,
Chris

That’s not really an advantage of arrays over - tightly packed! - structs: you can simply cast the received data as the expected struct and then memcopy that struct into the target struct (for saving it for later use etc.):

switch (static_cast<DataRequest>(objectData->dwRequestID)) {
    case DataRequest::AircraftPosition:
    {
        const SimConnectAircraftData *simConnectAircraftData;
        simConnectAircraftData = reinterpret_cast<const SimConnectAircraftData *>(&objectData->dwData);
        // Now we can copy the data from simConnectAircraftData (which points to the address
        // of the struct) into the target struct (or process the data otherwise, e.g. "transform" it into
        // another struct with a more suitable naming and data types etc:)
        ...

The advantage of a struct over an array is of course that you may have mixed types (ints, floats, doubles) and you don’t have to do the address computation yourself in order to access a given field), e.g. with the above example:

simConnectAircraftData->velocityX

etc.

But I don’t know your requirements respectively what you are aiming for, so an array may indeed better fit your needs :slight_smile:

UPDATE: Uh… you don’t even need to use memcpy in order to copy one struct into another: simple assignment of one struct to another does already the job :wink:

Ah, that’s exactly what I thought then. Pretty much what the SimvarWatcher example application (from the SDK) does then - your GUI seems to be derived from that app, too :wink:

Actually, the GUI is Windows Form CPP CLR. I didn’t want to have to study C#. It is really easy to design in Visual Studio 19 because there is a Form Designer. You just drag and drop from the Toolbar. I am finding out though that 99% of the tutorials out there are in C#. So I have to look at the C# code and figure out how the example could be converted to CPP. My biggest stumbling block lately was that no matter how I define the byte array for serialPort.Write(), It would not accept the argument. Then I find out that C# has its own array in the cli namespace. So if you are using std namespace, you will not get the cli array. You have to type cli::array..

The actual simconnect stuff was from andrezwicky’s throttlewatcher example which I got working in console mode.

Let me just pull back into the topic for now. Are there any sim variables that fall into the following ranges:

  1. Numbers above 65535
  2. Negative number above 16384 ( i.e. -20000)
  3. Negative number below 256 with a decimal. (i.e. -108.12)
  4. Requiring three decimal places ( i.e. 240.123 )

Thanks,
Chris

Ah yes, I just realised later that I confused this thread with some other thread, where a “SimvarWatcher derived UI” was being presented :wink:

Sorry about my bold assumption about your UI :wink:

As for your questions about value ranges:

  • The values that you get depend on the requested variable type, which defaults to FLOAT64
  • On the other hand when you request a FLOAT32 or even an INT32 that naturally has an effect on both the precision (decimal places) and max/min range
  • But most importantly the actual value range - and format - is defined by the actual “unit” (feet, percent, percent over 100, position, number, …) that you request!

So for instance the yoke position can be requested with the following units (if I remember correctly - to be verified!):

  • Percent: [-100.0, 100.0] for FLOAT, and I guess [-100, 100] for INT32
  • Percent over 100: [-1.0, 1.0] (I guess requesting as INT32 wouldn’t make sense here!)
  • Position: [-32768, 32767] (min/max values of a signed INT16 value)

And attention! Even percent values where one wouldn’t necessarily expect it can get negative! E.g. there exists “reverse thrust” (and “reverse propeller” (?)), so you might get e.g. -20% thrust. (And theoretically you could even get percentages larger than 100%, but in practise I don’t think that is true for any simulation variable).

Best is you play around with the provided “SimvarWatcher” (in the SDK). Note that the SimvarWatcher always requests variables as FLOAT64 (except for strings: in the latest SDK they added a “Is String” checkbox). And sometimes the SDK docs mention value ranges as well, but I find this more confusing than it helps, as it actually depends on the specific unit that is being requested.

And only a few “unit conversions” do make sense, of course, e.g. feet → metres perhaps - but I wouldn’t rely on those conversions either, but rather “use the intended unit”: so if the documentation talks about “altitude in feet”, then request “feet” (and not metres).

Oh, and about the precision: I find that e.g. latitude/longitude raw values come with a gazillion of decimal places - but that is most likely simply “noise”.

There is an “epsilon” value that you can specify - specifically in the case if you request updates only “when values change” - so you could specify that you’d only get an update if a given value has changed, say, at least in the 3rd decimal place or so.

For all the above discussed topcs - type, unit, epsilon - refer to the documentation again:

UPDATE:

Oh, and I forgot the most obvious :slight_smile: The actual value range of a given simulation variable most importantly depends on its “natural domain” (value range).

  • For a heading value you get anything within [0, 360[ (degrees)
  • For a bank angle you get anything within [-180, 180[ (degrees)
  • For an altitude value you get anything from “something below zero” (“Hello there, the Netherlands!” ;)) and 60,000+ feet (“Hello, space!”)
  • etc.

Thanks a lot. I really appreciate all the pointers.

I have been working today on the serial port communication stuff. I know some people in this forum are doing the same. Anyways I am finding out that the serial port stream is behaving like a soup. I thought I could just count on sequence of bytes. Send a request byte, expect a response data byte. Send another, expect another response. I am finding out that the arduino is switching the response data bytes. Send id 23 and got data for 25, send id 24 got data for 24, send id 25 got data for 23. Maybe its a peculiarity of the my setup. The only way I was able to get the id to sync with the proper data is to send the id back to the arduino as an identificator along with the data . Then I do a do while loop in arduino Serial read until I get the proper id and then I know the data is correct.

"Oh, where are my socks in this pile of mess. " :thinking:

“There, the one with a label ‘Socks’”; :face_with_monocle:

Anybody else running into this. Don’t know if this is the usual.

Also, don’t forget the capacitor.

Chris

I’d like to return to the question of an event driven simulation loop. My ConsoleApp simulation loop contains
dw = WaitForSingleObject(hEventHandle, INFINITE);, which appeared to work fine to start with, but deeper trials have discovered:
a) an event raised using SimConnect_TransmitClientEvent is seen in the SimConnect Callback as one would expect, but it does not release the WaitForSingleObject
b) to achieve a release in situation (a), a WIN function SetEvent() is needed as well.
c) events raised using SimConnect_SubscribeToSystemEvent do create a WIN event that releases the WaitForSingleObject.
d) raising a single event using either method (b) or (c), the Wait is released TWICE, that is this simulation loop is executed two times per event.

Does anybody know what is causing the effect (d)?

I have a question too:

How would you use NAV1_RADIO_SET to set the frequency of Nav1 Radio.

I think I need to use :

   SimConnect_TransmitClientEvent(hSimConnect, SIMCONNECT_OBJECT_ID_USER, ev_ID, 
    data, SIMCONNECT_GROUP_PRIORITY_HIGHEST, 
   SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY);

But I don’t know how to form the ‘data’ argument. According to the list it should be BCD but according to the method description it’s a DWORD.

How would I set the data to for example 111.11

Thanks,
Chris

I figured it out:
111.11, remove the decimal and convert to BCD:

    0001001001001001

Add a leading 0000 :

    0000001001001001001

Convert to decimal:

   69905

Convert to double and that is the format of the data argument.

Chris

Hey guys, I just released a Sim Connector application which I referred to here in some of my questions. Thanks for the guidance. I hope it will be useful to some users.

Cheers,
Chris

Hi Dragonlaird, I follow your posts with great interest and would ask you if you have finished the SimConnect wrapper. I ask you because I begun to have “System.AccessViolationException: 'Attempted to read or write protected memory.” messages at sim.ReceiveMessage() line that I cannot handle (tried several Exceptions);
I don’t know if your wrapper solve this issue, so I ask you.
I am just a basic hobby c# programmer (google assisted) so it your answer is complex don’t bother because I will not understand it.
I use a Timer every 1 second to grab the values. Thanks
private IntPtr WndProc(IntPtr hWnd, int iMsg, IntPtr hWParam, IntPtr hLParam, ref bool bHandled)
{
if (iMsg == WM_USER_SIMCONNECT)
{
if (sim != null)
{
try
{
sim.ReceiveMessage();
}
catch (COMException e)
{
string msg = "Exception from ReceiveMessage: " + e;
if (e.ErrorCode == -1073741648)
{ }// Thrown on sim exit; no idea how to prevent it:
//utils.geralog(msg); // just log at debug level in this case.
else
{
//Logger.Error(msg);
}
}

            }
        }

        return IntPtr.Zero;
    }

Long thread, but I was also experimenting with python-simconnect to repeatedly get data to drive an external steam gauge panel (see patricksurry/g3 on github) and getting errors if the fetch was too frequent, or ran too long. The problem stemmed from the python-simconnect wrapper repeatedly requesting each data item individually for every update.

Anyway, it inspired me to write yet another python wrapper that makes it easy to subscribe to a set of variables with a single call, then just watch for SDK events as those variables change. It also supports the “only send changed data” options of the SDK which helps if your dataset includes a bunch of things that rarely change (e.g. altimeter setting).

Take a look at patricksurry/pysimconnect on github or pypi - I’d love any feedback. My blurb there:
pysimconnect is a lightweight, high-performance wrapper for FlightSimulator 2020’s SimConnect SDK, inspired by Python-SimConnect. It provides a simple pythonic interface to read simulator variables, set editable variables, subscribe to variable changes, and trigger simulator events. It also exposes all of the low-level SDK methods, constants and enumerations based on an automatic translation of the SDK API defined by SimConnect.h