parse and display some information
This commit is contained in:
parent
10337f7db8
commit
25ae3359ea
|
|
@ -1,7 +1,8 @@
|
||||||
from pycsdr.modules import Convert
|
from pycsdr.modules import Convert
|
||||||
from pycsdr.types import Format
|
from pycsdr.types import Format
|
||||||
from csdr.chain.demodulator import ServiceDemodulator
|
from csdr.chain.demodulator import ServiceDemodulator
|
||||||
from owrx.adsb.dump1090 import Dump1090Module, RawDeframer, ModeSParser
|
from owrx.adsb.dump1090 import Dump1090Module, RawDeframer
|
||||||
|
from owrx.adsb.modes import ModeSParser
|
||||||
|
|
||||||
|
|
||||||
class Dump1090(ServiceDemodulator):
|
class Dump1090(ServiceDemodulator):
|
||||||
|
|
|
||||||
|
|
@ -272,6 +272,7 @@ $.fn.pocsagMessagePanel = function() {
|
||||||
|
|
||||||
AdsbMessagePanel = function(el) {
|
AdsbMessagePanel = function(el) {
|
||||||
MessagePanel.call(this, el);
|
MessagePanel.call(this, el);
|
||||||
|
this.aircraft = {}
|
||||||
this.initClearTimer();
|
this.initClearTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -286,7 +287,11 @@ AdsbMessagePanel.prototype.render = function() {
|
||||||
'<table>' +
|
'<table>' +
|
||||||
'<thead><tr>' +
|
'<thead><tr>' +
|
||||||
'<th class="address">ICAO</th>' +
|
'<th class="address">ICAO</th>' +
|
||||||
'<th class="message">Message</th>' +
|
'<th class="callsign">Callsign</th>' +
|
||||||
|
'<th class="altitude">Altitude</th>' +
|
||||||
|
'<th class="speed">Speed</th>' +
|
||||||
|
'<th class="track">Track</th>' +
|
||||||
|
'<th class="messages">Messages</th>' +
|
||||||
'</tr></thead>' +
|
'</tr></thead>' +
|
||||||
'<tbody></tbody>' +
|
'<tbody></tbody>' +
|
||||||
'</table>'
|
'</table>'
|
||||||
|
|
@ -295,16 +300,64 @@ AdsbMessagePanel.prototype.render = function() {
|
||||||
|
|
||||||
|
|
||||||
AdsbMessagePanel.prototype.pushMessage = function(message) {
|
AdsbMessagePanel.prototype.pushMessage = function(message) {
|
||||||
|
if (!('icao' in message)) return;
|
||||||
|
if (!(message.icao in this.aircraft)) {
|
||||||
|
var el = $("<tr>");
|
||||||
|
$(this.el).find('tbody').append(el);
|
||||||
|
this.aircraft[message.icao] = {
|
||||||
|
el: el,
|
||||||
|
messages: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var state = this.aircraft[message.icao];
|
||||||
|
Object.assign(state, message);
|
||||||
|
state.lastSeen = Date.now();
|
||||||
|
state.messages += 1;
|
||||||
|
|
||||||
|
var ifDefined = function(input, formatter) {
|
||||||
|
if (typeof(input) !== 'undefined') {
|
||||||
|
if (formatter) return formatter(input);
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
state.el.html(
|
||||||
|
'<td>' + state.icao + '</td>' +
|
||||||
|
'<td>' + ifDefined(state.identification) + '</td>' +
|
||||||
|
'<td>' + ifDefined(state.altitude) + '</td>' +
|
||||||
|
'<td>' + ifDefined(state.groundspeed, Math.round) + '</td>' +
|
||||||
|
'<td>' + ifDefined(state.groundtrack, Math.round) + '</td>' +
|
||||||
|
'<td>' + state.messages + '</td>'
|
||||||
|
);
|
||||||
|
|
||||||
var $b = $(this.el).find('tbody');
|
var $b = $(this.el).find('tbody');
|
||||||
$b.append($(
|
|
||||||
'<tr>' +
|
|
||||||
'<td class="address"></td>' +
|
|
||||||
'<td class="message">' + JSON.stringify(message) + '</td>' +
|
|
||||||
'</tr>'
|
|
||||||
));
|
|
||||||
$b.scrollTop($b[0].scrollHeight);
|
$b.scrollTop($b[0].scrollHeight);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AdsbMessagePanel.prototype.clearMessages = function(toRemain) {
|
||||||
|
console.info("clearing old aircraft...");
|
||||||
|
var now = Date.now();
|
||||||
|
var me = this;
|
||||||
|
Object.entries(this.aircraft).forEach(function(e) {
|
||||||
|
if (now - e[1].lastSeen > toRemain) {
|
||||||
|
console.info("removing " + e[0]);
|
||||||
|
delete me.aircraft[e[0]];
|
||||||
|
e[1].el.remove();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
console.info("done; tracking " + Object.keys(this.aircraft).length + " aircraft");
|
||||||
|
};
|
||||||
|
|
||||||
|
AdsbMessagePanel.prototype.initClearTimer = function() {
|
||||||
|
var me = this;
|
||||||
|
if (me.removalInterval) clearInterval(me.removalInterval);
|
||||||
|
me.removalInterval = setInterval(function () {
|
||||||
|
me.clearMessages(30000);
|
||||||
|
}, 15000);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
$.fn.adsbMessagePanel = function () {
|
$.fn.adsbMessagePanel = function () {
|
||||||
if (!this.data('panel')) {
|
if (!this.data('panel')) {
|
||||||
this.data('panel', new AdsbMessagePanel(this));
|
this.data('panel', new AdsbMessagePanel(this));
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from pycsdr.modules import ExecModule, Writer, TcpSource
|
from pycsdr.modules import ExecModule, Writer, TcpSource
|
||||||
from pycsdr.types import Format
|
from pycsdr.types import Format
|
||||||
from csdr.module import LogWriter, ThreadModule, PickleModule
|
from csdr.module import LogWriter, ThreadModule
|
||||||
from owrx.socket import getAvailablePort
|
from owrx.socket import getAvailablePort
|
||||||
import time
|
import time
|
||||||
import pickle
|
import pickle
|
||||||
|
|
@ -81,11 +81,3 @@ class RawDeframer(ThreadModule):
|
||||||
return bytes.fromhex(line[1:-1].decode())
|
return bytes.fromhex(line[1:-1].decode())
|
||||||
else:
|
else:
|
||||||
logger.warning("invalid raw message: %s", line)
|
logger.warning("invalid raw message: %s", line)
|
||||||
|
|
||||||
|
|
||||||
class ModeSParser(PickleModule):
|
|
||||||
def process(self, input):
|
|
||||||
return {
|
|
||||||
"mode": "ADSB",
|
|
||||||
"df": (input[0] & 0b11111000) >> 3
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
from csdr.module import PickleModule
|
||||||
|
from math import sqrt, atan2, pi
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
FEET_PER_METER = 3.28084
|
||||||
|
|
||||||
|
|
||||||
|
class ModeSParser(PickleModule):
|
||||||
|
def process(self, input):
|
||||||
|
format = (input[0] & 0b11111000) >> 3
|
||||||
|
message = {
|
||||||
|
"mode": "ADSB",
|
||||||
|
"format": format
|
||||||
|
}
|
||||||
|
if format == 17:
|
||||||
|
message["capability"] = input[0] & 0b111
|
||||||
|
message["icao"] = input[1:4].hex()
|
||||||
|
type = (input[4] & 0b11111000) >> 3
|
||||||
|
message["type"] = type
|
||||||
|
|
||||||
|
if type in range(1, 5):
|
||||||
|
# identification message
|
||||||
|
id = [
|
||||||
|
(input[5] & 0b11111100) >> 2,
|
||||||
|
((input[5] & 0b00000011) << 4) | ((input[6] & 0b11110000) >> 4),
|
||||||
|
((input[6] & 0b00001111) << 2) | ((input[7] & 0b11000000) >> 6),
|
||||||
|
input[7] & 0b00111111,
|
||||||
|
(input[8] & 0b11111100) >> 2,
|
||||||
|
((input[8] & 0b00000011) << 4) | ((input[9] & 0b11110000) >> 4),
|
||||||
|
((input[9] & 0b00001111) << 2) | ((input[10] & 0b11000000) >> 6),
|
||||||
|
input[10] & 0b00111111
|
||||||
|
]
|
||||||
|
|
||||||
|
message["identification"] = bytes(b + (0x40 if b < 27 else 0) for b in id).decode("ascii")
|
||||||
|
|
||||||
|
elif type in range(5, 9):
|
||||||
|
# surface position
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif type in range(9, 19):
|
||||||
|
# airborne position (w/ baro altitude)
|
||||||
|
q = (input[5] & 0b1)
|
||||||
|
altitude = ((input[5] & 0b11111110) << 3) | ((input[6] & 0b1111) >> 4)
|
||||||
|
if q:
|
||||||
|
message["altitude"] = altitude * 25 - 1000
|
||||||
|
else:
|
||||||
|
# TODO: it's gray encoded
|
||||||
|
message["altitude"] = altitude * 100
|
||||||
|
|
||||||
|
elif type == 19:
|
||||||
|
# airborne velocity
|
||||||
|
subtype = input[4] & 0b111
|
||||||
|
if subtype in range(1, 3):
|
||||||
|
dew = (input[5] & 0b00000100) >> 2
|
||||||
|
vew = ((input[5] & 0b00000011) << 8) | input[6]
|
||||||
|
dns = (input[7] & 0b10000000) >> 7
|
||||||
|
vns = ((input[7] & 0b01111111) << 3) | ((input[8] & 0b1110000000) >> 5)
|
||||||
|
vx = vew - 1
|
||||||
|
if dew:
|
||||||
|
vx *= -1
|
||||||
|
vy = vns - 1
|
||||||
|
if dns:
|
||||||
|
vy *= -1
|
||||||
|
# supersonic
|
||||||
|
if subtype == 2:
|
||||||
|
vx *= 4
|
||||||
|
vy *= 4
|
||||||
|
message["groundspeed"] = sqrt(vx ** 2 + vy ** 2)
|
||||||
|
message["groundtrack"] = (atan2(vx, vy) * 360 / (2 * pi)) % 360
|
||||||
|
else:
|
||||||
|
logger.debug("subtype: %i", subtype)
|
||||||
|
|
||||||
|
elif type in range(20, 23):
|
||||||
|
# airborne position (w/GNSS height)
|
||||||
|
altitude = (input[5] << 4) | ((input[6] & 0b1111) >> 4)
|
||||||
|
message["altitude"] = altitude * FEET_PER_METER
|
||||||
|
|
||||||
|
elif type == 28:
|
||||||
|
# aircraft status
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif type == 29:
|
||||||
|
# target state and status information
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif type == 31:
|
||||||
|
# aircraft operation status
|
||||||
|
pass
|
||||||
|
|
||||||
|
return message
|
||||||
Loading…
Reference in New Issue