Howto:Parsing binary data received via UDP in C

From FlightGear wiki
Jump to navigation Jump to search
This article is a stub. You can help the wiki by expanding it.


Note  The code shown here is currently platform specific (Windows) and still needs to be ported to use the SimGear wrappers to be truly cross-platform (i.e. compile on Linux/Unix and Mac OS). Equally, it would be a good idea to add self-contained snippets of code that actually compile (including the corresponding CMakeLists.txt). In the long term, it would be great to commit this as a demo to the contrib directory in $FG_SRC (see the talk page for pointers). Any help with this would be appreciated!

Contributed by: OsoMoore (12/2014)

I am parsing binary data sent by FlightGear via UDP. I am receiving the data with a C++ application written in Visual Studio 2013.

Here is my protocol XML (to be put into $FG_ROOT/Protocols), which determines the layout of each packet (see $FG_ROOT/Docs/README.IO for details):

<?xml version="1.0"?>
<PropertyList>

 <generic>

  <output>
   <binary_mode>true</binary_mode>

  <!-- note that chunk ordering/typing is critical here and needs to match the C struct below -->
   <chunk>
    <name>latitude</name>
    <type>float</type>
    <node>/position/latitude-deg</node>
   </chunk>

   <chunk>
    <name>longitude</name>
    <type>float</type>
    <node>/position/longitude-deg</node>
   </chunk>

   <chunk>
    <name>altitude</name>
    <type>int</type>
    <node>/position/altitude-ft</node>
   </chunk>

   <chunk>
    <name>airspeed</name>
    <type>int</type>
    <node>/velocities/airspeed-kt</node>
   </chunk>

   <chunk>
    <name>roll</name>
    <type>float</type>
    <node>/orientation/roll-deg</node>
   </chunk>

   <chunk>
    <name>pitch</name>
    <type>float</type>
    <node>/orientation/pitch-deg</node>
   </chunk>

   <chunk>
    <name>heading</name>
    <type>float</type>
    <node>/orientation/heading-deg</node>
   </chunk>

  <!-- any chunks added here, also need to be added to the C struct below -->

  </output>

 </generic>

</PropertyList>


Here is the struct into which I receive the data. The #pragma should result in the structure being packed tightly in memory.

#if defined(_WIN32) || defined(WIN32) 
#pragma pack(push, 1)
  typedef struct {
#else
  typedef struct __attribute__((__packed__))  {
#endif     
      // NOTE: ordering/typing needs to match the protocol spec above
      float latitude;
      float longitude;
      int altitude;
      int airspeed;
      float roll;
      float pitch;
      float heading;
   } flightgear_packet;
#if defined(_WIN32) || defined(WIN32) 
#pragma pack(pop)
#endif


Added this endian swapper function:

float float_swap(float value){
   int temp = htonl(*(unsigned int*)&value);
   return *(float*)&temp;
};

Here is the code which receives the packet and converts it into host byte ordering:

      flightgear_packet buff, buff2;

      //  initialize buffer (fill with 0)
      memset(&buff, 0, sizeof(buff));

         bytes_read = recvfrom(SOCKET(sock), (char*)(&buff), sizeof(buff), 0, (struct sockaddr *) &si_other, &slen);
         
         if (bytes_read > 0)
         {

            // populate structture
            buff2.airspeed = ntohl(buff.airspeed);
            buff2.altitude = ntohl(buff.altitude);
            buff2.heading = float_swap(buff.heading);
            buff2.latitude = float_swap(buff.latitude);
            buff2.longitude = float_swap(buff.longitude);
            buff2.pitch = float_swap(buff.pitch);
            buff2.roll = float_swap(buff.roll);

            // print out data
            printf("Airspeed %i, Altitude %i\n", buff2.airspeed, buff2.altitude);
            printf("Position %3.2f, %3.2f\n", buff2.latitude, buff2.longitude);
            printf("Pitch/Roll/Heading %3.2f, %3.2f, %3.2f\n", buff2.pitch, buff2.roll, buff2.heading);

         }

Hopefully this will be useful to someone else down the line.