FGAIS
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. |
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 |
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)
- http://www.flightradar24.com/
- http://planefinder.net/
- http://www.radarvirtuel.com/
- http://shipfinder.co/
- http://www.marinetraffic.com/ais/
Roadmap
Milestone 1: Move FGAIS to a background thread Done (28/10/2012)
Milestone 2: Send packets back to FlightGear Done (31/10/2012)
Milestone 3: Smooth AI traffic in Flightgear Done (01/11/2012)
Milestone 4: Continue using extrapolation when no new data is available Pending
Milestone: Use FGExternalMotionData properly 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
Milestone: Get rid of threading issues 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
- 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
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
- Altitude need to be computed with the climb rate calculated with the updateAIAircraf(); 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
- we should *never* iterate through the whole AI list in the main thread *never* 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
- sendAITraffic should then look at this list (which is only updated by the main thread, lock held) 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:
- fill the positionmap with messages for more than 60 seconds (e.g. 90)
- or start the background thread much earlier than after 60 seconds (i.e. 30)
- 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
Currently our extrapolation routine doesn't apply climb rate, we need to implement this. Done
Planned Features
Changelog
- Start using SimGear's SGThread Done
- Start using SimGear's SGMutex Done
- Use a std::map for the aircraft lookup table Done
- Rework URL building and move it to the constructor so that it's only done once Done
- add SimGear as a dependency so that all the headers/libs can be more easily used Done
- Use SGTime and SGTimeStamp now that we have full SimGear support Done
- fgms still uses copies of outdated SimGear sources which depend on depreciated stuff like sgdVec3 and sgMat4 Done
- make the recompute_aircraft() method send valid packets for all traffic within 100nm Done
- use a default model for "unknown" aircraft (a320 for the moment) Done
- Send packets to fgfs clients via sendAITraffic() method Done
- stop using a leaking MsgBuf pointer for the created PositionMap packets Done
Work in Progress
- actually use timeOffset in recompute_aircraft() method!
- improve altitude computation (F-JJTH) 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
- Only recompute positions once, in the FGAIS worker thread - and NOT in the fgms main thread !
- Move the vessel tracking code out into its own module (Hooray)
- Implement an OOP framework for different providers (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
- Split up large methods and use helper functions (Hooray)
- Use SG_LOG() instead of std::cout (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
- ↑ durk (Nov 12th, 2016). FGTraffic 2020: Road map for a new AI traffic system design .
Related content
Wiki articles
- Distributed Interactive Simulation
- Decoupling the AI Traffic System
- Multiplayer protocol
- Real Live AI traffic
Forum topics
- How does the Multiplayer Protocol work? topic on the forum (December 2014)
- Ships topic on the forum (August 2014-January 2014)
- Populate AI Traffic with real traffic topic on the forum (October 2012-May 2014)
- Real life traffic in FlightGear topic on the forum (March 2012-February 2018)
- Development Help topic on the forum (Sep 2011) - FGMS pointers
- $15 RTL2832 SDR dongles for ADS-B to get realtime traffic? topic on the forum (April 2010)
Mailing list threads
- [Flightgear-devel] Maritime traffic (December 2011)
- [Flightgear-devel] Real-time shipping traffic (November 2010-March 2011)