Add VRS JSON format support for readsb integration
Added comprehensive support for VRS (Virtual Radar Server) JSON format
as a simpler alternative to Beast binary protocol, enabling integration
with readsb --net-vrs-port output.
## Key Features:
- **VRS JSON Parser**: Stream parsing of newline-delimited JSON aircraft data
- **VRS Client**: TCP client with automatic reconnection and error recovery
- **Mixed Format Support**: Use Beast and VRS sources simultaneously
- **Enhanced Aircraft Data**: Added VRS-specific fields (registration, type, operator)
- **Position Source Tracking**: Identifies ADS-B, MLAT, TIS-B, and satellite positions
## Implementation:
- `internal/vrs/parser.go`: VRS JSON message parsing and validation
- `internal/client/vrs.go`: VRS TCP client implementation
- Enhanced `MultiSourceClient` to support both Beast and VRS formats
- Extended `Aircraft` struct with validity flags and additional metadata
- Updated configuration to include `format` field ("beast" or "vrs")
## Testing:
- Successfully tested against svovel:33005 VRS JSON stream
- Verified aircraft data parsing and position tracking
- Confirmed mixed-format operation with existing Beast clients
## Documentation:
- Updated README.md with VRS format configuration examples
- Enhanced ARCHITECTURE.md with VRS parser documentation
- Added data format comparison and configuration guide
This enables simpler integration with modern readsb installations while
maintaining full backward compatibility with existing Beast deployments.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
3d92ce4481
commit
073acb7304
10 changed files with 903 additions and 40 deletions
133
cmd/vrs-test/main.go
Normal file
133
cmd/vrs-test/main.go
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
// Package main implements a simple VRS JSON parser test utility.
|
||||
//
|
||||
// This utility connects to a VRS JSON source and displays the parsed
|
||||
// aircraft data for testing and debugging purposes.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"skyview/internal/vrs"
|
||||
)
|
||||
|
||||
func main() {
|
||||
host := flag.String("host", "svovel", "VRS host to connect to")
|
||||
port := flag.Int("port", 33005, "VRS port to connect to")
|
||||
flag.Parse()
|
||||
|
||||
fmt.Printf("Connecting to VRS JSON source at %s:%d...\n", *host, *port)
|
||||
|
||||
// Connect to VRS source
|
||||
addr := fmt.Sprintf("%s:%d", *host, *port)
|
||||
conn, err := net.DialTimeout("tcp", addr, 30*time.Second)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to %s: %v", addr, err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
fmt.Printf("Connected to %s\n", addr)
|
||||
|
||||
// Create VRS parser
|
||||
parser := vrs.NewParser(conn, "test")
|
||||
|
||||
// Set up channels for messages and errors
|
||||
msgChan := make(chan *vrs.VRSMessage, 100)
|
||||
errChan := make(chan error, 10)
|
||||
|
||||
// Set up signal handling
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
// Start parsing in background
|
||||
go parser.ParseStream(msgChan, errChan)
|
||||
|
||||
// Statistics tracking
|
||||
messageCount := 0
|
||||
aircraftCount := 0
|
||||
startTime := time.Now()
|
||||
|
||||
fmt.Println("Receiving VRS JSON data... (Press Ctrl+C to stop)")
|
||||
fmt.Println("----------------------------------------")
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
fmt.Println("\nShutting down...")
|
||||
return
|
||||
|
||||
case <-sigChan:
|
||||
fmt.Println("\nReceived interrupt signal")
|
||||
cancel()
|
||||
|
||||
case err := <-errChan:
|
||||
log.Printf("Parser error: %v", err)
|
||||
cancel()
|
||||
|
||||
case msg := <-msgChan:
|
||||
if msg == nil {
|
||||
cancel()
|
||||
continue
|
||||
}
|
||||
|
||||
messageCount++
|
||||
aircraftCount += len(msg.AcList)
|
||||
|
||||
fmt.Printf("Message %d: %d aircraft\n", messageCount, len(msg.AcList))
|
||||
|
||||
// Display first few aircraft for debugging
|
||||
for i, ac := range msg.AcList {
|
||||
if i >= 3 { // Only show first 3 aircraft per message
|
||||
fmt.Printf(" ... and %d more aircraft\n", len(msg.AcList)-i)
|
||||
break
|
||||
}
|
||||
|
||||
icao, _ := ac.GetICAO24()
|
||||
fmt.Printf(" ICAO: %06X", icao)
|
||||
|
||||
if ac.Call != "" {
|
||||
fmt.Printf(" Call: %-8s", ac.Call)
|
||||
}
|
||||
|
||||
if ac.HasPosition() {
|
||||
fmt.Printf(" Pos: %7.3f°, %8.3f°", ac.Lat, ac.Long)
|
||||
}
|
||||
|
||||
if ac.HasAltitude() {
|
||||
fmt.Printf(" Alt: %5d ft", ac.GetAltitude())
|
||||
}
|
||||
|
||||
if ac.Spd > 0 {
|
||||
fmt.Printf(" Spd: %3.0f kt", ac.Spd)
|
||||
}
|
||||
|
||||
if ac.Trak > 0 {
|
||||
fmt.Printf(" Hdg: %3.0f°", ac.Trak)
|
||||
}
|
||||
|
||||
if ac.Gnd {
|
||||
fmt.Printf(" [GND]")
|
||||
}
|
||||
|
||||
fmt.Printf(" Src: %s\n", ac.GetPositionSource())
|
||||
}
|
||||
|
||||
// Show statistics every 10 messages
|
||||
if messageCount%10 == 0 {
|
||||
elapsed := time.Since(startTime)
|
||||
fmt.Printf("Stats: %d messages, %d total aircraft, %.1fs elapsed\n",
|
||||
messageCount, aircraftCount, elapsed.Seconds())
|
||||
}
|
||||
|
||||
fmt.Println("----------------------------------------")
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue