compare panel, docs update, wind visualisation
This commit is contained in:
parent
b7f7ec8dc5
commit
48140f0f77
29 changed files with 2299 additions and 38 deletions
|
|
@ -61,6 +61,9 @@ import sys
|
|||
import time
|
||||
from pathlib import Path
|
||||
|
||||
# 2025-04-06 03:00:00 UTC
|
||||
BASE_TS = 1743908400
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Trajectory loaders
|
||||
|
|
@ -94,7 +97,7 @@ def load_csv(path: Path) -> list[dict]:
|
|||
"lat": float(row["lat"]),
|
||||
"lon": float(row["lon"]),
|
||||
"alt": float(row["alt"]),
|
||||
"timestamp": int(row.get("timestamp", time.time())),
|
||||
"timestamp": int(row.get("timestamp", BASE_TS + len(points) * 10)),
|
||||
})
|
||||
return points
|
||||
|
||||
|
|
@ -111,7 +114,7 @@ def load_log(path: Path) -> list[dict]:
|
|||
Duplicate positions (identical lat/lon/alt) are silently dropped.
|
||||
Points with no GPS fix (lat_raw == lon_raw == 0) are also dropped.
|
||||
"""
|
||||
base_ts = int(time.time())
|
||||
base_ts = BASE_TS
|
||||
points: list[dict] = []
|
||||
seen: set[tuple] = set()
|
||||
|
||||
|
|
@ -192,7 +195,7 @@ def deduplicate(points: list[dict]) -> list[dict]:
|
|||
def generate_sample(path: Path, n: int = 50) -> None:
|
||||
"""Generate a simple ascending balloon trajectory."""
|
||||
base_lat, base_lon = 62.0, 129.5
|
||||
base_ts = int(time.time())
|
||||
base_ts = BASE_TS
|
||||
points = []
|
||||
for i in range(n):
|
||||
angle = i * 0.05
|
||||
|
|
@ -221,25 +224,28 @@ async def run_ws(server: str, satellite: str, token: str,
|
|||
print(f"Connecting to {url}")
|
||||
|
||||
iteration = 0
|
||||
abs_idx = 0 # never resets across loop iterations — drives advancing timestamps
|
||||
while True:
|
||||
try:
|
||||
async with websockets.connect(url) as ws:
|
||||
print("Connected.")
|
||||
for i, point in enumerate(points):
|
||||
for point in points:
|
||||
packet = {
|
||||
"lat": float(point.get("lat", 0)),
|
||||
"lon": float(point.get("lon", 0)),
|
||||
"alt": float(point.get("alt", 0)),
|
||||
"timestamp": int(point.get("timestamp", time.time())),
|
||||
"timestamp": BASE_TS + abs_idx * int(interval),
|
||||
"payload": point.get("payload", {}),
|
||||
"raw_data": point.get("raw_data", {}),
|
||||
}
|
||||
abs_idx += 1
|
||||
await ws.send(json.dumps(packet))
|
||||
print(
|
||||
f"[{i+1}/{len(points)}] "
|
||||
f"[{abs_idx}/{len(points)}] "
|
||||
f"lat={packet['lat']:.5f} "
|
||||
f"lon={packet['lon']:.5f} "
|
||||
f"alt={packet['alt']:.1f} m"
|
||||
f"alt={packet['alt']:.1f} m "
|
||||
f"ts={packet['timestamp']}"
|
||||
)
|
||||
|
||||
try:
|
||||
|
|
@ -250,8 +256,7 @@ async def run_ws(server: str, satellite: str, token: str,
|
|||
except asyncio.TimeoutError:
|
||||
pass # server only responds on errors
|
||||
|
||||
if i < len(points) - 1:
|
||||
await asyncio.sleep(interval)
|
||||
await asyncio.sleep(interval)
|
||||
|
||||
print("All points sent.")
|
||||
iteration += 1
|
||||
|
|
@ -285,10 +290,11 @@ async def run_rest(server: str, satellite: str,
|
|||
while True:
|
||||
for i, point in enumerate(points):
|
||||
packet = {
|
||||
"lat": float(point.get("lat", 0)),
|
||||
"lon": float(point.get("lon", 0)),
|
||||
"alt": float(point.get("alt", 0)),
|
||||
"payload": point.get("payload", {}),
|
||||
"lat": float(point.get("lat", 0)),
|
||||
"lon": float(point.get("lon", 0)),
|
||||
"alt": float(point.get("alt", 0)),
|
||||
"timestamp": int(point.get("timestamp", BASE_TS + i * int(interval))),
|
||||
"payload": point.get("payload", {}),
|
||||
}
|
||||
async with session.post(url, json=packet) as resp:
|
||||
print(
|
||||
|
|
@ -313,7 +319,8 @@ def main() -> None:
|
|||
help="Base server URL (default: ws://localhost:8000)")
|
||||
parser.add_argument("--satellite", help="Satellite UUID")
|
||||
parser.add_argument("--token", help="Auth token (required for WebSocket mode)")
|
||||
parser.add_argument("--file", help="Trajectory file (.json, .csv, or .log)")
|
||||
parser.add_argument("--file", nargs='+', metavar="FILE",
|
||||
help="One or more trajectory files (.json, .csv, or .log); merged in order")
|
||||
parser.add_argument("--interval", type=float, default=5.0,
|
||||
help="Seconds between packets (default: 10)")
|
||||
parser.add_argument("--mode", choices=["ws", "rest"], default="ws",
|
||||
|
|
@ -335,18 +342,22 @@ def main() -> None:
|
|||
if args.mode == "ws" and not args.token:
|
||||
parser.error("--token is required for WebSocket mode")
|
||||
|
||||
path = Path(args.file)
|
||||
if not path.exists():
|
||||
sys.exit(f"File not found: {path}")
|
||||
merged: list[dict] = []
|
||||
for f in args.file:
|
||||
path = Path(f)
|
||||
if not path.exists():
|
||||
sys.exit(f"File not found: {path}")
|
||||
loaded = load_trajectory(path)
|
||||
print(f" {path}: {len(loaded)} points")
|
||||
merged.extend(loaded)
|
||||
|
||||
points = load_trajectory(path)
|
||||
if not points:
|
||||
sys.exit(f"No valid points found in {path}")
|
||||
before = len(points)
|
||||
points = deduplicate(points)
|
||||
before = len(merged)
|
||||
points = deduplicate(merged)
|
||||
removed = before - len(points)
|
||||
print(f"Loaded {before} points from {path}" +
|
||||
(f", removed {removed} duplicates → {len(points)} unique" if removed else f" ({len(points)} points)"))
|
||||
if not points:
|
||||
sys.exit("No valid points found in the provided files")
|
||||
print(f"Total: {before} points" +
|
||||
(f", removed {removed} duplicates → {len(points)} unique" if removed else f" ({len(points)} unique)"))
|
||||
|
||||
if args.mode == "ws":
|
||||
# Swap http(s) → ws(s) if the user passed an HTTP URL
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue