698 lines
20 KiB
JavaScript
698 lines
20 KiB
JavaScript
// -*- mode: javascript; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
|
"use strict";
|
|
|
|
let NBSP='\u00a0';
|
|
let NNBSP='\u202f';
|
|
let DEGREES='\u00b0'
|
|
let ENDASH='\u2013';
|
|
let UP_TRIANGLE='\u25b2'; // U+25B2 BLACK UP-POINTING TRIANGLE
|
|
let DOWN_TRIANGLE='\u25bc'; // U+25BC BLACK DOWN-POINTING TRIANGLE
|
|
let EM_QUAD = '\u2001';
|
|
|
|
let TrackDirections = ["North","NE","East","SE","South","SW","West","NW"];
|
|
let TrackDirectionArrows = ["\u21e7","\u2b00","\u21e8","\u2b02","\u21e9","\u2b03","\u21e6","\u2b01"];
|
|
|
|
let UnitLabels = {
|
|
'altitude': { metric: "m", imperial: "ft", nautical: "ft"},
|
|
'speed': { metric: "km/h", imperial: "mph", nautical: "kt" },
|
|
'distance': { metric: "km", imperial: "mi", nautical: "nmi" },
|
|
'verticalRate': { metric: "m/s", imperial: "ft/min", nautical: "ft/min" },
|
|
'distanceShort': { metric: "m", imperial: "ft", nautical: "m" }
|
|
};
|
|
|
|
let aircraftCategories = {
|
|
'A0': 'Unspecified powered aircraft',
|
|
'A1': `Light (< 15${NNBSP}500${NBSP}lb)`,
|
|
'A2': `Small (15${NNBSP}500 to 75${NNBSP}000${NBSP}lb)`,
|
|
'A3': `Large (75${NNBSP}000 to 300${NNBSP}000${NBSP}lb)`,
|
|
'A4': 'High Vortex Large(aircraft such as B-757)',
|
|
'A5': `Heavy (> 300${NNBSP}000${NBSP}lb)`,
|
|
'A6': `High Performance (> 5${NBSP}g acceleration and > 400${NBSP}kt)`,
|
|
'A7': 'Rotorcraft',
|
|
'B0': 'Unspecified unpowered aircraft or UAV or spacecraft',
|
|
'B1': 'Glider/sailplane',
|
|
'B2': 'Lighter-than-Air',
|
|
'B3': 'Parachutist/Skydiver',
|
|
'B4': 'Ultralight/hang-glider/paraglider',
|
|
'B6': 'Unmanned Aerial Vehicle',
|
|
'B7': 'Space/Trans-atmospheric vehicle',
|
|
'C0': 'Unspecified ground installation or vehicle',
|
|
'C1': `Surface Vehicle ${ENDASH} Emergency Vehicle`,
|
|
'C2': `Surface Vehicle ${ENDASH} Service Vehicle`,
|
|
'C3': 'Fixed Ground or Tethered Obstruction'
|
|
};
|
|
|
|
// formatting helpers
|
|
|
|
function get_category_label(category) {
|
|
if (!category)
|
|
return '';
|
|
let label = aircraftCategories[category];
|
|
if (!label)
|
|
return '';
|
|
return label;
|
|
}
|
|
|
|
function get_unit_label(quantity, systemOfMeasurement) {
|
|
let labels = UnitLabels[quantity];
|
|
if (labels !== undefined && labels[systemOfMeasurement] !== undefined) {
|
|
return labels[systemOfMeasurement];
|
|
}
|
|
return "";
|
|
}
|
|
|
|
// track in degrees (0..359)
|
|
function format_track_brief(track, rounded) {
|
|
if (track == null){
|
|
return "n/a";
|
|
}
|
|
|
|
return track.toFixed(rounded ? 0 : 1) + DEGREES;
|
|
}
|
|
|
|
// track in degrees (0..359)
|
|
function format_track_long(track, rounded) {
|
|
if (track == null){
|
|
return "n/a";
|
|
}
|
|
|
|
let trackDir = Math.floor((360 + track % 360 + 22.5) / 45) % 8;
|
|
return TrackDirections[trackDir] + ":" + NNBSP + track.toFixed(rounded ? 0 : 1) + DEGREES;
|
|
}
|
|
function format_track_arrow(track) {
|
|
if (track == null){
|
|
return "";
|
|
}
|
|
|
|
let trackDir = Math.floor((360 + track % 360 + 22.5) / 45) % 8;
|
|
return TrackDirectionArrows[trackDir];
|
|
}
|
|
|
|
// alt in feet
|
|
function format_altitude_brief(alt, vr, displayUnits, withUnits) {
|
|
let alt_text;
|
|
|
|
if (alt == null){
|
|
return NBSP + '?' + NBSP;
|
|
} else if (alt === "ground"){
|
|
return "ground";
|
|
}
|
|
|
|
alt_text = Math.round(convert_altitude(alt, displayUnits)).toString();
|
|
if (withUnits)
|
|
alt_text += NNBSP + get_unit_label("altitude", displayUnits);
|
|
|
|
// Vertical Rate Triangle
|
|
let verticalRateTriangle = "";
|
|
if (vr > 245){
|
|
verticalRateTriangle = UP_TRIANGLE;
|
|
} else if (vr < -245){
|
|
verticalRateTriangle = DOWN_TRIANGLE;
|
|
} else {
|
|
verticalRateTriangle = NNBSP;
|
|
}
|
|
|
|
return alt_text + verticalRateTriangle;
|
|
}
|
|
|
|
// alt in feet
|
|
function format_altitude_long(alt, vr, displayUnits) {
|
|
let alt_text = "";
|
|
|
|
if (alt == null) {
|
|
return "n/a";
|
|
} else if (alt === "ground") {
|
|
return "on ground";
|
|
}
|
|
|
|
alt_text = Math.round(convert_altitude(alt, displayUnits)).toString() + NNBSP + get_unit_label("altitude", displayUnits);
|
|
|
|
if (vr > 192) {
|
|
return UP_TRIANGLE + NNBSP + alt_text;
|
|
} else if (vr < -192) {
|
|
return DOWN_TRIANGLE + NNBSP + alt_text;
|
|
} else {
|
|
return alt_text;
|
|
}
|
|
}
|
|
|
|
// alt in feet
|
|
function format_altitude(alt, displayUnits) {
|
|
let alt_text = "";
|
|
|
|
if (alt == null) {
|
|
return "n/a";
|
|
} else if (alt === "ground") {
|
|
return "on ground";
|
|
}
|
|
|
|
alt_text = Math.round(convert_altitude(alt, displayUnits)).toString() + NNBSP + get_unit_label("altitude", displayUnits);
|
|
|
|
return alt_text;
|
|
}
|
|
|
|
|
|
// alt ground/airborne
|
|
function format_onground (alt) {
|
|
if (alt == null) {
|
|
return "n/a";
|
|
} else if (alt === "ground") {
|
|
return "on ground";
|
|
} else {
|
|
return "airborne";
|
|
}
|
|
}
|
|
|
|
// alt in feet
|
|
function convert_altitude(alt, displayUnits) {
|
|
if (displayUnits === "metric") {
|
|
return alt * 0.3048; // feet to meters
|
|
}
|
|
|
|
return alt;
|
|
}
|
|
|
|
// speed in knots
|
|
function format_speed_brief(speed, displayUnits, withUnits) {
|
|
if (speed == null || isNaN(speed)) {
|
|
return "";
|
|
}
|
|
let speed_text = Math.round(convert_speed(speed, displayUnits)).toString();
|
|
if (withUnits)
|
|
speed_text += NNBSP + get_unit_label("speed", displayUnits);
|
|
|
|
return speed_text;
|
|
}
|
|
|
|
// speed in knots
|
|
function format_speed_long(speed, displayUnits) {
|
|
if (speed == null) {
|
|
return "n/a";
|
|
}
|
|
|
|
let speed_text = Math.round(convert_speed(speed, displayUnits)) + NNBSP + get_unit_label("speed", displayUnits);
|
|
|
|
return speed_text;
|
|
}
|
|
|
|
// speed in knots
|
|
function convert_speed(speed, displayUnits) {
|
|
if (displayUnits === "metric") {
|
|
return speed * 1.852; // knots to kilometers per hour
|
|
}
|
|
else if (displayUnits === "imperial") {
|
|
return speed * 1.151; // knots to miles per hour
|
|
}
|
|
|
|
return speed;
|
|
}
|
|
|
|
// dist in meters
|
|
function format_distance_brief(dist, displayUnits) {
|
|
if (dist == null) {
|
|
return "";
|
|
}
|
|
|
|
return convert_distance(dist, displayUnits).toFixed(1);
|
|
}
|
|
|
|
// dist in meters
|
|
function format_distance_long(dist, displayUnits, fixed) {
|
|
if (dist == null) {
|
|
return "n/a";
|
|
}
|
|
|
|
if (typeof fixed === 'undefined') {
|
|
fixed = 1;
|
|
}
|
|
|
|
let dist_text = convert_distance(dist, displayUnits).toFixed(fixed) + NNBSP + get_unit_label("distance", displayUnits);
|
|
|
|
return dist_text;
|
|
}
|
|
|
|
function format_distance_short (dist, displayUnits) {
|
|
if (dist == null) {
|
|
return "n/a";
|
|
}
|
|
|
|
let dist_text = Math.round(convert_distance_short(dist, displayUnits)) + NNBSP + get_unit_label("distanceShort", displayUnits);
|
|
|
|
return dist_text;
|
|
}
|
|
|
|
// dist in meters
|
|
function convert_distance(dist, displayUnits) {
|
|
if (displayUnits === "metric") {
|
|
return (dist / 1000); // meters to kilometres
|
|
}
|
|
else if (displayUnits === "imperial") {
|
|
return (dist / 1609); // meters to miles
|
|
}
|
|
return (dist / 1852); // meters to nautical miles
|
|
}
|
|
|
|
// dist in meters
|
|
// converts meters to feet or just returns metres
|
|
function convert_distance_short(dist, displayUnits) {
|
|
if (displayUnits === "imperial") {
|
|
return (dist / 0.3048); // meters to feet
|
|
}
|
|
return dist; // just meters
|
|
}
|
|
|
|
// rate in ft/min
|
|
function format_vert_rate_brief(rate, displayUnits) {
|
|
if (rate == null) {
|
|
return "";
|
|
}
|
|
|
|
return convert_vert_rate(rate, displayUnits).toFixed(displayUnits === "metric" ? 1 : 0);
|
|
}
|
|
|
|
// rate in ft/min
|
|
function format_vert_rate_long(rate, displayUnits) {
|
|
if (rate == null){
|
|
return "n/a";
|
|
}
|
|
|
|
let rate_text = convert_vert_rate(rate, displayUnits).toFixed(displayUnits === "metric" ? 1 : 0) + NNBSP + get_unit_label("verticalRate", displayUnits);
|
|
|
|
return rate_text;
|
|
}
|
|
|
|
// rate in ft/min
|
|
function convert_vert_rate(rate, displayUnits) {
|
|
if (displayUnits === "metric") {
|
|
return (rate / 196.85); // ft/min to m/s
|
|
}
|
|
|
|
return rate;
|
|
}
|
|
|
|
// p is a [lon, lat] coordinate
|
|
function format_latlng(p) {
|
|
return p[1].toFixed(3) + DEGREES + "," + NNBSP + p[0].toFixed(3) + DEGREES;
|
|
}
|
|
|
|
function format_data_source(source) {
|
|
switch (source) {
|
|
case 'uat' :
|
|
return "UAT";
|
|
case 'mlat':
|
|
return "MLAT";
|
|
case 'adsb':
|
|
case 'adsb_icao':
|
|
case 'adsb_other':
|
|
return "ADS-B";
|
|
case 'adsb_icao_nt':
|
|
return "ADS-B noTP";
|
|
case 'adsr':
|
|
case 'adsr_icao':
|
|
case 'adsr_other':
|
|
return "ADS-R or UAT";
|
|
case 'tisb_icao':
|
|
case 'tisb_trackfile':
|
|
case 'tisb_other':
|
|
case 'tisb':
|
|
return "TIS-B";
|
|
case 'modeS':
|
|
return "Mode S";
|
|
case 'mode_ac':
|
|
return "Mode A/C";
|
|
case 'adsc':
|
|
return "Sat. ADS-C";
|
|
case 'other':
|
|
return "Other";
|
|
}
|
|
|
|
return "Unknown";
|
|
}
|
|
|
|
function format_nac_p (value) {
|
|
switch (value) {
|
|
case 0:
|
|
return "EPU ≥ 18.5 km";
|
|
case 1:
|
|
return "EPU < 18.5 km";
|
|
case 2:
|
|
return "EPU < 7.4 km";
|
|
case 3:
|
|
return "EPU < 3.7 km";
|
|
case 4:
|
|
return "EPU < 1.8 km";
|
|
case 5:
|
|
return "EPU < 926 m";
|
|
case 6:
|
|
return "EPU < 555 m";
|
|
case 7:
|
|
return "EPU < 185 m";
|
|
case 8:
|
|
return "EPU < 92 m";
|
|
case 9:
|
|
return "EPU < 30 m";
|
|
case 10:
|
|
return "EPU < 10 m";
|
|
case 11:
|
|
return "EPU < 3 m";
|
|
default:
|
|
return "n/a";
|
|
|
|
}
|
|
}
|
|
|
|
function format_nac_v (value) {
|
|
switch (value) {
|
|
case 0:
|
|
return "≥ 10 m/s";
|
|
case 1:
|
|
return "< 10 m/s";
|
|
case 2:
|
|
return "< 3 m/s";
|
|
case 3:
|
|
return "< 1 m/s";
|
|
case 4:
|
|
return "< 0.3 m/s";
|
|
default:
|
|
return "n/a";
|
|
}
|
|
}
|
|
|
|
function format_duration(seconds) {
|
|
if (seconds == null)
|
|
return "n/a";
|
|
if (seconds < 20)
|
|
return seconds.toFixed(1) + ' s';
|
|
if (seconds < 5 * 60)
|
|
return seconds.toFixed(0) + ' s';
|
|
if (seconds < 3 * 60 * 60)
|
|
return (seconds/60).toFixed(0) + ' min';
|
|
return (seconds/60/60).toFixed(0) + ' h';
|
|
|
|
}
|
|
|
|
function iOSVersion() {
|
|
if (/iP(hone|od|ad)/.test(navigator.platform)) {
|
|
// supports iOS 2.0 and later: <http://bit.ly/TJjs1V>
|
|
var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
|
|
return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)];
|
|
}
|
|
}
|
|
|
|
function wqi(data) {
|
|
const INT32_MAX = 2147483647;
|
|
const buffer = data.buffer;
|
|
//console.log(buffer);
|
|
let u32 = new Uint32Array(data.buffer, 0, 12);
|
|
data.now = u32[0] / 1000 + u32[1] * 4294967.296;
|
|
//console.log(data.now);
|
|
let stride = u32[2];
|
|
data.global_ac_count_withpos = u32[3];
|
|
data.globeIndex = u32[4];
|
|
|
|
let limits = new Int16Array(buffer, 20, 4);
|
|
data.south = limits[0];
|
|
data.west = limits[1];
|
|
data.north = limits[2];
|
|
data.east = limits[3];
|
|
|
|
data.messages = u32[7];
|
|
|
|
let s32 = new Int32Array(data.buffer, 0, stride / 4);
|
|
let receiver_lat = s32[8] / 1e6;
|
|
let receiver_lon = s32[9] / 1e6;
|
|
|
|
|
|
const binCraftVersion = u32[10];
|
|
|
|
data.messageRate = u32[11] / 10;
|
|
|
|
if (receiver_lat != 0 && receiver_lon != 0) {
|
|
//console.log("receiver_lat: " + receiver_lat + " receiver_lon: " + receiver_lon);
|
|
let position = {
|
|
coords: {
|
|
latitude: receiver_lat,
|
|
longitude: receiver_lon,
|
|
},
|
|
};
|
|
if (receiver_lat != SiteLat || receiver_lon != SiteLon) {
|
|
onLocationChange(position);
|
|
}
|
|
}
|
|
|
|
data.aircraft = [];
|
|
for (let off = stride; off < buffer.byteLength; off += stride) {
|
|
let ac = {}
|
|
let u32 = new Uint32Array(buffer, off, stride / 4);
|
|
let s32 = new Int32Array(buffer, off, stride / 4);
|
|
let u16 = new Uint16Array(buffer, off, stride / 2);
|
|
let s16 = new Int16Array(buffer, off, stride / 2);
|
|
let u8 = new Uint8Array(buffer, off, stride);
|
|
let t = s32[0] & (1<<24);
|
|
ac.hex = (s32[0] & ((1<<24) - 1)).toString(16).padStart(6, '0');
|
|
ac.hex = t ? ('~' + ac.hex) : ac.hex;
|
|
ac.seen_pos = u16[2] / 10;
|
|
ac.seen = u16[3] / 10;
|
|
|
|
ac.lon = s32[2] / 1e6;
|
|
ac.lat = s32[3] / 1e6;
|
|
|
|
ac.baro_rate = s16[8] * 8;
|
|
ac.geom_rate = s16[9] * 8;
|
|
ac.alt_baro = s16[10] * 25;
|
|
ac.alt_geom = s16[11] * 25;
|
|
|
|
ac.nav_altitude_mcp = u16[12] * 4;
|
|
ac.nav_altitude_fms = u16[13] * 4;
|
|
ac.nav_qnh = s16[14] / 10;
|
|
ac.nav_heading = s16[15] / 90;
|
|
|
|
const s = u16[16].toString(16).padStart(4, '0');
|
|
if (s[0] > '9') {
|
|
ac.squawk = String(parseInt(s[0], 16)) + s[1] + s[2] + s[3];
|
|
} else {
|
|
ac.squawk = s;
|
|
}
|
|
ac.gs = s16[17] / 10;
|
|
ac.mach = s16[18] / 1000;
|
|
ac.roll = s16[19] / 100;
|
|
|
|
ac.track = s16[20] / 90;
|
|
ac.track_rate = s16[21] / 100;
|
|
ac.mag_heading = s16[22] / 90;
|
|
ac.true_heading = s16[23] / 90;
|
|
|
|
ac.wd = s16[24];
|
|
ac.ws = s16[25];
|
|
ac.oat = s16[26];
|
|
ac.tat = s16[27];
|
|
|
|
ac.tas = u16[28];
|
|
ac.ias = u16[29];
|
|
ac.rc = u16[30];
|
|
|
|
if (globeIndex && binCraftVersion >= 20220916) {
|
|
ac.messageRate = u16[31] / 10;
|
|
} else {
|
|
ac.messages = u16[31];
|
|
}
|
|
|
|
ac.category = u8[64] ? u8[64].toString(16).toUpperCase() : undefined;
|
|
ac.nic = u8[65];
|
|
|
|
let nav_modes = u8[66];
|
|
ac.nav_modes = true;
|
|
ac.emergency = u8[67] & 15;
|
|
ac.type = (u8[67] & 240) >> 4;
|
|
|
|
ac.airground = u8[68] & 15;
|
|
ac.nav_altitude_src = (u8[68] & 240) >> 4;
|
|
|
|
ac.sil_type = u8[69] & 15;
|
|
ac.adsb_version = (u8[69] & 240) >> 4;
|
|
|
|
ac.adsr_version = u8[70] & 15;
|
|
ac.tisb_version = (u8[70] & 240) >> 4;
|
|
|
|
ac.nac_p = u8[71] & 15;
|
|
ac.nac_v = (u8[71] & 240) >> 4;
|
|
|
|
ac.sil = u8[72] & 3;
|
|
ac.gva = (u8[72] & 12) >> 2;
|
|
ac.sda = (u8[72] & 48) >> 4;
|
|
ac.nic_a = (u8[72] & 64) >> 6;
|
|
ac.nic_c = (u8[72] & 128) >> 7;
|
|
|
|
|
|
ac.flight = "";
|
|
for (let i = 78; u8[i] && i < 86; i++) {
|
|
ac.flight += String.fromCharCode(u8[i]);
|
|
}
|
|
|
|
ac.dbFlags = u16[43];
|
|
|
|
ac.t = "";
|
|
for (let i = 88; u8[i] && i < 92; i++) {
|
|
ac.t += String.fromCharCode(u8[i]);
|
|
}
|
|
ac.r = "";
|
|
for (let i = 92; u8[i] && i < 104; i++) {
|
|
ac.r += String.fromCharCode(u8[i]);
|
|
}
|
|
ac.receiverCount = u8[104];
|
|
|
|
ac.rssi = 10 * Math.log(u8[105]*u8[105]/65025 + 1.125e-5)/Math.log(10);
|
|
|
|
ac.extraFlags = u8[106];
|
|
ac.nogps = ac.extraFlags & 1;
|
|
if (ac.nogps && nogpsOnly && s32[3] != INT32_MAX) {
|
|
u8[73] |= 64;
|
|
u8[73] |= 16;
|
|
}
|
|
|
|
// must come after the stuff above (validity bits)
|
|
|
|
ac.nic_baro = (u8[73] & 1);
|
|
ac.alert1 = (u8[73] & 2);
|
|
ac.spi = (u8[73] & 4);
|
|
ac.flight = (u8[73] & 8) ? ac.flight : undefined;
|
|
ac.alt_baro = (u8[73] & 16) ? ac.alt_baro : undefined;
|
|
ac.alt_geom = (u8[73] & 32) ? ac.alt_geom : undefined;
|
|
|
|
ac.lat = (u8[73] & 64) ? ac.lat : undefined;
|
|
ac.lon = (u8[73] & 64) ? ac.lon : undefined;
|
|
ac.seen_pos = (u8[73] & 64) ? ac.seen_pos : undefined;
|
|
|
|
ac.gs = (u8[73] & 128) ? ac.gs : undefined;
|
|
|
|
ac.ias = (u8[74] & 1) ? ac.ias : undefined;
|
|
ac.tas = (u8[74] & 2) ? ac.tas : undefined;
|
|
ac.mach = (u8[74] & 4) ? ac.mach : undefined;
|
|
ac.track = (u8[74] & 8) ? ac.track : undefined;
|
|
ac.calc_track = !(u8[74] & 8) ? ac.track : undefined;
|
|
ac.track_rate = (u8[74] & 16) ? ac.track_rate : undefined;
|
|
ac.roll = (u8[74] & 32) ? ac.roll : undefined;
|
|
ac.mag_heading = (u8[74] & 64) ? ac.mag_heading : undefined;
|
|
ac.true_heading = (u8[74] & 128) ? ac.true_heading : undefined;
|
|
|
|
ac.baro_rate = (u8[75] & 1) ? ac.baro_rate : undefined;
|
|
ac.geom_rate = (u8[75] & 2) ? ac.geom_rate : undefined;
|
|
ac.nic_a = (u8[75] & 4) ? ac.nic_a : undefined;
|
|
ac.nic_c = (u8[75] & 8) ? ac.nic_c : undefined;
|
|
ac.nic_baro = (u8[75] & 16) ? ac.nic_baro : undefined;
|
|
ac.nac_p = (u8[75] & 32) ? ac.nac_p : undefined;
|
|
ac.nac_v = (u8[75] & 64) ? ac.nac_v : undefined;
|
|
ac.sil = (u8[75] & 128) ? ac.sil : undefined;
|
|
|
|
ac.gva = (u8[76] & 1) ? ac.gva : undefined;
|
|
ac.sda = (u8[76] & 2) ? ac.sda : undefined;
|
|
ac.squawk = (u8[76] & 4) ? ac.squawk : undefined;
|
|
ac.emergency = (u8[76] & 8) ? ac.emergency : undefined;
|
|
ac.spi = (u8[76] & 16) ? ac.spi : undefined;
|
|
ac.nav_qnh = (u8[76] & 32) ? ac.nav_qnh : undefined;
|
|
ac.nav_altitude_mcp = (u8[76] & 64) ? ac.nav_altitude_mcp : undefined;
|
|
ac.nav_altitude_fms = (u8[76] & 128) ? ac.nav_altitude_fms : undefined;
|
|
|
|
ac.nav_altitude_src = (u8[77] & 1) ? ac.nav_altitude_src : undefined;
|
|
ac.nav_heading = (u8[77] & 2) ? ac.nav_heading : undefined;
|
|
ac.nav_modes = (u8[77] & 4) ? ac.nav_modes : undefined;
|
|
ac.alert1 = (u8[77] & 8) ? ac.alert1 : undefined;
|
|
ac.ws = (u8[77] & 16) ? ac.ws : undefined;
|
|
ac.wd = (u8[77] & 16) ? ac.wd : undefined;
|
|
ac.oat = (u8[77] & 32) ? ac.oat : undefined;
|
|
ac.tat = (u8[77] & 32) ? ac.tat : undefined;
|
|
|
|
if (ac.airground == 1)
|
|
ac.alt_baro = "ground";
|
|
|
|
if (ac.nav_modes) {
|
|
ac.nav_modes = [];
|
|
if (nav_modes & 1) ac.nav_modes.push('autopilot');
|
|
if (nav_modes & 2) ac.nav_modes.push('vnav');
|
|
if (nav_modes & 4) ac.nav_modes.push('alt_hold');
|
|
if (nav_modes & 8) ac.nav_modes.push('approach');
|
|
if (nav_modes & 16) ac.nav_modes.push('lnav');
|
|
if (nav_modes & 32) ac.nav_modes.push('tcas');
|
|
}
|
|
switch (ac.type) {
|
|
case 0: ac.type = 'adsb_icao'; break;
|
|
case 1: ac.type = 'adsb_icao_nt'; break;
|
|
case 2: ac.type = 'adsr_icao'; break;
|
|
case 3: ac.type = 'tisb_icao'; break;
|
|
case 4: ac.type = 'adsc'; break;
|
|
case 5: ac.type = 'mlat'; break;
|
|
case 6: ac.type = 'other'; break;
|
|
case 7: ac.type = 'mode_s'; break;
|
|
case 8: ac.type = 'adsb_other'; break;
|
|
case 9: ac.type = 'adsr_other'; break;
|
|
case 10: ac.type = 'tisb_trackfile'; break;
|
|
case 11: ac.type = 'tisb_other'; break;
|
|
case 12: ac.type = 'mode_ac'; break;
|
|
default: ac.type = 'unknown';
|
|
}
|
|
const type4 = ac.type.slice(0, 4);
|
|
if (type4 == 'adsb') {
|
|
ac.version = ac.adsb_version;
|
|
} else if (type4 == 'adsr') {
|
|
ac.version = ac.adsr_version;
|
|
} else if (type4 == 'tisb') {
|
|
ac.version = ac.tisb_version;
|
|
}
|
|
if (stride == 112) {
|
|
ac.rId = u32[27].toString(16).padStart(8, '0');
|
|
//ac.rId = ac.rId.slice(0, 4) + '-' + ac.rId.slice(4);
|
|
}
|
|
|
|
data.aircraft.push(ac);
|
|
}
|
|
}
|
|
|
|
function ItemCache(maxItems) {
|
|
this.maxItems = maxItems;
|
|
this.items = {};
|
|
this.keys = [];
|
|
}
|
|
ItemCache.prototype.clear = function() {
|
|
this.items = {};
|
|
this.keys = [];
|
|
}
|
|
ItemCache.prototype.get = function(key) {
|
|
return this.items[key];
|
|
}
|
|
ItemCache.prototype.add = function(key, value) {
|
|
|
|
if (!(key in this.items)) {
|
|
this.keys.push(key);
|
|
}
|
|
this.items[key] = value;
|
|
|
|
if (this.maxItems && this.maxItems > 0) {
|
|
while (this.keys.length > this.maxItems) {
|
|
const key = this.keys.shift();
|
|
delete this.items[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
function itemCacheTest() {
|
|
let a = new ItemCache(4);
|
|
a.add(8, 4);
|
|
a.add(5, 2);
|
|
a.add(4, 2);
|
|
a.add(3, 2);
|
|
a.add(1, 2);
|
|
a.add(1, 3);
|
|
a.add(1, 5);
|
|
let items = JSON.stringify(a.items)
|
|
let keys = JSON.stringify(a.keys);
|
|
const expectedItems = '{"1":5,"3":2,"4":2,"5":2}';
|
|
const expectedKeys = '[5,4,3,1]';
|
|
if (items != expectedItems || keys != expectedKeys || g.get(1) != 5) {
|
|
console.error(`ItemCache broken!`);
|
|
console.log(`got: items: ${items} keys: ${keys}`);
|
|
console.log(`expected: items: ${expectedItems} keys: ${expectedKeys}`);
|
|
} else {
|
|
console.log(`ItemCache tested correctly!`);
|
|
}
|
|
}
|