Implement server-side trail tracking and fix aircraft marker orientation
- Replace client-side trail collection with server-provided position history - Fix aircraft markers to properly orient based on track heading using SVG rotation - Add beast-dump binary to debian package with comprehensive man pages - Trail visualization now uses gradient effect where newer positions are brighter - Marker icons update when track heading changes by more than 5 degrees for performance 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b527f5a8ee
commit
da4645d483
5 changed files with 231 additions and 32 deletions
|
|
@ -74,16 +74,13 @@ export class AircraftManager {
|
||||||
const oldPos = marker.getLatLng();
|
const oldPos = marker.getLatLng();
|
||||||
marker.setLatLng(pos);
|
marker.setLatLng(pos);
|
||||||
|
|
||||||
// Update rotation using Leaflet's options if available, otherwise skip rotation
|
// Update icon if track has changed to apply new rotation
|
||||||
if (aircraft.Track !== undefined) {
|
if (aircraft.Track !== undefined) {
|
||||||
if (marker.setRotationAngle) {
|
const currentRotation = marker._currentRotation || 0;
|
||||||
// Use Leaflet rotation plugin method if available
|
if (Math.abs(currentRotation - aircraft.Track) > 5) { // Update if rotation changed by more than 5 degrees
|
||||||
marker.setRotationAngle(aircraft.Track);
|
marker.setIcon(this.createAircraftIcon(aircraft));
|
||||||
} else if (marker.options) {
|
marker._currentRotation = aircraft.Track;
|
||||||
// Update the marker's options for consistency
|
|
||||||
marker.options.rotationAngle = aircraft.Track;
|
|
||||||
}
|
}
|
||||||
// Don't manually set CSS transforms - let Leaflet handle it
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle popup exactly like Leaflet expects
|
// Handle popup exactly like Leaflet expects
|
||||||
|
|
@ -99,10 +96,12 @@ export class AircraftManager {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const marker = L.marker(pos, {
|
const marker = L.marker(pos, {
|
||||||
icon: icon,
|
icon: icon
|
||||||
rotationAngle: aircraft.Track || 0
|
|
||||||
}).addTo(this.map);
|
}).addTo(this.map);
|
||||||
|
|
||||||
|
// Store current rotation for future updates
|
||||||
|
marker._currentRotation = aircraft.Track || 0;
|
||||||
|
|
||||||
marker.bindPopup(this.createPopupContent(aircraft), {
|
marker.bindPopup(this.createPopupContent(aircraft), {
|
||||||
maxWidth: 450,
|
maxWidth: 450,
|
||||||
className: 'aircraft-popup'
|
className: 'aircraft-popup'
|
||||||
|
|
@ -124,7 +123,7 @@ export class AircraftManager {
|
||||||
|
|
||||||
// Update trails
|
// Update trails
|
||||||
if (this.showTrails) {
|
if (this.showTrails) {
|
||||||
this.updateAircraftTrail(icao, pos);
|
this.updateAircraftTrail(icao, aircraft);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,6 +131,7 @@ export class AircraftManager {
|
||||||
const iconType = this.getAircraftIconType(aircraft);
|
const iconType = this.getAircraftIconType(aircraft);
|
||||||
const color = this.getAircraftColor(iconType);
|
const color = this.getAircraftColor(iconType);
|
||||||
const size = aircraft.OnGround ? 12 : 16;
|
const size = aircraft.OnGround ? 12 : 16;
|
||||||
|
const rotation = aircraft.Track || 0;
|
||||||
|
|
||||||
// Create different SVG shapes based on aircraft type
|
// Create different SVG shapes based on aircraft type
|
||||||
let aircraftPath;
|
let aircraftPath;
|
||||||
|
|
@ -182,7 +182,7 @@ export class AircraftManager {
|
||||||
|
|
||||||
const svg = `
|
const svg = `
|
||||||
<svg width="${size * 2}" height="${size * 2}" viewBox="0 0 32 32">
|
<svg width="${size * 2}" height="${size * 2}" viewBox="0 0 32 32">
|
||||||
<g transform="translate(16,16)">
|
<g transform="translate(16,16) rotate(${rotation})">
|
||||||
${aircraftPath}
|
${aircraftPath}
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
@ -236,33 +236,48 @@ export class AircraftManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
updateAircraftTrail(icao, pos) {
|
updateAircraftTrail(icao, aircraft) {
|
||||||
|
// Use server-provided position history
|
||||||
|
if (!aircraft.position_history || aircraft.position_history.length < 2) {
|
||||||
|
// No trail data available or not enough points
|
||||||
|
if (this.aircraftTrails.has(icao)) {
|
||||||
|
const trail = this.aircraftTrails.get(icao);
|
||||||
|
if (trail.polyline) {
|
||||||
|
this.map.removeLayer(trail.polyline);
|
||||||
|
}
|
||||||
|
this.aircraftTrails.delete(icao);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert position history to Leaflet format
|
||||||
|
const trailPoints = aircraft.position_history.map(point => [point.lat, point.lon]);
|
||||||
|
|
||||||
|
// Get or create trail object
|
||||||
if (!this.aircraftTrails.has(icao)) {
|
if (!this.aircraftTrails.has(icao)) {
|
||||||
this.aircraftTrails.set(icao, []);
|
this.aircraftTrails.set(icao, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
const trail = this.aircraftTrails.get(icao);
|
const trail = this.aircraftTrails.get(icao);
|
||||||
trail.push(pos);
|
|
||||||
|
|
||||||
// Keep only last 50 positions
|
// Remove old polyline if it exists
|
||||||
if (trail.length > 50) {
|
if (trail.polyline) {
|
||||||
trail.shift();
|
this.map.removeLayer(trail.polyline);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw polyline
|
// Create gradient effect - newer points are brighter
|
||||||
const trailLine = L.polyline(trail, {
|
const segments = [];
|
||||||
color: '#00d4ff',
|
for (let i = 1; i < trailPoints.length; i++) {
|
||||||
weight: 2,
|
const opacity = 0.2 + (0.6 * (i / trailPoints.length)); // Fade from 0.2 to 0.8
|
||||||
opacity: 0.6
|
const segment = L.polyline([trailPoints[i-1], trailPoints[i]], {
|
||||||
}).addTo(this.map);
|
color: '#00d4ff',
|
||||||
|
weight: 2,
|
||||||
// Store reference for cleanup
|
opacity: opacity
|
||||||
if (!this.aircraftTrails.get(icao).polyline) {
|
});
|
||||||
this.aircraftTrails.get(icao).polyline = trailLine;
|
segments.push(segment);
|
||||||
} else {
|
|
||||||
this.map.removeLayer(this.aircraftTrails.get(icao).polyline);
|
|
||||||
this.aircraftTrails.get(icao).polyline = trailLine;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create a feature group for all segments
|
||||||
|
trail.polyline = L.featureGroup(segments).addTo(this.map);
|
||||||
}
|
}
|
||||||
|
|
||||||
createPopupContent(aircraft) {
|
createPopupContent(aircraft) {
|
||||||
|
|
|
||||||
1
debian/DEBIAN/control
vendored
1
debian/DEBIAN/control
vendored
|
|
@ -19,4 +19,5 @@ Description: Multi-source ADS-B aircraft tracker with Beast format support
|
||||||
- Historical flight tracking
|
- Historical flight tracking
|
||||||
- Mobile-responsive design
|
- Mobile-responsive design
|
||||||
- Systemd integration for service management
|
- Systemd integration for service management
|
||||||
|
- Beast-dump utility for raw ADS-B data analysis
|
||||||
Homepage: https://github.com/skyview/skyview
|
Homepage: https://github.com/skyview/skyview
|
||||||
|
|
|
||||||
BIN
debian/usr/bin/beast-dump
vendored
Executable file
BIN
debian/usr/bin/beast-dump
vendored
Executable file
Binary file not shown.
95
debian/usr/share/man/man1/beast-dump.1
vendored
Normal file
95
debian/usr/share/man/man1/beast-dump.1
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
.TH BEAST-DUMP 1 "2024-08-24" "SkyView 2.0.0" "User Commands"
|
||||||
|
.SH NAME
|
||||||
|
beast-dump \- Utility for analyzing raw ADS-B data in Beast binary format
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B beast-dump
|
||||||
|
[\fIOPTIONS\fR] [\fIFILE\fR]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
beast-dump is a command-line utility for analyzing and decoding ADS-B
|
||||||
|
(Automatic Dependent Surveillance-Broadcast) data stored in Beast binary
|
||||||
|
format. It can read from files or connect to Beast format TCP streams
|
||||||
|
to decode and display aircraft messages.
|
||||||
|
.PP
|
||||||
|
The Beast format is a compact binary representation of Mode S/ADS-B
|
||||||
|
messages commonly used by dump1090 and similar software-defined radio
|
||||||
|
applications for aircraft tracking.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.B \-host \fIstring\fR
|
||||||
|
Connect to TCP host instead of reading from file
|
||||||
|
.TP
|
||||||
|
.B \-port \fIint\fR
|
||||||
|
TCP port to connect to (default 30005)
|
||||||
|
.TP
|
||||||
|
.B \-format \fIstring\fR
|
||||||
|
Output format: text, json, or csv (default "text")
|
||||||
|
.TP
|
||||||
|
.B \-filter \fIstring\fR
|
||||||
|
Filter by ICAO hex code (e.g., "A1B2C3")
|
||||||
|
.TP
|
||||||
|
.B \-types \fIstring\fR
|
||||||
|
Message types to display (comma-separated)
|
||||||
|
.TP
|
||||||
|
.B \-count \fIint\fR
|
||||||
|
Maximum number of messages to process
|
||||||
|
.TP
|
||||||
|
.B \-stats
|
||||||
|
Show statistics summary
|
||||||
|
.TP
|
||||||
|
.B \-verbose
|
||||||
|
Enable verbose output
|
||||||
|
.TP
|
||||||
|
.B \-h, \-help
|
||||||
|
Show help message and exit
|
||||||
|
.SH EXAMPLES
|
||||||
|
.TP
|
||||||
|
Analyze Beast format file:
|
||||||
|
.B beast-dump data.bin
|
||||||
|
.TP
|
||||||
|
Connect to live Beast stream:
|
||||||
|
.B beast-dump \-host localhost \-port 30005
|
||||||
|
.TP
|
||||||
|
Export to JSON format with statistics:
|
||||||
|
.B beast-dump \-format json \-stats data.bin
|
||||||
|
.TP
|
||||||
|
Filter messages for specific aircraft:
|
||||||
|
.B beast-dump \-filter A1B2C3 \-verbose data.bin
|
||||||
|
.TP
|
||||||
|
Process only first 1000 messages as CSV:
|
||||||
|
.B beast-dump \-format csv \-count 1000 data.bin
|
||||||
|
.SH OUTPUT FORMAT
|
||||||
|
The default text output shows decoded message fields:
|
||||||
|
.PP
|
||||||
|
.nf
|
||||||
|
ICAO: A1B2C3 Type: 17 Time: 12:34:56.789
|
||||||
|
Position: 51.4700, -0.4600
|
||||||
|
Altitude: 35000 ft
|
||||||
|
Speed: 450 kt
|
||||||
|
Track: 090°
|
||||||
|
.fi
|
||||||
|
.PP
|
||||||
|
JSON output provides structured data suitable for further processing.
|
||||||
|
CSV output includes headers and is suitable for spreadsheet import.
|
||||||
|
.SH MESSAGE TYPES
|
||||||
|
Common ADS-B message types:
|
||||||
|
.IP \(bu 2
|
||||||
|
Type 4/20: Altitude and identification
|
||||||
|
.IP \(bu 2
|
||||||
|
Type 5/21: Surface position
|
||||||
|
.IP \(bu 2
|
||||||
|
Type 9/18/22: Airborne position (baro altitude)
|
||||||
|
.IP \(bu 2
|
||||||
|
Type 10/18/22: Airborne position (GNSS altitude)
|
||||||
|
.IP \(bu 2
|
||||||
|
Type 17: Extended squitter ADS-B
|
||||||
|
.IP \(bu 2
|
||||||
|
Type 19: Military extended squitter
|
||||||
|
.SH FILES
|
||||||
|
Beast format files typically use .bin or .beast extensions.
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR skyview (1),
|
||||||
|
.BR dump1090 (1)
|
||||||
|
.SH BUGS
|
||||||
|
Report bugs at: https://github.com/skyview/skyview/issues
|
||||||
|
.SH AUTHOR
|
||||||
|
SkyView Team <admin@skyview.local>
|
||||||
88
debian/usr/share/man/man1/skyview.1
vendored
Normal file
88
debian/usr/share/man/man1/skyview.1
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
.TH SKYVIEW 1 "2024-08-24" "SkyView 2.0.0" "User Commands"
|
||||||
|
.SH NAME
|
||||||
|
skyview \- Multi-source ADS-B aircraft tracker with Beast format support
|
||||||
|
.SH SYNOPSIS
|
||||||
|
.B skyview
|
||||||
|
[\fIOPTIONS\fR]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
SkyView is a standalone application that connects to multiple dump1090 Beast
|
||||||
|
format TCP streams and provides a modern web frontend for aircraft tracking.
|
||||||
|
It features real-time aircraft tracking, signal strength analysis, coverage
|
||||||
|
mapping, and 3D radar visualization.
|
||||||
|
.PP
|
||||||
|
The application serves a web interface on port 8080 by default and connects
|
||||||
|
to one or more Beast format data sources (typically dump1090 instances) to
|
||||||
|
aggregate aircraft data from multiple receivers.
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.B \-config \fIstring\fR
|
||||||
|
Path to configuration file (default "config.json")
|
||||||
|
.TP
|
||||||
|
.B \-port \fIint\fR
|
||||||
|
HTTP server port (default 8080)
|
||||||
|
.TP
|
||||||
|
.B \-debug
|
||||||
|
Enable debug logging
|
||||||
|
.TP
|
||||||
|
.B \-version
|
||||||
|
Show version information and exit
|
||||||
|
.TP
|
||||||
|
.B \-h, \-help
|
||||||
|
Show help message and exit
|
||||||
|
.SH FILES
|
||||||
|
.TP
|
||||||
|
.I /etc/skyview/config.json
|
||||||
|
System-wide configuration file
|
||||||
|
.TP
|
||||||
|
.I ~/.config/skyview/config.json
|
||||||
|
Per-user configuration file
|
||||||
|
.SH EXAMPLES
|
||||||
|
.TP
|
||||||
|
Start with default configuration:
|
||||||
|
.B skyview
|
||||||
|
.TP
|
||||||
|
Start with custom config file:
|
||||||
|
.B skyview \-config /path/to/config.json
|
||||||
|
.TP
|
||||||
|
Start on port 9090 with debug logging:
|
||||||
|
.B skyview \-port 9090 \-debug
|
||||||
|
.SH CONFIGURATION
|
||||||
|
The configuration file uses JSON format with the following structure:
|
||||||
|
.PP
|
||||||
|
.nf
|
||||||
|
{
|
||||||
|
"sources": [
|
||||||
|
{
|
||||||
|
"id": "source1",
|
||||||
|
"name": "Local Receiver",
|
||||||
|
"host": "localhost",
|
||||||
|
"port": 30005,
|
||||||
|
"latitude": 51.4700,
|
||||||
|
"longitude": -0.4600
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"web": {
|
||||||
|
"port": 8080,
|
||||||
|
"assets_path": "/usr/share/skyview/assets"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.fi
|
||||||
|
.SH WEB INTERFACE
|
||||||
|
The web interface provides:
|
||||||
|
.IP \(bu 2
|
||||||
|
Interactive map view with aircraft markers
|
||||||
|
.IP \(bu 2
|
||||||
|
Aircraft data table with filtering and sorting
|
||||||
|
.IP \(bu 2
|
||||||
|
Real-time statistics and charts
|
||||||
|
.IP \(bu 2
|
||||||
|
Coverage heatmaps and range circles
|
||||||
|
.IP \(bu 2
|
||||||
|
3D radar visualization
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR beast-dump (1),
|
||||||
|
.BR dump1090 (1)
|
||||||
|
.SH BUGS
|
||||||
|
Report bugs at: https://github.com/skyview/skyview/issues
|
||||||
|
.SH AUTHOR
|
||||||
|
SkyView Team <admin@skyview.local>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue