• Streaming Aircraft Position From FSX To Google Maps With Realtime

    Streaming Aircraft Position From FSX To Google Maps With Realtime

    By Sergio Costa

    Streaming aircraft position from FSX to Google Maps with Realtime

    This demo allowed me to join a few of my favorite things: developing, aircraft, gaming and working with maps. I had this project in my mind for quite some time already -- even before the Combat Helo demo/project -- and decided it was time to give it a go. So I took the dust off my old Saitek X-45, reinstalled Flight Simulator X and got my hands dirty.

    The idea is to make a prototype of a .Net application that streams data from FSX to Google Maps, through Realtime.

    To do so, we'll use Peter Dowson's FSUIPC, a free module that allows you to get and send data to and from FSX (and Prepar3D).

    So FSUIPC will connect to FSX and our application (which I called FSGoogleMaps) will connect to FSUIPC and to ORTC (Realtime's messaging servers) to broadcast data. Realtime works using the Publish-Subscribe pattern and it has JavaScript APIs, so we'll be able to connect browsers to it and get the data.

    FSGoogleMaps Architecture
    FSGoogleMaps Architecture

    The cool thing is that we won't be needing the browser to query for data every few seconds (using AJAX, for example), which would spend both bandwidth and CPU from our servers. In fact we don't even need a server. We can open the web page directly from our file system and it will work: we will be pushing data into the browser. And we're not limited to sending from a single FSX session. Multiple players can stream their data and everyone will be visible on the browser.

    FSGoogleMaps Architecture with multiple broadcasters
    FSGoogleMaps Architecture with multiple broadcasters

    Off to the fun stuff!

    The .Net application

    Before we start, we'll need to download Realtime's .Net API, which can be found at http://www.xrtml.org/downloads_62.html#pubsub:net. We will be using the PubSub (ORTC) dlls: Ibt.Ortc.Api.dll,Ibt.Ortc.Api.Extensibility.dll and also the plugin for the Realtime servers (Ibt.Ortc.Plugin.IbtRealTimeSJ.dll). We'll be also grabbing the FSUIPC .Net dll, which can be found at Pete Dowson's FSUIPC website and add it to our project.

    Adding ORTC References
    Adding ORTC References

    We'll then start to build our interface. I won't go through all the details since you'll be able to download the code. You'll have a form like the one bellow.

    FSGoogleMaps interface
    FSGoogleMaps interface

    Now the fun part begins and we're adding some code. I kinda split my code into three different areas: one for handling everything regarding the connection with FSX, one for handling things with ORTC and another one, somewhere in the middle that takes care of the common stuff.

    As you can see on the screenshot above, I added the fields for your Realtime.co application data (if you're not a Realtime developer already, you can get your free account -- which works great, I'm using one for this project -- at Realtime.co's website. You'll get your own Application and Private keys right away on your email, so you'll be on your way to do your own cool Realtime-powered stuff.

    As you can see on the video in the beginning of this post I'm not using Authentication. I should if I was using this application in a production environment but I wanted to keep things simple. All the code for authenticating you is in my app code though, so if you want to use authentication, you'll be able to. Just remember that you'll need to implement it on the webpage as well, as I haven't done it for you (sorry, I'm lazy!). If you want to learn more about authentication, you can watch a video for a webinar I did a few months ago about this matter at YouTube:

    Once we click our Connect button (don't forget to insert your own Application key or it won't work), we'll connect to FSX, by calling our ConnectToFSX() method:

        /// <summary>
        /// Tries to connect to FSX
        /// </summary>
        /// <returns></returns>
        private bool ConnectToFSX()
        {
            try
            {
                // Attempt a connection to FSUIPC (running any version of Flight Sim)
                FSUIPCConnection.Open();
                return true;
            }
            catch (Exception)
            {
                FSUIPCConnection.Close();
                return false;
            }
        }

    The connection to ORTC is also established, through our ConnectToORTC() method.

        /// <summary>
        /// Connects to the ORTC server
        /// </summary>
        /// <returns></returns>
        private bool ConnectToORTC()
        {
            Log(String.Format("Connecting to: {0} ", ORTC_SERVER_URL));
    
            ortcClient.ClusterUrl = ORTC_SERVER_URL;
            ortcClient.Connect(txtORTCAppKey.Text, txtORTCAuthToken.Text);
    
            return true;
        }

    Once we're connected to both FSX and the ORTC servers, the application will start broadcasting data at a set interval (default is 1000ms) as you'll be able to see on the log box.

    FSGoogleMaps broadcasting data
    FSGoogleMaps broadcasting data

    Getting data from FSX through FSUIPC is pretty simple as the .Net dll that comes with it handles all the hard work for us. We just need to start by adding the offsets we want to read at the top of our code.

        private Offset<int> airspeed = new Offset<int>(0x02BC);  // Basic integer read example
        private Offset<string> aircraftType = new Offset<string>("AircraftInfo", 0x3160, 24); // Aircraft type
        private Offset<int> engineType = new Offset<int>(0x0609); // Offset for engine type
        private Offset<short> pause = new Offset<short>(0x0262, true); // Pause
        private Offset<long> playerLatitude = new Offset<long>(0x0560); // Offset for Lat/Lon features
        private Offset<long> playerLongitude = new Offset<long>(0x0568); // Offset for Lat/Lon features
        private Offset<short> magVar = new Offset<short>(0x02A0); // Offset for Lat/Lon features
        private Offset<uint> playerHeadingTrue = new Offset<uint>(0x0580); // Offset for the heading
        private Offset<long> playerAltitude = new Offset<long>(0x0570); // Offset for the altitude
        private Offset<short> slewMode = new Offset<short>(0x05DC); // Offset ndicating slew mode

    For this demo we're getting only a few of the dozens of available data offsets (you'll get a list of all the available offsets with the FSUIPC manual):

    • Aircraft airspeed
    • Aircraft type
    • Engine type -- which will help us find out if we're flying a helicopter, for example
    • Pause indication -- we're not using it but I decided to leave it there for your reference
    • Aircraft Latitude and Longitude
    • The heading magnetic variation
    • Aircraft true heading
    • Aircraft altitude
    • Slew mode -- again, we're not using it, but I decided to leave it anyway

    You might be wondering why we're getting the aircraft's true heading and the magnetic variation. You can read about it here but the very short version is that there's a difference between true North and the magnetic North (the one the instruments know where it's at). By subtracting the magnetic variation from the true heading, we get the final result.

    We will be reading this data at a set interval (1000ms by default, but you can change this on the interface) and send it through ORTC in a very simple and easy way. To get the data from FSX we only need to tell our FSUIPCConnection to process the data. After we do all the calculations we use ORTC's client Send method to actually send the data to ORTC, by means of a JSON string.

        /// <summary>
        /// Actually broadcasts the data
        /// </summary>
        private void SendData(object sender, EventArgs e)
        {
            Log("Getting data from FSX"); // Log
    
            // Gets the data from FS
            FsLongitude lon = new FsLongitude(playerLongitude.Value);
            FsLatitude lat = new FsLatitude(playerLatitude.Value);
            FSUIPCConnection.Process(); // Process the request to FSUIPC
            FSUIPCConnection.Process("AircraftInfo"); // For aicraft type
    
            int ias = (int)((double)airspeed.Value / 128d); // Aircraft speed (KIAS)
            int hdg = (int)((double)(playerHeadingTrue.Value - magVar.Value) * 8.3819E-008d); // Heading
            int alt = (int)(playerAltitude.Value * 3.28084 / (65536d * 65536d)); // Altitude (feet)
            string acType = aircraftType.Value.ToString(); // Aircraft type ex.: Jetranger
            int engType = (int)engineType.Value; // Engine type. Helicopters are usually type 3
                     // but there are some exceptions (like the default Robinson)
    
            // This will help us force the period as our decimal separator
            NumberFormatInfo nfi = new NumberFormatInfo();
            nfi.NumberDecimalSeparator = ".";
            nfi.NumberGroupSeparator = ",";
    
            // Checks if we have data
            if (lat.DecimalDegrees != 0 || lon.DecimalDegrees != 0)
            {
                // Build the JSON string
                string data = string.Format(@"{{""id"": ""{0}"", ""type"": ""{1}"",
                              ""icon"": ""{2}"", ""ias"": {3}, ""hdg"": {4},
                              ""lat"": {5}, ""lon"": {6}, ""alt"": {7}}}",
                    1,
                    acType,
                    (engType == 3) ? "heli" : "fixed", // our icon will depend on the engine type.
                        // For simplicity sake, we're assuming helicopter are always type 3
                    ias,
                    hdg,
                    lat.DecimalDegrees.ToString(nfi),
                    lon.DecimalDegrees.ToString(nfi),
                    alt);
    
                Log(string.Format("Sending data to ORTC: {0}", data)); // Log
                ortcClient.Send(txtORTCChannel.Text, data); // Sends the data to ORTC
            }
        }

    Here's a short version of the workflow, that will help you understand what the program does. We are:

    1. Connecting to FSX
    2. Connecting to ORTC
    3. Getting data from FSX
    4. Sending data to ORTC
    5. After an elapsed amount of time, return to 3.
    FSGoogleMaps program workflow short version
    FSGoogleMaps program workflow short version

    Once our data is broadcasted to the ORTC servers, any client that is subscribing to our channel will get it and may do whatever it wants with it. In our case, we'll update our position on a page containing Google Maps.

    The webpage

    Just like our program, the webpage is very simple. We will use a Google Maps API key which we can get at https://developers.google.com/maps/signup and the Realtime JavaSscript API. Instead of using the PubSub API, I went for the xRTML one, since it provides me with the some more features than the PubSub ones. I also find it a bit more pleasant to work with. Instead of downloading it I'm linking directly to Realtime's CDN. Please note that downloading the file gives you control over the size of the file, since you'll be able to choose the features you'll want to use. Linking to the CDN means you're downloading everything xRTML has to offer you -- even if you're not using it.

    So we'll start by adding a div for Google maps to place its map into, as well as the Google Maps (don't forget to add your key) and the xRTML scripts.

    <!doctype html>
    <html>
    <head>
        <style type="text/css">
        html { height: 100% }
        body { height: 100%; margin: 0; padding: 0 }
        #map-canvas { height: 100% }
      </style>
    </head>
    <body>
    
        <div id="map-canvas"/>
    
        <script type="text/javascript" src="https://maps.googleapis.com/
                      maps/api/js?key=YOUR_GOOGLE_MAPS_KEY&sensor=false"></script>
        <script src="http://code.xrtml.org/xrtml-3.2.0.js"></script>
        <script src="js/map.js"></script>
        <script src="js/unit.js"></script>
    
    </body>
    </html>

    I also created and added two other script files: map.js and unit.js. These files will actually manage our units (aircraft) that will be displayed on the map.

    unit.js is pretty simple and handles our aircraft data. It's just comprised of a bunch of variables (properties) a custom method (update()) that will let us update a unit and the constructor. We initialize an instantiation of Unit with data passed as parameter, we'll immediately fill the properties. When we want to update an unit we simply call the update() method.

    function Unit(data) {
    	// Variables
    	this.id = '';
    	this.type = '';
    	this.icon = '';
    	this.heading = 0;
    	this.altitude = 0;
    	this.latitude = 0;
    	this.longitude = 0;
    	this.speed = 0;
    	this.googleMarker = null;
    
    	// Methods
    	this.update = function(data) {
    		if (data != undefined)
    		{
    			this.id = (data.id != undefined) ? data.id : '';
    			this.type = (data.type != undefined) ? data.type : '';
    			this.icon = (data.icon != undefined) ? data.icon : '';
    			this.heading = (data.hdg != undefined) ? data.hdg : 0;
    			this.altitude = (data.alt != undefined) ? data.alt : 0;
    			this.latitude = (data.lat != undefined) ? data.lat : 0;
    			this.longitude = (data.lon != undefined) ? data.lon : 0;
    			this.speed = (data.ias != undefined) ? data.ias : 0;
    		}
    	}
    
    	// Constructor (kinda)
    	if (data != undefined)
    	{
    		this.update(data);
    	}
    }

    The Map object, is a bit more complex but it's no big deal. Its function is to add or update a unit (aircraft) on the Google map.

    /*
     Holds our general map logic
    */
    function Map() {
        // Helper function (private)
        var pad = function(text, size, character) {
            var t = text.toString();
            while(t.length < size)
               {
                       t = character + t;
               }
               return t;
        }
        this.units = {}; // Our units array
        // Sets or updates a unit
        this.unit = function(map, data) {               // Checks if we have data
            if (map != undefined && data != undefined)
            {
                var unit; // our unit
                var myLatlng = new google.maps.LatLng(data.lat, data.lon); // the unit's new position
                var marker; // our Google Maps Marker
                // Check if our unit already exists
                if (this.units[data.id] != undefined) {
                     // It does -> get it
                     unit = this.units[data.id];
    
                     // Update the position
                     marker = unit.googleMarker; // get the Google Maps Marker
                     marker.setPosition(myLatlng); // update the position
                     marker.setIcon('img/' + unit.icon + '/' + pad(unit.heading, 3, '0')
                            + '.png'); // update the marker image
                     marker.setShadow('img/' + unit.icon + '/shadow/' + pad(unit.heading, 3, '0')
                            + '.png'); // update the marker shadow image
                     unit.googleMarker = marker; // assign the marker back
    
                     unit.update(data); // update our unit
                }
                else {
                     // It doesn't -> create a new unit
                     unit = new Unit(data);
                     // Create a new marker
                     marker = new google.maps.Marker({
                         position: myLatlng,
                         title: unit.id,
                         icon: 'img/' + unit.icon + '/' + pad(unit.heading, 3, '0') + '.png',
                         shadow: 'img/' + unit.icon + '/shadow/' + pad(unit.heading, 3, '0') + '.png',
                         anchor: new google.maps.Point(32, 32),
                         map: map
                     });
    
                     unit.googleMarker = marker; // Assign the marker to our unit
    
                     // Add the unit to our array
                     this.units[unit.id] = unit;
                     }
                }
            }
    }

    What took me a bit more to do here was the creation of several images to show the heading of the aircraft. Since the Google Maps API doesn't allow us to assign CSS to our custom markers. We can assign our own image and update it, though, which works. I have created 360 images and named them 000.png to 360.png. When I update the aircraft position, I also update the marker, assigning the correct image. When the aircraft is heading 090, for example, the image shown is called 090.png.

    Now that we have our code for adding markers, we need to actually get our page to display the map and communicate with ORTC: connect and subscribe our channel.

    We start by adding a few variables that we will use later. Don't forget to replace YOUR_APPLICATION_KEY with your real Realtime application key. We'll also initialize a new Google Map.

            var appkey = 'YOUR_APPLICATION_KEY';
            var url = 'http://ortc-developers.realtime.co/server/2.1';
            var authToken = 'AUTHENTICATION_TOKEN';
            var channel = 'fsx:session1';
            var gMap;
            var map = new Map();

    The next step will be to add our xRTML code that will connect our browser to ORTC. We'll do so once xRTML is loaded, so we'll encapsulate everything inside the xRTML.load() function. We'll be turning debug mode on, which allow us to see what's happening on the browser's console and, finally adding our connection code, by creating a connection.

            xRTML.load(function(){
    
                xRTML.Config.debug = true;
    
                xRTML.ConnectionManager.create({
                    id: 'myConn',
                    appkey: appkey,
                    authToken: authToken,
                    url: url,
                    channels: [
                    {name: channel}
                    ]
                }).bind({
                    message: function(e) {
                        map.unit(gMap, xRTML.JSON.parse(e.message));
                	}
        		});
    	    });

    Open up the code on the browser, open up the console and you should see something like the image below.

    Chrome connected to ORTC
    Chrome connected to ORTC

    The last step will be to actually render the map, using Google Map's API.

        function initialize() {
            var mapOptions = {
              center: new google.maps.LatLng(25.005973, -12.140625),
              zoom: 3,
              mapTypeId: google.maps.MapTypeId.ROADMAP
            };
            gMap = new google.maps.Map(document.getElementById("map-canvas"),mapOptions);
        }

    Once we have all our code set up, the final result should be something like this:

    Final result
    Final result

    Start the application, boot up FSX, hit connect and magic should happen. You can host your html file on a remote webserver or send the files to your friends and everyone should watch your aircraft position as you move along the map, just like the video I posted on YouTube.

    This was a very fun project for me. The ability to get data being displayed on Google Maps without anything else except a small client application is amazing and I believe that, along with my other post about Combat Helo, perfectly demonstrates the great potential Realtime has on flight simulation, and gaming in general.

    Code download

    As usual, you can download all my code for you to use at will at Github. Snatch everything from this article at https://github.com/sergiomscosta/fsgooglemaps.

    Please feel free to add your comments or questions here or contact me directly.

    Hope you enjoy it. Thank you very much for reading!

    Sergio Costa
    http://www.sergiocosta.me


    7 Comments
    1. maukanet's Avatar
      maukanet -
      Aloha Sergio,

      I'm a "hobby" programmer and the fact you included concept diagrams, flowcharts, code samples, and a working proof of concept makes this something wonderful. Mahalo for this sir!

      Alan
    1. SeanG's Avatar
      SeanG -
      Great article! I'd love to spend the time to make something like this for my sim cockpit ;-)

      Any plans on releasing an end-user app?

      SeanG
    1. Spac3Rat's Avatar
      Spac3Rat -
      Quote Originally Posted by SeanG View Post
      Great article! I'd love to spend the time to make something like this for my sim cockpit ;-)

      Any plans on releasing an end-user app?

      SeanG
      Hi Sean. Thanks for the nice feedback.

      I'm sorry to say that I have no plans on releasing an app anytime soon, mainly due to time constraints. I do have a couple ideas that I can start working on sometime soon but I cannot make any promises. My days are full already

      Nonetheless, since I am releasing all my code as open source, any developer out there can easily start working on any kind of application in a very short amount of time.
    1. Spac3Rat's Avatar
      Spac3Rat -
      Quote Originally Posted by maukanet View Post
      Aloha Sergio,

      I am "hobby" programmer and the fact you included concept diagrams, flowcharts, code samples, and a working proof of concept makes this something wonderful. Mahalo for this sir!

      Alan
      Anytime, Alan. If you need help with anything, just let me know, will you? Good luck and have fun!
    1. iangbusa's Avatar
      iangbusa -
      Excellent work and excellent article.

      I've been working on incorporating Google (and other map servers) into my own full FSX utility app but using SimConnect. It's hard and the FSX SDK documentation is useless. Have you tried it? Also wondered if you've tried other map servers.

      With your concept, if I check for the presence of FSUPIC I can use it to connect. I imagine this is something like Tim Arnot's approach with his excellent Plan-G nav software - worth a look but it's now moved away from Google probably because of issues with the tile server and Google constraints?

      Many thanks for sharing all your hard work and bright ideas.

      ian
    1. ceparsons's Avatar
      ceparsons -
      Look at FlightMap available at Google Play store. It uses FlightConnect to connect to FSX and display location on Google maps, along with display of the flight plan, and airspeed altitude, and fuel for Android devices. Only $4.99. Yes I'm a beta tester, but thought I'd throw this out to everyone so you could see this in action.
    1. fsxfollow's Avatar
      fsxfollow -
      Great article. This is similar to http://fsxlive.com