compare panel, docs update, wind visualisation

This commit is contained in:
Vasilisk9812 2026-06-17 00:20:55 +09:00
parent b7f7ec8dc5
commit 48140f0f77
29 changed files with 2299 additions and 38 deletions

View file

@ -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