# See: http://wiki.flightgear.org/MapStructure # Class things: ## Airbus Terrain on ND by InuYaksa*2021 ## EGPWS device - Applicable to: MSN 0112 ## Ident.: DSC-31-45-00009586.0012001 / 22 MAY 12 ## Ident.: DSC-31-45-00009586.0009001 / 08 AUG 13 ## inspired from work on 787-family - thanks a lots ## and a great help from legoboyvdlp var name = 'TERRAIN'; var parents = [DotSym]; var __self__ = caller(0)[0]; DotSym.makeinstance( name, __self__ ); var element_type = "group"; var terrain_minalt = props.globals.initNode("/instrumentation/efis[0]/nd/terrain-on-nd/min-altitude", 0,"INT"); var terrain_maxalt = props.globals.initNode("/instrumentation/efis[0]/nd/terrain-on-nd/max-altitude", -9999,"INT"); var terrain_maxcol = props.globals.initNode("/instrumentation/efis[0]/nd/terrain-on-nd/max-color", 0,"INT"); # 0= grn, 1= yel, 2= red var terrain_alert = props.globals.getNode("/instrumentation/mk-viii/outputs/alert-mode"); var tile_list = [ nil,"tile_gl.png","tile_gh.png","tile_al.png","tile_ah.png","tile_rh.png", # 0-5 low alt - imho real ND displays as amber-ish color than yellow one "tile_gl.png","tile_gh.png","tile_gh.png","tile_gs.png", # 6-9 hi alt "tile_ml.png","tile_cl.png", # 10 magenta - 11 cyan-ish/blue (water) "tile_as.png","tile_rs.png" # 12-13 alert - solid colors ]; var is_terrain = 0; var get_elevation = func (lat, lon) { var info = geodinfo(lat, lon); var elevation = 0; if (info != nil) { elevation = int(info[0] * 3.2808399); me.is_terrain = (info[1] == nil) ? 1 : info[1].solid; } else { elevation = nil; } return elevation; } var updateTerrain = func { if (me.reference == nil) return; if(me.fetching) return; me.fetching = 1; if (me.request_clear == 1) { me.request_clear = 0; me.clear(); me.group.setVisible(1); } var RAD2DEG = 57.2957795; var DEG2RAD = 0.016774532925; var pos_lat = me.reference.lat(); var pos_lon = me.reference.lon(); var heading = me.refheading; var altitudeft = me.refaltitudeft; var lowaltft = me.reflowaltft; var basealtft = me.basealtitudeft; var alert_level = me.terrain_alert.getValue(); var side = (math.mod(me.radar_beacon,2)==0) ? "L" : "R"; var a = int(me.radar_beacon/2); var col = a + 0.5; if (side == "R") { col = -col; } var trn = me.terrlayer[side ~ a]; var len = size(trn); var range = me.range; var tiles = me.tile_list; #var proj_lon = pos_lon + ((col * (range/30) * math.sin(DEG2RAD * (heading - 90))) / 40); #var proj_lat = pos_lat + ((col * (range/30) * math.cos(DEG2RAD * (heading - 90))) / 40); # if me.tileradiusw == 20 var range_20f = range / 18.75; var heading_sin = math.sin(DEG2RAD * heading); var heading_cos = math.cos(DEG2RAD * heading); var proj_lon = pos_lon + ((col * range_20f * math.sin(DEG2RAD * (heading - 90))) / 60); var proj_lat = pos_lat + ((col * range_20f * math.cos(DEG2RAD * (heading - 90))) / 60); var elevft = []; me.radar_cleared = 0; for (var row = 0; row < len; row += 1) { if (trn[row] == nil) { append(elevft,-1); continue; } var point_lon = proj_lon + ((row * range_20f / 60) * heading_sin); var point_lat = proj_lat + ((row * range_20f / 60) * heading_cos); var elev = me.get_elevation(point_lat, point_lon); var grad = 0; #black if (elev != nil) { if (elev>me.max_altitude) me.max_altitude = elev; if (me.is_terrain) { if (elev=0) { grad = int(diff/1000) + 3; if (grad>5) grad = 5; if (alert_level > 0 and a < 6 and grad > 3) { if (alert_level == 1 and (grad == 3 or grad == 4)) grad = 12; # solid yellow else if (alert_level == 2 and grad == 5) grad = 13; # solid red } } else { if (me.hialtmode == 0) { if (diff>=lowaltft) grad = 3; # lite yellow else { grad = int(diff/1000) + 2; if (grad<0) grad = 0; } } else { if (diff>=lowaltft) grad = 3; # lite yellow else { if (me.bands_range > 0 and elev > me.bands_minalt) { grad = 9 - int((me.bands_maxalt - elev) / me.bands_range); if (grad>9) grad = 9; # solid green else if (grad<6) grad = 6; #light green } } } } } } else { grad = 11; #water/cyan } append(elevft,grad); # 0-5 } else { append(elevft,0); # no data - black (magenta) } } if (me.min_altitude > me.max_altitude) me.min_altitude = me.max_altitude; # occurs on sea areas for (var r=0; r < len; r+=1) { var imgx = elevft[r]; if (imgx == -1) continue; if (imgx < 1) trn[r].hide(); else trn[r].setFile(me.imgpath ~ me.tile_list[imgx]).show(); } me.radar_beacon += 1; if (me.radar_beacon >= (me.tileradiusw*2)) { me.restart_beacon(); } me.fetching = 0; }; var update_altitudes = func { me.terrain_minalt.setValue(me.min_altitude); me.terrain_maxalt.setValue(me.max_altitude); var altdif = me.max_altitude - me.refaltitudeft; if (altdif <= 0) { if (altdif >= me.reflowaltft) me.terrain_maxcol.setValue(1); else me.terrain_maxcol.setValue(0); } else { if (altdif>2000) me.terrain_maxcol.setValue(2); else me.terrain_maxcol.setValue(1); } if (me.min_altitude == 9999) { me.avg_minalt = me.min_altitude; me.avg_maxalt = me.max_altitude; } else { #if (me.min_altitude < me.avg_minalt) me.avg_minalt = me.min_altitude;else me.avg_minalt = math.round((me.avg_minalt * 2 + me.min_altitude) / 3); #if (me.max_altitude > me.avg_maxalt) me.avg_maxalt = me.max_altitude;else me.avg_maxalt = math.round((me.avg_maxalt * 2 + me.max_altitude) / 3); } me.avg_peakalt = me.avg_maxalt - me.avg_minalt; if (me.onground == 0 and (me.max_altitude + 250) < me.refaltitudeft) { # 250 ft tollerance me.hialtmode = 1; var range = math.min(2400,(me.avg_maxalt - me.avg_minalt)) - 400; if (range < 400) { # min elev number me.bands_range = 0; } else { me.bands_range = int(range / 4); me.bands_maxalt = me.avg_maxalt; me.bands_minalt = math.max( me.avg_maxalt - range, me.avg_minalt + 400 ); } } else { me.hialtmode = 0; #me.avg_minalt = 9999; } me.min_altitude = 9999; me.max_altitude = -9999; } var restart_beacon = func { me.radar_beacon = 0; me.radar_cycle += 1; me.reference = nil; }; var init = func { #print('TERRAIN init'); me.tile = 33; me.fetching = 0; me.fetchRad = me.model.fetchRad; # Radius of radar layer to fetch me.range = me.model.rangeNm; # Range of Navigation Display me.viewport_radius = me.getOption('viewport_radius', 670); me.imgpath = get_local_path('res/terrainv2/'); me.radar_beacon = 0; me.radar_cycle = 0; me.radar_cleared = 1; me.request_clear = 0; me.min_altitude = 9999; me.max_altitude = -9999; me.avg_minalt = 9999; me.avg_maxalt = -9999; me.avg_peakalt = nil; me.maxalt_col = 0; # 0 = grn, 1 = yel, 2 = red me.bands_minalt = 0; me.bands_maxalt = 0; me.bands_range = 0; me.basealtitudeft = nil; me.reference = nil; me.onfailure = 0; me.hialtmode = 0; # high aircraft relative altitude mode me.checkarrival = 0; me.onground = 1; var tile = me.tile; var gx = int(me.viewport_radius / tile); me.tileradius = gx; var limx = int((512/tile)+0.5); # display width is smaller than height me.tileradiusw = limx; me.terrlayer = {}; var centx = 0; var centy = -me.viewport_radius; var group = me.group.createChild("group").set("z-index", -100); #me.element for (var c=0; c499) ? 400 : 140; var vspeed30s = int(getprop("velocities/vertical-speed-fps") * 30); if (vspeed30s<-500) me.refaltitudeft = math.max(me.avg_minalt , me.refaltitudeft + vspeed30s); if (me.basealtitudeft == nil) { # first basealt set me.basealtitudeft = me.groundaltft + flatalt; me.flatalt = flatalt; me.checkarrival = 1; print("set REFALT [init]: "~me.basealtitudeft); } else if (fmgc.FMGCInternal.phase < 2) { # starting at phase < 2 if (me.flatalt != flatalt) { me.basealtitudeft = me.groundaltft + flatalt; me.flatalt = flatalt; print("set REFALT [flat]: "~me.basealtitudeft); } } else if (fmgc.FMGCInternal.phase == 5) { if (me.checkarrival == 1) { me.checkarrival = 0; me.basealtitudeft = nil; if (fmgc.FMGCInternal.arrApt != nil) { var airport = airportinfo(fmgc.FMGCInternal.arrApt); if (airport != nil) me.basealtitudeft = flatalt + int(airport.elevation * M2FT); print("set REFALT [arrApt]: "~me.basealtitudeft); } if (me.basealtitudeft == nil) { me.basealtitudeft = flatalt + me.avg_minalt; # that's fun } } } else if (fmgc.FMGCInternal.phase == 6) { if (me.checkarrival == 0) { me.checkarrival = 1; #me.basealtitudeft = 0; } } else if (fmgc.FMGCInternal.phase == 7) { if (me.checkarrival == 0) { me.checkarrival = 1; me.basealtitudeft = me.groundaltft + flatalt; print("set REFALT [done]: "~me.basealtitudeft); } } else if (fmgc.FMGCInternal.phase == 2) { var expdaltft = me.groundaltft + flatalt; if (me.basealtitudeft > expdaltft) { me.basealtitudeft = expdaltft; print("set REFALT [blwbase]: "~me.basealtitudeft); } } else if (fmgc.FMGCInternal.phase >= 2) { me.basealtitudeft = math.avg(me.basealtitudeft,flatalt + me.groundaltft,int(flatalt + me.avg_minalt)); #if (me.basealtitudeft > me.avg_maxalt) { # if (me.avg_maxalt < 1000) me.basealtitudeft = 0; # 1000ft min elev number # else me.basealtitudeft = int(400 + me.avg_minalt); # print("set REFALT [blwmin]: "~me.basealtitudeft); #} #else if (me.basealtitudeft == 0 and me.avg_maxalt >= 1000) { # 1000ft min elev number # me.basealtitudeft = int(400 + me.avg_minalt); # print("set REFALT [abvmin]: "~me.basealtitudeft); #} } } } else { var range = me.layer.map.getRange(); # Range of Navigation Display var update_size = (range != me.range); me.range = range; if (update_size) { me.request_clear = 1; } me.updateTerrain(); # left me.updateTerrain(); # right } };