mirror of
https://github.com/craigerl/aprsd.git
synced 2024-12-12 09:55:10 -05:00
Update WebChat
This patch changes the location string to include Compass rose instead of bearing degrees. Also adds the time timeago package for calculating how much time since the beacon to format to something like "12 days ago".
This commit is contained in:
parent
d42638efe3
commit
9f7d169c18
@ -1,7 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import math
|
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
@ -14,13 +13,14 @@ from flask_httpauth import HTTPBasicAuth
|
|||||||
from flask_socketio import Namespace, SocketIO
|
from flask_socketio import Namespace, SocketIO
|
||||||
from geopy.distance import geodesic
|
from geopy.distance import geodesic
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
import timeago
|
||||||
from werkzeug.security import check_password_hash, generate_password_hash
|
from werkzeug.security import check_password_hash, generate_password_hash
|
||||||
import wrapt
|
import wrapt
|
||||||
|
|
||||||
import aprsd
|
import aprsd
|
||||||
from aprsd import (
|
from aprsd import cli_helper, client, packets, plugin_utils, stats, threads
|
||||||
cli_helper, client, packets, plugin_utils, stats, threads, utils,
|
from aprsd import utils
|
||||||
)
|
from aprsd import utils as aprsd_utils
|
||||||
from aprsd.client import client_factory, kiss
|
from aprsd.client import client_factory, kiss
|
||||||
from aprsd.main import cli
|
from aprsd.main import cli
|
||||||
from aprsd.threads import aprsd as aprsd_threads
|
from aprsd.threads import aprsd as aprsd_threads
|
||||||
@ -131,47 +131,6 @@ def verify_password(username, password):
|
|||||||
return username
|
return username
|
||||||
|
|
||||||
|
|
||||||
def calculate_initial_compass_bearing(point_a, point_b):
|
|
||||||
"""
|
|
||||||
Calculates the bearing between two points.
|
|
||||||
The formulae used is the following:
|
|
||||||
θ = atan2(sin(Δlong).cos(lat2),
|
|
||||||
cos(lat1).sin(lat2) − sin(lat1).cos(lat2).cos(Δlong))
|
|
||||||
:Parameters:
|
|
||||||
- `pointA: The tuple representing the latitude/longitude for the
|
|
||||||
first point. Latitude and longitude must be in decimal degrees
|
|
||||||
- `pointB: The tuple representing the latitude/longitude for the
|
|
||||||
second point. Latitude and longitude must be in decimal degrees
|
|
||||||
:Returns:
|
|
||||||
The bearing in degrees
|
|
||||||
:Returns Type:
|
|
||||||
float
|
|
||||||
"""
|
|
||||||
if (type(point_a) is not tuple) or (type(point_b) is not tuple):
|
|
||||||
raise TypeError("Only tuples are supported as arguments")
|
|
||||||
|
|
||||||
lat1 = math.radians(point_a[0])
|
|
||||||
lat2 = math.radians(point_b[0])
|
|
||||||
|
|
||||||
diff_long = math.radians(point_b[1] - point_a[1])
|
|
||||||
|
|
||||||
x = math.sin(diff_long) * math.cos(lat2)
|
|
||||||
y = math.cos(lat1) * math.sin(lat2) - (
|
|
||||||
math.sin(lat1)
|
|
||||||
* math.cos(lat2) * math.cos(diff_long)
|
|
||||||
)
|
|
||||||
|
|
||||||
initial_bearing = math.atan2(x, y)
|
|
||||||
|
|
||||||
# Now we have the initial bearing but math.atan2 return values
|
|
||||||
# from -180° to + 180° which is not what we want for a compass bearing
|
|
||||||
# The solution is to normalize the initial bearing as shown below
|
|
||||||
initial_bearing = math.degrees(initial_bearing)
|
|
||||||
compass_bearing = (initial_bearing + 360) % 360
|
|
||||||
|
|
||||||
return compass_bearing
|
|
||||||
|
|
||||||
|
|
||||||
def _build_location_from_repeat(message):
|
def _build_location_from_repeat(message):
|
||||||
# This is a location message Format is
|
# This is a location message Format is
|
||||||
# ^ld^callsign:latitude,longitude,altitude,course,speed,timestamp
|
# ^ld^callsign:latitude,longitude,altitude,course,speed,timestamp
|
||||||
@ -188,16 +147,19 @@ def _build_location_from_repeat(message):
|
|||||||
course = float(b[3])
|
course = float(b[3])
|
||||||
speed = float(b[4])
|
speed = float(b[4])
|
||||||
time = int(b[5])
|
time = int(b[5])
|
||||||
|
compass_bearing = aprsd_utils.degrees_to_cardinal(course)
|
||||||
data = {
|
data = {
|
||||||
"callsign": callsign,
|
"callsign": callsign,
|
||||||
"lat": lat,
|
"lat": lat,
|
||||||
"lon": lon,
|
"lon": lon,
|
||||||
"altitude": alt,
|
"altitude": alt,
|
||||||
"course": course,
|
"course": course,
|
||||||
|
"compass_bearing": compass_bearing,
|
||||||
"speed": speed,
|
"speed": speed,
|
||||||
"lasttime": time,
|
"lasttime": time,
|
||||||
|
"timeago": timeago.format(time),
|
||||||
}
|
}
|
||||||
LOG.warning(f"Location data from REPEAT {data}")
|
LOG.debug(f"Location data from REPEAT {data}")
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@ -208,25 +170,32 @@ def _calculate_location_data(location_data):
|
|||||||
alt = location_data["altitude"]
|
alt = location_data["altitude"]
|
||||||
speed = location_data["speed"]
|
speed = location_data["speed"]
|
||||||
lasttime = location_data["lasttime"]
|
lasttime = location_data["lasttime"]
|
||||||
|
timeago_str = location_data.get(
|
||||||
|
"timeago",
|
||||||
|
timeago.format(lasttime),
|
||||||
|
)
|
||||||
# now calculate distance from our own location
|
# now calculate distance from our own location
|
||||||
distance = 0
|
distance = 0
|
||||||
if CONF.webchat.latitude and CONF.webchat.longitude:
|
if CONF.webchat.latitude and CONF.webchat.longitude:
|
||||||
our_lat = float(CONF.webchat.latitude)
|
our_lat = float(CONF.webchat.latitude)
|
||||||
our_lon = float(CONF.webchat.longitude)
|
our_lon = float(CONF.webchat.longitude)
|
||||||
distance = geodesic((our_lat, our_lon), (lat, lon)).kilometers
|
distance = geodesic((our_lat, our_lon), (lat, lon)).kilometers
|
||||||
bearing = calculate_initial_compass_bearing(
|
bearing = aprsd_utils.calculate_initial_compass_bearing(
|
||||||
(our_lat, our_lon),
|
(our_lat, our_lon),
|
||||||
(lat, lon),
|
(lat, lon),
|
||||||
)
|
)
|
||||||
|
compass_bearing = aprsd_utils.degrees_to_cardinal(bearing)
|
||||||
return {
|
return {
|
||||||
"callsign": location_data["callsign"],
|
"callsign": location_data["callsign"],
|
||||||
"lat": lat,
|
"lat": lat,
|
||||||
"lon": lon,
|
"lon": lon,
|
||||||
"altitude": alt,
|
"altitude": alt,
|
||||||
"course": f"{bearing:0.1f}",
|
"course": f"{bearing:0.1f}",
|
||||||
|
"compass_bearing": compass_bearing,
|
||||||
"speed": speed,
|
"speed": speed,
|
||||||
"lasttime": lasttime,
|
"lasttime": lasttime,
|
||||||
"distance": f"{distance:0.3f}",
|
"timeago": timeago_str,
|
||||||
|
"distance": f"{distance:0.1f}",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -174,14 +174,29 @@ def load_entry_points(group):
|
|||||||
print(traceback.format_exc(), file=sys.stderr)
|
print(traceback.format_exc(), file=sys.stderr)
|
||||||
|
|
||||||
|
|
||||||
def calculate_initial_compass_bearing(start, end):
|
def calculate_initial_compass_bearing(point_a, point_b):
|
||||||
if (type(start) != tuple) or (type(end) != tuple): # noqa: E721
|
"""
|
||||||
|
Calculates the bearing between two points.
|
||||||
|
The formulae used is the following:
|
||||||
|
θ = atan2(sin(Δlong).cos(lat2),
|
||||||
|
cos(lat1).sin(lat2) − sin(lat1).cos(lat2).cos(Δlong))
|
||||||
|
:Parameters:
|
||||||
|
- `pointA: The tuple representing the latitude/longitude for the
|
||||||
|
first point. Latitude and longitude must be in decimal degrees
|
||||||
|
- `pointB: The tuple representing the latitude/longitude for the
|
||||||
|
second point. Latitude and longitude must be in decimal degrees
|
||||||
|
:Returns:
|
||||||
|
The bearing in degrees
|
||||||
|
:Returns Type:
|
||||||
|
float
|
||||||
|
"""
|
||||||
|
if (type(point_a) != tuple) or (type(point_b) != tuple): # noqa: E721
|
||||||
raise TypeError("Only tuples are supported as arguments")
|
raise TypeError("Only tuples are supported as arguments")
|
||||||
|
|
||||||
lat1 = math.radians(float(start[0]))
|
lat1 = math.radians(float(point_a[0]))
|
||||||
lat2 = math.radians(float(end[0]))
|
lat2 = math.radians(float(point_b[0]))
|
||||||
|
|
||||||
diff_long = math.radians(float(end[1]) - float(start[1]))
|
diff_long = math.radians(float(point_b[1]) - float(point_a[1]))
|
||||||
|
|
||||||
x = math.sin(diff_long) * math.cos(lat2)
|
x = math.sin(diff_long) * math.cos(lat2)
|
||||||
y = math.cos(lat1) * math.sin(lat2) - (
|
y = math.cos(lat1) * math.sin(lat2) - (
|
||||||
|
@ -22,20 +22,19 @@ function build_location_string(msg) {
|
|||||||
loc += "<br>Latitude: " + msg['lat'] + "<br>Longitude: " + msg['lon'];
|
loc += "<br>Latitude: " + msg['lat'] + "<br>Longitude: " + msg['lon'];
|
||||||
loc += "<br>" + "Altitude: " + msg['altitude'] + " m";
|
loc += "<br>" + "Altitude: " + msg['altitude'] + " m";
|
||||||
loc += "<br>" + "Speed: " + msg['speed'] + " kph";
|
loc += "<br>" + "Speed: " + msg['speed'] + " kph";
|
||||||
loc += "<br>" + "Bearing: " + msg['course'] + "°";
|
loc += "<br>" + "Bearing: " + msg['compass_bearing'];
|
||||||
loc += "<br>" + "distance: " + msg['distance'] + " km";
|
loc += "<br>" + "distance: " + msg['distance'] + " km";
|
||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
function build_location_string_small(msg) {
|
function build_location_string_small(msg) {
|
||||||
|
|
||||||
dt = new Date(parseInt(msg['lasttime']) * 1000);
|
dt = new Date(parseInt(msg['lasttime']) * 1000);
|
||||||
|
|
||||||
loc = "" + msg['distance'] + "km";
|
loc = "" + msg['distance'] + "km";
|
||||||
//loc += "Lat " + msg['lat'] + " Lon " + msg['lon'];
|
//loc += "Lat " + msg['lat'] + " Lon " + msg['lon'];
|
||||||
loc += "@" + msg['course'] + "°";
|
loc += " " + msg['compass_bearing'];
|
||||||
//loc += " Distance " + msg['distance'] + " km";
|
//loc += " Distance " + msg['distance'] + " km";
|
||||||
loc += " " + dt.toLocaleString();
|
//loc += " " + dt.toLocaleString();
|
||||||
|
loc += " " + msg['timeago'];
|
||||||
return loc;
|
return loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,9 +345,9 @@ function create_callsign_tab_content(callsign, active=false) {
|
|||||||
item_html += '<div class="" style="border: 1px solid #999999;background-color:#aaaaaa;">';
|
item_html += '<div class="" style="border: 1px solid #999999;background-color:#aaaaaa;">';
|
||||||
item_html += '<div class="row" style="padding-top:4px;padding-bottom:4px;background-color:#aaaaaa;margin:0px;">';
|
item_html += '<div class="row" style="padding-top:4px;padding-bottom:4px;background-color:#aaaaaa;margin:0px;">';
|
||||||
item_html += '<div class="d-flex col-md-10 justify-content-left" style="padding:0px;margin:0px;">';
|
item_html += '<div class="d-flex col-md-10 justify-content-left" style="padding:0px;margin:0px;">';
|
||||||
item_html += '<button onclick="call_callsign_location(\''+callsign+'\');" style="margin-left:2px;padding: 0px 4px 0px 4px;" type="button" class="btn btn-primary">';
|
item_html += '<button onclick="call_callsign_location(\''+callsign+'\');" style="margin-left:2px;padding: 0px 4px 0px 4px;font-size: .9rem" type="button" class="btn btn-primary">';
|
||||||
item_html += '<span id="'+location_id+'Spinner" class="d-none spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>Update</button>';
|
item_html += '<span id="'+location_id+'Spinner" class="d-none spinner-border spinner-border-sm" role="status" aria-hidden="true" style="font-size: .9rem"></span>Update</button>';
|
||||||
item_html += ' <span id="'+location_id+'">'+location_str+'</span></div>';
|
item_html += ' <span id="'+location_id+'" style="font-size: .9rem">'+location_str+'</span></div>';
|
||||||
item_html += '</div>';
|
item_html += '</div>';
|
||||||
item_html += '<div class="speech-wrapper" id="'+wrapper_id+'"></div>';
|
item_html += '<div class="speech-wrapper" id="'+wrapper_id+'"></div>';
|
||||||
item_html += '</div>';
|
item_html += '</div>';
|
||||||
|
@ -34,7 +34,7 @@ imagesize==1.4.1 # via sphinx
|
|||||||
iniconfig==2.0.0 # via pytest
|
iniconfig==2.0.0 # via pytest
|
||||||
isort==5.13.2 # via -r requirements-dev.in, gray
|
isort==5.13.2 # via -r requirements-dev.in, gray
|
||||||
jinja2==3.1.4 # via sphinx
|
jinja2==3.1.4 # via sphinx
|
||||||
libcst==1.5.0 # via fixit
|
libcst==1.5.1 # via fixit
|
||||||
m2r==0.3.1 # via -r requirements-dev.in
|
m2r==0.3.1 # via -r requirements-dev.in
|
||||||
markupsafe==3.0.2 # via jinja2
|
markupsafe==3.0.2 # via jinja2
|
||||||
mccabe==0.7.0 # via flake8
|
mccabe==0.7.0 # via flake8
|
||||||
|
@ -27,3 +27,4 @@ tzlocal
|
|||||||
update_checker
|
update_checker
|
||||||
wrapt
|
wrapt
|
||||||
pytz
|
pytz
|
||||||
|
timeago
|
||||||
|
@ -37,8 +37,8 @@ markupsafe==3.0.2 # via jinja2, werkzeug
|
|||||||
marshmallow==3.23.1 # via dataclasses-json
|
marshmallow==3.23.1 # via dataclasses-json
|
||||||
mypy-extensions==1.0.0 # via typing-inspect
|
mypy-extensions==1.0.0 # via typing-inspect
|
||||||
netaddr==1.3.0 # via oslo-config
|
netaddr==1.3.0 # via oslo-config
|
||||||
oslo-config==9.6.0 # via -r requirements.in
|
oslo-config==9.7.0 # via -r requirements.in
|
||||||
oslo-i18n==6.4.0 # via oslo-config
|
oslo-i18n==6.5.0 # via oslo-config
|
||||||
packaging==24.2 # via marshmallow
|
packaging==24.2 # via marshmallow
|
||||||
pbr==6.1.0 # via oslo-i18n, stevedore
|
pbr==6.1.0 # via oslo-i18n, stevedore
|
||||||
pluggy==1.5.0 # via -r requirements.in
|
pluggy==1.5.0 # via -r requirements.in
|
||||||
@ -57,9 +57,10 @@ shellingham==1.5.4 # via -r requirements.in
|
|||||||
simple-websocket==1.1.0 # via python-engineio
|
simple-websocket==1.1.0 # via python-engineio
|
||||||
six==1.16.0 # via -r requirements.in
|
six==1.16.0 # via -r requirements.in
|
||||||
soupsieve==2.6 # via beautifulsoup4
|
soupsieve==2.6 # via beautifulsoup4
|
||||||
stevedore==5.3.0 # via oslo-config
|
stevedore==5.4.0 # via oslo-config
|
||||||
tabulate==0.9.0 # via -r requirements.in
|
tabulate==0.9.0 # via -r requirements.in
|
||||||
thesmuggler==1.0.1 # via -r requirements.in
|
thesmuggler==1.0.1 # via -r requirements.in
|
||||||
|
timeago==1.0.16 # via -r requirements.in
|
||||||
typing-extensions==4.12.2 # via typing-inspect
|
typing-extensions==4.12.2 # via typing-inspect
|
||||||
typing-inspect==0.9.0 # via dataclasses-json
|
typing-inspect==0.9.0 # via dataclasses-json
|
||||||
tzlocal==5.2 # via -r requirements.in
|
tzlocal==5.2 # via -r requirements.in
|
||||||
|
Loading…
Reference in New Issue
Block a user