FGAIS

From FlightGear wiki
Jump to navigation Jump to search
Caution  Developer information:

Some, and possibly most, of the details below are likely to be affected, or even deprecated, by the ongoing work on FGTraffic.
This is because Durk is inclined to break away from the approach he took for the current AI traffic system. In his opinion, the code has become too clunky too be maintainable, he will try to spend a limited portion of his development time for bug fixing the current code, but please don't expect any major changes any more. The new approach he has in mind should surpass everything we currently have, but it's going to take some time until it's all implemented. [1] To avoid conflicting efforts, you are advised not to start working on anything directly related to this without first coordinating your ideas with FlightGear core developers using the FlightGear developers mailing list. talk page..

FGAIS - real world traffic over multiplayer
Developed by F-JJTH & Hooray
Initial release n/a (proof of concept only)
Written in C++ (FGMS/SimGear/Boost)
OS Cross platform (using cmake)
Development status put on hold as of (01/2013)
License GNU General Public License v2
Website
Initial FGAIS over FGMS experiments (11/2012)
FGAIS over FGMS in use at LFML with real life AI traffic (11/2012)
FGAIS over FGMS showing the Map dialog and the MP pilot list in FGFS (11/2012)

My dream is simple : populate FG world with data from these kind of websites. Imagine : you are in your home and an aircraft is in the sky, you run at your computer and start FG then you can fly around the aircraft that you have just see in real life.

I think this feature can bring a lot of realism for FG. For example you are at EDDF and in real life there is a 777 landing, in FG the same thing appear. In other words : AI traffic in FG is in fact real traffic in the world in real time.

Objective

Implement a new FlightGear component that allows using web data feeds (JSON) for aircraft/vessel tracking (TCAS/AIS based) to populate the FlightGear world using the FlightGear Multiplayer system via an extended version of fgms.

Status (02/2013)

A basic proof of concept has been shown to work properly (with some bugs and other issues remaining), the project is currently put on hold because of the recent advances in the HLA department, see [1] for further information.

Feeds (Aircraft & Vessel Tracking)

Roadmap

Milestone 1: Move FGAIS to a background thread Done Done (28/10/2012)

Milestone 2: Send packets back to FlightGear Done Done (31/10/2012)

Milestone 3: Smooth AI traffic in Flightgear Done Done (01/11/2012)

Milestone 4: Continue using extrapolation when no new data is available Pending Pending

Milestone: Use FGExternalMotionData properly Done Done

Thus now, how to simulate it for our AI ? I think that we can simply do the same but instead of considering "when FG client has started" we can consider "when this AI has appears in our m-AircraftList". What do you think about that ? Of course we need to update it before send his position to FG client. In fact this motionInfo.time is only here to say "I'm born XX.XXX seconds ago"

add an SGTimeStamp field to the AI_Item class and initialize it in addAIAircraft() - i.e. when the aircraft is first "seen" by FGAIS, and in updateAIAircraft() always update the timestamp - in recompute_aircraft() we could then use the actual timestamp + offset

  • "orientation" is where the nose of the aircraft is pointing
  • "linearVel" is it's velocity vector (whereto it's flying)
  • "angularVel": angular velocity vector (how it is rotating)
  • and then the corresponging acceleration vectors

This data is used for displaying the orientation, velocity and accelerations of the aircrafts in the simulation at the receiver of the datagrams. Since the position is only updated at intervals (shortly after the reception of each datagram), the velocity value is used for displaying it in the meantime, between updates. If velocity was zero but position changes, then the aircraft would jump between positions at each new datagram.

If the heading (included in "orientation") is zero, the nose of the aircraft will always point north (I presume), regardless of where it's flying. And so on. Of course, that may look funny in the simulation. But that's as good as it can be without any data about orientation, velocity and accelerations.

You could simply set the unknown data to zeros to begin with, to comply to the protocol. (and live with aircrafts jumping around with their noses always pointing north and never banking in turns)

Or you could try to design an algorithm to calculate/predict/guess them. And then you have to find a compromise between correctness and time delay, according to the sampling theorem. And live with strange flying aircraft each time you guess wrong, depending on how much time delay you can accept. (Aftermath is the only truly exact science.)

Milestone: Make more use of smart pointers Pending Pending

Milestone: Get rid of threading issues Pending Pending

Milestone: Generalize design and support other providers, beyond just planefinder.net

Milestone: Generalize Output Feed and prepare support for different backends, i.e. HLA

Milestone: Get rid of FlightGear warnings/errors Pending Pending

  • the MP pilot list is not able to deal with 190+ pilots: it shows "too many live PUI interfaces" and even causes segfaults in FG ...
