36
edits
No edit summary |
|||
Line 10: | Line 10: | ||
== Scenario Loading on Demand == | == 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. This means that files will pile up in rttemp and should be deleted at the end of the session. | 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. | |||
<syntaxhighlight lang="nasal"> | <syntaxhighlight lang="nasal"> | ||
Line 52: | Line 56: | ||
I am using Perl but of course any suitable language would do. | 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). | 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. | |||
<syntaxhighlight lang="perl"> | <syntaxhighlight lang="perl"> | ||
#!/usr/bin/perl -w | #!/usr/bin/perl -w | ||
use HTTP::Tiny; | use HTTP::Tiny; | ||
my $infile = "/root/.fgfs/cache/pipein.txt"; | |||
my $infile = "/ | my $outfile = "/root/.fgfs/cache/testpipe.txt"; | ||
my $outfile = "/ | my $types = "/root/.fgfs/cache/types.txt"; | ||
my $types = "/ | |||
unlink $outfile; | unlink $outfile; | ||
unlink $infile; | unlink $infile; | ||
Line 75: | Line 78: | ||
my @prevInterp; | my @prevInterp; | ||
my $count = 0; | my $count = 0; | ||
my $metar =""; | |||
while(!eof(INFILE)){ | while(!eof(INFILE)){ | ||
$count = $count + 1; | |||
my $row = INFILE->getline(); | my $row = INFILE->getline(); | ||
#print $row; | #print $row; | ||
chomp $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'); | 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){ | if ($response->{status} != 200){ | ||
Line 118: | Line 126: | ||
foreach $a (@matches){ | foreach $a (@matches){ | ||
@datum = split(/,/,$a); | @datum = split(/,/,$a); | ||
Line 127: | Line 134: | ||
$planesLast{$datum[9]} = $a; | $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 | @datumLast = split(/,/,$planesLast{$datum[9]}); # get previous for this plane | ||
# create interpolation | # create interpolation | ||
Line 154: | Line 168: | ||
# TYPES->flush; | # TYPES->flush; | ||
$planesLast{$datum[9]} = $a; | $planesLast{$datum[9]} = $a; | ||
} | |||
} | } | ||
print OUTFILE "end\n"; | print OUTFILE "end ".$metar. "\n"; | ||
OUTFILE->flush; | OUTFILE->flush; | ||
print TYPES "end\n"; | print TYPES "end ".$metar. "\n"; | ||
TYPES->flush; | TYPES->flush; | ||
} | } | ||
Line 164: | Line 180: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 239: | Line 256: | ||
var planes = {}; | var planes = {}; | ||
var plane = {lat:0,lon:0,heading:0,alt:0,callsign:0,type:0}; | var plane = {lat:0,lon:0,heading:0,alt:0,callsign:0,type:0}; | ||
var path = "/ | var path = "/root/.fgfs/cache/testpipe.txt"; # output from data server | ||
var pathin = "/ | 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 rtpipe = nil; | ||
var rtpipein = nil; | var rtpipein = nil; | ||
var RealPlanes = props.globals.getNode("/RealPlanes",1); # real planes data | var RealPlanes = props.globals.getNode("/RealPlanes",1); # real planes data | ||
var RealPlanesAirport = props.globals.getNode("/RealPlanesAirport",1); # real planes data | |||
var rttimer = nil; | var rttimer = nil; | ||
var planefiles ={}; | var planefiles ={}; | ||
Line 259: | Line 279: | ||
var updateOK=1; | var updateOK=1; | ||
var lastTime=0; | var lastTime=0; | ||
var activeList = ""; | |||
var metarinf=""; | |||
var airportname ="EGPF"; | |||
var lastairportname =""; | |||
var icaoparam="NO"; | |||
# | # | ||
# create hash table from plane hash file | # create hash table from plane hash file | ||
Line 335: | Line 360: | ||
p = RealPlanes.getChild(pname,0,0); | p = RealPlanes.getChild(pname,0,0); | ||
if(p!= nil){ | if(p!= nil){ | ||
if (p.getNode("pitchcounter").getValue() < 20){ | if ((p.getNode("pitchcounter").getValue() < 20) and (p.getNode("pitchcounter").getValue() > 0)){ | ||
p.getNode("pitchcounter").setIntValue(p.getNode("pitchcounter").getValue()+ | p.getNode("pitchcounter").setIntValue(p.getNode("pitchcounter").getValue() + p.getNode("pitchcounterInc").getValue()); | ||
} | } | ||
p.getNode("pitch").setDoubleValue(p.getNode("pitchcounter").getValue()/2); | p.getNode("pitch").setDoubleValue(p.getNode("pitchcounter").getValue()/2); | ||
Line 557: | Line 582: | ||
## flare effect | ## flare effect | ||
## | ## | ||
if(((plane.getNode("alt",1).getValue() - plane.getNode("elev").getValue())< 200 ) | if(((plane.getNode("alt",1).getValue() - plane.getNode("elev").getValue())< 100 ) | ||
and (plane.getNode("speed",1).getValue() < 200) | # and (plane.getNode("speed",1).getValue() < 200) | ||
and (plane.getNode("flare").getValue()==1) | # and (plane.getNode("flare").getValue()==1) | ||
and (plane.getNode("speed",1).getValue() > 100)){ | 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); | plane.getNode("flare").setBoolValue(0); | ||
# print("zz"~ plane.getNode("current-speed",1).getValue()); | # print("zz"~ plane.getNode("current-speed",1).getValue()); | ||
if(approach.contains(plane.getName()) == 0){ | if(approach.contains(plane.getName()) == 0){ | ||
plane.getNode("pitchcounterInc",1).setIntValue(1); | |||
approach.append(plane.getName()); | approach.append(plane.getName()); | ||
} | } | ||
Line 602: | Line 636: | ||
} | } | ||
if((approach.contains(plane.getName()) == 1) | if((approach.contains(plane.getName()) == 1) | ||
and (plane.getNode("speed").getValue() <= | and ((plane.getNode("alt",1).getValue() - plane.getNode("elev").getValue())< 10) | ||
and (plane.getNode("speed").getValue() <= 70)){ | |||
plane.getNode("flare").setBoolValue(0); | plane.getNode("flare").setBoolValue(0); | ||
approach.remove(plane.getName()); | approach.remove(plane.getName()); | ||
Line 608: | Line 643: | ||
altitudeup.remove(plane.getName()); | altitudeup.remove(plane.getName()); | ||
} | } | ||
plane.getNode("pitchcounter").setIntValue(0); | plane.getNode("pitchcounterInc").setIntValue(-1); | ||
# plane.getNode("pitchcounter").setIntValue(0); | |||
# plane.getNode("pitch").setDoubleValue(0); | |||
plane.getNode("targetpitch").setDoubleValue(0); | plane.getNode("targetpitch").setDoubleValue(0); | ||
} | } | ||
Line 768: | Line 804: | ||
print(targetnode.getPath()); | print("ZZZZtargetnode" ~ targetnode.getPath()); | ||
loadparm = targetnode; | loadparm = targetnode; | ||
clsgn = loadparm.getParent().getNode("clsgn").getValue(); | clsgn = loadparm.getParent().getNode("clsgn").getValue(); | ||
Line 793: | Line 829: | ||
# | # | ||
var rtscen = io.readfile(getprop("sim/fg- | var rtscen = io.readfile(getprop("sim/fg-home") ~"/cache/rttemplate.xml"); | ||
rtscen = string.replace(rtscen,"CALLSIGN",clsgn); | rtscen = string.replace(rtscen,"CALLSIGN",clsgn); | ||
rtscen = string.replace(rtscen,"PLANEFILE",planefile); | rtscen = string.replace(rtscen,"PLANEFILE",planefile); | ||
scenfile = io.open(getprop("sim/fg- | scenfile = io.open(getprop("sim/fg-home") ~"/cache/rttemp/scen"~clsgn~".xml", "w"); | ||
Line 814: | Line 850: | ||
var args = { 'name': xmlname,'load-property': loadparm.getPath()}; | var args = { 'name': xmlname,'load-property': loadparm.getPath()}; | ||
var currentCount = models.getNode("count").getValue(); | var currentCount = models.getNode("count").getValue(); | ||
print("at fgcommand"); | |||
fgcommand("load-scenario", args); | fgcommand("load-scenario", args); | ||
print(xmlname~" "~loadparm.getPath()); | |||
} | } | ||
print("pre-binding A"~clsgn); | print("pre-binding A"~clsgn); | ||
if (loadparm.getValue() == 1){ | if (loadparm.getValue() == 1){ | ||
# | # | ||
Line 906: | Line 950: | ||
# open the pipes | # 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){ | if (getprop("/RealPlanesActive") == 1){ | ||
while(rtpipe == nil){ | |||
rtpipe = io.open(path,"r"); | |||
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{ | else{ | ||
#rtpipe.close(); | if (path == path2){ | ||
# rtpipe.close(); | |||
} | |||
rttimer.stop(); | rttimer.stop(); | ||
Line 962: | Line 1,029: | ||
# | # | ||
printf("rtpipein set position"); # prints file size | |||
io.write(rtpipein,"read "~ pt.lat~","~ pb.lat~","~ pl.lon~","~pr.lon~"\n"); | 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); | io.flush(rtpipein); | ||
#activeList =""; | |||
#print("start loop"); | #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){ | while(1){ | ||
# | # | ||
# read a batch of data from flightradar24 | # read a batch of data from flightradar24 | ||
# | # | ||
line = io.readln(rtpipe); | |||
if(line == "end"){ | print(line); | ||
if(substr(line,0,4) == "http"){ | |||
print(line); | |||
continue; | |||
} | |||
if(substr(line,0,3) == "end"){ | |||
updateOK = 1; #1 | updateOK = 1; #1 | ||
print("END"); | print("END"); | ||
var metarinfs = (split("end ",line)); | |||
metarinf = metarinfs[1]; | |||
metarinf = string.replace(metarinf,"/"," "); | |||
print(metarinf); | |||
break; | break; | ||
} | } | ||
Line 984: | Line 1,107: | ||
break; | break; | ||
} | } | ||
if(line == "restart"){ | |||
io.close(rtpipe); | |||
rtpipe = io.open(path,"r"); | |||
print("RESTART"); | |||
break; | |||
} | |||
#var RealPlanes = props.globals.getNode("/RealPlanes",1); | #var RealPlanes = props.globals.getNode("/RealPlanes",1); | ||
Line 991: | Line 1,120: | ||
var elevinit = 0; | var elevinit = 0; | ||
var | var clsgnraw = bits[8]; | ||
clsgn = split('"', | clsgn = split('"',clsgnraw); | ||
clsgn = split('-',clsgn[1]); | clsgn = split('-',clsgn[1]); | ||
clsgn = string.join("_",clsgn); | clsgn = string.join("_",clsgn); | ||
Line 1,000: | Line 1,129: | ||
var elevinit=0; | var elevinit=0; | ||
print("CLSGN"~clsgn); | #print("CLSGN"~clsgn); | ||
if (clsgn != ""){ | if (clsgn != ""){ | ||
Line 1,010: | Line 1,139: | ||
else{ | else{ | ||
elevinit = 1; | elevinit = 1; | ||
elev = (elev *3.28); | elev = (elev * 3.28); | ||
} | } | ||
plane = RealPlanes.getNode("_"~clsgn,1); | plane = RealPlanes.getNode("_"~clsgn,1); | ||
Line 1,018: | Line 1,147: | ||
if(getprop("/RealPlanes/_"~clsgn~"/model") == nil){ | if(getprop("/RealPlanes/_"~clsgn~"/model") == nil){ | ||
# initialise new real plane | # initialise new real plane | ||
gearheight = | gearheight = 9; | ||
type = split('"',bits[7]); | type = split('"',bits[7]); | ||
type = type[1]; | type = type[1]; | ||
Line 1,057: | Line 1,186: | ||
plane.getNode("icao",1).setValue(icao); | plane.getNode("icao",1).setValue(icao); | ||
plane.getNode("pitch",1).setDoubleValue(0); | plane.getNode("pitch",1).setDoubleValue(0); | ||
plane.getNode("pitchcounter",1).setIntValue(0); | plane.getNode("pitchcounter",1).setIntValue(1); | ||
plane.getNode("pitchcounterInc",1).setIntValue(0); | |||
plane.getNode("targetpitch",1).setDoubleValue(0); | plane.getNode("targetpitch",1).setDoubleValue(0); | ||
plane.getNode("roll",1).setDoubleValue(0); | plane.getNode("roll",1).setDoubleValue(0); | ||
Line 1,084: | Line 1,214: | ||
plane.getNode("takeoff-landing",1).setIntValue(0); | plane.getNode("takeoff-landing",1).setIntValue(0); | ||
plane.getNode("log",1); | plane.getNode("log",1); | ||
plane.getNode("airport",1).setValue((airportinfo( plane.getNode("lat").getValue(),plane.getNode("lon").getValue())).id); | |||
} | } | ||
Line 1,133: | Line 1,265: | ||
# | # | ||
# AI model is active | # AI model is active | ||
if (!(string.match(activeList, "*"~clsgnraw~"*"))){ | |||
print( | 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((plane.getNode("latold").getValue() == inLat)and(plane.getNode("lonold").getValue() == inLon)){ | ||
Line 1,219: | Line 1,356: | ||
# assume landing if speed < 200 and lower than 2000 | # 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); | |||
} | |||
} | |||
} | } | ||
Line 1,274: | Line 1,410: | ||
targetHeading = targetHeading - 360; | targetHeading = targetHeading - 360; | ||
} | } | ||
plane.getNode("roll").setDoubleValue((targetHeading - heading)); | plane.getNode("roll").setDoubleValue((targetHeading - heading)); | ||
Line 1,297: | Line 1,431: | ||
if(plane.getNode("takeoff-landing").getValue() == 1){ | if(plane.getNode("takeoff-landing").getValue() == 1){ | ||
if(plane.getNode("alt").getValue() < (70 + plane.getNode("elev").getValue()) ){ | if(plane.getNode("alt").getValue() < (70 + plane.getNode("elev").getValue()) ){ | ||
if( plane.getNode("input-speed").getValue() > | if( plane.getNode("input-speed").getValue() > 60){ | ||
# check heading valid | # check heading valid | ||
if(math.abs(plane.getNode("input-heading").getValue() - plane.getNode("runway-reference-bearing").getValue()) < 10){ | if(math.abs(plane.getNode("input-heading").getValue() - plane.getNode("runway-reference-bearing").getValue()) < 10){ | ||
Line 1,340: | Line 1,474: | ||
} | } | ||
if (getprop("/RealPlanesAutoModel")){ | if (getprop("/RealPlanesAutoModel")){ | ||
Line 1,347: | Line 1,481: | ||
} | } | ||
} | } | ||
} | |||
# props.globals.getNode("/RealPlanesActiveList",1).setIntValue(0); | |||
props.globals.getNode("/RealPlanesUpdated",1).setIntValue(1); | |||
} | } | ||
var manualstart = func{ | var manualstart = func{ | ||
Line 1,352: | Line 1,490: | ||
altitudeup = std.Vector.new(); | altitudeup = std.Vector.new(); | ||
rttimer = maketimer(5,realtraffic); | rttimer = maketimer(5,realtraffic); | ||
#datatimer = maketimer(5 | |||
flaretimer = maketimer(1,fixpitch); | flaretimer = maketimer(1,fixpitch); | ||
flaretimer.start(); | flaretimer.start(); | ||
Line 1,393: | Line 1,532: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Line 1,969: | Line 1,556: | ||
add /Aircraft/name/Nasal/viewhelper.nas as in '''Extra Views''' | add /Aircraft/name/Nasal/viewhelper.nas as in '''Extra Views''' | ||
== References == | == References == | ||
{{Appendix}} | {{Appendix}} |
edits