P3D v2 How to get aircrafts altitude?

#1
Hi! Im pretty new to c# and creating addons for p3d and fsx but im trying to get the current altitude of the aircraft.

Currently im trying to do like this:

Code:
                    simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "Title", null, SIMCONNECT_DATATYPE.STRING256, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                    simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "Plane Altitude", "feet", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);

                    simconnect.RegisterDataDefineStruct<Struct1>(DEFINITIONS.Struct1);
                    simconnect.OnRecvSimobjectDataBytype += new SimConnect.RecvSimobjectDataBytypeEventHandler(simconnect_OnRecvSimobjectDataBytype);
and I got this method:

Code:
     void simconnect_OnRecvSimobjectDataBytype(SimConnect sender, SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE data)
        {


            switch ((DATA_REQUESTS)data.dwRequestID)
            {
                case DATA_REQUESTS.REQUEST_1:
                    Struct1 s1 = (Struct1)data.dwData[0];

                    MessageBox.Show(s1.altitude.ToString());
                    break;

                default:
                    MessageBox.Show("1");
                    break;
            }

         
        }
I need to get the altitude and but it in a variable. If you can help me that would be awesome!
 
#2
I see you are trying to use code from the Managed Data example. Are you getting that to work? What if any are the errors?

I suggest not using a message box - write to a edit text box or RichTextBox.

upload_2015-4-10_17-38-9.png
 
#3
Yeah I'm trying to use that example but I'm trying to implement it on my own Windows form application so I can understand what every line does. Right now I don't really understand what I'm doing since the example code doesn't really explain much, they just do it. I don't get any errors and the message box doesn't even come up. And why can't I use a message box? Just wondering..
 
#5
The reason I suggest not using a messagebox is that your program will most like display a lot of information and having a messagebox pop up all the time is not ideal.

I'll try to explain what I know about what going on in the program and the examples. You create a windows C# form program project and it creates three files. program.cs and form1.cs forms.design. The program.cs is basically the entry point for your program. The WinMain or Main function. You must add a reference to the simconnect managed library.

Program.c is a single thread program. You use a WM_USER_SIMCONNECT message to use on this thread.

Code:
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
Application.Run(new Form1()) creates your windows and starts the window building.

The Form1 constructor builds the windows and all the other controls, buttons, edits etc. InitializeComponent does the initialization and is created by VS.

Now you have a window. At this time the data members are setup and initialized. (below)

Code:
        // User-defined win32 event
        const int WM_USER_SIMCONNECT = 0x0402;

        // SimConnect object
        SimConnect simconnect = null;

        enum DEFINITIONS
        {
            Struct1,
        }

        enum DATA_REQUESTS
        {
            REQUEST_1,
        };

        // this is how you declare a data structure so that
        // simconnect knows how to fill it/read it.
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        struct Struct1
        {
            // this is how you declare a fixed size string
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
            public String title;
            public double latitude;
            public double longitude;
            public double altitude;
        };
WM_USER_SIMCONNECT is a message variable that you use to tell the simconnect server (FSX or P3D) to send when something you requested is ready to be processed. It's used in the simconnect "open" function. So when P3D has something it will send your program a message. Triggers the simconnect.recievemessage function.

simconnect is the handle to the simconnect server - all programs need one.

DEFINITIONS is an enumeration of any and all the data definition structures you have for all the data you want and send. Simconnect uses structures a lot.

DATA_REQUESTS is an enumeration of any and all requests you have.

Each of these enumeration groups needs to be unique ... unique enums of Definitions and a separate unique group of requests. You don't really need enum, it's just a easy way to keep the uniqueness. Otherwise your program has to keep track of what "number" is used and not used.

Struct1 is a structure you need to set up for your data request. Simconnect sends data in packets back and forth and setting up one data entity helps make this data transfer more efficient. All the other stuff is really for the interop - with non managed code in FSX and P3D.

Now P3D is running and your program is running, but there is no communication yet. Click on the Connect button. This runs the buttonConnect code.

Code:
            if (simconnect == null)
            {
                try
                {
                    // the constructor is similar to SimConnect_Open in the native API
                    simconnect = new SimConnect("Managed Data Request", this.Handle, WM_USER_SIMCONNECT, null, 0);

                    setButtons(false, true, true);

                    initDataRequest();

                }
                catch (COMException ex)
                {
                    displayText("Unable to connect to Prepar3D:\n\n" + ex.Message);
                }
            }
simconnect is the handle to the server, but is initially null, so a new simconnect object is created sending the name of the connection, a handle to your client program and the WM_USER_SIMCONNECT message to use to alert your program data is ready. The you set the buttons and initialize (InitDataRequest) the request and definitions and communicate that to P3D via the simconnect handle.

Code:
                // listen to connect and quit msgs
                simconnect.OnRecvOpen += new SimConnect.RecvOpenEventHandler(simconnect_OnRecvOpen);
                simconnect.OnRecvQuit += new SimConnect.RecvQuitEventHandler(simconnect_OnRecvQuit);

                // listen to exceptions
                simconnect.OnRecvException += new SimConnect.RecvExceptionEventHandler(simconnect_OnRecvException);

                // define a data structure
                simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "title", null, SIMCONNECT_DATATYPE.STRING256, 0.0f, SimConnect.SIMCONNECT_UNUSED); .
        .
        .
        .
                simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "Plane Altitude", "feet", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);

                // IMPORTANT: register it with the simconnect managed wrapper marshaller
                // if you skip this step, you will only receive a uint in the .dwData field.
                simconnect.RegisterDataDefineStruct<Struct1>(DEFINITIONS.Struct1);

                // catch a simobject data request
                simconnect.OnRecvSimobjectDataBytype += new SimConnect.RecvSimobjectDataBytypeEventHandler(simconnect_OnRecvSimobjectDataBytype);
The managed code message loop default window proc processes WM_USER_SIMCONNECT message. Then the simconnect.recieveMessage is called to trigger all the other event functions.

simconnect.OnRecvOpen - when you get the OnRecvOpen event message do the open function
simconnect.OnRecvQuit - when you get the OnRecvQuit event message do the quit function

simconnect.AddToDataDefinition - tell simconnect the the data "title" is part of the Struct1 data definition that you send to your client application and its a STRING 256 in size (managed code does not do variable strings)

Also one for each of the other Struct1 data members you setup in the Struct1 - Altitude is a float64

simconnect.RegisterDataDefineStruct<Struct1>(DEFINITIONS.Struct1); - register the struct1 structure with simconnect

simconnect.OnRecvSimobjectDataBytype is the event handler code - when the server has data ready that you requested this event is triggered and you decode the data set back to your program via the struct1 structure.

Code:
            simconnect.RequestDataOnSimObjectType(DATA_REQUESTS.REQUEST_1,DEFINITIONS.Struct1, 0, SIMCONNECT_SIMOBJECT_TYPE.USER);
The buttonRequestData asks for the data in the struct1 structure. You send a request via the DATA_REQUESTS.REQUEST_1 enum, use the DEFINITIONS.Struct1 enum for the data and provide the data for the SIMCONNECT_SIMOBJECT_TYPE.USER aircraft.

You should be able to follow what happens when you click a button and an event is triggered.

I'm no expert on C#, but this is kinda what happens - I might have some things that are not explained exactly as MS makes it work.
 
#6
The reason I suggest not using a messagebox is that your program will most like display a lot of information and having a messagebox pop up all the time is not ideal.

I'll try to explain what I know about what going on in the program and the examples. You create a windows C# form program project and it creates three files. program.cs and form1.cs forms.design. The program.cs is basically the entry point for your program. The WinMain or Main function. You must add a reference to the simconnect managed library.

Program.c is a single thread program. You use a WM_USER_SIMCONNECT message to use on this thread.

Code:
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
Application.Run(new Form1()) creates your windows and starts the window building.

The Form1 constructor builds the windows and all the other controls, buttons, edits etc. InitializeComponent does the initialization and is created by VS.

Now you have a window. At this time the data members are setup and initialized. (below)

Code:
        // User-defined win32 event
        const int WM_USER_SIMCONNECT = 0x0402;

        // SimConnect object
        SimConnect simconnect = null;

        enum DEFINITIONS
        {
            Struct1,
        }

        enum DATA_REQUESTS
        {
            REQUEST_1,
        };

        // this is how you declare a data structure so that
        // simconnect knows how to fill it/read it.
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        struct Struct1
        {
            // this is how you declare a fixed size string
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
            public String title;
            public double latitude;
            public double longitude;
            public double altitude;
        };
WM_USER_SIMCONNECT is a message variable that you use to tell the simconnect server (FSX or P3D) to send when something you requested is ready to be processed. It's used in the simconnect "open" function. So when P3D has something it will send your program a message. Triggers the simconnect.recievemessage function.

simconnect is the handle to the simconnect server - all programs need one.

DEFINITIONS is an enumeration of any and all the data definition structures you have for all the data you want and send. Simconnect uses structures a lot.

DATA_REQUESTS is an enumeration of any and all requests you have.

