I think a lot of you know this kind of website who display aircraft from real world in real time. Some example :
- http://www.flightradar24.com/
- http://planefinder.net/
- http://www.radarvirtuel.com/
There is the same for ship :
- http://shipfinder.co/
- http://www.marinetraffic.com/ais/
My dream is simple : populate FG world with data from these kind of website.
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.
Now the idea is presented, I can speak a little bit about the technical problem :
1) we need to receive the data from a website who provide aircraft position : this task is really easy and I have already modified FG source code for this
2) we need to adapt aircraft data received for FG multiplayer format. Data received from website are JSON format, so we need to parse JSON data in order to exctract position, model, heading, airspeed...
3) once we have to create a network packet filled by exctracted datas and send it every 20 seconds on the Multiplayer network.
This work should be done by a special server/tool.
I have already make some experimentation : i am able to create AI aircraft in local session with data from planefinder.net. In this way I have a dozens of aircraft in my /ai/models/aircraft[n] with data refreshed every 20 seconds. Undortunately this solution doesn't create a real AI aircraft but create only a simple Node in the property tree.
The nasal code for this is :
$FG_DATA/Nasal/real_traffic.nas
- Code: Select all
#########################################################################
# Real time traffic manager. Add aircraft and ship in the world
# with data from planefinder.net
#
#
#
#
#########################################################################
props.globals.initNode("/sim/traffic-manager/real-traffic", 0, "BOOL");
props.globals.initNode("/sim/traffic-manager/aircraft-data", "", "STRING");
var aircraftLoop = func {
if(getprop("/sim/traffic-manager/real-traffic")){
var aircraftUrl = "PUT_YOUR_URL_HERE";
fgcommand("jsonhttprequest", props.Node.new({"url":aircraftUrl, "targetnode":"/sim/traffic-manager/aircraft-data"}) );
var jsonSource = getprop("/sim/traffic-manager/aircraft-data");
var jsonSplittedA = split("{", jsonSource);
var jsonSplittedB = split("}", jsonSplittedA[2]);
var jsonSplittedC = split("],", jsonSplittedB[0]);
var aircraftData = {};
var _hexcode = "";
var _type = "";
var _callsign = "";
var _latitude = "";
var _longitude = "";
var _altitude = "";
var _heading = "";
var _airspeed = "";
for(var i=0; i < size(jsonSplittedC); i=i+1){
var jsonSplittedD = split(":[", jsonSplittedC[i]);
var jsonSplittedE = split("]", jsonSplittedD[1]);
jsonSplittedD[1] = jsonSplittedE[0];
var jsonSplittedH = split('"', jsonSplittedD[0]);
_hexcode = jsonSplittedH[1];
var jsonSplittedF = split(",", jsonSplittedD[1]);
for(var z=0; z < size(jsonSplittedF); z=z+1){
var jsonSplittedG = split('"', jsonSplittedF[z]);
if(size(jsonSplittedG) > 1){
if(z == 0){
_type = jsonSplittedG[1];
}elsif(z == 1){
_callsign = jsonSplittedG[1];
}
}else{
if(z == 3){
_latitude =jsonSplittedG[0];
}elsif(z == 4){
_longitude = jsonSplittedG[0];
}elsif(z == 5){
_altitude =jsonSplittedG[0];
}elsif(z == 6){
_heading = jsonSplittedG[0];
}elsif(z == 7){
_airspeed = jsonSplittedG[0];
}
}
}
aircraftData[i] = {
hexcode:_hexcode,
type:_type,
callsign:_callsign,
latitude:_latitude,
longitude:_longitude,
altitude:_altitude,
heading:_heading,
airspeed:_airspeed
};
}
for(var y=0; y < size(aircraftData); y=y+1){
print("##### Aircraft "~y);
print("HexCode: "~aircraftData[y].hexcode);
print("Callsign: "~aircraftData[y].callsign);
print("Latitude: "~aircraftData[y].latitude);
print("Longitude: "~aircraftData[y].longitude);
print("Altitude: "~aircraftData[y].altitude);
print("Heading: "~aircraftData[y].heading);
print("Airspeed: "~aircraftData[y].airspeed);
print();
var aiPath = "/ai/models/aircraft";
props.globals.getNode(aiPath~"["~y~"]"~"/hexcode", 1).setValue(aircraftData[y].hexcode);
props.globals.getNode(aiPath~"["~y~"]"~"/callsign", 1).setValue(aircraftData[y].callsign);
props.globals.getNode(aiPath~"["~y~"]"~"/position/latitude-deg", 1).setValue(aircraftData[y].latitude);
props.globals.getNode(aiPath~"["~y~"]"~"/position/longitude-deg", 1).setValue(aircraftData[y].longitude);
props.globals.getNode(aiPath~"["~y~"]"~"/position/altitude-ft", 1).setValue(aircraftData[y].altitude);
props.globals.getNode(aiPath~"["~y~"]"~"/orientation/true-heading-deg", 1).setValue(aircraftData[y].heading);
props.globals.getNode(aiPath~"["~y~"]"~"/velocities/true-airspeed-kt", 1).setValue(aircraftData[y].airspeed);
}
}
settimer(aircraftLoop, 20);
}
var nasalInit = setlistener("/sim/signals/fdm-initialized", func{
# We sent 1 request at starting in order to fill the property
var aircraftUrl = "http://planefinder.net/endpoints/update.php?faa=1&bounds=43.765091%2C-3.155133%2C48.010089%2C10.204242&_=1348996787623";
fgcommand("jsonhttprequest", props.Node.new({"url":aircraftUrl, "targetnode":"/sim/traffic-manager/aircraft-data"}) );
settimer(aircraftLoop, 2);
removelistener(nasalInit);
});
The function added to flightgear is :
flightgear/src/Main/fg_commands.cxx
- Code: Select all
class RemoteJSONRequest : public simgear::HTTP::Request
{
public:
SGPropertyNode_ptr _complete;
SGPropertyNode_ptr _status;
SGPropertyNode_ptr _failed;
SGPropertyNode_ptr _target;
string propsData;
RemoteJSONRequest(const std::string& url, SGPropertyNode* targetNode) :
simgear::HTTP::Request(url),
_target(targetNode)
{
}
void setCompletionProp(SGPropertyNode_ptr p)
{
_complete = p;
}
void setStatusProp(SGPropertyNode_ptr p)
{
_status = p;
}
void setFailedProp(SGPropertyNode_ptr p)
{
_failed = p;
}
protected:
virtual void gotBodyData(const char* s, int n)
{
propsData += string(s, n);
}
virtual void responseComplete()
{
int response = responseCode();
bool failed = false;
if (response == 200) {
try {
const char* buffer = propsData.c_str();
_target->setStringValue(buffer);
} catch (const sg_exception &e) {
SG_LOG(SG_IO, SG_WARN, "parsing JSON from remote, failed: " << e.getFormattedMessage());
failed = true;
response = 406; // 'not acceptable', anything better?
}
} else {
failed = true;
}
// now the response data is output, signal Nasal / listeners
if (_complete) _complete->setBoolValue(true);
if (_status) _status->setIntValue(response);
if (_failed) _failed->setBoolValue(failed);
}
};
static bool
do_load_json_from_url(const SGPropertyNode * arg)
{
std::string url(arg->getStringValue("url"));
if (url.empty())
return false;
SGPropertyNode *targetnode;
if (arg->hasValue("targetnode"))
targetnode = fgGetNode(arg->getStringValue("targetnode"), true);
else
targetnode = const_cast<SGPropertyNode *>(arg)->getNode("data", true);
RemoteJSONRequest* req = new RemoteJSONRequest(url, targetnode);
// connect up optional reporting properties
if (arg->hasValue("complete"))
req->setCompletionProp(fgGetNode(arg->getStringValue("complete"), true));
if (arg->hasValue("failure"))
req->setFailedProp(fgGetNode(arg->getStringValue("failure"), true));
if (arg->hasValue("status"))
req->setStatusProp(fgGetNode(arg->getStringValue("status"), true));
FGHTTPClient::instance()->makeRequest(req);
return true;
}
If you are able to compile FG with this change and add the real_time.nas file in $FG_DATA/Nasal you just need to set the /sim/traffic-manager/real-traffic property to True and you will see your /ai/models/ tree growing with aircraft[n] who represent aircraft position currently flying in real life.
Of course this technical solution is not the good solution. The good solution is to follow instructions available at the top of my post.
I have spent a lot of time reading flightgear/src/MultiPlayer/multiplayermgr.cxx I have also read the mpdummy tools and the fgms source code, but my skills in C++ are not sufficient for create a standalone tool who create network packets ready to be sent on FG network.
Now come the questions :
- What do you think about this feature ?
- Do you think that adding real traffic in real time inside flightgear is not an interesting feature ? or per contra you thing this feature should be really great for the realism ?
- Who is able to create this kind of tool ?
Cheers,
Clément