Add experimental TCAS layer which should not negatively impact performance like the last one by tdammers
This commit is contained in:
parent
34f949f937
commit
4a8dd54f9e
5 changed files with 378 additions and 0 deletions
|
@ -4694,6 +4694,11 @@
|
||||||
<file>Aircraft/A320-family/Nasal/MCDU/DATA2.nas</file>
|
<file>Aircraft/A320-family/Nasal/MCDU/DATA2.nas</file>
|
||||||
<file>Aircraft/A320-family/Nasal/MCDU/STATUS.nas</file>
|
<file>Aircraft/A320-family/Nasal/MCDU/STATUS.nas</file>
|
||||||
</mcdu>
|
</mcdu>
|
||||||
|
<!-- Traffic layer from E-jet -->
|
||||||
|
<traffic>
|
||||||
|
<file>Aircraft/A320-family/Nasal/Displays/projection.nas</file>
|
||||||
|
<file>Aircraft/A320-family/Nasal/Displays/traffic.nas</file>
|
||||||
|
</traffic>
|
||||||
<!-- Canvas -->
|
<!-- Canvas -->
|
||||||
<canvas_pfd>
|
<canvas_pfd>
|
||||||
<file>Aircraft/A320-family/Models/Instruments/PFD/PFD.nas</file>
|
<file>Aircraft/A320-family/Models/Instruments/PFD/PFD.nas</file>
|
||||||
|
|
|
@ -338,6 +338,41 @@ setlistener("sim/signals/fdm-initialized", func {
|
||||||
ND_2 = canvas_ND_2.new(group_nd2);
|
ND_2 = canvas_ND_2.new(group_nd2);
|
||||||
ND_2_test = canvas_ND_2_test.new(group_nd2_test, "Aircraft/A320-family/Models/Instruments/Common/res/du-test.svg");
|
ND_2_test = canvas_ND_2_test.new(group_nd2_test, "Aircraft/A320-family/Models/Instruments/Common/res/du-test.svg");
|
||||||
|
|
||||||
|
setlistener("/instrumentation/efis[0]/inputs/range-nm", func() {
|
||||||
|
canvas_nd.ND_1.NDCpt.trafficLayer.camera.range = getprop("/instrumentation/efis[0]/inputs/range-nm");
|
||||||
|
}, 1, 0);
|
||||||
|
|
||||||
|
setlistener("/instrumentation/efis[1]/inputs/range-nm", func() {
|
||||||
|
canvas_nd.ND_2.NDFo.trafficLayer.camera.range = getprop("/instrumentation/efis[1]/inputs/range-nm");
|
||||||
|
}, 1, 0);
|
||||||
|
|
||||||
|
setlistener("/instrumentation/efis[0]/inputs/nd-centered", func() {
|
||||||
|
canvas_nd.ND_1.NDCpt.trafficLayer.camera.screenRange = getprop("/instrumentation/efis[0]/inputs/nd-centered") ? 436.8545 : 710;
|
||||||
|
canvas_nd.ND_1.NDCpt.trafficLayer.camera.screenCY = getprop("/instrumentation/efis[0]/inputs/nd-centered") ? 512 : 850;
|
||||||
|
}, 1, 0);
|
||||||
|
|
||||||
|
setlistener("/instrumentation/efis[1]/inputs/nd-centered", func() {
|
||||||
|
canvas_nd.ND_2.NDFo.trafficLayer.camera.screenRange = getprop("/instrumentation/efis[1]/inputs/nd-centered") ? 436.8545 : 710;
|
||||||
|
canvas_nd.ND_2.NDFo.trafficLayer.camera.screenCY = getprop("/instrumentation/efis[1]/inputs/nd-centered") ? 512 : 850;
|
||||||
|
}, 1, 0);
|
||||||
|
|
||||||
|
setlistener("/instrumentation/tcas/inputs/mode", func() {
|
||||||
|
if (getprop("/instrumentation/efis[1]/nd/canvas-display-mode") != "PLAN") {
|
||||||
|
canvas_nd.ND_1.NDCpt.trafficGroup.setVisible(pts.Instrumentation.TCAS.Inputs.mode.getValue() >= 2 ? 1 : 0);
|
||||||
|
}
|
||||||
|
if (getprop("/instrumentation/efis[1]/nd/canvas-display-mode") != "PLAN") {
|
||||||
|
canvas_nd.ND_2.NDFo.trafficGroup.setVisible(pts.Instrumentation.TCAS.Inputs.mode.getValue() >= 2 ? 1 : 0);
|
||||||
|
}
|
||||||
|
}, 1, 0);
|
||||||
|
|
||||||
|
setlistener("/instrumentation/efis[0]/nd/canvas-display-mode", func() {
|
||||||
|
canvas_nd.ND_1.NDCpt.trafficGroup.setVisible(getprop("/instrumentation/efis[0]/nd/canvas-display-mode") == "PLAN" ? 0 : 1);
|
||||||
|
}, 1, 0);
|
||||||
|
|
||||||
|
setlistener("/instrumentation/efis[1]/nd/canvas-display-mode", func() {
|
||||||
|
canvas_nd.ND_2.NDFo.trafficGroup.setVisible(getprop("/instrumentation/efis[1]/nd/canvas-display-mode") == "PLAN" ? 0 : 1);
|
||||||
|
}, 1, 0);
|
||||||
|
|
||||||
nd_update.start();
|
nd_update.start();
|
||||||
if (getprop("systems/acconfig/options/nd-rate") > 1) {
|
if (getprop("systems/acconfig/options/nd-rate") > 1) {
|
||||||
rateApply();
|
rateApply();
|
||||||
|
|
|
@ -234,6 +234,17 @@ canvas.NavDisplay.newMFD = func(canvas_group, parent=nil, nd_options=nil, update
|
||||||
event_handler();
|
event_handler();
|
||||||
} # foreach layer
|
} # foreach layer
|
||||||
|
|
||||||
|
me.mapCamera = traffic.Camera.new({
|
||||||
|
range: 20,
|
||||||
|
screenRange: 436.8545,
|
||||||
|
screenCX: 512,
|
||||||
|
screenCY: 512,
|
||||||
|
});
|
||||||
|
me.trafficGroup = me.nd.createChild("group");
|
||||||
|
me.trafficLayer = traffic.TrafficLayer.new(me.mapCamera, me.trafficGroup);
|
||||||
|
me.trafficLayer.start();
|
||||||
|
me.trafficGroup.set("z-index", -1);
|
||||||
|
|
||||||
#print("navdisplay.mfd:ND layer setup completed");
|
#print("navdisplay.mfd:ND layer setup completed");
|
||||||
|
|
||||||
# TODO: move this to RTE.lcontroller ?
|
# TODO: move this to RTE.lcontroller ?
|
||||||
|
@ -391,6 +402,13 @@ canvas.NavDisplay.update = func() # FIXME: This stuff is still too aircraft spec
|
||||||
else
|
else
|
||||||
me.map.setTranslation(512,824);
|
me.map.setTranslation(512,824);
|
||||||
}
|
}
|
||||||
|
me.mapCamera.repositon(geo.aircraft_position(), me.aircraft_source.get_hdg_tru());
|
||||||
|
me.pos = props.globals.getNode("position");
|
||||||
|
me.trafficLayer.setRefAlt(me.pos.getValue("altitude-ft"));
|
||||||
|
if (me.trafficGroup.getVisible()) {
|
||||||
|
me.trafficLayer.update();
|
||||||
|
me.trafficLayer.redraw();
|
||||||
|
}
|
||||||
var vor1_path = "/instrumentation/nav[2]";
|
var vor1_path = "/instrumentation/nav[2]";
|
||||||
var vor2_path = "/instrumentation/nav[3]";
|
var vor2_path = "/instrumentation/nav[3]";
|
||||||
var dme1_path = "/instrumentation/dme[2]";
|
var dme1_path = "/instrumentation/dme[2]";
|
||||||
|
|
41
Nasal/Displays/projection.nas
Normal file
41
Nasal/Displays/projection.nas
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# Projection-related helper functions for the MFD maps
|
||||||
|
|
||||||
|
var Camera = {
|
||||||
|
new: func(options) {
|
||||||
|
var m = {
|
||||||
|
parents: [Camera],
|
||||||
|
|
||||||
|
camGeo: options['camGeo'] or geo.aircraft_position(),
|
||||||
|
camHdg: options['camHdg'] or 0,
|
||||||
|
range: options['range'] or 10.0,
|
||||||
|
screenRange: options['screenRange'] or 256.0,
|
||||||
|
screenCX: options['screenCX'] or options['screenRange'] or 256.0,
|
||||||
|
screenCY: options['screenCY'] or options['screenRange'] or 256.0,
|
||||||
|
};
|
||||||
|
return m;
|
||||||
|
},
|
||||||
|
|
||||||
|
setRange: func(range) {
|
||||||
|
me.range = range;
|
||||||
|
},
|
||||||
|
|
||||||
|
repositon: func(geo, hdg) {
|
||||||
|
me.camGeo = geo;
|
||||||
|
me.camHdg = hdg;
|
||||||
|
},
|
||||||
|
|
||||||
|
project: func(targetGeo) {
|
||||||
|
var dist = me.camGeo.distance_to(targetGeo) * M2NM;
|
||||||
|
var bearing = me.camGeo.course_to(targetGeo) - me.camHdg;
|
||||||
|
return me.projectDistBearing(dist, bearing);
|
||||||
|
},
|
||||||
|
|
||||||
|
projectDistBearing: func(dist, bearing) {
|
||||||
|
var bearingRad = bearing * D2R;
|
||||||
|
var tx = math.sin(bearingRad) * dist;
|
||||||
|
var ty = -math.cos(bearingRad) * dist;
|
||||||
|
var x = tx * me.screenRange / me.range + me.screenCX;
|
||||||
|
var y = ty * me.screenRange / me.range + me.screenCY;
|
||||||
|
return [x, y];
|
||||||
|
},
|
||||||
|
};
|
279
Nasal/Displays/traffic.nas
Normal file
279
Nasal/Displays/traffic.nas
Normal file
|
@ -0,0 +1,279 @@
|
||||||
|
# Traffic layer
|
||||||
|
#
|
||||||
|
|
||||||
|
var colorByLevel = {
|
||||||
|
# 0: other
|
||||||
|
0: [0.8,0.8,0.8],
|
||||||
|
# 1: proximity
|
||||||
|
1: [0.8,0.8,0.8],
|
||||||
|
# 2: traffic advisory (TA)
|
||||||
|
2: [1,0.75,0],
|
||||||
|
# 3: resolution advisory (RA)
|
||||||
|
3: [1,0,0],
|
||||||
|
};
|
||||||
|
|
||||||
|
var doFill = {
|
||||||
|
0: 0,
|
||||||
|
1: 1,
|
||||||
|
2: 1,
|
||||||
|
3: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
var colorDefault = [0.8,0.8,0.8];
|
||||||
|
|
||||||
|
var drawBlip = func(elem, threatLvl) {
|
||||||
|
if (threatLvl == 3) {
|
||||||
|
# resolution advisory
|
||||||
|
elem.reset()
|
||||||
|
.moveTo(-17,-17)
|
||||||
|
.horiz(34)
|
||||||
|
.vert(34)
|
||||||
|
.horiz(-34)
|
||||||
|
.close();
|
||||||
|
}
|
||||||
|
elsif (threatLvl == 2) {
|
||||||
|
# traffic advisory
|
||||||
|
elem.reset()
|
||||||
|
.moveTo(-17,0)
|
||||||
|
.arcSmallCW(17,17,0,34,0)
|
||||||
|
.arcSmallCW(17,17,0,-34,0);
|
||||||
|
}
|
||||||
|
elsif (threatLvl == 1) {
|
||||||
|
# proximate traffic
|
||||||
|
elem.reset()
|
||||||
|
.moveTo(-14,0)
|
||||||
|
.lineTo(0,-17)
|
||||||
|
.lineTo(14,0)
|
||||||
|
.lineTo(0,17)
|
||||||
|
.close();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# other traffic
|
||||||
|
elem.reset()
|
||||||
|
.moveTo(-14,0)
|
||||||
|
.lineTo(0,-17)
|
||||||
|
.lineTo(14,0)
|
||||||
|
.lineTo(0,17)
|
||||||
|
.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var TrafficLayer = {
|
||||||
|
new: func(camera, group) {
|
||||||
|
var m = {
|
||||||
|
parents: [TrafficLayer],
|
||||||
|
camera: camera,
|
||||||
|
refAlt: 0,
|
||||||
|
group: group,
|
||||||
|
items: {},
|
||||||
|
updateKeys: [],
|
||||||
|
addListener: nil,
|
||||||
|
delListener: nil,
|
||||||
|
};
|
||||||
|
return m;
|
||||||
|
},
|
||||||
|
|
||||||
|
makeElems: func () {
|
||||||
|
if (me.group == nil) return nil;
|
||||||
|
var elems = {};
|
||||||
|
elems['master'] = me.group.createChild('group');
|
||||||
|
elems['blip'] = elems.master.createChild('path')
|
||||||
|
.setStrokeLineWidth(0);
|
||||||
|
elems['text'] = elems.master.createChild('text')
|
||||||
|
.setDrawMode(canvas.Text.TEXT)
|
||||||
|
.setText(sprintf("0"))
|
||||||
|
.setFont("LiberationFonts/LiberationSans-Regular.ttf")
|
||||||
|
.setColor(1,1,1)
|
||||||
|
.setFontSize(20)
|
||||||
|
.setAlignment("center-center");
|
||||||
|
elems['master'].hide();
|
||||||
|
elems['arrowUp'] = elems.master.createChild("text")
|
||||||
|
.setDrawMode(canvas.Text.TEXT)
|
||||||
|
.setText(sprintf("↑"))
|
||||||
|
.setFont("LiberationFonts/LiberationSans-Regular.ttf")
|
||||||
|
.setColor(1,1,1)
|
||||||
|
.setFontSize(40)
|
||||||
|
.setTranslation(16, 0)
|
||||||
|
.setAlignment("left-center");
|
||||||
|
elems['arrowDown'] = elems.master.createChild("text")
|
||||||
|
.setDrawMode(canvas.Text.TEXT)
|
||||||
|
.setText(sprintf("↓"))
|
||||||
|
.setFont("LiberationFonts/LiberationSans-Regular.ttf")
|
||||||
|
.setColor(1,1,1)
|
||||||
|
.setFontSize(40)
|
||||||
|
.setTranslation(16, 0)
|
||||||
|
.setAlignment("left-center");
|
||||||
|
return elems;
|
||||||
|
},
|
||||||
|
|
||||||
|
start: func() {
|
||||||
|
me.stop();
|
||||||
|
var self = me;
|
||||||
|
me.addListener = setlistener('/ai/models/model-added', func(changed, listen, mode, is_child) {
|
||||||
|
var path = changed.getValue();
|
||||||
|
if (path == nil) return;
|
||||||
|
#printf("ADD: %s", path);
|
||||||
|
var masterProp = props.globals.getNode(path);
|
||||||
|
var prop = {
|
||||||
|
'master': masterProp,
|
||||||
|
};
|
||||||
|
if (me.items[path] == nil) {
|
||||||
|
me.items[path] = {
|
||||||
|
prop: prop,
|
||||||
|
elems: me.makeElems(),
|
||||||
|
data: {'threatLevel': -2},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
me.items[path].prop = prop;
|
||||||
|
me.items[path].data = {'threatLevel': -2};
|
||||||
|
}
|
||||||
|
}, 1, 1);
|
||||||
|
me.delListener = setlistener('/ai/models/model-removed', func(changed, listen, mode, is_child) {
|
||||||
|
var path = changed.getValue();
|
||||||
|
if (path == nil) return;
|
||||||
|
#printf("DEL: %s", path);
|
||||||
|
if (me.items[path] == nil) return;
|
||||||
|
if (me.items[path] != nil) {
|
||||||
|
me.items[path].prop = nil;
|
||||||
|
me.items[path].elems.master.hide();
|
||||||
|
me.items[path].data = {};
|
||||||
|
}
|
||||||
|
}, 1, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
stop: func() {
|
||||||
|
if (me.addListener != nil) {
|
||||||
|
removelistener(me.addListener);
|
||||||
|
me.addListener = nil;
|
||||||
|
}
|
||||||
|
if (me.delListener != nil) {
|
||||||
|
removelistener(me.delListener);
|
||||||
|
me.delListener = nil;
|
||||||
|
}
|
||||||
|
me.items = {};
|
||||||
|
if (me.group != nil) {
|
||||||
|
me.group.removeAllChildren();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
update: func() {
|
||||||
|
if (size(me.updateKeys) == 0) {
|
||||||
|
me.updateKeys = keys(me.items);
|
||||||
|
}
|
||||||
|
var path = pop(me.updateKeys);
|
||||||
|
foreach (var path; keys(me.items)) {
|
||||||
|
me.updateItem(path);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
redraw: func() {
|
||||||
|
foreach (var path; keys(me.items)) {
|
||||||
|
me.redrawItem(me.items[path]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setRefAlt: func(alt) {
|
||||||
|
me.refAlt = alt;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateItem: func(path) {
|
||||||
|
var item = me.items[path];
|
||||||
|
if (item == nil) return;
|
||||||
|
if (item.prop == nil) {
|
||||||
|
if (item.elems != nil) {
|
||||||
|
item.elems.master.hide();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.prop['lat'] == nil) {
|
||||||
|
item.prop['lat'] = item.prop.master.getNode('position/latitude-deg');
|
||||||
|
}
|
||||||
|
if (item.prop['lon'] == nil) {
|
||||||
|
item.prop['lon'] = item.prop.master.getNode('position/longitude-deg');
|
||||||
|
}
|
||||||
|
if (item.prop['alt'] == nil) {
|
||||||
|
item.prop['alt'] = item.prop.master.getNode('position/altitude-ft');
|
||||||
|
}
|
||||||
|
if (item.prop['threatLevel'] == nil) {
|
||||||
|
item.prop['threatLevel'] = item.prop.master.getNode('tcas/threat-level');
|
||||||
|
}
|
||||||
|
if (item.prop['callsign'] == nil) {
|
||||||
|
item.prop['callsign'] = item.prop.master.getNode('callsign');
|
||||||
|
}
|
||||||
|
if (item.prop['vspeed'] == nil) {
|
||||||
|
item.prop['vspeed'] = item.prop.master.getNode('velocities/vertical-speed-fps');
|
||||||
|
}
|
||||||
|
|
||||||
|
# this item has a prop associated with it
|
||||||
|
if (item.elems == nil) {
|
||||||
|
item.elems = me.makeElems();
|
||||||
|
}
|
||||||
|
var oldThreatLevel = item.data['threatLevel'];
|
||||||
|
foreach (var k; ['lat', 'lon', 'alt', 'threatLevel', 'callsign', 'vspeed']) {
|
||||||
|
if (item.prop[k] != nil) {
|
||||||
|
item.data[k] = item.prop[k].getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (oldThreatLevel != item.data['threatLevel']) {
|
||||||
|
item.data['threatLevelDirty'] = 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
redrawItem: func (item) {
|
||||||
|
#debug.dump("REDRAW ", item.data);
|
||||||
|
var lat = item.data['lat'];
|
||||||
|
var lon = item.data['lon'];
|
||||||
|
var alt = item.data['alt'];
|
||||||
|
var vspeed = item.data['vspeed'];
|
||||||
|
var threatLevelDirty = item.data['threatLevelDirty'];
|
||||||
|
if (lat != nil and lon != nil and vspeed != nil) {
|
||||||
|
var coords = geo.Coord.new();
|
||||||
|
coords.set_latlon(lat, lon);
|
||||||
|
var (x, y) = me.camera.project(coords);
|
||||||
|
item.elems.master.setTranslation(x, y);
|
||||||
|
#printf("%f %f", x, y);
|
||||||
|
if (threatLevelDirty) {
|
||||||
|
#printf('%s THREAT LVL: %i', item.data['callsign'] or '???', item.data['threatLevel']);
|
||||||
|
var threatLevel = item.data['threatLevel'];
|
||||||
|
#debug.dump(item.data, threatLevel);
|
||||||
|
drawBlip(item.elems.blip, threatLevel);
|
||||||
|
var rgb = colorByLevel[threatLevel];
|
||||||
|
if (rgb == nil) rgb = colorDefault;
|
||||||
|
var color = canvas._getColor(rgb);
|
||||||
|
var (r, g, b) = rgb;
|
||||||
|
item.elems.blip.setColorFill(r, g, b);
|
||||||
|
item.elems.text.setColor(r, g, b);
|
||||||
|
item.elems.arrowUp.setColor(r, g, b);
|
||||||
|
item.elems.arrowDown.setColor(r, g, b);
|
||||||
|
item.elems.master.set('z-index', threatLevel + 2);
|
||||||
|
item.data['threatLevelDirty'] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.elems.arrowUp.setVisible(vspeed * 60 > 500);
|
||||||
|
item.elems.arrowDown.setVisible(vspeed * 60 < -500);
|
||||||
|
|
||||||
|
var altDiff100 = ((item.data['alt'] or me.refAlt) - me.refAlt) / 100;
|
||||||
|
item.elems.text.setVisible(math.abs(altDiff100) > 0.5);
|
||||||
|
item.elems.text.setText(sprintf("%+02.0f", altDiff100));
|
||||||
|
if (altDiff100 < 0) {
|
||||||
|
item.elems.text.setTranslation(0, 30);
|
||||||
|
item.elems.arrowUp.setTranslation(16, 30);
|
||||||
|
item.elems.arrowDown.setTranslation(16, 30);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
item.elems.text.setTranslation(0, -30);
|
||||||
|
item.elems.arrowUp.setTranslation(16, -30);
|
||||||
|
item.elems.arrowDown.setTranslation(16, -30);
|
||||||
|
}
|
||||||
|
|
||||||
|
item.elems.master.show();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
item.elems.master.hide();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
Loading…
Add table
Reference in a new issue