Hi fellow wiki editors!

To help newly registered users get more familiar with the wiki (and maybe older users too) there is now a {{Welcome to the wiki}} template. Have a look at it and feel free to add it to new users discussion pages (and perhaps your own).

I have tried to keep the template short, but meaningful. /Johan G

Real Live AI traffic

From FlightGear wiki
Jump to: navigation, search

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