mirror of
https://github.com/ha7ilm/openwebrx.git
synced 2026-01-29 11:34:19 +01:00
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.types import Format
|
||||
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):
|
||||
|
|
|
|||
|
|
@ -272,6 +272,7 @@ $.fn.pocsagMessagePanel = function() {
|
|||
|
||||
AdsbMessagePanel = function(el) {
|
||||
MessagePanel.call(this, el);
|
||||
this.aircraft = {}
|
||||
this.initClearTimer();
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +287,11 @@ AdsbMessagePanel.prototype.render = function() {
|
|||
'<table>' +
|
||||
'<thead><tr>' +
|
||||
'<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>' +
|
||||
'<tbody></tbody>' +
|
||||
'</table>'
|
||||
|
|
@ -295,16 +300,64 @@ AdsbMessagePanel.prototype.render = function() {
|
|||
|
||||
|
||||
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');
|
||||
$b.append($(
|
||||
'<tr>' +
|
||||
'<td class="address"></td>' +
|
||||
'<td class="message">' + JSON.stringify(message) + '</td>' +
|
||||
'</tr>'
|
||||
));
|
||||
$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 () {
|
||||
if (!this.data('panel')) {
|
||||
this.data('panel', new AdsbMessagePanel(this));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from pycsdr.modules import ExecModule, Writer, TcpSource
|
||||
from pycsdr.types import Format
|
||||
from csdr.module import LogWriter, ThreadModule, PickleModule
|
||||
from csdr.module import LogWriter, ThreadModule
|
||||
from owrx.socket import getAvailablePort
|
||||
import time
|
||||
import pickle
|
||||
|
|
@ -81,11 +81,3 @@ class RawDeframer(ThreadModule):
|
|||
return bytes.fromhex(line[1:-1].decode())
|
||||
else:
|
||||
logger.warning("invalid raw message: %s", line)
|
||||
|
||||
|
||||
class ModeSParser(PickleModule):
|
||||
def process(self, input):
|
||||
return {
|
||||
"mode": "ADSB",
|
||||
"df": (input[0] & 0b11111000) >> 3
|
||||
}
|
||||
|
|
|
|||
94
owrx/adsb/modes.py
Normal file
94
owrx/adsb/modes.py
Normal file
|
|
@ -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 a new issue