1
0
mirror of https://github.com/craigerl/aprsd.git synced 2024-12-03 05:25:22 -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:
Hemna 2024-11-20 11:47:56 -05:00
parent d42638efe3
commit 9f7d169c18
6 changed files with 58 additions and 73 deletions

View File

@ -1,7 +1,6 @@
import datetime
import json
import logging
import math
import signal
import sys
import threading
@ -14,13 +13,14 @@ from flask_httpauth import HTTPBasicAuth
from flask_socketio import Namespace, SocketIO
from geopy.distance import geodesic
from oslo_config import cfg
import timeago
from werkzeug.security import check_password_hash, generate_password_hash
import wrapt
import aprsd
from aprsd import (
cli_helper, client, packets, plugin_utils, stats, threads, utils,
)
from aprsd import cli_helper, client, packets, plugin_utils, stats, threads
from aprsd import utils
from aprsd import utils as aprsd_utils
from aprsd.client import client_factory, kiss
from aprsd.main import cli
from aprsd.threads import aprsd as aprsd_threads
@ -131,47 +131,6 @@ def verify_password(username, password):
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):
# This is a location message Format is
# ^ld^callsign:latitude,longitude,altitude,course,speed,timestamp
@ -188,16 +147,19 @@ def _build_location_from_repeat(message):
course = float(b[3])
speed = float(b[4])
time = int(b[5])
compass_bearing = aprsd_utils.degrees_to_cardinal(course)
data = {
"callsign": callsign,
"lat": lat,
"lon": lon,
"altitude": alt,
"course": course,
"compass_bearing": compass_bearing,
"speed": speed,
"lasttime": time,
"timeago": timeago.format(time),
}
LOG.warning(f"Location data from REPEAT {data}")
LOG.debug(f"Location data from REPEAT {data}")
return data
@ -208,25 +170,32 @@ def _calculate_location_data(location_data):
alt = location_data["altitude"]
speed = location_data["speed"]
lasttime = location_data["lasttime"]
timeago_str = location_data.get(
"timeago",
timeago.format(lasttime),
)
# now calculate distance from our own location
distance = 0
if CONF.webchat.latitude and CONF.webchat.longitude:
our_lat = float(CONF.webchat.latitude)
our_lon = float(CONF.webchat.longitude)
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),
(lat, lon),
)
compass_bearing = aprsd_utils.degrees_to_cardinal(bearing)
return {
"callsign": location_data["callsign"],
"lat": lat,
"lon": lon,
"altitude": alt,
"course": f"{bearing:0.1f}",
"compass_bearing": compass_bearing,
"speed": speed,
"lasttime": lasttime,
"distance": f"{distance:0.3f}",
"timeago": timeago_str,
"distance": f"{distance:0.1f}",
}

View File

@ -174,14 +174,29 @@ def load_entry_points(group):
print(traceback.format_exc(), file=sys.stderr)
def calculate_initial_compass_bearing(start, end):
if (type(start) != tuple) or (type(end) != tuple): # noqa: E721
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) != tuple) or (type(point_b) != tuple): # noqa: E721
raise TypeError("Only tuples are supported as arguments")
lat1 = math.radians(float(start[0]))
lat2 = math.radians(float(end[0]))
lat1 = math.radians(float(point_a[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)
y = math.cos(lat1) * math.sin(lat2) - (

View File

@ -17,25 +17,24 @@ function reload_popovers() {
}
function build_location_string(msg) {
dt = new Date(parseInt(msg['lasttime']) * 1000);
loc = "Last Location Update: " + dt.toLocaleString();
loc += "<br>Latitude: " + msg['lat'] + "<br>Longitude: " + msg['lon'];
loc += "<br>" + "Altitude: " + msg['altitude'] + " m";
loc += "<br>" + "Speed: " + msg['speed'] + " kph";
loc += "<br>" + "Bearing: " + msg['course'] + "°";
loc += "<br>" + "distance: " + msg['distance'] + " km";
return loc;
dt = new Date(parseInt(msg['lasttime']) * 1000);
loc = "Last Location Update: " + dt.toLocaleString();
loc += "<br>Latitude: " + msg['lat'] + "<br>Longitude: " + msg['lon'];
loc += "<br>" + "Altitude: " + msg['altitude'] + " m";
loc += "<br>" + "Speed: " + msg['speed'] + " kph";
loc += "<br>" + "Bearing: " + msg['compass_bearing'];
loc += "<br>" + "distance: " + msg['distance'] + " km";
return loc;
}
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 += "Lat " + msg['lat'] + "&nbsp;Lon " + msg['lon'];
loc += "@" + msg['course'] + "°";
loc += "&nbsp;" + msg['compass_bearing'];
//loc += "&nbsp;Distance " + msg['distance'] + " km";
loc += "&nbsp;" + dt.toLocaleString();
//loc += "&nbsp;" + dt.toLocaleString();
loc += "&nbsp;" + msg['timeago'];
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="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 += '<button onclick="call_callsign_location(\''+callsign+'\');" style="margin-left:2px;padding: 0px 4px 0px 4px;" 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 += '&nbsp;<span id="'+location_id+'">'+location_str+'</span></div>';
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" style="font-size: .9rem"></span>Update</button>';
item_html += '&nbsp;<span id="'+location_id+'" style="font-size: .9rem">'+location_str+'</span></div>';
item_html += '</div>';
item_html += '<div class="speech-wrapper" id="'+wrapper_id+'"></div>';
item_html += '</div>';

View File

@ -34,7 +34,7 @@ imagesize==1.4.1 # via sphinx
iniconfig==2.0.0 # via pytest
isort==5.13.2 # via -r requirements-dev.in, gray
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
markupsafe==3.0.2 # via jinja2
mccabe==0.7.0 # via flake8

View File

@ -27,3 +27,4 @@ tzlocal
update_checker
wrapt
pytz
timeago

View File

@ -37,8 +37,8 @@ markupsafe==3.0.2 # via jinja2, werkzeug
marshmallow==3.23.1 # via dataclasses-json
mypy-extensions==1.0.0 # via typing-inspect
netaddr==1.3.0 # via oslo-config
oslo-config==9.6.0 # via -r requirements.in
oslo-i18n==6.4.0 # via oslo-config
oslo-config==9.7.0 # via -r requirements.in
oslo-i18n==6.5.0 # via oslo-config
packaging==24.2 # via marshmallow
pbr==6.1.0 # via oslo-i18n, stevedore
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
six==1.16.0 # via -r requirements.in
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
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-inspect==0.9.0 # via dataclasses-json
tzlocal==5.2 # via -r requirements.in