Compare commits
2 commits
51dc62a68f
...
522202b89e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
522202b89e | ||
|
|
afc45cc9cc |
10 changed files with 241 additions and 21 deletions
58
src/lib/telemetry.ts
Normal file
58
src/lib/telemetry.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { writable } from "svelte/store"
|
||||||
|
import type { LatLngExpression } from "leaflet";
|
||||||
|
|
||||||
|
import L from "leaflet";
|
||||||
|
|
||||||
|
interface TelemetryPoint {
|
||||||
|
altitude: number;
|
||||||
|
datetime: string;
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
payload: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ParsedTelemetry {
|
||||||
|
flight_path: [number, number, number][];
|
||||||
|
launch: {
|
||||||
|
latlng: LatLngExpression;
|
||||||
|
datetime: Date;
|
||||||
|
};
|
||||||
|
datapoints: TelemetryPoint[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const latestTelemetry = writable({
|
||||||
|
metadata: {
|
||||||
|
complete_datetime: "",
|
||||||
|
start_datetime: ""
|
||||||
|
},
|
||||||
|
telemetry: [
|
||||||
|
{
|
||||||
|
altitude: 0.0,
|
||||||
|
datetime: "",
|
||||||
|
latitude: 0.0,
|
||||||
|
longitude: 0.0,
|
||||||
|
payload: ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
export const latestTelemetryParsed = writable({} as ParsedTelemetry);
|
||||||
|
|
||||||
|
export function parseTelemetry(telemetry: TelemetryPoint[]): ParsedTelemetry {
|
||||||
|
const flight_path: [number, number, number][] = telemetry.map((point) => [
|
||||||
|
point.latitude,
|
||||||
|
point.longitude,
|
||||||
|
point.altitude
|
||||||
|
]);
|
||||||
|
|
||||||
|
const launch = {
|
||||||
|
latlng: L.latLng(telemetry[0].latitude, telemetry[0].longitude),
|
||||||
|
datetime: new Date(telemetry[0].datetime)
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
flight_path,
|
||||||
|
launch,
|
||||||
|
datapoints: telemetry
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,26 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import Map from './map.svelte';
|
|
||||||
import ControlPanel from './ControlPanel.svelte';
|
|
||||||
import Navbar from './Navbar.svelte';
|
import Navbar from './Navbar.svelte';
|
||||||
// import BurstCalculator from './BurstCalculator.svelte';
|
|
||||||
|
|
||||||
let coordinates = {
|
|
||||||
lat: '56.3576',
|
|
||||||
lng: '39.8666'
|
|
||||||
}
|
|
||||||
|
|
||||||
function handlePositionUpdate(event) {
|
|
||||||
coordinates.lat = event.detail.lat;
|
|
||||||
coordinates.lng = event.detail.lng;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<Map bind:coordinates>
|
|
||||||
<ControlPanel
|
|
||||||
{coordinates}
|
|
||||||
on:updatePosition={handlePositionUpdate}
|
|
||||||
/>
|
|
||||||
</Map>
|
|
||||||
</main>
|
</main>
|
||||||
|
|
@ -21,10 +21,10 @@
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link nav-full-height {$page.url.pathname === '/' ? 'active' : ''}" href="/">Прогнозирование</a>
|
<a class="nav-link nav-full-height {window.location.pathname === '/predict' ? 'active' : ''}" href="/predict">Прогнозирование</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link nav-full-height {$page.url.pathname === '/page2' ? 'active' : ''}" href="/page2">Слежение</a>
|
<a class="nav-link nav-full-height {window.location.pathname === '/track' ? 'active' : ''}" href="/track">Слежение</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="navbar-nav">
|
<ul class="navbar-nav">
|
||||||
|
|
|
||||||
74
src/routes/TelemetryPanel.svelte
Normal file
74
src/routes/TelemetryPanel.svelte
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
<script>
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
//import { telemetryStore } from '../lib/telemetry.ts'; // Import your telemetry store
|
||||||
|
|
||||||
|
let telemetry = {};
|
||||||
|
let isCollapsed = false;
|
||||||
|
|
||||||
|
// Subscribe to the telemetry store
|
||||||
|
//const unsubscribe = telemetryStore.subscribe((data) => {
|
||||||
|
// telemetry = data;
|
||||||
|
//});
|
||||||
|
|
||||||
|
telemetry = {
|
||||||
|
latitude: 56.3576,
|
||||||
|
longitude: 39.8666,
|
||||||
|
altitude: 1000
|
||||||
|
};
|
||||||
|
|
||||||
|
// onMount(() => {
|
||||||
|
// return () => {
|
||||||
|
// unsubscribe(); // Cleanup subscription on component destroy
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="card shadow-lg position-absolute bottom-0 end-0 m-3" style="width: 23rem; max-height: 80vh; overflow-y: auto; z-index: 1000;">
|
||||||
|
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
||||||
|
<h6 class="card-title mb-0">Последние данные телеметрии</h6>
|
||||||
|
<button class="btn btn-sm btn-primary" on:click={() => (isCollapsed = !isCollapsed)}>
|
||||||
|
{#if isCollapsed}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-left-fill" viewBox="0 0 16 16">
|
||||||
|
<path d="m3.86 8.753 5.482 4.796c.646.566 1.658.106 1.658-.753V3.204a1 1 0 0 0-1.659-.753l-5.48 4.796a1 1 0 0 0 0 1.506z"/>
|
||||||
|
</svg>
|
||||||
|
{:else}
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-caret-down" viewBox="0 0 16 16">
|
||||||
|
<path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/>
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{#if !isCollapsed}
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-2">
|
||||||
|
<label class="form-label small">Широта:</label>
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<input type="text" class="form-control" value={telemetry.latitude || 'N/A'} readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-2">
|
||||||
|
<label class="form-label small">Долгота:</label>
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<input type="text" class="form-control" value={telemetry.longitude || 'N/A'} readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-2">
|
||||||
|
<label class="form-label small">Высота (м):</label>
|
||||||
|
<div class="input-group input-group-sm">
|
||||||
|
<input type="text" class="form-control" value={telemetry.altitude || 'N/A'} readonly>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.card {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
.card-header {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
import { distHaversine } from '../lib/mathutil.ts';
|
import { distHaversine } from '../lib/mathutil.ts';
|
||||||
|
|
||||||
import { latestPredictionParsed } from '../lib/prediction.ts';
|
import { latestPredictionParsed } from '../lib/prediction.ts';
|
||||||
|
import { latestTelemetryParsed } from '../lib/telemetry.ts';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {L.Map}
|
* @type {L.Map}
|
||||||
|
|
@ -147,6 +148,65 @@
|
||||||
map.setView(launch.latlng, 8);
|
map.setView(launch.latlng, 8);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const plotTelemetryTrack = (telemetry) => {
|
||||||
|
console.log("Telemetry data parsed, creating map plot...");
|
||||||
|
|
||||||
|
// Clear existing map items
|
||||||
|
if (marker) {
|
||||||
|
map.eachLayer((layer) => {
|
||||||
|
if (layer instanceof L.Marker || layer instanceof L.Polyline) {
|
||||||
|
map.removeLayer(layer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create custom icons for telemetry markers
|
||||||
|
const telemetryIcon = L.icon({
|
||||||
|
iconUrl: 'marker-sm-red.png',
|
||||||
|
iconSize: [10, 10],
|
||||||
|
iconAnchor: [5, 5],
|
||||||
|
});
|
||||||
|
|
||||||
|
const launchIcon = L.icon({
|
||||||
|
iconUrl: 'target-blue.png',
|
||||||
|
iconSize: [10, 10],
|
||||||
|
iconAnchor: [5, 5],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add markers to the map
|
||||||
|
const launchMarker = L.marker(telemetry.launch.latlng, {
|
||||||
|
title: `Launch (${telemetry.launch.latlng.lat.toFixed(4)}, ${telemetry.launch.latlng.lng.toFixed(4)}) at ${telemetry.launch.datetime.toUTCString()}`,
|
||||||
|
icon: launchIcon,
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
// interface TelemetryPoint {
|
||||||
|
// altitude: number;
|
||||||
|
// datetime: string;
|
||||||
|
// latitude: number;
|
||||||
|
// longitude: number;
|
||||||
|
// payload: string;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Add telemetry markers to the map
|
||||||
|
telemetry.datapoints.forEach((point) => {
|
||||||
|
const telemetryMarker = L.marker([point.latitude, point.longitude], {
|
||||||
|
title: `Telemetry (${point.latitude.toFixed(4)}, ${point.longitude.toFixed(4)}) at ${point.datetime}`,
|
||||||
|
icon: telemetryIcon,
|
||||||
|
}).addTo(map)
|
||||||
|
.bindPopup(() => {
|
||||||
|
return `
|
||||||
|
<b>Telemetry Point</b><br>, Lat: ${point.latitude.toFixed(6)}<br>, Lon: ${point.longitude.toFixed(6)}<br>
|
||||||
|
`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add flight path polyline
|
||||||
|
const pathPolyline = L.polyline(telemetry.flight_path, {
|
||||||
|
weight: 3,
|
||||||
|
color: "#000000",
|
||||||
|
}).addTo(map);
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="map-container">
|
<div class="map-container">
|
||||||
|
|
|
||||||
26
src/routes/predict/+page.svelte
Normal file
26
src/routes/predict/+page.svelte
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
<script>
|
||||||
|
import Map from '../map.svelte';
|
||||||
|
import ControlPanel from '../ControlPanel.svelte';
|
||||||
|
import Navbar from '../Navbar.svelte';
|
||||||
|
// import BurstCalculator from './BurstCalculator.svelte';
|
||||||
|
|
||||||
|
let coordinates = {
|
||||||
|
lat: '56.3576',
|
||||||
|
lng: '39.8666'
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePositionUpdate(event) {
|
||||||
|
coordinates.lat = event.detail.lat;
|
||||||
|
coordinates.lng = event.detail.lng;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<Navbar />
|
||||||
|
<Map bind:coordinates>
|
||||||
|
<ControlPanel
|
||||||
|
{coordinates}
|
||||||
|
on:updatePosition={handlePositionUpdate}
|
||||||
|
/>
|
||||||
|
</Map>
|
||||||
|
</main>
|
||||||
1
src/routes/predict/+page.ts
Normal file
1
src/routes/predict/+page.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export const ssr =false;
|
||||||
19
src/routes/track/+page.svelte
Normal file
19
src/routes/track/+page.svelte
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<script>
|
||||||
|
import Map from '../map.svelte';
|
||||||
|
import TelemetryPanel from '../TelemetryPanel.svelte';
|
||||||
|
import Navbar from '../Navbar.svelte';
|
||||||
|
// import BurstCalculator from './BurstCalculator.svelte';
|
||||||
|
|
||||||
|
let coordinates = {
|
||||||
|
lat: '56.3576',
|
||||||
|
lng: '39.8666'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<Navbar />
|
||||||
|
<Map bind:coordinates>
|
||||||
|
<TelemetryPanel
|
||||||
|
/>
|
||||||
|
</Map>
|
||||||
|
</main>
|
||||||
1
src/routes/track/+page.ts
Normal file
1
src/routes/track/+page.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export const ssr =false;
|
||||||
BIN
static/marker-sm-red.png
Normal file
BIN
static/marker-sm-red.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 421 B |
Loading…
Add table
Add a link
Reference in a new issue