Each of these enumeration groups needs to be unique ... unique enums of Definitions and a separate unique group of requests. You don't really need enum, it's just a easy way to keep the uniqueness. Otherwise your program has to keep track of what "number" is used and not used.

Struct1 is a structure you need to set up for your data request. Simconnect sends data in packets back and forth and setting up one data entity helps make this data transfer more efficient. All the other stuff is really for the interop - with non managed code in FSX and P3D.

Now P3D is running and your program is running, but there is no communication yet. Click on the Connect button. This runs the buttonConnect code.

Code:
            if (simconnect == null)
            {
                try
                {
                    // the constructor is similar to SimConnect_Open in the native API
                    simconnect = new SimConnect("Managed Data Request", this.Handle, WM_USER_SIMCONNECT, null, 0);

                    setButtons(false, true, true);

                    initDataRequest();

                }
                catch (COMException ex)
                {
                    displayText("Unable to connect to Prepar3D:\n\n" + ex.Message);
                }
            }
simconnect is the handle to the server, but is initially null, so a new simconnect object is created sending the name of the connection, a handle to your client program and the WM_USER_SIMCONNECT message to use to alert your program data is ready. The you set the buttons and initialize (InitDataRequest) the request and definitions and communicate that to P3D via the simconnect handle.

Code:
                // listen to connect and quit msgs
                simconnect.OnRecvOpen += new SimConnect.RecvOpenEventHandler(simconnect_OnRecvOpen);
                simconnect.OnRecvQuit += new SimConnect.RecvQuitEventHandler(simconnect_OnRecvQuit);

                // listen to exceptions
                simconnect.OnRecvException += new SimConnect.RecvExceptionEventHandler(simconnect_OnRecvException);

                // define a data structure
                simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "title", null, SIMCONNECT_DATATYPE.STRING256, 0.0f, SimConnect.SIMCONNECT_UNUSED); .
        .
        .
        .
                simconnect.AddToDataDefinition(DEFINITIONS.Struct1, "Plane Altitude", "feet", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);

                // IMPORTANT: register it with the simconnect managed wrapper marshaller
                // if you skip this step, you will only receive a uint in the .dwData field.
                simconnect.RegisterDataDefineStruct<Struct1>(DEFINITIONS.Struct1);

                // catch a simobject data request
                simconnect.OnRecvSimobjectDataBytype += new SimConnect.RecvSimobjectDataBytypeEventHandler(simconnect_OnRecvSimobjectDataBytype);
The managed code message loop default window proc processes WM_USER_SIMCONNECT message. Then the simconnect.recieveMessage is called to trigger all the other event functions.

simconnect.OnRecvOpen - when you get the OnRecvOpen event message do the open function
simconnect.OnRecvQuit - when you get the OnRecvQuit event message do the quit function

simconnect.AddToDataDefinition - tell simconnect the the data "title" is part of the Struct1 data definition that you send to your client application and its a STRING 256 in size (managed code does not do variable strings)

Also one for each of the other Struct1 data members you setup in the Struct1 - Altitude is a float64

simconnect.RegisterDataDefineStruct<Struct1>(DEFINITIONS.Struct1); - register the struct1 structure with simconnect

simconnect.OnRecvSimobjectDataBytype is the event handler code - when the server has data ready that you requested this event is triggered and you decode the data set back to your program via the struct1 structure.

Code:
            simconnect.RequestDataOnSimObjectType(DATA_REQUESTS.REQUEST_1,DEFINITIONS.Struct1, 0, SIMCONNECT_SIMOBJECT_TYPE.USER);
The buttonRequestData asks for the data in the struct1 structure. You send a request via the DATA_REQUESTS.REQUEST_1 enum, use the DEFINITIONS.Struct1 enum for the data and provide the data for the SIMCONNECT_SIMOBJECT_TYPE.USER aircraft.

You should be able to follow what happens when you click a button and an event is triggered.

I'm no expert on C#, but this is kinda what happens - I might have some things that are not explained exactly as MS makes it work.
Thank you for this awesome explanation! But in what variable does the information save in? Is it in that double that we create at the beginning of the application?
 
#8
Okey I think I got something. My program gets an exception at simconnect.ReceiveMessage();


Code:
+        $exception    {"Type 'WindowsFormsApplication1.Form1+Struct1' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed."}    System.Exception {System.ArgumentException}
Code:
+        m    {msg=0x402 hwnd=0x1207d2 wparam=0x0 lparam=0x0 result=0x0}    System.Windows.Forms.Message
 
Last edited:
Top