Nasal getValue: property /ai[0]/models[0]/multiplayer[143]/position[0]/global-z[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[144]/position[0]/global-x[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[144]/position[0]/global-y[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[144]/position[0]/global-z[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[145]/position[0]/global-x[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[145]/position[0]/global-y[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[145]/position[0]/global-z[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[150]/position[0]/global-x[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[150]/position[0]/global-y[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[150]/position[0]/global-z[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[153]/position[0]/global-x[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[153]/position[0]/global-y[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[153]/position[0]/global-z[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[156]/position[0]/global-x[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[156]/position[0]/global-y[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[156]/position[0]/global-z[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[159]/position[0]/global-x[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[159]/position[0]/global-y[0] is NaN
Nasal getValue: property /ai[0]/models[0]/multiplayer[159]/position[0]/global-z[0] is NaN

The above error is now solved Done Done

Milestone: Improve 32bit support

FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data
FGMultiplayMgr::MP_ProcessData - Position message received with insufficient data

Milestone: Allow to toggle FGAIS subscription on/off via a special filtered chat message to server

Milestone: Use dedicated planefinder.net json api

As you can see the Json format has changed. We need to redone the parser... But this new Json format is more standard ("key":"value") thus maybe we can use a Json reader library ? Before I wasn't use a Json library because their public feed use a special Json format who required a specified parser, but now we can use a standard library. The good new with this new API is that it provide the climbrate. We no longer need to calculate it thus no longer need to store m_PrevAircraftList.

Also we need to change the download() in order to make a POST request with Curl... (I've no idea how it's doable for now)

I suggest to continue to use the public API for now in order to help us to continue to experiment movement/velocity... Then implement the new API directly in your "multi-providers manager". When we are ready to switch to the new API we just need to delete the download() and related thing in fg_aimgr.cxx then start to use PlanefinderProvider.cxx, JSONParser.hxx etc etc

Status (10/2012)

As of 28/10/2012, the modified fgms version is already able to:

  • download JSON feeds from planefinder.net in a background thread
  • parse the downloaded information (background thread)
  • populate internal data structures (background thread)
  • determine if the traffic is accelerating/delecerating
  • filter all traffic based on a range of 100nm (fgms main thread)

As of 31/10/2012, the modified fgms version is now also able to:

  • create MP packets for each AI traffic
  • send MP packets to FG client as MP traffic


Position Computation

  • groundspeed seems still way off, can be easily seen when watching landing aircraft
  • vertical speed / altitude also needs improvements, sometimes showing -80000 ft :-)

FGAIS Issues

  • Support multiple clients without segfaulting... Pending Pending
  • Altitude need to be computed with the climb rate calculated with the updateAIAircraf(); Done Done
  • When aircraft is on ground, planefinder.net send altitude=0 even if the runway is on top Everest mountain... It seem that planefinder send us a relative altitude when aircraft is on ground and send us absolute altitude when aircraft is flying.

Timing

At the moment, the fgais background thread will sleep until 60 seconds have passed, only then, will it start downloading/parsing/computing new data. However, by that time, the fgms main thread is already "starving", because it has no more data to send to the fgfs client - in other words, it has to wait until the worker thread has completed. We should be preparing a new set of data earlier than that. For now, a usable workaround would be to compute even more data and use a FIFO

it's because we have sent all our positions in stock (1...59) then the download() lock all. The problem is that the new download() come after 60 seconds but he take 6~10 seconds, the result is :

  • 0s : first download (took 8s)
  • 8s : parse and recompute (took 0.5s)
  • 8,5s : start to send our first position, since we have 60 position we can go at 68,5 seconds
  • 68s : new download (took 8s seonds)
  • 76s :parse and recompute (took 0.5s)
  • 76.5 : start to send our positions

As you can see between 68.5s and 76.5s we have sent no position to our client, our client has passed 8 seconds without receiving position. Imagine that we have some trouble with the connection, the download can take 10s and with 10 seconds of download : the client doesn't receive data during 12 seconds. Since 10 seconds is the limit of live : our AI traffic is gone at the client side.

To solve it we need to force download every (60s - prevDownloadTime) or use a scheduler. But it's not really a big problem, it's easy to solve. We can also simply reduce the time between each download but keep the generation of 60 positions.

My solution allow an auto regulation speed of the loop. For example if I have an ultra high speee connection the download() will take 2 secondes, but if I have an ultra low speed connection it could take 9~10 seconds. Instead of use an hard coded variable the script can adapt totally to the connection speed. Done Done

  • we should *never* iterate through the whole AI list in the main thread *never* Done Done
  • we definitely should not be doing that for each client (we do that now!)
  • we should only maintain a vector/map of local clients (players!) Done Done
  • sendAITraffic should then look at this list (which is only updated by the main thread, lock held) Done Done
  • Using the positions of each local client, we should then traverse the ai_list once in the background thread
  • i.e. in recompute() - and check if any of the ai aircraft is in range of a local player
  • if it is, we need to check if we have previously computed a positionMap for that ai aircraft
  • if we don't have one, we need to create it
  • once we have a positionMap foreach aircraft in range, we need to update an vector with pointers to relevant aircraft
  • this could be in FG_Player
  • when the background thread is idle, it can update other aircraft
  • it should prioritize aircraft that are going to be in range soon
  • and then the remaining aircraft

Finally, our buffer of computed positions must not be equal to our request interval: When it only contains positions for 60 seconds, the fgms main thread inevitably needs to wait for the worker thread to fill the positionMap from scratch.

Thus, the positionMap should contain more positions.

The other problem is that our worker thread currently destroys previously computed messages stored in the positionMap. However, it would make sense to "append" new positions to the buffer. Like a FIFO queue or a vector, so that we can:

  1. fill the positionmap with messages for more than 60 seconds (e.g. 90)
  2. or start the background thread much earlier than after 60 seconds (i.e. 30)
  3. and merely create missing packets, without overwriting the old positionMap

That would ensure that the main thread has still enough data, even though our background thread is limited to run only at 1 minute intervals. Otherwise the main thread blocks, because it has to wait for new data from the positionMap.

In order to see a smooth movement of our AI traffic we need to calculate a fictive velocity, this should be done in recompute_aircraft() Done Done

Currently our extrapolation routine doesn't apply climb rate, we need to implement this.Done Done

Planned Features

Changelog

  • Start using SimGear's SGThread Done Done
  • Start using SimGear's SGMutex Done Done
  • Use a std::map for the aircraft lookup table Done Done
  • Rework URL building and move it to the constructor so that it's only done once Done Done
  • add SimGear as a dependency so that all the headers/libs can be more easily used Done Done
  • Use SGTime and SGTimeStamp now that we have full SimGear support Done Done
  • fgms still uses copies of outdated SimGear sources which depend on depreciated stuff like sgdVec3 and sgMat4 Done Done
  • make the recompute_aircraft() method send valid packets for all traffic within 100nm Done Done
  • use a default model for "unknown" aircraft (a320 for the moment) Done Done
  • Send packets to fgfs clients via sendAITraffic() method Done Done
  • stop using a leaking MsgBuf pointer for the created PositionMap packets Done Done

Work in Progress

  • actually use timeOffset in recompute_aircraft() method!
  • improve altitude computation (F-JJTH) Done Done
  • currently, we are using the hexcode as callsign to deal with identical callsigns (fg/fgms restriction)
  • Implement aircraft_recompute() method to update positions using interpolation/extrapolation 100}% completed
  • Only recompute positions once, in the FGAIS worker thread - and NOT in the fgms main thread ! 30}% completed
  • Move the vessel tracking code out into its own module 20}% completed (Hooray)
  • Implement an OOP framework for different providers 20}% completed (Hooray)
    • Data source (e.g. HTTP)
    • Parser (e.g. JSON)
    • Processor (e.g. extrapolate/interpolate)
  • Generalize the parser and move it to a separate file Pending Pending
  • Split up large methods and use helper functions 70}% completed (Hooray)
  • Use SG_LOG() instead of std::cout 40}% completed (Hooray, separate branch!)

Todo (Nobody working on any of these

  • the logstream sources in fgms were customized by fgms developers so that they cannot currently be replaced with SG_LOG from SimGear HEAD
  • make SG_LOG thread-safe and use a Mutex to serialize the output
  • use a real task scheduler and not just sleep()
  • Fix potential race conditions and other threading related issues
  • Use SimGear's HTTPRequest instead of Curl (?)
  • Use try/catch blocks (exception handling)
  • Use smart pointers
  • Fix the CMakeList.txt file (fgms is not re-linked even though libs were rebuilt)
    • move FGAIS sources to a separate sub folder
    • use separate CMakeLists.txt files for each sub folder
    • build a library for each folder
  • Use a spatial data structure for faster lookup?
  • planefinder.net send us the timestamp of the position, we should use it instead of our local timestamp

Possible Optimizations

  • Use pointers / const references
  • Implement real copy constructors
  • use a faster string implementation
  • Use a faster map implementation, i.e. unordered_map or something from boost (speed-up 2x-3x)
  • Use a spatial data structure (i.e. quadtree) to partition the space and only look up relevant aircraft
  • also use the aircraft's current position, heading and FOV to determine highly relevant aircraft
  • Provide support for multiple update/output threads to update fgfs clients in a handful of worker threads
  • run list cleanup (expired aircraft) in another worker thread
  • don't send positions but derivative information and compute positions at the client side (also requires changes in fgfs)
  • change the aircraftToPath methods to std::map<std::string ref, std::string path> Example: aircraftRef["B772"] = "AI/Aircraft/777/777-200ER-set.xml";

References

Related content

Wiki articles

Forum topics

Mailing list threads