... | ... |
@@ -76,7 +76,7 @@ |
76 | 76 |
y="1003.079">+30</tspan></text> |
77 | 77 |
<text |
78 | 78 |
sodipodi:linespacing="125%" |
79 |
- id="Alt_below" |
|
79 |
+ id="Callsign" |
|
80 | 80 |
y="1052.1428" |
81 | 81 |
x="38.328125" |
82 | 82 |
style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:end;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;font-family:Liberation Mono;-inkscape-font-specification:Liberation Mono" |
... | ... |
@@ -84,7 +84,7 @@ |
84 | 84 |
y="1052.1428" |
85 | 85 |
x="38.328125" |
86 | 86 |
id="tspan2993" |
87 |
- sodipodi:role="line">-30</tspan></text> |
|
87 |
+ sodipodi:role="line">callsign</tspan></text> |
|
88 | 88 |
<path |
89 | 89 |
style="color:#000000;fill:#e8e8d8;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" |
90 | 90 |
d="m 41.3125,16.09375 -5.9375,10.25 4,0 0,21.5 3.84375,0 0,-21.5 4,0 -5.90625,-10.25 z" |
... | ... |
@@ -151,50 +151,54 @@ var deviceClass = { |
151 | 151 |
data.timers.map.singleShot = 1; |
152 | 152 |
data.timers.map.start(); |
153 | 153 |
} |
154 |
- if (getprop('/instrumentation/tcas/serviceable') != nil) { |
|
155 |
- m.data.tcas = 0; |
|
156 |
- if (!contains(data.timers, 'tcas')) { |
|
157 |
- data.timers.tcas = maketimer ( 1, |
|
158 |
- func { |
|
159 |
- var tcas_dirty = []; |
|
160 |
- var _range = 6; |
|
161 |
- var _minLevel = -1; |
|
162 |
- foreach (var ac; props.globals.getNode("/ai/models").getChildren("multiplayer")) { |
|
163 |
- if (ac.getValue("valid")) { |
|
164 |
- var range = ac.getNode("radar/range-nm").getValue(); |
|
165 |
- if (range != nil) { |
|
166 |
- if (debug.isnan(range) == 0) { |
|
167 |
- if ( (range > 0) and (range <= _range) ) { |
|
168 |
- var nTcasThreat = ac.getNode("tcas/threat-level"); |
|
169 |
- if (nTcasThreat != nil) { |
|
170 |
- var level = nTcasThreat.getValue(); |
|
171 |
- if (level > _minLevel) { |
|
172 |
- var lat = ac.getNode("position/latitude-deg").getValue(); |
|
173 |
- var lon = ac.getNode("position/longitude-deg").getValue(); |
|
174 |
- var aAlt = ac.getNode("position/altitude-ft").getValue(); |
|
175 |
- var vs = ac.getNode("velocities/vertical-speed-fps").getValue(); |
|
176 |
- var alt = math.floor(((aAlt - data.alt) / 100) + 0.5); |
|
177 |
- if (debug.isnan(lat) == 0 and |
|
178 |
- debug.isnan(lon) == 0 and |
|
179 |
- debug.isnan(vs) == 0 and |
|
180 |
- debug.isnan(alt) == 0) { |
|
181 |
- append(tcas_dirty, { |
|
182 |
- lat: lat, |
|
183 |
- lon: lon, |
|
184 |
- vs: vs, |
|
185 |
- alt: alt, |
|
186 |
- level: level, |
|
187 |
- }); |
|
188 |
- } } } } } } } } |
|
189 |
- data.tcas = tcas_dirty; |
|
154 |
+ if (!contains(data.timers, 'tcas')) { |
|
155 |
+ data.timers.tcas = maketimer ( 5, func { |
|
156 |
+ var traffic_displayed = 0; |
|
157 |
+ foreach (var name; keys(flightdeck)) |
|
158 |
+ traffic_displayed += flightdeck[name].map.layers.tcas.group.getVisible(); |
|
159 |
+ var tcas_dirty = []; |
|
160 |
+ var level_dirty = 0; |
|
161 |
+ foreach (var AItype; [ 'aircraft', 'multiplayer' ]) |
|
162 |
+ foreach (var ac; props.globals.getNode("/ai/models").getChildren(AItype)) { |
|
163 |
+ if (ac.getValue("valid")) { |
|
164 |
+ var lat = ac.getNode("position/latitude-deg").getValue(); |
|
165 |
+ var lon = ac.getNode("position/longitude-deg").getValue(); |
|
166 |
+ var alt = ac.getNode("position/altitude-ft").getValue(); |
|
167 |
+ var vs = ac.getNode("velocities/vertical-speed-fps").getValue(); |
|
168 |
+ if (isnum(lat) and isnum(lon) and isnum(vs) and isnum(alt)) { |
|
169 |
+ alt = math.floor(((alt - data.alt) / 100) + 0.5); |
|
170 |
+ var (course, dist) = courseAndDistance(lat, lon, |
|
171 |
+ geo.Coord.new().set_latlon(data.lat, data.lon)); |
|
172 |
+ if (dist < 50) { |
|
173 |
+ var dir = ac.getNode('orientation/true-heading-deg').getValue() - course; |
|
174 |
+ if (dist < 5 and abs(alt) < 10) |
|
175 |
+ level = 3; |
|
176 |
+ elsif (dist < 10 and alt * vs < 0 and abs(dir) < 10) |
|
177 |
+ level = 3; |
|
178 |
+ elsif (dist < 15 and abs(alt) < 5) |
|
179 |
+ level = 2; |
|
180 |
+ elsif (dist < 15 and abs(alt) < 50) |
|
181 |
+ level = 1; |
|
182 |
+ else |
|
183 |
+ level = 0; |
|
184 |
+ level_dirty = level > level_dirty ? level : level_dirty; |
|
185 |
+ if (traffic_displayed) |
|
186 |
+ append(tcas_dirty, { |
|
187 |
+ lat: lat, |
|
188 |
+ lon: lon, |
|
189 |
+ vs: vs, |
|
190 |
+ alt: alt, |
|
191 |
+ level: level, |
|
192 |
+ callsign: ac.getValue('callsign') |
|
193 |
+ }); |
|
194 |
+ } |
|
195 |
+ } |
|
196 |
+ } |
|
190 | 197 |
} |
191 |
- ); |
|
192 |
- data.timers.tcas.start(); |
|
193 |
- } |
|
194 |
- } |
|
195 |
- else { |
|
196 |
- delete(m.softkeys.bindings.PFD.INSET, 'TRAFFIC'); |
|
197 |
- delete(m.softkeys.bindings.MFD.MAP, 'TRAFFIC'); |
|
198 |
+ data.tcas = tcas_dirty; |
|
199 |
+ data.tcas_level = level_dirty; |
|
200 |
+ }); |
|
201 |
+ data.timers.tcas.start(); |
|
198 | 202 |
} |
199 | 203 |
m.display.showInitProgress(); |
200 | 204 |
|
... | ... |
@@ -702,7 +702,7 @@ var displayClass = { |
702 | 702 |
me.screenElements['SlipSkid'] |
703 | 703 |
.setTranslation(getprop("/instrumentation/slip-skid-ball/indicated-slip-skid") * 10, 0); |
704 | 704 |
me.screenElements['Traffic'] |
705 |
- .setVisible(size(data.tcas)); |
|
705 |
+ .setVisible(data.tcas_level > 1); |
|
706 | 706 |
}, |
707 | 707 |
#}}} |
708 | 708 |
|
... | ... |
@@ -8,16 +8,14 @@ var TcasItemClass = { |
8 | 8 |
|
9 | 9 |
canvas.parsesvg(m._group, data.zkv1000_reldir ~ "Models/tcas.svg"); |
10 | 10 |
m._can = { |
11 |
- Alt : [ |
|
12 |
- m._group.getElementById("Alt_above").setVisible(0), |
|
13 |
- m._group.getElementById("Alt_below").setVisible(0) |
|
14 |
- ], |
|
11 |
+ Alt : m._group.getElementById("Alt_above").setVisible(0), |
|
15 | 12 |
Arrow : [ |
16 | 13 |
m._group.getElementById("Arrow_climb").setVisible(0), |
17 | 14 |
m._group.getElementById("Arrow_descent").setVisible(0) |
18 |
- ] |
|
15 |
+ ], |
|
16 |
+ Callsign: m._group.getElementById("Callsign").setVisible(0), |
|
17 |
+ ThreadLevel: [], |
|
19 | 18 |
}; |
20 |
- m._can.ThreadLevel = []; |
|
21 | 19 |
|
22 | 20 |
for (var i=0; i<4; i+=1) |
23 | 21 |
append(m._can.ThreadLevel, |
... | ... |
@@ -28,41 +26,38 @@ var TcasItemClass = { |
28 | 26 |
return m; |
29 | 27 |
}, |
30 | 28 |
|
31 |
- setData : func(lat, lon, alt, vs, level) { |
|
29 |
+ setData : func(lat, lon, alt, vs, level, callsign) { |
|
32 | 30 |
me._group.setVisible(1); |
33 | 31 |
me._group.setGeoPosition(lat, lon); |
34 | 32 |
|
35 |
- if (alt == 0) { |
|
36 |
- me._can.Alt[0].setVisible(0); |
|
37 |
- me._can.Alt[1].setVisible(0); |
|
38 |
- } |
|
39 |
- else { |
|
40 |
- me._can.Alt[alt < 0] |
|
33 |
+ if (alt and level) |
|
34 |
+ me._can.Alt |
|
41 | 35 |
.setText(sprintf("%+i", alt)) |
42 | 36 |
.set('fill', me._colors[level]) |
43 | 37 |
.setVisible(1); |
44 |
- |
|
45 |
- me._can.Alt[alt > 0].setVisible(0); |
|
46 |
- } |
|
38 |
+ else |
|
39 |
+ me._can.Alt.setVisible(0); |
|
47 | 40 |
|
48 |
- if (vs >= -3 and vs <= 3) { |
|
49 |
- me._can.Arrow[0].setVisible(0); |
|
50 |
- me._can.Arrow[1].setVisible(0); |
|
51 |
- } |
|
52 |
- else { |
|
53 |
- me._can.Arrow[vs < -3] |
|
41 |
+ if (abs(vs) > 50 and level > 1) { |
|
42 |
+ me._can.Arrow[vs < 0] |
|
54 | 43 |
.set('fill', me._colors[level]) |
55 | 44 |
.set('stroke', me._colors[level]) |
56 | 45 |
.setVisible(1); |
57 | 46 |
|
58 |
- me._can.Arrow[vs > 3].setVisible(0); |
|
47 |
+ me._can.Arrow[vs > 0].setVisible(0); |
|
59 | 48 |
} |
60 |
- |
|
61 |
- me._can.ThreadLevel[0].setVisible(level == 0); |
|
62 |
- me._can.ThreadLevel[1].setVisible(level == 1); |
|
63 |
- me._can.ThreadLevel[2].setVisible(level == 2); |
|
64 |
- me._can.ThreadLevel[3].setVisible(level == 3); |
|
65 |
- me._group.set("z-index", level + 1); |
|
49 |
+ else { |
|
50 |
+ me._can.Arrow[0].setVisible(0); |
|
51 |
+ me._can.Arrow[1].setVisible(0); |
|
52 |
+ } |
|
53 |
+ |
|
54 |
+ for (var i = 0; i < 4; i += 1) |
|
55 |
+ me._can.ThreadLevel[i].setVisible(level == i); |
|
56 |
+ |
|
57 |
+ me._can.Callsign |
|
58 |
+ .setText(callsign) |
|
59 |
+ .set('fill', me._colors[level]) |
|
60 |
+ .setVisible(1); |
|
66 | 61 |
}, |
67 | 62 |
}; |
68 | 63 |
# }}} |
... | ... |
@@ -72,27 +67,15 @@ var MapTcas = { |
72 | 67 |
new : func(device, group) { |
73 | 68 |
var m = {parents:[MapTcas]}; |
74 | 69 |
m.device = device; |
75 |
- if (getprop('/instrumentation/tcas/serviceable') == nil) { |
|
76 |
- m.update = func; |
|
77 |
- m.setVisible = func; |
|
78 |
- } |
|
79 |
- else { |
|
80 |
- m.visibility = 0; |
|
81 |
- m.group = group.createChild('map', 'tcas') |
|
82 |
- .setTranslation( |
|
83 |
- m.device.role == 'MFD' ? (m.device.data.mapview[0] + m.device.data.mapclip.left)/2 : 120, |
|
84 |
- m.device.role == 'MFD' ? 400 : 600) |
|
85 |
- .setVisible(m.visibility); |
|
86 |
- m._item = []; |
|
87 |
- m._itemIndex = 0; |
|
88 |
- m._itemCount = 0; |
|
89 |
-# /instrumentation/tcas/inputs/mode |
|
90 |
- m.MODE = ["Normal","Above","Unlimited","Below"]; |
|
91 |
- } |
|
92 |
- if (m.device.role == 'PFD') |
|
93 |
- m.device.softkeys.colored.INSETTRAFFIC = 1; |
|
94 |
- if (m.device.role == 'MFD') |
|
95 |
- m.device.softkeys.colored.MAPTRAFFIC = 1; |
|
70 |
+ m.visibility = 0; |
|
71 |
+ m.group = group.createChild('map', 'tcas') |
|
72 |
+ .setTranslation( |
|
73 |
+ m.device.role == 'MFD' ? (m.device.data.mapview[0] + m.device.data.mapclip.left)/2 : 120, |
|
74 |
+ m.device.role == 'MFD' ? 400 : 600) |
|
75 |
+ .setVisible(m.visibility); |
|
76 |
+ m._item = []; |
|
77 |
+ m._itemIndex = 0; |
|
78 |
+ m._itemCount = 0; |
|
96 | 79 |
return m; |
97 | 80 |
}, |
98 | 81 |
|
... | ... |
@@ -101,7 +84,7 @@ var MapTcas = { |
101 | 84 |
}, |
102 | 85 |
|
103 | 86 |
update : func() { |
104 |
- if (me.device.data.tcas == 0) |
|
87 |
+ if (me.group.getVisible() == 0) |
|
105 | 88 |
return; |
106 | 89 |
me.group._node.getNode('ref-lat', 1).setDoubleValue(data.lat); |
107 | 90 |
me.group._node.getNode('ref-lon', 1).setDoubleValue(data.lon); |
... | ... |
@@ -113,7 +96,7 @@ var MapTcas = { |
113 | 96 |
append(me._item, TcasItemClass.new(me.group, me._itemIndex)); |
114 | 97 |
me._itemCount += 1; |
115 | 98 |
} |
116 |
- me._item[me._itemIndex].setData(ac.lat, ac.lon, ac.alt, ac.vs, ac.level); |
|
99 |
+ me._item[me._itemIndex].setData(ac.lat, ac.lon, ac.alt, ac.vs, ac.level, ac.callsign); |
|
117 | 100 |
me._itemIndex += 1; |
118 | 101 |
} |
119 | 102 |
|
... | ... |
@@ -77,7 +77,7 @@ Please report bug at <zkv1000@seb.lautre.net>. |
77 | 77 |
* single-prop EIS: texts displayed, animations for fuel |
78 | 78 |
* ND and map display: synchronized tiles and navaids, range change, map heads up |
79 | 79 |
* rotation and zooming of online maps in-flight |
80 |
- * simple Traffic Alert Collision Avoidance System (TCAS) |
|
80 |
+ * Traffic alert and display on map |
|
81 | 81 |
* screen brightness and body lighting settings |
82 | 82 |
* checklists management (emergency and normal) |
83 | 83 |
* possibility to fit the size of the devices to enter the panel |
... | ... |
@@ -272,21 +272,6 @@ Actually there are only two "types of display": MFD or PFD, which is known by th |
272 | 272 |
Other devices as keyboard or non-display can also exists, as long as they don't have a `status` property... |
273 | 273 |
Not sur I'm clear on this point though :) |
274 | 274 |
|
275 |
-## TCAS |
|
276 |
-To enable TCAS you need to make it serviceable in your aircraft before the ZKV1000 being powered on, so the best is to set it in the aircraft configuration files. |
|
277 |
-Actually it checks if the property `/instrumentation/tcas/serviceable` is set to boolean true value. |
|
278 |
- |
|
279 |
- <tcas> |
|
280 |
- <name>tcas</name> |
|
281 |
- <number>0</number> |
|
282 |
- <serviceable type='bool'>true</serviceable> |
|
283 |
- <inputs> |
|
284 |
- <mode type="int">5</mode> |
|
285 |
- </inputs> |
|
286 |
- </tcas> |
|
287 |
- |
|
288 |
-Moreover it only shows alerts with an annunciation "TRAFFIC" on PFD, and shows icons on maps (MFD and INSET), no decision is taken, no relation with transponder and no evasion scenari and no evasion scenario. |
|
289 |
- |
|
290 | 275 |
## Autopilot |
291 | 276 |
There are two systems supported: the GFC700 shipped with the official Stuart Buchanan's FG1000 and the Octal450's STEC 55X (and at time of writing only HDG and buggy NAV/GPS modes are available). |
292 | 277 |
|
... | ... |
@@ -39,6 +39,7 @@ var data = { # set of data common to all devices |
39 | 39 |
aoa : 0, |
40 | 40 |
fpSize : 0, |
41 | 41 |
tcas: [], |
42 |
+ tcas_level: 0, |
|
42 | 43 |
settings: { |
43 | 44 |
units: { |
44 | 45 |
pressure: 'inhg', |