kopia lustrzana https://github.com/projecthorus/radiosonde_auto_rx
Added az/el/range display, and LOS line.
rodzic
abbc448ec4
commit
cfe6ae5ecd
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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>
|
||||
|
|
Ładowanie…
Reference in New Issue