Real Live AI traffic
The idea is to inject real live AI traffic by capturing data from Flightradar24 to control FlightGear AI aircraft.
This is done with a nasal timer cycle which sends a request by named pipe to a perl http loader program which returns the data from Flightradar24 to FlightGear. fg then builds a property tree of active aircraft from which we can select those to incarnate as models.
- Using the real planes interface
Scenario Loading on Demand
To get the ai models on demand I'm using a scenario template then editing it and then loading the model. CALLSIGN and PLANEFILE are replaced by the the property name and the model-file appropriate. The amended file is saved in a subfolder: /AI/rttemp. "load-scenario" command needs a unique file name for each plane and only loads from within AI folder. rttemp is a simlink to fg-home/cache/rttemp. I do this because fg 2018 allows read write access only to fg-home / cache.
This means that files will pile up in rttemp and should be deleted at the end of the session.
fgcommand("load-scenario", args);
<?xml version="1.0"?>
<PropertyList>
<scenario>
<name>Real AI</name>
<description>
template top put Ai model on demand
This file Modified from refueling_demo.xml by Dave Culp
</description>
<nasal>
</nasal>
<entry>
<callsign>CALLSIGN</callsign>
<type>aircraft</type>
<class>ufo</class>
<model>PLANEFILE</model>
<flightplan>FlightPlans/KSFO_depart_south_28L.xml</flightplan>
</entry>
</scenario>
</PropertyList>
Real Data Capture
I am using Perl but of course any suitable language would do. Input is the latitude and longitude range and a list of position altitude and speed etc is returned for each plane (looks similar to JSON format, i.e. Nasal hashes). The capture routine also collects live METAR weather. This can be easily amended for changes in the server.
#!/usr/bin/perl -w
use HTTP::Tiny;
my $infile = "/root/.fgfs/cache/pipein.txt";
my $outfile = "/root/.fgfs/cache/testpipe.txt";
my $types = "/root/.fgfs/cache/types.txt";
unlink $outfile;
unlink $infile;
`mkfifo $outfile`;
`mkfifo $infile`;
print "real traffic server initialised\n";
open(OUTFILE, ">", $outfile) or die "Could not open $outfile\n";
open(INFILE, "<", $infile) or die "Could not read $infile\n";
open(TYPES, ">", $types) or die "Could not open $types\n";
$| = 1;
my %planefixes;
my %planesLast;
my @interp;
my @prevInterp;
my $count = 0;
my $metar ="";
while(!eof(INFILE)){
$count = $count + 1;
my $row = INFILE->getline();
#print $row;
chomp $row;
@words = split(/ /, $row);
print "list $words[2]\n";
my $requesta = 'http://data-live.flightradar24.com/zones/fcgi/feed.js?bounds='.$words[1].'&faa=1&mlat=1&flarm=1&adsb=1&gnd=1&air=1&vehicles=1&estimated=0&maxage=900&gliders=1&stats=1';
my $response = HTTP::Tiny->new(timeout => 2)->get('http://data-live.flightradar24.com/zones/fcgi/feed.js?bounds='.$words[1].'&faa=1&mlat=1&flarm=1&adsb=1&gnd=1&air=1&vehicles=1&estimated=0&maxage=900&gliders=1&stats=1');
print TYPES "$requesta\n";
if ($words[3] =~ ".TXT"){
my $metarresponse = HTTP::Tiny->new(timeout => 2)->get('https://tgftp.nws.noaa.gov/data/observations/metar/stations/'.$words[3]);
if ($metarresponse->{status} == 200){
$metar = $metarresponse->{content};
$metar = (split(/\n/,$metar))[1];
}
}
if ($response->{status} != 200){
print "time\n";
print OUTFILE "time\n";
OUTFILE->flush;
next;
}
# die "Failed!\n" unless $response->{success};
print "status $response->{status} reason $response->{reason}\n";
while (my ($k, $v) = each %{$response->{headers}}) {
for (ref $v eq 'ARRAY' ? @$v : $v) {
print "$k: $_\n";
}
}
print $response->{content} if length $response->{content};
my $str = $response->{content};
my @matches = ( $str =~ /\[.*\]/g );
if(scalar @matches == 0){
print OUTFILE " ";
OUTFILE->flush;
}
foreach $a (@matches){
@datum = split(/,/,$a);
$planefixes{$datum[9]} = [];
if(!$planesLast{$datum[9]}){
$planesLast{$datum[9]} = $a;
}
print "xxxxx: $words[2] :xxxxx: $datum[9] :\n";
my $k = index($words[2], $datum[9]);
print "k:$k\n";
if (($words[2] =~ "all")||($k > -1)){
print "xxxxxxxxxxxxxxxxxx\n";
print $a;
@datumLast = split(/,/,$planesLast{$datum[9]}); # get previous for this plane
# create interpolation
# current record
$targetHdg = $datum[3];
$targetAlt = $datum[4];
$targetLat = $datum[1];
$targetLon = $datum[2];
$targetSpd = $datum[5];
# from previous record
$d1 = $datumLast[1];
$d2 = $datumLast[2];
$d4 = $datumLast[4];
$d3 = $datumLast[3];
$d5 = $datumLast[5];
print OUTFILE "$d1,$d2,$d3,$d4,$d5,$datum[6],$datum[7],$datum[8],$datum[9],$targetHdg,$targetAlt,$targetSpd,$datum[16],$targetLat,$targetLon\n";
print TYPES "$d1,$d2,$d3,$d4,$d5,$datum[6],$datum[7],$datum[8],$datum[9],$targetHdg,$targetAlt,$targetSpd,$datum[16],$targetLat,$targetLon\n";
#print TYPES $a;
TYPES->flush;
OUTFILE->flush;
print "$d1,$d2,$d3,$d4,$d5,$datum[6],$datum[7],$datum[8],$datum[9],$targetHdg,$targetAlt,$targetSpd,$datum[16]\n";
# $id = substr $datum[16], 1,3;
# $type = substr $datum[8], 1,4;
# print TYPES "$type$id\n";
# TYPES->flush;
$planesLast{$datum[9]} = $a;
}
}
print OUTFILE "end ".$metar. "\n";
OUTFILE->flush;
print TYPES "end ".$metar. "\n";
TYPES->flush;
}
To get the correct model / livery for the plane I use this hash table file: AI/rttypes.hash. The numbers after the file names 3 6 9 etc are the heights of the landing gear!
A320 "Aircraft/A320/A320-EasyJet.xml" 3 A319 "Aircraft/A319/A319-EasyJet.xml" 3 B752 "Aircraft/757/757-200-IcelandAir.xml" 3 DH8D "Aircraft/Dash8/Dash8-400-Flybe.xml" 6 A310 "Aircraft/A310/A310-AirTransat.xml" 3 A388 "Aircraft/A380/A380-Emirates.xml" 6 B738 "Aircraft/737/737-800-Qantas.xml" 9 B748 "Aircraft/747-400/747-400-CargoLux.xml" 3 SB20 "Aircraft/saab-340/REX-Saab-340.xml" 3 A332 "Aircraft/A332/A332-BritishMidland.xml" 3 A332RRR "Aircraft/A332/A332-BritishMidland.xml" A343FIN "Aircraft/A343/A343-Finnair.xml" B738RYR "Aircraft/737/737-800-Ryanair.xml" B748ABW "Aircraft/747-400/747-400-CargoLux.xml" A320EZY "Aircraft/A320/A320-EasyJet.xml" A320BAW "Aircraft/A320/A320-BritishAirways.xml" A319EZY "Aircraft/A319/A319-EasyJet.xml" A319BAW "Aircraft/A319/A319-BritishAirways.xml"
Needs more entries!
Nasal Code
The Nasal code goes in the /Nasal subfolder so that it will run at start up. A timer calls requests data from the perl server every 3 or so seconds - this can be adjusted with property /RealPlanesTune.
The children of the /RealPlanes branch of the property tree are identified by callsign. e.g. /RealPlanes/_EI_DHT and have the following elements among others:
<PropertyList>
<speed type="double">303</speed>
<alt type="int">11475</alt>
<targetalt type="int">11475</targetalt>
<targetspeed type="double">303</targetspeed>
<heading type="double">351</heading>
<targetheading type="double">351</targetheading>
<oldlat type="double">99999</oldlat>
<oldlon type="double">99999</oldlon>
<lat type="double">55.3031</lat>
<lon type="double">-3.0356</lon>
<update type="double">1483188149</update>
<model type="bool">false</model>
<icao type="string">YR9</icao>
<elev type="double">0</elev>
<clsgn type="string">EI_DHT</clsgn>
<type type="string">"B738"</type>
<deltaT type="double">3.369394064</deltaT>
</PropertyList>
If the callsign is not in the branch a new element is created The timer loop keeps these elements up to date. If deltaT the update interval becomes large the element is discarded. When the model property is set to true a listener creates the corresponding AI model scenario and removes it when model is set to false.
Here is the nasal code of the process realplanesmonitor.nas in my /Nasal folder. I've added smoothing for altitude and heading changes.
var l = nil;
var r = nil;
var approach = nil;
var altitudeup = nil;
var headingup = nil;
var landing = nil;
var takeoff = nil;
var monitortimer = nil;
var planes = {};
var plane = {lat:0,lon:0,heading:0,alt:0,callsign:0,type:0};
var path = "/root/.fgfs/cache/testpipe.txt"; # output from data server
var path2 = "/root/.fgfs/cache/testpipetest.txt"; # output from data server
var pathin = "/root/.fgfs/cache/pipein.txt"; # path input to data server
var pathin2 = "/root/.fgfs/cache/pipeintest.txt"; # path input to data server
var rtpipe = nil;
var rtpipein = nil;
var RealPlanes = props.globals.getNode("/RealPlanes",1); # real planes data
var RealPlanesAirport = props.globals.getNode("/RealPlanesAirport",1); # real planes data
var rttimer = nil;
var planefiles ={};
var planegear ={};
var planehashfile = io.open(getprop("sim/fg-root") ~"/AI/rttypes.hash","r"); # hash to identify models
var planehash ="";
var poslat = 55.0;
var poslon = -4.0;
var models = props.globals.getNode("/ai/models");
var flareListener=nil;
var flaretimer = nil;
var checkendrunwaytimer = nil;
var rtstarted = 0;
var bits = [0,0,0,0];
var updateOK=1;
var lastTime=0;
var activeList = "";
var metarinf="";
var airportname ="EGPF";
var lastairportname ="";
var icaoparam="NO";
#
# create hash table from plane hash file
#
var planehashfile = io.open(getprop("sim/fg-root") ~"/AI/rttypes.hash","r"); # hash to identify models
var planefiles ={};
while(1){
planehash = io.readln(planehashfile);
if (planehash == ""){
break;
}
if (planehash == nil){
break;
}
# print(planehash);
hashentry = split(" ",planehash);
planefiles[hashentry[0]] = split('"',hashentry[1])[1];
if (size(hashentry) == 3){
planegear[hashentry[0]] = hashentry[2];
}
}
var getBearing = func(start,end){
var startr = {};
startr.lat = start.lat * math.pi / 180 ;
startr.lon = start.lon * math.pi / 180 ;
var endr = {};
endr.lat = end.lat * math.pi / 180 ;
endr.lon = end.lon * math.pi / 180 ;
lon2 = endr.lon;
lon1 = startr.lon;
lat2 = endr.lat;
lat1 = startr.lat;
var brng = 180 * (math.atan2(math.sin(lon2-lon1)*math.cos(lat2),
math.cos(lat1)*math.sin(lat2)-math.sin(lat1)*math.cos(lat2)*math.cos(lon2-lon1)) ) / math.pi;
return brng;
}
var getDist = func(start,end){
# get shortest dist between start and end
var startr = {};
startr.lat = start.lat * math.pi / 180 ;
startr.lon = start.lon * math.pi / 180 ;
var endr = {};
endr.lat = end.lat * math.pi / 180 ;
endr.lon = end.lon * math.pi / 180 ;
var distStart = 3440.065 * 2 * math.asin(math.sqrt(math.pow((math.sin((startr.lat - endr.lat)/2)),2 )+ math.cos(startr.lat) * math.cos(endr.lat) * (math.pow(math.sin((startr.lon - endr.lon)/2),2))));
return distStart;
}
var getPointAtDistance = func(start,bearing,distance){
#
# get location from distance and bearing
#
var startr = {};
startr.lat = start.lat * math.pi / 180 ;
startr.lon = start.lon * math.pi / 180 ;
brng = bearing * math.pi / 180;
R = 3440.065;
DbyR = distance/R;
var lat2 = math.asin( math.sin(startr.lat)*math.cos(DbyR) +
math.cos(startr.lat)*math.sin(DbyR)*math.cos(brng) );
var lon2 = startr.lon + math.atan2(math.sin(brng)*math.sin(DbyR)*math.cos(startr.lat),
math.cos(DbyR)-math.sin(startr.lat)*math.sin(lat2));
var pointr = {};
pointr.lat = lat2 * 180 / math.pi;
pointr.lon = lon2 * 180 / math.pi;
return pointr;
}
var fixpitch = func(){
foreach (pname; approach.vector) {
p = RealPlanes.getChild(pname,0,0);
if(p!= nil){
if ((p.getNode("pitchcounter").getValue() < 20) and (p.getNode("pitchcounter").getValue() > 0)){
p.getNode("pitchcounter").setIntValue(p.getNode("pitchcounter").getValue() + p.getNode("pitchcounterInc").getValue());
}
p.getNode("pitch").setDoubleValue(p.getNode("pitchcounter").getValue()/2);
}
}
foreach (pname; altitudeup.vector) {
p = RealPlanes.getChild(pname,0,0);
if(p!= nil){
var inc = (p.getNode("newaltitude").getValue() - p.getNode("alt").getValue())/50;
if(math.abs(inc) < 0.1){
p.getNode("alt").setDoubleValue(p.getNode("newaltitude").getValue());
}
else{
p.getNode("alt").setDoubleValue(p.getNode("alt").getValue()+inc);
}
}
}
foreach (pname; headingup.vector) {
p = RealPlanes.getChild(pname,0,0);
if(p!= nil){
var inc = (p.getNode("newheading").getValue() - p.getNode("heading").getValue());
if (math.abs(inc) > 180){
inc = inc - 360;
}
inc = inc / 10;
if(math.abs(inc) < 0.1){
p.getNode("heading").setDoubleValue(p.getNode("newheading").getValue());
headingup.remove(pname);
}
p.getNode("heading").setDoubleValue(p.getNode("heading").getValue()+inc);
}
}
}
var guidetakeoff = func(plane){
if (plane.getNode("alt").getValue() < 1000){
var position = {};
var startrun = {};
var endrwy = {};
plane.getNode("update",1).setDoubleValue(systime());
position.lat = plane.getNode("lat").getValue();
position.lon = plane.getNode("lon").getValue();
endrwy.lat = plane.getNode("runway-end-lat").getValue();
endrwy.lon = plane.getNode("runway-end-lon").getValue();
endbrng = getBearing(position,endrwy);
startrun.lat = plane.getNode("runway-start-lat").getValue();
startrun.lon = plane.getNode("runway-start-lon").getValue();
var rundist = getDist(position,startrun);
plane.getNode("runway-distance").setDoubleValue(rundist* 1852);
plane.getNode("runway-end").setDoubleValue(getDist(position,endrwy) * 1852);
if (plane.getNode("runway-end").getValue() > 0){
# log = plane.getNode("log",1);
# # log.addChild("entry").setValue("guidetakeoff:runway-end>0 targetalt speed tspeed");
plane.getNode("targetalt").setDoubleValue(2000);
plane.getNode("speed").setDoubleValue(150);
plane.getNode("targetspeed").setDoubleValue(150);
}
}
else{
plane.getNode("takeoff-landing").setIntValue(0);
plane.getNode("sim-status").setValue("cruise");
}
}
var checkendrunway = func(plane){
if (plane != nil){
pname = plane.getName();
if(plane.getNode("input-ogl-alt").getValue() < 50){
var position = {};
var startrun = {};
var endrwy = {};
position.lat = plane.getNode("lat").getValue();
position.lon = plane.getNode("lon").getValue();
endrwy.lat = plane.getNode("runway-end-lat").getValue();
endrwy.lon = plane.getNode("runway-end-lon").getValue();
endbrng = getBearing(position,endrwy);
startrun.lat = plane.getNode("runway-start-lat").getValue();
startrun.lon = plane.getNode("runway-start-lon").getValue();
var rundist = getDist(position,startrun);
plane.getNode("runway-distance").setDoubleValue(rundist * 1852);
plane.getNode("runway-end").setDoubleValue(getDist(position,endrwy) * 1852);
# print("check "~endbrng~" reb "~plane.getNode("runway-end-bearing").getValue());
# plane.getNode("sim-status").setValue("in cer 2 "~math.abs(endbrng - plane.getNode("runway-end-bearing").getValue()));
if (math.abs(endbrng - plane.getNode("runway-end-bearing").getValue()) > 10){
plane.getNode("takeoff-landing").setIntValue(0);
plane.getNode("sim-status").setValue("off runway");
}
plane.getNode("overshoot").setBoolValue(0);
if (math.abs(endbrng - plane.getNode("runway-end-bearing").getValue()) > 90){
# print("overshoot "~endbrng);
plane.getNode("targetspeed").setDoubleValue(0);
plane.getNode("speed").setDoubleValue(0);
plane.getNode("sim-status").setValue("overshoot");
plane.getNode("overshoot").setBoolValue(1);
}
if (plane.getNode("runway-end").getValue() < 200){
# print("near end "~endbrng);
plane.getNode("sim-status").setValue("near end");
#log = plane.getNode("log",1);
# log.addChild("entry").setValue("checkendrunway:near end tspeed speed");
plane.getNode("targetspeed").setDoubleValue(0);
plane.getNode("input-speed").setDoubleValue(0);
plane.getNode("speed").setDoubleValue(0);
plane.getNode("overshoot").setBoolValue(1);
}
}
}
# }
}
var checkglide = func(plane){
var position = {};
var endrwy = {};
var startrun = {};
position.lat = plane.getNode("lat").getValue();
position.lon = plane.getNode("lon").getValue();
endrwy.lat = plane.getNode("runway-end-lat").getValue();
endrwy.lon = plane.getNode("runway-end-lon").getValue();
endbrng = getBearing(position,endrwy);
startrun.lat = plane.getNode("runway-start-lat").getValue();
startrun.lon = plane.getNode("runway-start-lon").getValue();
var rundist = getDist(position,startrun);
plane.getNode("runway-distance").setDoubleValue(rundist * 1852);
plane.getNode("runway-end").setDoubleValue(getDist(position,endrwy) * 1852);
# print("check "~endbrng~" reb "~plane.getNode("runway-end-bearing").getValue());
var startbearing = getBearing(position,startrun);
plane.getNode("runway-start-bearing",1).setDoubleValue(startbearing);
plane.getNode("runway-end-bearing").setDoubleValue(endbrng);
# print("rundist "~plane.getNode("runway-distance",1).getValue());
if (plane.getNode("runway-distance").getValue() < 8000){
plane.getNode("sim-status").setValue("run dist < 8000");
if(math.abs(startbearing - endbrng) < 90){ ## before runway start
# plane.getNode("update",1).setDoubleValue(systime());
plane.getNode("glide-counter").setIntValue(plane.getNode("glide-counter").getValue() +1);
#infoElev = plane.getNode("runway-elevation").getValue(); # in feet
# plane.getNode("elev",1).setDoubleValue(plane.getNode("runway-elevation").getValue());
# calculated alt
var tcurrentAlt = (
(plane.getNode("runway-distance").getValue() * 300 / 1852)
+ (plane.getNode("runway-elevation").getValue() )
+ plane.getNode("gearheight",1).getValue());
if((tcurrentAlt <= plane.getNode("glide-alt").getValue()) or (plane.getNode("glide-counter").getValue() == 1)){
plane.getNode("sim-status").setValue("approach glide");
# log = plane.getNode("log",1);
# log.addChild("entry").setValue("checkglide:set alt altold talt="~tcurrentAlt);
plane.getNode("targetalt").setDoubleValue( (plane.getNode("runway-elevation").getValue() )
+ plane.getNode("gearheight").getValue());
plane.getNode("newaltitude").setDoubleValue(tcurrentAlt);
altitudeup.append(plane.getName());
plane.getNode("glide-alt").setDoubleValue(tcurrentAlt);
deltaD = (getprop("/RealPlanesTune") * plane.getNode("speed").getValue() * 1.7)/20;
}
else{
plane.getNode("sim-status").setValue("approach level");
# log = plane.getNode("log",1);
# log.addChild("entry").setValue("checkglide:set alt altold talt=input-alt "~plane.getNode("input-alt",1).getValue());
plane.getNode("newaltitude").setDoubleValue((plane.getNode("glide-alt").getValue())
+ plane.getNode("gearheight").getValue());
altitudeup.append(plane.getName());
plane.getNode("targetalt").setDoubleValue( (plane.getNode("elev").getValue() )
+ plane.getNode("gearheight").getValue());
}
}
else{ #cross thresh
plane.getNode("sim-status").setValue("cross threshold");
plane.getNode("newaltitude").setDoubleValue((plane.getNode("elev").getValue() )
+ plane.getNode("gearheight").getValue());
altitudeup.append(plane.getName());
# plane.getNode("alt").setDoubleValue( (plane.getNode("elev").getValue() )
# + plane.getNode("gearheight").getValue());
plane.getNode("targetalt").setDoubleValue( (plane.getNode("elev").getValue() )
+ plane.getNode("gearheight").getValue());
}
##
## flare effect
##
if(((plane.getNode("alt",1).getValue() - plane.getNode("elev").getValue())< 100 )
# and (plane.getNode("speed",1).getValue() < 200)
# and (plane.getNode("flare").getValue()==1)
and (plane.getNode("speed",1).getValue() > 70)){
# and (plane.getNode("speed",1).getValue() < 200)
# and (plane.getNode("flare").getValue()==1)
# and (plane.getNode("speed",1).getValue() > 100)){
plane.getNode("flare").setBoolValue(0);
# print("zz"~ plane.getNode("current-speed",1).getValue());
if(approach.contains(plane.getName()) == 0){
plane.getNode("pitchcounterInc",1).setIntValue(1);
approach.append(plane.getName());
}
}
}
else{
plane.getNode("glide-counter").setIntValue(0);
plane.getNode("takeoff-landing").setIntValue(0);
plane.getNode("sim-status").setValue("airborne");
if(altitudeup.contains(plane.getName()) == 1){
altitudeup.remove(plane.getName());
}
}
}
var monitor = func(){
var RealPlanes = props.globals.getNode("/RealPlanes",1); # real planes data
var pcount = size(RealPlanes.getChildren());
foreach (plane; RealPlanes.getChildren()) {
#log = plane.getNode("log",1);
# log.addChild("entry").setValue("monitor:entry");
if (props.globals.getNode("/RealPlanesTestCallsign",1).getValue() == plane.getNode("clsgn",1).getValue()){
continue;
}
if (( plane.getNode("takeoff-landing").getValue() == 1)
and (plane.getNode("input-speed").getValue() < 20)){
plane.getNode("takeoff-landing").setIntValue(0);
plane.getNode("sim-status").setValue("Landed - Parking");
}
else{
if(altitudeup.contains(plane.getName())){
altitudeup.remove(plane.getName());
}
}
if((approach.contains(plane.getName()) == 1)
and ((plane.getNode("alt",1).getValue() - plane.getNode("elev").getValue())< 10)
and (plane.getNode("speed").getValue() <= 70)){
plane.getNode("flare").setBoolValue(0);
approach.remove(plane.getName());
if(altitudeup.contains(plane.getName())){
altitudeup.remove(plane.getName());
}
plane.getNode("pitchcounterInc").setIntValue(-1);
# plane.getNode("pitchcounter").setIntValue(0);
# plane.getNode("pitch").setDoubleValue(0);
plane.getNode("targetpitch").setDoubleValue(0);
}
if(plane.getNode("takeoff-landing").getValue() == 1){
plane.getNode("targetspeed").setDoubleValue( plane.getNode("input-target-speed").getValue());
plane.getNode("speed").setDoubleValue( plane.getNode("input-speed").getValue());
checkglide(plane);
checkendrunway(plane);
}
else{
if(plane.getNode("takeoff-landing").getValue() == 2){
plane.getNode("targetspeed").setDoubleValue( plane.getNode("input-target-speed").getValue());
plane.getNode("speed").setDoubleValue( plane.getNode("input-speed").getValue());
guidetakeoff(plane);
}
else{
if(plane.getNode("input-ogl-alt").getValue() == 0){
checkendrunway(plane);
}
# log = plane.getNode("log",1);
# log.addChild("entry").setValue("monitor:set alt altold talt");
plane.getNode("alt").setDoubleValue(plane.getNode("input-alt").getValue());
plane.getNode("newaltitude").setDoubleValue(plane.getNode("input-alt").getValue());
plane.getNode("altold").setDoubleValue(plane.getNode("input-alt").getValue());
plane.getNode("targetalt").setDoubleValue(plane.getNode("input-target-alt").getValue());
# log = plane.getNode("log",1);
# log.addChild("entry").setValue("monitor:set tspeed speed "~plane.getNode("input-speed").getValue());
if (plane.getNode("overshoot").getValue() == 0){
if((plane.getNode("input-ogl-alt").getValue() == 0)
and (plane.getNode("input-speed").getValue() < 20)){
plane.getNode("targetspeed").setDoubleValue( plane.getNode("input-target-speed").getValue()/4);
plane.getNode("speed").setDoubleValue( plane.getNode("input-speed").getValue()/4);
}
else{
plane.getNode("targetspeed").setDoubleValue( plane.getNode("input-target-speed").getValue());
plane.getNode("speed").setDoubleValue( plane.getNode("input-speed").getValue());
}
}
}
}
var t = systime();
plane.getNode("deltaT").setIntValue(t-plane.getNode("update").getValue());
# var models = props.globals.getNode("/ai/models");
## remove planes not responding
if(plane.getNode("input-ogl-alt",1).getValue() > 200 ){
if((plane.getNode("deltaT").getValue() > 6)and (updateOK)){
# in the air
plane.getNode("model",1).setBoolValue(0);
plane.getNode("sim-status",1).setValue("DELETED");
print("DELETED timeout1"~plane.getName());
# var models = props.globals.getNode("/ai/models");
# if(models.getChild("aircraft",plane.getNode("model-index").getValue(),0) != nil){
# models.getNode("model-removed").setValue(models.removeChild("aircraft",plane.getNode("model-index").getValue(),1).getPath());
# models.getNode("count").setIntValue(models.getNode("count").getValue() - 1);
# }
removelistener(plane.getNode("model-listener",1).getValue());
var k = RealPlanes.removeChildren(plane.getName());
}
}
## pause planes on ground with slow update
if(plane.getNode("input-ogl-alt",1).getValue() < 20 ){
if((plane.getNode("deltaT").getValue() > 4) and (plane.getNode("deltaT",1).getValue() < 6)){
plane.getNode("sim-status",1).setValue("PAUSED");
print("PAUSED "~plane.getName());
plane.getNode("speed").setDoubleValue(0);
plane.getNode("targetspeed").setDoubleValue(0);
plane.getNode("targetheading").setDoubleValue(plane.getNode("heading").getValue());
}
else{
#plane.getNode("sim-status",1).setValue("Parking");
}
if((plane.getNode("deltaT").getValue() >= 6) and (updateOK)){
plane.getNode("model",1).setBoolValue(0);
plane.getNode("sim-status").setValue("DELETED");
print("DELETED timeout2"~plane.getName());
removelistener(plane.getNode("model-listener",1).getValue());
var k = RealPlanes.removeChildren(plane.getName());
}
}
# check if landing
if (plane.getNode("runway-end-bearing").getValue() < 9999){
var position = {};
var endrwy = {};
var startrun = {};
position.lat = plane.getNode("lat").getValue();
position.lon = plane.getNode("lon").getValue();
endrwy.lat = plane.getNode("runway-end-lat").getValue();
endrwy.lon = plane.getNode("runway-end-lon").getValue();
endbrng = getBearing(position,endrwy);
startrun.lat = plane.getNode("runway-start-lat").getValue();
startrun.lon = plane.getNode("runway-start-lon").getValue();
startbrng = getBearing(position,startrun);
var startdist = getDist(position,startrun) * 1852;
if (startbrng < 0){
startbrng = 360 + startbrng ;
}
if (endbrng < 0){
endbrng = 360 + endbrng ;
}
plane.getNode("runway-distance").setDoubleValue(startdist);
plane.getNode("runway-end").setDoubleValue(getDist(position,endrwy) * 1852);
plane.getNode("debug-endbrng-startbrng",1).setDoubleValue((math.abs(endbrng - startbrng)));
plane.getNode("debug-heading-startbrng",1).setDoubleValue(math.abs(plane.getNode("heading").getValue() - startbrng));
if ((startdist < 9000)
and (startdist > 1000)
and (math.abs(endbrng - startbrng) < 90)
and (math.abs(plane.getNode("heading").getValue() - startbrng) < 30)
and (plane.getNode("takeoff-landing").getValue() == 0)
){
## switch landing on
# if (math.abs(endbrng - plane.getNode("runway-reference-bearing").getValue()) < 15){
plane.getNode("takeoff-landing").setIntValue(1);
plane.getNode("sim-status").setValue("landing");
plane.getNode("flare").setBoolValue(1);
}
# check if take off
plane.getNode("acceleration",1).setDoubleValue(plane.getNode("input-target-speed").getValue() - plane.getNode("input-speed").getValue());
if (( plane.getNode("takeoff-landing").getValue() == 0)
and (plane.getNode("input-speed").getValue() > 40)
and (math.abs(endbrng - startbrng) > 170)
and (plane.getNode("input-ogl-alt").getValue() < 100)
and (startdist > plane.getNode("runway-end").getValue())){
plane.getNode("takeoff-landing").setIntValue(2);
plane.getNode("sim-status").setValue("rotate");
}
}
}
}
var setAIModel = func(targetnode){
# install an model
# first find aircraft type and carrier
# use the planefiles hash to get correct plane xml
print("ZZZZtargetnode" ~ targetnode.getPath());
loadparm = targetnode;
clsgn = loadparm.getParent().getNode("clsgn").getValue();
type = loadparm.getParent().getNode("type").getValue();
icao = loadparm.getParent().getNode("icao").getValue();
type = split('"',type);
type = type[1];
print("Type "~type~icao);
planefile = planefiles[type~icao];
if(!planefile){
planefile = planefiles[type];
if(!planefile){
#planefile = "Aircraft/saab-340/REX-Saab-340.xml";
planefile = "Aircraft/E190/e190.xml";
# planefile = "Aircraft/c172/csna.xml";
}
}
#
# # create scenario file
#
var rtscen = io.readfile(getprop("sim/fg-home") ~"/cache/rttemplate.xml");
rtscen = string.replace(rtscen,"CALLSIGN",clsgn);
rtscen = string.replace(rtscen,"PLANEFILE",planefile);
scenfile = io.open(getprop("sim/fg-home") ~"/cache/rttemp/scen"~clsgn~".xml", "w");
if ((loadparm.getValue() == 1)
or ((loadparm.getParent().getNode("model-index").getValue() >-1)
and (loadparm.getValue() == 0))){
io.write(scenfile,rtscen);
io.close(scenfile);
xmlname = "rttemp/scen"~clsgn;
#
# load AI model using scenario file
# loadparm is 1 or 0 for load unload
#
var args = { 'name': xmlname,'load-property': loadparm.getPath()};
var currentCount = models.getNode("count").getValue();
print("at fgcommand");
fgcommand("load-scenario", args);
print(xmlname~" "~loadparm.getPath());
}
print("pre-binding A"~clsgn);
if (loadparm.getValue() == 1){
#
# alias model values to real downloaded values
#
foreach (m; models.getChildren("aircraft")) {
if(m == nil){
continue;
}
if(m.getNode("callsign") == nil){
continue;
}
if (m.getNode("callsign").getValue() == clsgn){
loadparm.getParent().getNode("model-index",1).setIntValue(1);
print("binding"~clsgn);
prop = props.globals.getNode("/RealPlanes/_"~clsgn~"/lat");
saveValue = prop.getValue();
prop.unalias();
prop.alias(m.getNode("position/latitude-deg"));
prop.setDoubleValue(saveValue);
prop = props.globals.getNode("/RealPlanes/_"~clsgn~"/lon");
saveValue = prop.getValue();
prop.unalias();
prop.alias(m.getNode("position/longitude-deg"));
prop.setDoubleValue(saveValue);
prop = props.globals.getNode("/RealPlanes/_"~clsgn~"/alt");
saveValue = prop.getValue();
prop.unalias();
prop.alias(m.getNode("position/altitude-ft"));
prop.setDoubleValue(saveValue);
prop = props.globals.getNode("/RealPlanes/_"~clsgn~"/targetalt");
saveValue = prop.getValue();
prop.unalias();
prop.alias(m.getNode("controls/flight/target-alt"));
prop.setIntValue(saveValue);
prop = props.globals.getNode("/RealPlanes/_"~clsgn~"/heading");
saveValue = prop.getValue();
prop.unalias();
prop.alias(m.getNode("orientation/true-heading-deg"));
prop.setIntValue(saveValue);
prop = props.globals.getNode("/RealPlanes/_"~clsgn~"/targetheading");
saveValue = prop.getValue();
prop.unalias();
prop.alias(m.getNode("controls/flight/target-hdg"));
prop.setIntValue(saveValue);
prop = props.globals.getNode("/RealPlanes/_"~clsgn~"/speed");
prop.unalias();
prop.alias(m.getNode("velocities/true-airspeed-kt"));
prop = props.globals.getNode("/RealPlanes/_"~clsgn~"/targetspeed");
prop.unalias();
prop.alias(m.getNode("controls/flight/target-spd"));
prop = props.globals.getNode("/RealPlanes/_"~clsgn~"/pitch");
prop.unalias();
prop.alias(m.getNode("orientation/pitch-deg"));
prop = props.globals.getNode("/RealPlanes/_"~clsgn~"/roll");
prop.unalias();
prop.alias(m.getNode("controls/flight/target-roll"));
prop = props.globals.getNode("/RealPlanes/_"~clsgn~"/targetpitch");
prop.unalias();
prop.alias(m.getNode("controls/flight/target-pitch"));
break;
}
}
}
else{
print("load param 0 "~clsgn);
var k = RealPlanes.removeChildren(loadparm.getParent().getName());
}
}
var realtrafficOpen = func(){
#
# open the pipes
#
if (call(io.readfile, [path2], nil, nil, var err=[]) != nil){
print("test path exists");
path = path2;
pathin = pathin2;
}
if (getprop("/RealPlanesActive") == 1){
while(rtpipe == nil){
var stat = io.stat(path);
if (stat != nil){
printf("File time: %s ", stat[9]); # prints file time
#if ((systime() - stat[9] ) < 10){
printf("opening rtpipe"); # prints file size
rtpipe = io.open(path,"r");
rtpipein = io.open(pathin,"w");
# start the cycle
rttimer.start();
}
}
}
else{
if (path == path2){
# rtpipe.close();
}
rttimer.stop();
RealPlanes.removeAllChildren();
}
}
var realtraffic = func(){
if(!rtstarted){
approach = std.Vector.new();
altitudeup = std.Vector.new();
headingup = std.Vector.new();
flaretimer = maketimer(0.05,fixpitch);
monitortimer = maketimer(1,monitor);
monitortimer.start();
flaretimer.start();
rtstarted = 1;
}
rttimer.restart(getprop("/RealPlanesTune"));
var line = '55.8662,-4.4419,95,0,14,"0625","T-EGPF13","A320","D-AIZT';
var pcount = size(RealPlanes.getChildren());
#
# get lat lon ranges for flightradar24
# taking range nm around current position
#
poslat = getprop("/position/latitude-deg");
poslon = getprop("/position/longitude-deg");
var bearing = 90;
var range = getprop("/RealPlanesRange");
var startr = {};
startr.lat = poslat ;
startr.lon = poslon ;
var brng = bearing ;
var pl = getPointAtDistance(startr,bearing,-range);
var pr = getPointAtDistance(startr,bearing,range);
var bearing = 0;
var pb = getPointAtDistance(startr,bearing,-range);
var pt = getPointAtDistance(startr,bearing,range);
#print("read "~ pt.lat~","~ pb.lat~","~ pl.lon~","~pr.lon);
##
# request data from perl server
#
printf("rtpipein set position"); # prints file size
if (getprop("/RealPlanesActiveList") == 1){
if (!(string.match(activeList, "*all,*"))){
activeList = activeList ~ "all,";
}
}
else{
activeList = string.replace(activeList,"all,","");
}
if (activeList == ""){
activeList = "all,";
}
printf("AL>"~activeList~"\n");
props.globals.getNode("/RealPlanesUpdated",1).setIntValue(0);
info = airportinfo(getprop("position/latitude-deg"),getprop("position/longitude-deg"));
if (getprop("/RealPlanesAirport") == nil){
airportname = info.id;
}
else{
airportname = getprop("/RealPlanesAirport");
}
if (airportname != lastairportname){
icaoparam = airportname~".TXT";
lastairportname = airportname;
}
else{
icaoparam = "NO";
}
io.write(rtpipein,"read "~ pt.lat~","~ pb.lat~","~ pl.lon~","~pr.lon~" "~activeList~" "~icaoparam~"\n");
io.flush(rtpipein);
#activeList ="";
#print("start loop");
#
var stat = io.stat(path);
printf("stat[9] %s systime %s",stat[9],lastTime);
if (path != path2){
if (stat[9] == lastTime){
return;
}
}
lastTime = stat[9];
setprop("/environment/metar[0]/data",metarinf);
while(1){
#
# read a batch of data from flightradar24
#
line = io.readln(rtpipe);
print(line);
if(substr(line,0,4) == "http"){
print(line);
continue;
}
if(substr(line,0,3) == "end"){
updateOK = 1; #1
print("END");
var metarinfs = (split("end ",line));
metarinf = metarinfs[1];
metarinf = string.replace(metarinf,"/"," ");
print(metarinf);
break;
}
if(line == "time"){
print("TIME");
updateOK = 0;
break;
}
if(line == "restart"){
io.close(rtpipe);
rtpipe = io.open(path,"r");
print("RESTART");
break;
}
#var RealPlanes = props.globals.getNode("/RealPlanes",1);
#var line = '55.8662,-4.4419,95,0,14,"0625","T-EGPF13","A320","D-AIZT';
bits = split(",",line);
var elevinit = 0;
var clsgnraw = bits[8];
clsgn = split('"',clsgnraw);
clsgn = split('-',clsgn[1]);
clsgn = string.join("_",clsgn);
var icao = bits[12];
icao = split('"',icao);
icao = substr(icao[1],0,3);
var elevinit=0;
#print("CLSGN"~clsgn);
if (clsgn != ""){
elev = geo.elevation(bits[13], bits[14]) ;
if(elev == nil){
elevinit = 0;
elev = 0;
}
else{
elevinit = 1;
elev = (elev * 3.28);
}
plane = RealPlanes.getNode("_"~clsgn,1);
if(elevinit == 1){
plane.getNode("elev",1).setDoubleValue(elev);
}
if(getprop("/RealPlanes/_"~clsgn~"/model") == nil){
# initialise new real plane
gearheight = 9;
type = split('"',bits[7]);
type = type[1];
if(planegear[type] != nil){
gearheight = planegear[type];
}
plane.getNode("elev",1).setDoubleValue(elev);
plane.getNode("deltaT",1).setIntValue(0);
plane.getNode("speed",1).setDoubleValue(bits[4]);
plane.getNode("speedold",1).setDoubleValue(-9999);
plane.getNode("alt",1).setDoubleValue(bits[3]);
plane.getNode("newaltitude",1).setDoubleValue(bits[3]);
plane.getNode("altold",1).setDoubleValue(99999);
plane.getNode("targetalt",1).setDoubleValue(bits[10]);
plane.getNode("targetspeed",1).setDoubleValue(bits[11]);
plane.getNode("heading",1).setDoubleValue(bits[9]);
plane.getNode("newheading",1).setDoubleValue(bits[9]);
plane.getNode("input-alt",1).setDoubleValue(50);
plane.getNode("input-target-alt",1).setDoubleValue(0);
plane.getNode("input-speed",1).setDoubleValue(0);
plane.getNode("input-target-speed",1).setDoubleValue(bits[11]);
plane.getNode("input-heading",1).setDoubleValue(bits[9]);
plane.getNode("input-target-heading",1).setDoubleValue(bits[9]);
plane.getNode("input-ogl-alt",1).setDoubleValue(bits[3]);
plane.getNode("headingold",1).setDoubleValue(bits[9]);
plane.getNode("targetheading",1).setDoubleValue(bits[9]);
plane.getNode("latold",1).setDoubleValue(99999);
plane.getNode("lonold",1).setDoubleValue(99999);
plane.getNode("lat",1).setDoubleValue(bits[0]);
plane.getNode("lon",1).setDoubleValue(bits[1]);
plane.getNode("update",1).setDoubleValue(systime());
plane.getNode("model",1).setBoolValue(0);
plane.getNode("model-index",1).setIntValue(-1);
plane.getNode("icao",1).setValue(icao);
plane.getNode("pitch",1).setDoubleValue(0);
plane.getNode("pitchcounter",1).setIntValue(1);
plane.getNode("pitchcounterInc",1).setIntValue(0);
plane.getNode("targetpitch",1).setDoubleValue(0);
plane.getNode("roll",1).setDoubleValue(0);
plane.getNode("gearheight",1).setIntValue(gearheight);
plane.getNode("clsgn",1).setValue(clsgn);
plane.getNode("type",1).setValue(bits[7]);
plane.getNode("flare-listener",1).setIntValue(0);
plane.getNode("model-listener",1).setIntValue(setlistener("/RealPlanes/_"~clsgn~"/model", func(n) {setAIModel(n)},runtime=0));
plane.getNode("flare",1).setBoolValue(0);
plane.getNode("runway-distance",1).setDoubleValue(1000);
plane.getNode("glide-counter",1).setIntValue(0);
plane.getNode("glide-alt",1).setDoubleValue(0);
plane.getNode("overshoot",1).setBoolValue(0);
plane.getNode("sim-status",1).setValue("OK");
plane.getNode("dup-data",1).setIntValue(0);
plane.getNode("runway-end",1).setDoubleValue(0);
plane.getNode("runway-end-lat",1).setDoubleValue(9999);
plane.getNode("runway-end-lon",1).setDoubleValue(9999);
plane.getNode("runway-start-lat",1).setDoubleValue(9999);
plane.getNode("runway-start-lon",1).setDoubleValue(9999);
plane.getNode("runway-end-bearing",1).setDoubleValue(0);
plane.getNode("runway-reference-bearing",1).setDoubleValue(0);
plane.getNode("runway-elevation",1).setDoubleValue(0);
plane.getNode("runway-deviation",1).setDoubleValue(0);
plane.getNode("acceleration",1).setDoubleValue(0);
plane.getNode("takeoff-landing",1).setIntValue(0);
plane.getNode("log",1);
plane.getNode("airport",1).setValue((airportinfo( plane.getNode("lat").getValue(),plane.getNode("lon").getValue())).id);
}
# update details
if (props.globals.getNode("/RealPlanesTestCallsign",1).getValue() == clsgn){
continue;
}
# duplicates?
plane.getNode("input-heading",1).setDoubleValue(bits[2]);
plane.getNode("input-target-heading",1).setDoubleValue(bits[9]);
var inLat = num(bits[0]);
var inLon = num(bits[1]);
var position = {};
position.lat = inLat;
position.lon = inLon;
plane.getNode("input-ogl-alt").setDoubleValue(bits[3]);
var noPosition = 0;
plane.getNode("input-speed").setDoubleValue(bits[4]);
plane.getNode("input-target-speed").setDoubleValue(bits[11]);
plane.getNode("input-target-alt").setDoubleValue(
bits[10]
+ plane.getNode("gearheight",1).getValue());
# + plane.getNode("elev").getValue()
plane.getNode("input-alt").setDoubleValue(
plane.getNode("input-ogl-alt").getValue()
+ plane.getNode("gearheight",1).getValue());
if(plane.getNode("input-ogl-alt").getValue() == 0){
plane.getNode("input-alt").setDoubleValue(
plane.getNode("gearheight",1).getValue()
+ plane.getNode("elev").getValue() );
}
else{
plane.getNode("input-alt").setDoubleValue(
plane.getNode("input-ogl-alt").getValue()
+ plane.getNode("gearheight",1).getValue());
}
if(getprop("/RealPlanes/_"~clsgn~"/model") == 1){
#
# AI model is active
if (!(string.match(activeList, "*"~clsgnraw~"*"))){
activeList = activeList ~ clsgnraw ~ ',';
print("AAA updated"~activeList);
}
else{
print("AAA Matched" ~ clsgnraw ~ " in "~activeList);
}
if((plane.getNode("latold").getValue() == inLat)and(plane.getNode("lonold").getValue() == inLon)){
#
# if no change in lat lon do not update
# as this will cause plane to backtrack
#
# print("lat in XX"~bits[0]~ " lat old " ~ plane.getNode("oldlat").getValue());
noPosition = 1;
if ((plane.getNode("input-ogl-alt").getValue() < 20)
and (plane.getNode("input-speed").getValue() > 20)){
plane.getNode("input-target-speed",1).setDoubleValue(20);
}
plane.getNode("dup-data",1).setIntValue(plane.getNode("dup-data").getValue() + 1);
#
}
else{
plane.getNode("dup-data",1).setIntValue(0);
}
if (plane.getNode("takeoff-landing").getValue() == 1){
# if this plane is landing (possible bad data)
# validate position also near end of runway (due to bad data)
#
var endrwy = {};
endrwy.lat = plane.getNode("runway-end-lat").getValue();
endrwy.lon = plane.getNode("runway-end-lon").getValue();
# set distance in metres from end runway
plane.getNode("runway-end").setDoubleValue(getDist(position,endrwy) * 1852);
sbearing = getBearing(position,endrwy);
lbearing = getBearing(position,endrwy);
plane.getNode("runway-deviation",1).setDoubleValue(math.abs(lbearing - plane.getNode("runway-end-bearing").getValue()));
#print("eor deviation "~plane.getNode("runway-deviation",1).getValue() "distend "~distend);
if (plane.getNode("runway-deviation",1).getValue() < 20 and (plane.getNode("runway-end").getValue() > 200)){
if(noPosition == 0){ # plausible position and heading
plane.getNode("lat").setDoubleValue(inLat);
plane.getNode("lon").setDoubleValue(inLon);
plane.getNode("latold").setDoubleValue(inLat);
plane.getNode("lonold").setDoubleValue(inLon);
}
}
if ((plane.getNode("input-speed").getValue() < 20)
){
# plane in parking area?
plane.getNode("takeoff-landing").setIntValue(0);
plane.getNode("sim-status").setValue("Parking-speed");
}
}
else{
# not landing - just update
if(noPosition == 0){ # plausible position and heading
plane.getNode("lat").setDoubleValue(inLat);
plane.getNode("lon").setDoubleValue(inLon);
plane.getNode("latold").setDoubleValue(inLat);
plane.getNode("lonold").setDoubleValue(inLon);
}
}
#
var deltaAlt = plane.getNode("targetalt").getValue() - plane.getNode("alt").getValue();
var deltaT = systime() - plane.getNode("update").getValue();
var pitch = 0;
var lastDistend=100;
var leastDist=100;
var endrwy={};
var startlat = 9999;
var startlon = 9999;
var startrun = {};
var info=nil;
if ((plane.getNode("input-ogl-alt").getValue() < 2000 ) and ( plane.getNode("input-speed").getValue() <200)) {
# assume landing if speed < 200 and lower than 2000
if(plane.getNode("runway-end-lat").getValue() == 9999){
var endbrng =9999;
plane.getNode("runway-end-bearing").setDoubleValue(endbrng);
info = airportinfo(position.lat,position.lon);
foreach(var rwy; keys(info.runways)){
var endr = {};
endr.lat = info.runways[rwy].lat;
endr.lon = info.runways[rwy].lon;
var startbearing = getBearing(position,endr);
var heading = plane.getNode("input-heading").getValue();
var startdev = math.abs(startbearing - heading);
if (startdev > 180){
startdev = math.abs(360 - startdev);
}
if (startdev > 90){
startdev = math.abs(180 - startdev);
}
if (startdev < 10){
var dist = getDist(position,endr);
var length = info.runways[rwy].length ;
if (dist < leastDist){
leastDist = dist;
rwylength = length;
endrwy = getPointAtDistance(endr,info.runways[rwy].heading,length/1852);
startlat = endr.lat ;
startlon = endr.lon ;
endbrng = getBearing(endr,endrwy);
}
}
}
if (endbrng < 9999){
plane.getNode("runway-elevation").setDoubleValue(info.elevation * 3.28);
plane.getNode("runway-end-lat").setDoubleValue(endrwy.lat);
plane.getNode("runway-end-lon").setDoubleValue(endrwy.lon);
plane.getNode("runway-start-lat").setDoubleValue(startlat);
plane.getNode("runway-start-lon").setDoubleValue(startlon);
plane.getNode("runway-distance",1).setDoubleValue(leastDist);
plane.getNode("runway-end-bearing").setDoubleValue(endbrng);
plane.getNode("runway-reference-bearing").setDoubleValue(endbrng);
}
}
}
# bank on turns
if(plane.getNode("alt").getValue() > (70 + plane.getNode("elev").getValue()) ){
var heading = plane.getNode("input-heading").getValue();
var targetHeading = plane.getNode("input-target-heading").getValue();
if (heading > 180){
heading = heading - 360;
}
if (targetHeading > 180){
targetHeading = targetHeading - 360;
}
plane.getNode("roll").setDoubleValue((targetHeading - heading));
}
else{
plane.getNode("roll").setDoubleValue(0);
}
}
plane.getNode("update").setDoubleValue(systime());
var oldheading = plane.getNode("heading",1).getValue();
var diff = math.abs(oldheading - plane.getNode("input-heading").getValue());
if (diff > 180){
diff = 360 - diff;
}
if(plane.getNode("takeoff-landing").getValue() == 1){
if(plane.getNode("alt").getValue() < (70 + plane.getNode("elev").getValue()) ){
if( plane.getNode("input-speed").getValue() > 60){
# check heading valid
if(math.abs(plane.getNode("input-heading").getValue() - plane.getNode("runway-reference-bearing").getValue()) < 10){
plane.getNode("heading").setDoubleValue(plane.getNode("input-heading").getValue());
plane.getNode("targetheading",1).setDoubleValue(plane.getNode("input-target-heading").getValue());
}
else{
plane.getNode("input-speed").setDoubleValue(49);
plane.getNode("input-target-speed").setDoubleValue(49);
}
}
}
else{
if( plane.getNode("input-speed").getValue() > 0){
plane.getNode("newheading").setDoubleValue(plane.getNode("input-target-heading").getValue());
headingup.append(plane.getName());
}
plane.getNode("targetheading",1).setDoubleValue(plane.getNode("input-target-heading").getValue());
}
}
else{
if( plane.getNode("input-speed").getValue() > 30){
# # check heading valid
# if(math.abs(plane.getNode("input-heading").getValue() - plane.getNode("runway-reference-bearing")) < 10){
plane.getNode("heading").setDoubleValue(plane.getNode("input-heading").getValue());
plane.getNode("targetheading",1).setDoubleValue(plane.getNode("input-target-heading").getValue());
# }
}
else{
if( plane.getNode("input-speed").getValue() > 0){
plane.getNode("newheading").setDoubleValue(plane.getNode("input-target-heading").getValue());
headingup.append(plane.getName());
}
plane.getNode("targetheading",1).setDoubleValue(plane.getNode("input-target-heading").getValue());
# log = plane.getNode("log",1);
# log.addChild("entry").setValue("main:<=20 setheading");
}
}
}
if (getprop("/RealPlanesAutoModel")){
foreach (plane; RealPlanes.getChildren()) {
plane.getNode("model",1).setBoolValue(1);
}
}
}
# props.globals.getNode("/RealPlanesActiveList",1).setIntValue(0);
props.globals.getNode("/RealPlanesUpdated",1).setIntValue(1);
}
var manualstart = func{
approach = std.Vector.new();
altitudeup = std.Vector.new();
rttimer = maketimer(5,realtraffic);
#datatimer = maketimer(5
flaretimer = maketimer(1,fixpitch);
flaretimer.start();
var RealPlanesActive = props.globals.getNode("/RealPlanesActive",1);
props.globals.getNode("/RealPlanesTune",1).setDoubleValue(3);
RealPlanesActive.setBoolValue(0);
props.globals.getNode("/RealPlanesAutoModel",1).setBoolValue(0);
props.globals.getNode("/RealPlanesRange",1).setIntValue(40);
props.globals.getNode("/RealPlanesTestCallsign",1).setValue("");
setlistener("/RealPlanesActive", realtrafficOpen);
print("realplanesmonitor.nas loaded");
}
_setlistener("/sim/signals/nasal-dir-initialized", func {
# io.load_nasal(getprop("/sim/fg-root") ~ "/Nasal/realplanesmonitor.nas", "example");
# example.manualstart();
rttimer = maketimer(5,realtraffic);
var RealPlanesActive = props.globals.getNode("/RealPlanesActive",1);
props.globals.getNode("/RealPlanesTune",1).setDoubleValue(4);
RealPlanesActive.setBoolValue(0);
props.globals.getNode("/RealPlanesAutoModel",1).setBoolValue(0);
props.globals.getNode("/RealPlanesRange",1).setIntValue(40);
props.globals.getNode("/RealPlanesTestCallsign",1).setValue("");
setlistener("/RealPlanesActive", realtrafficOpen);
print("realplanesmonitor.nas loaded");
});
var alert = func(message){
setprop("/sim/freeze/master",1);
canvas.MessageBox.information(
"alert",
message,
cb = nil,
buttons = canvas.MessageBox.Ok
);
setprop("/sim/freeze/master",0);
}
Code Snippets
This is a checklist which I hope is complete:
Things to install
/AI/rttemplate.xml as shown in Scenario Loading on Demand A perl or other standalone executable interface as in Real Data Capture
/AI/rttypes.hash as shown in Real Data Capture
/Nasal/realplanemonitor.nas as in Nasal Code
/Nasal/canvas/gui/dialogs/RealPlanes.nas in Canvas Control Panel
amend /Nasal/canvas/gui/widgets/Button.nas in Canvas Control Panel
amend /gui/menubar.xml in Canvas Control Panel
amend your /Aircraft/name/aircraft-set.xml as in Extra Views
add /Aircraft/name/Nasal/viewhelper.nas as in Extra Views
References
References
|
Related content
Wiki artices
Forum topics
- How does the Multiplayer Protocol work? topic on the forum (December 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)
- $15 RTL2832 SDR dongles for ADS-B to get realtime traffic? topic on the forum (April 2010)