Added az/el/range display, and LOS line.

pull/84/head
Mark Jessop 2018-08-01 21:07:55 +09:30
rodzic abbc448ec4
commit cfe6ae5ecd
4 zmienionych plików z 145 dodań i 7 usunięć

Wyświetl plik

@ -18,7 +18,7 @@ import traceback
import autorx
from autorx.scan import SondeScanner
from autorx.decode import SondeDecoder
from autorx.decode import SondeDecoder, VALID_SONDE_TYPES
from autorx.logger import TelemetryLogger
from autorx.email_notification import EmailNotification
from autorx.habitat import HabitatUploader
@ -230,6 +230,12 @@ def handle_scan_results():
continue
else:
logging.info("Detected new %s sonde on %.3f MHz!" % (_type, _freq/1e6))
# Break if we don't support this sonde type.
if (_type not in VALID_SONDE_TYPES):
logging.error("Unsupported sonde type: %s" % _type)
continue
if allocate_sdr(check_only=True) is not None :
# There is a SDR free! Start the decoder on that SDR
start_decoder(_freq, _type)

Wyświetl plik

@ -18,6 +18,8 @@ from types import FunctionType, MethodType
from .utils import AsynchronousFileReader, rtlsdr_test
from .gps import get_ephemeris, get_almanac
# Global valid sonde types list.
VALID_SONDE_TYPES = ['RS92', 'RS41', 'DFM']
class SondeDecoder(object):

Wyświetl plik

@ -6,6 +6,9 @@
var colour_values = ['blue','red','green','purple'];
var colour_idx = 0;
var los_color = '#00FF00';
var los_opacity = 0.6;
// Create a set of icons for the different colour values.
var sondeAscentIcons = {};
var sondeDescentIcons = {};
@ -23,3 +26,56 @@ for (_col in colour_values){
iconAnchor: [23, 76]
});
}
// calculates look angles between two points
// format of a and b should be {lon: 0, lat: 0, alt: 0}
// returns {elevention: 0, azimut: 0, bearing: "", range: 0}
//
// based on earthmath.py
// Copyright 2012 (C) Daniel Richman; GNU GPL 3
var DEG_TO_RAD = Math.PI / 180.0;
var EARTH_RADIUS = 6371000.0;
function calculate_lookangles(a, b) {
// degrees to radii
a.lat = a.lat * DEG_TO_RAD;
a.lon = a.lon * DEG_TO_RAD;
b.lat = b.lat * DEG_TO_RAD;
b.lon = b.lon * DEG_TO_RAD;
var d_lon = b.lon - a.lon;
var sa = Math.cos(b.lat) * Math.sin(d_lon);
var sb = (Math.cos(a.lat) * Math.sin(b.lat)) - (Math.sin(a.lat) * Math.cos(b.lat) * Math.cos(d_lon));
var bearing = Math.atan2(sa, sb);
var aa = Math.sqrt(Math.pow(sa, 2) + Math.pow(sb, 2));
var ab = (Math.sin(a.lat) * Math.sin(b.lat)) + (Math.cos(a.lat) * Math.cos(b.lat) * Math.cos(d_lon));
var angle_at_centre = Math.atan2(aa, ab);
var great_circle_distance = angle_at_centre * EARTH_RADIUS;
ta = EARTH_RADIUS + a.alt;
tb = EARTH_RADIUS + b.alt;
ea = (Math.cos(angle_at_centre) * tb) - ta;
eb = Math.sin(angle_at_centre) * tb;
var elevation = Math.atan2(ea, eb) / DEG_TO_RAD;
// Use Math.coMath.sine rule to find unknown side.
var distance = Math.sqrt(Math.pow(ta, 2) + Math.pow(tb, 2) - 2 * tb * ta * Math.cos(angle_at_centre));
// Give a bearing in range 0 <= b < 2pi
bearing += (bearing < 0) ? 2 * Math.PI : 0;
bearing /= DEG_TO_RAD;
var value = Math.round(bearing % 90);
value = ((bearing > 90 && bearing < 180) || (bearing > 270 && bearing < 360)) ? 90 - value : value;
var str_bearing = "" + ((bearing < 90 || bearing > 270) ? 'N' : 'S')+ " " + value + '° ' + ((bearing < 180) ? 'E' : 'W');
return {
'elevation': elevation,
'azimuth': bearing,
'range': distance,
'bearing': str_bearing
};
}

Wyświetl plik

@ -147,6 +147,14 @@
var home_marker = L.marker(sondemap.getCenter(),
{title: 'Receiver Location', icon: homeIcon}
).addTo(sondemap);
function mapMovedEvent(e){
// The user has panned the map, stop following things.
$("#sondeAutoFollow").prop('checked', false);
}
sondemap.on('dragend',mapMovedEvent);
// Telemetry Data Table
// Using tabulator makes this *really* easy.
@ -156,16 +164,20 @@
layoutColumnsOnNewData:true,
columns:[ //Define Table Columns
{title:"SDR", field:"sdr_device_idx", headerSort:false},
{title:"Age", field:"age", headerSort:false},
{title:"Type", field:"type", headerSort:false},
{title:'Freq (MHz)', field:"freq", headerSort:false},
{title:"ID", field:"id", formatter:'html', headerSort:false},
{title:"Timestamp", field:"datetime", headerSort:false},
{title:"Time", field:"datetime", headerSort:false},
{title:"Frame", field:"frame", headerSort:false},
{title:"Latitude", field:"lat", headerSort:false},
{title:"Longitude", field:"lon", headerSort:false},
{title:"Altitude (m)", field:"alt", headerSort:false},
{title:"Asc Rate (m/s)", field:"vel_v", headerSort:false},
{title:"Temp (deg C)", field:"temp", headerSort:false}
{title:"Alt (m)", field:"alt", headerSort:false},
{title:"Asc (m/s)", field:"vel_v", headerSort:false},
{title:"Temp (°C)", field:"temp", headerSort:false},
{title:"Az (°)", field:"azimuth", headerSort:false},
{title:"El (°)", field:"elevation", headerSort:false},
{title:"Range (km)", field:"range", headerSort:false}
// TODO: Azimuth, elevation, Range calculations.
]
});
@ -178,10 +190,32 @@
}else{
for (sonde_id in sonde_positions){
var sonde_id_data = Object.assign({},sonde_positions[sonde_id].latest_data);
var sonde_id_age = sonde_positions[sonde_id].age;
if ((Date.now()-sonde_id_age)>180000){
var sonde_id_age = Date.now() - sonde_positions[sonde_id].age;
if (sonde_id_age>180000){
sonde_id_data.sdr_device_idx = "";
sonde_id_data.age = "old";
}else{
sonde_id_data.age = (sonde_id_age/1000.0).toFixed(0) + " s";
}
// If we have a station lat/lon/alt set, calculate az/el/range.
if (autorx_config.station_lat != 0.0){
// There is a station lat/lon set.
var _bal = {lat:sonde_id_data.lat, lon:sonde_id_data.lon, alt:sonde_id_data.alt};
var _station = {lat:autorx_config.station_lat, lon:autorx_config.station_lon, alt:autorx_config.station_alt};
var _look_angles = calculate_lookangles(_station, _bal);
sonde_id_data.azimuth = _look_angles.azimuth.toFixed(0);
sonde_id_data.elevation = _look_angles.elevation.toFixed(0);
sonde_id_data.range = (_look_angles.range/1000).toFixed(1);
} else{
// Insert blank data.
sonde_id_data.azimuth = "";
sonde_id_data.elevation = "";
sonde_id_data.range = "";
}
// Modify some of the fields to fixed point values.
sonde_id_data.lat = sonde_id_data.lat.toFixed(5);
sonde_id_data.lon = sonde_id_data.lon.toFixed(5);
@ -193,6 +227,7 @@
sonde_id_data.id = "<a href='http://sondehub.org/" + sonde_id + "' target='_blank'>" + sonde_id + "</a>";
}
}
telem_data.push(sonde_id_data);
}
}
@ -244,6 +279,15 @@
.bindTooltip(sonde_id,{permanent:false,direction:'right'})
.addTo(sondemap);
if(autorx_config.station_lat != 0.0){
sonde_positions[sonde_id].los_path = L.polyline([],
{
color:los_color,
opacity:los_opacity
}
).addTo(sondemap);
}
if (telem.vel_v < 0){
sonde_positions[sonde_id].marker.setIcon(sondeDescentIcons[sonde_positions[sonde_id].colour]);
}
@ -284,6 +328,16 @@
.bindTooltip(msg.id,{permanent:false,direction:'right'})
.addTo(sondemap);
// If there is a station location defined, show the path from the station to the sonde.
if(autorx_config.station_lat != 0.0){
sonde_positions[msg.id].los_path = L.polyline([[autorx_config.station_lat, autorx_config.station_lon],[msg.lat, msg.lon]],
{
color:los_color,
opacity:los_opacity
}
).addTo(sondemap);
}
colour_idx = (colour_idx+1)%colour_values.length;
// If this is our first sonde since the browser has been opened, follow it.
if (Object.keys(sonde_positions).length == 1){
@ -301,6 +355,10 @@
}else{
sonde_positions[msg.id].marker.setIcon(sondeAscentIcons[sonde_positions[msg.id].colour]);
}
if(autorx_config.station_lat != 0.0){
sonde_positions[msg.id].los_path.setLatLngs([[autorx_config.station_lat, autorx_config.station_lon],[msg.lat, msg.lon]]);
}
}
// Update the telemetry table display
@ -338,6 +396,12 @@
}, sonde_follow_timeout);
// Update telemetry table every 500ms
window.setInterval(function(){
updateTelemetryTable();
}, 1000);
// Tell the server we are connected and ready for data.
socket.on('connect', function() {
socket.emit('client_connected', {data: 'I\'m connected!'});
@ -353,6 +417,15 @@
}
});
// Callback function for Hide Map checkbox.
$( "#hideMap" ).click(function() {
if (document.getElementById("hideMap").checked == true){
$("#sonde_map").hide("fast");
} else {
$("#sonde_map").show("fast");
}
});
});
</script>
</head>
@ -371,6 +444,7 @@
<div class='col-12'>
Auto-Follow Latest Sonde: <input type="checkbox" id="sondeAutoFollow" checked>
<div id="sonde_map" style="height:400px;width:100%"></div>
Hide Map: <input type="checkbox" id="hideMap">
<br>
</div>
</div>