kopia lustrzana https://github.com/cyoung/stratux
commit
1437a07f7a
133
1090es_relay.go
133
1090es_relay.go
|
@ -1,133 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
ipadAddr = "192.168.10.255:49002"
|
|
||||||
dump1090Addr = "127.0.0.1:30003"
|
|
||||||
maxDatagramSize = 8192
|
|
||||||
)
|
|
||||||
|
|
||||||
type PositionInfo struct {
|
|
||||||
lat string
|
|
||||||
lng string
|
|
||||||
alt string
|
|
||||||
hdg string
|
|
||||||
vel string
|
|
||||||
vr string
|
|
||||||
tail string
|
|
||||||
last_seen time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
func cleanupOldEntries() {
|
|
||||||
for icaoDec, pi := range blips {
|
|
||||||
s := time.Since(pi.last_seen)
|
|
||||||
if s.Seconds() >= float64(15) { // Timeout time.
|
|
||||||
//log.Printf("REMOVED %d\n", icaoDec)
|
|
||||||
delete(blips, icaoDec)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ipadUpdate(mutex *sync.Mutex) {
|
|
||||||
addr, err := net.ResolveUDPAddr("udp", ipadAddr)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
outConn, err := net.DialUDP("udp", nil, addr)
|
|
||||||
|
|
||||||
for {
|
|
||||||
mutex.Lock()
|
|
||||||
cleanupOldEntries()
|
|
||||||
for icaoDec, pi := range blips {
|
|
||||||
msg := fmt.Sprintf("XTRAFFICMy Sim,%d,%s,%s,%s,%s,1,%s,%s,%s", icaoDec, pi.lat, pi.lng, pi.alt, pi.vr, pi.hdg, pi.vel, pi.tail)
|
|
||||||
//log.Println(msg)
|
|
||||||
outConn.Write([]byte(msg))
|
|
||||||
}
|
|
||||||
mutex.Unlock()
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
// c.Write([]byte("XTRAFFICMy Sim,168,42.503464,-83.622551,3749.9,-213.0,1,68.2,126.0,KS6"))
|
|
||||||
}
|
|
||||||
|
|
||||||
var blips map[int64]PositionInfo
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
mutex := &sync.Mutex{}
|
|
||||||
blips = make(map[int64]PositionInfo)
|
|
||||||
go ipadUpdate(mutex)
|
|
||||||
for {
|
|
||||||
inConn, err := net.Dial("tcp", dump1090Addr)
|
|
||||||
if err != nil {
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
rdr := bufio.NewReader(inConn)
|
|
||||||
for {
|
|
||||||
buf, err := rdr.ReadString('\n')
|
|
||||||
if err != nil { // Must have disconnected?
|
|
||||||
break
|
|
||||||
}
|
|
||||||
buf = strings.Trim(buf, "\r\n")
|
|
||||||
//log.Printf("%s\n", buf)
|
|
||||||
x := strings.Split(buf, ",")
|
|
||||||
//TODO: Add more sophisticated stuff that combines heading/speed updates with the location.
|
|
||||||
if len(x) < 22 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
icao := x[4]
|
|
||||||
icaoDec, err := strconv.ParseInt(icao, 16, 32)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
mutex.Lock()
|
|
||||||
// Retrieve previous information on this ICAO code.
|
|
||||||
var pi PositionInfo
|
|
||||||
if _, ok := blips[icaoDec]; ok {
|
|
||||||
pi = blips[icaoDec]
|
|
||||||
}
|
|
||||||
|
|
||||||
if x[1] == "3" {
|
|
||||||
//MSG,3,111,11111,AC2BB7,111111,2015/07/28,03:59:12.363,2015/07/28,03:59:12.353,,5550,,,42.35847,-83.42212,,,,,,0
|
|
||||||
alt := x[11]
|
|
||||||
lat := x[14]
|
|
||||||
lng := x[15]
|
|
||||||
|
|
||||||
//log.Printf("icao=%s, icaoDec=%d, alt=%s, lat=%s, lng=%s\n", icao, icaoDec, alt, lat, lng)
|
|
||||||
pi.alt = alt
|
|
||||||
pi.lat = lat
|
|
||||||
pi.lng = lng
|
|
||||||
}
|
|
||||||
if x[1] == "4" {
|
|
||||||
// MSG,4,111,11111,A3B557,111111,2015/07/28,06:13:36.417,2015/07/28,06:13:36.398,,,414,278,,,-64,,,,,0
|
|
||||||
vel := x[12]
|
|
||||||
hdg := x[13]
|
|
||||||
vr := x[16]
|
|
||||||
|
|
||||||
//log.Printf("icao=%s, icaoDec=%d, vel=%s, hdg=%s, vr=%s\n", icao, icaoDec, vel, hdg, vr)
|
|
||||||
pi.vel = vel
|
|
||||||
pi.hdg = hdg
|
|
||||||
pi.vr = vr
|
|
||||||
}
|
|
||||||
if x[1] == "1" {
|
|
||||||
// MSG,1,,,%02X%02X%02X,,,,,,%s,,,,,,,,0,0,0,0
|
|
||||||
tail := x[10]
|
|
||||||
pi.tail = tail
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update "last seen" (any type of message).
|
|
||||||
pi.last_seen = time.Now()
|
|
||||||
|
|
||||||
blips[icaoDec] = pi // Update information on this ICAO code.
|
|
||||||
mutex.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
2
Makefile
2
Makefile
|
@ -1,4 +1,4 @@
|
||||||
all:
|
all:
|
||||||
GOARCH=6 go-linux-arm build gen_gdl90.go traffic.go ry835ai.go
|
GOOS=linux GOARCH=arm GOARM=6 go build gen_gdl90.go traffic.go ry835ai.go network.go
|
||||||
clean:
|
clean:
|
||||||
rm -f gen_gdl90
|
rm -f gen_gdl90
|
|
@ -11,3 +11,7 @@ Supported WiFi adapters:
|
||||||
|
|
||||||
Tested RTL-SDR:
|
Tested RTL-SDR:
|
||||||
*NooElec NESDR Mini 2
|
*NooElec NESDR Mini 2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
https://www.reddit.com/r/stratux
|
36
gen_gdl90.go
36
gen_gdl90.go
|
@ -17,6 +17,7 @@ import (
|
||||||
const (
|
const (
|
||||||
stratuxVersion = "v0.1"
|
stratuxVersion = "v0.1"
|
||||||
configLocation = "/etc/stratux.conf"
|
configLocation = "/etc/stratux.conf"
|
||||||
|
managementAddr = "127.0.0.1:9110"
|
||||||
maxDatagramSize = 8192
|
maxDatagramSize = 8192
|
||||||
UPLINK_BLOCK_DATA_BITS = 576
|
UPLINK_BLOCK_DATA_BITS = 576
|
||||||
UPLINK_BLOCK_BITS = (UPLINK_BLOCK_DATA_BITS + 160)
|
UPLINK_BLOCK_BITS = (UPLINK_BLOCK_DATA_BITS + 160)
|
||||||
|
@ -45,7 +46,6 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var Crc16Table [256]uint16
|
var Crc16Table [256]uint16
|
||||||
var outConn *net.UDPConn
|
|
||||||
|
|
||||||
var myGPS GPSData
|
var myGPS GPSData
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ func makeOwnshipReport() bool {
|
||||||
|
|
||||||
msg[18] = 0x01 // "Light (ICAO) < 15,500 lbs"
|
msg[18] = 0x01 // "Light (ICAO) < 15,500 lbs"
|
||||||
|
|
||||||
outConn.Write(prepareMessage(msg))
|
sendMsg(prepareMessage(msg))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,7 +218,7 @@ func makeOwnshipGeometricAltitudeReport() bool {
|
||||||
msg[3] = 0x00
|
msg[3] = 0x00
|
||||||
msg[4] = 0x0A
|
msg[4] = 0x0A
|
||||||
|
|
||||||
outConn.Write(prepareMessage(msg))
|
sendMsg(prepareMessage(msg))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,19 +265,20 @@ func relayMessage(msgtype uint16, msg []byte) {
|
||||||
ret[i+4] = msg[i]
|
ret[i+4] = msg[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
outConn.Write(prepareMessage(ret))
|
sendMsg(prepareMessage(ret))
|
||||||
}
|
}
|
||||||
|
|
||||||
func heartBeatSender() {
|
func heartBeatSender() {
|
||||||
|
timer := time.Tick(1 * time.Second)
|
||||||
for {
|
for {
|
||||||
outConn.Write(makeHeartbeat())
|
<-timer
|
||||||
// outConn.Write(makeTrafficReport())
|
sendMsg(makeHeartbeat())
|
||||||
|
// sendMsg(makeTrafficReport())
|
||||||
makeOwnshipReport()
|
makeOwnshipReport()
|
||||||
makeOwnshipGeometricAltitudeReport()
|
makeOwnshipGeometricAltitudeReport()
|
||||||
outConn.Write(makeInitializationMessage())
|
sendMsg(makeInitializationMessage())
|
||||||
sendTrafficUpdates()
|
sendTrafficUpdates()
|
||||||
updateStatus()
|
updateStatus()
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,7 +359,7 @@ type settings struct {
|
||||||
UAT_Enabled bool
|
UAT_Enabled bool
|
||||||
ES_Enabled bool
|
ES_Enabled bool
|
||||||
GPS_Enabled bool
|
GPS_Enabled bool
|
||||||
IpadAddr string
|
GDLOutputPorts []uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
type status struct {
|
type status struct {
|
||||||
|
@ -411,7 +412,7 @@ func handleManagementConnection(conn net.Conn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func managementInterface() {
|
func managementInterface() {
|
||||||
ln, err := net.Listen("tcp", "127.0.0.1:9110")
|
ln, err := net.Listen("tcp", managementAddr)
|
||||||
if err != nil { //TODO
|
if err != nil { //TODO
|
||||||
log.Printf("couldn't open management port: %s\n", err.Error())
|
log.Printf("couldn't open management port: %s\n", err.Error())
|
||||||
return
|
return
|
||||||
|
@ -430,7 +431,7 @@ func defaultSettings() {
|
||||||
globalSettings.UAT_Enabled = true //TODO
|
globalSettings.UAT_Enabled = true //TODO
|
||||||
globalSettings.ES_Enabled = false //TODO
|
globalSettings.ES_Enabled = false //TODO
|
||||||
globalSettings.GPS_Enabled = false //TODO
|
globalSettings.GPS_Enabled = false //TODO
|
||||||
globalSettings.IpadAddr = "192.168.10.255:4000" // Port 4000 for FreeFlight RANGR.
|
globalSettings.GDLOutputPorts = []uint16{4000, 43211}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readSettings() {
|
func readSettings() {
|
||||||
|
@ -489,21 +490,16 @@ func main() {
|
||||||
go gpsReader()
|
go gpsReader()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open UDP port to send out the messages.
|
//TODO: network stuff
|
||||||
addr, err := net.ResolveUDPAddr("udp", globalSettings.IpadAddr)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
outConn, err = net.DialUDP("udp", nil, addr)
|
|
||||||
if err != nil {
|
|
||||||
panic("error creating UDP socket: " + err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the heartbeat message loop in the background, once per second.
|
// Start the heartbeat message loop in the background, once per second.
|
||||||
go heartBeatSender()
|
go heartBeatSender()
|
||||||
// Start the management interface.
|
// Start the management interface.
|
||||||
go managementInterface()
|
go managementInterface()
|
||||||
|
|
||||||
|
// Initialize the (out) network handler.
|
||||||
|
initNetwork()
|
||||||
|
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var messageQueue chan []byte
|
||||||
|
var outSockets map[string]*net.UDPConn
|
||||||
|
var dhcpLeases map[string]string
|
||||||
|
var netMutex *sync.Mutex
|
||||||
|
|
||||||
|
// Read the "dhcpd.leases" file and parse out IP/hostname.
|
||||||
|
func getDHCPLeases() (map[string]string, error) {
|
||||||
|
dat, err := ioutil.ReadFile("/var/lib/dhcp/dhcpd.leases")
|
||||||
|
ret := make(map[string]string)
|
||||||
|
if err != nil {
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
lines := strings.Split(string(dat), "\n")
|
||||||
|
open_block := false
|
||||||
|
block_ip := ""
|
||||||
|
for _, line := range lines {
|
||||||
|
spaced := strings.Split(line, " ")
|
||||||
|
if len(spaced) > 2 && spaced[0] == "lease" {
|
||||||
|
open_block = true
|
||||||
|
block_ip = spaced[1]
|
||||||
|
} else if open_block && len(spaced) >= 4 && spaced[2] == "client-hostname" {
|
||||||
|
hostname := strings.TrimRight(strings.TrimLeft(strings.Join(spaced[3:], " "), "\""), "\";")
|
||||||
|
ret[block_ip] = hostname
|
||||||
|
open_block = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendToAllConnectedClients(msg []byte) {
|
||||||
|
netMutex.Lock()
|
||||||
|
defer netMutex.Unlock()
|
||||||
|
for _, sock := range outSockets {
|
||||||
|
sock.Write(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just returns the number of DHCP leases for now.
|
||||||
|
func getNetworkStats() int {
|
||||||
|
return len(dhcpLeases)
|
||||||
|
}
|
||||||
|
|
||||||
|
// See who has a DHCP lease and make a UDP connection to each of them.
|
||||||
|
func refreshConnectedClients() {
|
||||||
|
netMutex.Lock()
|
||||||
|
defer netMutex.Unlock()
|
||||||
|
validConnections := make(map[string]bool)
|
||||||
|
t, err := getDHCPLeases()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("getDHCPLeases(): %s\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dhcpLeases = t
|
||||||
|
// Client connected that wasn't before.
|
||||||
|
for ip, hostname := range dhcpLeases {
|
||||||
|
for _, port := range globalSettings.GDLOutputPorts {
|
||||||
|
ipAndPort := ip + ":" + strconv.Itoa(int(port))
|
||||||
|
if _, ok := outSockets[ipAndPort]; !ok {
|
||||||
|
log.Printf("client connected: %s:%d (%s).\n", ip, port, hostname)
|
||||||
|
addr, err := net.ResolveUDPAddr("udp", ipAndPort)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("ResolveUDPAddr(%s): %s\n", ipAndPort, err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
outConn, err := net.DialUDP("udp", nil, addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("DialUDP(%s): %s\n", ipAndPort, err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
outSockets[ipAndPort] = outConn
|
||||||
|
}
|
||||||
|
validConnections[ipAndPort] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Client that was connected before that isn't.
|
||||||
|
for ipAndPort, conn := range outSockets {
|
||||||
|
if _, ok := validConnections[ipAndPort]; !ok {
|
||||||
|
log.Printf("removed connection %s.\n", ipAndPort)
|
||||||
|
conn.Close()
|
||||||
|
delete(outSockets, ipAndPort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func messageQueueSender() {
|
||||||
|
secondTimer := time.NewTicker(1 * time.Second)
|
||||||
|
dhcpRefresh := time.NewTicker(30 * time.Second)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case msg := <-messageQueue:
|
||||||
|
sendToAllConnectedClients(msg)
|
||||||
|
case <-secondTimer.C:
|
||||||
|
getNetworkStats()
|
||||||
|
case <-dhcpRefresh.C:
|
||||||
|
refreshConnectedClients()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendMsg(msg []byte) {
|
||||||
|
messageQueue <- msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func initNetwork() {
|
||||||
|
messageQueue = make(chan []byte, 1024) // Buffered channel, 1024 messages.
|
||||||
|
outSockets = make(map[string]*net.UDPConn)
|
||||||
|
netMutex = &sync.Mutex{}
|
||||||
|
refreshConnectedClients()
|
||||||
|
go messageQueueSender()
|
||||||
|
}
|
116
traffic.go
116
traffic.go
|
@ -3,12 +3,12 @@ package main
|
||||||
import (
|
import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"math"
|
"math"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-0b2b48fe3aef1f88621a0856110a31c01105c4e6c4e6c40a9a820300000000000000;rs=7;
|
//-0b2b48fe3aef1f88621a0856110a31c01105c4e6c4e6c40a9a820300000000000000;rs=7;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
HDR:
|
HDR:
|
||||||
|
@ -67,8 +67,11 @@ type TrafficInfo struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
var traffic map[uint32]TrafficInfo
|
var traffic map[uint32]TrafficInfo
|
||||||
|
var trafficMutex *sync.Mutex
|
||||||
|
|
||||||
func cleanupOldEntries() {
|
func cleanupOldEntries() {
|
||||||
|
trafficMutex.Lock()
|
||||||
|
defer trafficMutex.Unlock()
|
||||||
for icao_addr, ti := range traffic {
|
for icao_addr, ti := range traffic {
|
||||||
if time.Since(ti.last_seen).Seconds() > float64(60) { //FIXME: 60 seconds with no update on this address - stop displaying.
|
if time.Since(ti.last_seen).Seconds() > float64(60) { //FIXME: 60 seconds with no update on this address - stop displaying.
|
||||||
delete(traffic, icao_addr)
|
delete(traffic, icao_addr)
|
||||||
|
@ -77,6 +80,8 @@ func cleanupOldEntries() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendTrafficUpdates() {
|
func sendTrafficUpdates() {
|
||||||
|
trafficMutex.Lock()
|
||||||
|
defer trafficMutex.Unlock()
|
||||||
cleanupOldEntries()
|
cleanupOldEntries()
|
||||||
for _, ti := range traffic {
|
for _, ti := range traffic {
|
||||||
makeTrafficReport(ti)
|
makeTrafficReport(ti)
|
||||||
|
@ -85,6 +90,7 @@ func sendTrafficUpdates() {
|
||||||
|
|
||||||
func initTraffic() {
|
func initTraffic() {
|
||||||
traffic = make(map[uint32]TrafficInfo)
|
traffic = make(map[uint32]TrafficInfo)
|
||||||
|
trafficMutex = &sync.Mutex{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeTrafficReport(ti TrafficInfo) {
|
func makeTrafficReport(ti TrafficInfo) {
|
||||||
|
@ -113,11 +119,10 @@ func makeTrafficReport(ti TrafficInfo) {
|
||||||
msg[9] = tmp[1] // Longitude.
|
msg[9] = tmp[1] // Longitude.
|
||||||
msg[10] = tmp[2] // Longitude.
|
msg[10] = tmp[2] // Longitude.
|
||||||
|
|
||||||
|
//Altitude: OK
|
||||||
//Altitude: OK
|
//TODO: 0xFFF "invalid altitude."
|
||||||
//TODO: 0xFFF "invalid altitude."
|
|
||||||
alt := uint16(ti.alt)
|
alt := uint16(ti.alt)
|
||||||
alt = (alt + 1000)/25
|
alt = (alt + 1000) / 25
|
||||||
alt = alt & 0xFFF // Should fit in 12 bits.
|
alt = alt & 0xFFF // Should fit in 12 bits.
|
||||||
|
|
||||||
msg[11] = byte((alt & 0xFF0) >> 4) // Altitude.
|
msg[11] = byte((alt & 0xFF0) >> 4) // Altitude.
|
||||||
|
@ -134,7 +139,7 @@ func makeTrafficReport(ti TrafficInfo) {
|
||||||
|
|
||||||
// Vertical velocity.
|
// Vertical velocity.
|
||||||
vvel := ti.vvel / 64 // 64fpm resolution.
|
vvel := ti.vvel / 64 // 64fpm resolution.
|
||||||
msg[15] = msg[15] | byte((vvel & 0x0F00) >> 8)
|
msg[15] = msg[15] | byte((vvel&0x0F00)>>8)
|
||||||
msg[16] = byte(vvel & 0x00FF)
|
msg[16] = byte(vvel & 0x00FF)
|
||||||
|
|
||||||
// Track.
|
// Track.
|
||||||
|
@ -143,10 +148,9 @@ func makeTrafficReport(ti TrafficInfo) {
|
||||||
|
|
||||||
msg[18] = 0x01 // "light"
|
msg[18] = 0x01 // "light"
|
||||||
|
|
||||||
outConn.Write(prepareMessage(msg))
|
sendMsg(prepareMessage(msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func parseDownlinkReport(s string) {
|
func parseDownlinkReport(s string) {
|
||||||
var ti TrafficInfo
|
var ti TrafficInfo
|
||||||
s = s[1:]
|
s = s[1:]
|
||||||
|
@ -154,12 +158,12 @@ func parseDownlinkReport(s string) {
|
||||||
hex.Decode(frame, []byte(s))
|
hex.Decode(frame, []byte(s))
|
||||||
|
|
||||||
// Header.
|
// Header.
|
||||||
// msg_type := (uint8(frame[0]) >> 3) & 0x1f
|
// msg_type := (uint8(frame[0]) >> 3) & 0x1f
|
||||||
ti.addr_type = uint8(frame[0]) & 0x07
|
ti.addr_type = uint8(frame[0]) & 0x07
|
||||||
ti.icao_addr = (uint32(frame[1]) << 16) | (uint32(frame[2]) << 8) | uint32(frame[3])
|
ti.icao_addr = (uint32(frame[1]) << 16) | (uint32(frame[2]) << 8) | uint32(frame[3])
|
||||||
|
|
||||||
// OK.
|
// OK.
|
||||||
// fmt.Printf("%d, %d, %06X\n", msg_type, ti.addr_type, ti.icao_addr)
|
// fmt.Printf("%d, %d, %06X\n", msg_type, ti.addr_type, ti.icao_addr)
|
||||||
|
|
||||||
nic := uint8(frame[11]) & 15 //TODO: Meaning?
|
nic := uint8(frame[11]) & 15 //TODO: Meaning?
|
||||||
|
|
||||||
|
@ -186,20 +190,20 @@ func parseDownlinkReport(s string) {
|
||||||
ti.position_valid = position_valid
|
ti.position_valid = position_valid
|
||||||
|
|
||||||
raw_alt := (uint32(frame[10]) << 4) | ((uint32(frame[11]) & 0xf0) >> 4)
|
raw_alt := (uint32(frame[10]) << 4) | ((uint32(frame[11]) & 0xf0) >> 4)
|
||||||
// alt_geo := false // Barometric if not geometric.
|
// alt_geo := false // Barometric if not geometric.
|
||||||
alt := uint32(0)
|
alt := uint32(0)
|
||||||
if raw_alt != 0 {
|
if raw_alt != 0 {
|
||||||
// alt_geo = (uint8(frame[9]) & 1) != 0
|
// alt_geo = (uint8(frame[9]) & 1) != 0
|
||||||
alt = ((raw_alt - 1) * 25) - 1000
|
alt = ((raw_alt - 1) * 25) - 1000
|
||||||
}
|
}
|
||||||
ti.alt = alt
|
ti.alt = alt
|
||||||
|
|
||||||
//OK.
|
//OK.
|
||||||
// fmt.Printf("%d, %t, %f, %f, %t, %d\n", nic, position_valid, lat, lng, alt_geo, alt)
|
// fmt.Printf("%d, %t, %f, %f, %t, %d\n", nic, position_valid, lat, lng, alt_geo, alt)
|
||||||
|
|
||||||
airground_state := (uint8(frame[12]) >> 6) & 0x03
|
airground_state := (uint8(frame[12]) >> 6) & 0x03
|
||||||
//OK.
|
//OK.
|
||||||
// fmt.Printf("%d\n", airground_state)
|
// fmt.Printf("%d\n", airground_state)
|
||||||
|
|
||||||
ns_vel := int16(0)
|
ns_vel := int16(0)
|
||||||
ew_vel := int16(0)
|
ew_vel := int16(0)
|
||||||
|
@ -207,7 +211,7 @@ func parseDownlinkReport(s string) {
|
||||||
speed_valid := false
|
speed_valid := false
|
||||||
speed := uint16(0)
|
speed := uint16(0)
|
||||||
vvel := int16(0)
|
vvel := int16(0)
|
||||||
// vvel_geo := false
|
// vvel_geo := false
|
||||||
if airground_state == 0 || airground_state == 1 { // Subsonic. Supersonic.
|
if airground_state == 0 || airground_state == 1 { // Subsonic. Supersonic.
|
||||||
// N/S velocity.
|
// N/S velocity.
|
||||||
ns_vel_valid := false
|
ns_vel_valid := false
|
||||||
|
@ -238,7 +242,7 @@ func parseDownlinkReport(s string) {
|
||||||
if ns_vel_valid && ew_vel_valid {
|
if ns_vel_valid && ew_vel_valid {
|
||||||
if ns_vel != 0 && ew_vel != 0 {
|
if ns_vel != 0 && ew_vel != 0 {
|
||||||
//TODO: Track type
|
//TODO: Track type
|
||||||
track = (360 + 90 - uint16(math.Atan2(float64(ns_vel), float64(ew_vel)) * 180 / math.Pi)) % 360
|
track = (360 + 90 - uint16(math.Atan2(float64(ns_vel), float64(ew_vel))*180/math.Pi)) % 360
|
||||||
}
|
}
|
||||||
speed_valid = true
|
speed_valid = true
|
||||||
speed = uint16(math.Sqrt(float64((ns_vel * ns_vel) + (ew_vel * ew_vel))))
|
speed = uint16(math.Sqrt(float64((ns_vel * ns_vel) + (ew_vel * ew_vel))))
|
||||||
|
@ -247,7 +251,7 @@ func parseDownlinkReport(s string) {
|
||||||
// Vertical velocity.
|
// Vertical velocity.
|
||||||
raw_vvel := ((int16(frame[15]) & 0x7f) << 4) | ((int16(frame[16]) & 0xf0) >> 4)
|
raw_vvel := ((int16(frame[15]) & 0x7f) << 4) | ((int16(frame[16]) & 0xf0) >> 4)
|
||||||
if (raw_vvel & 0x1ff) != 0 {
|
if (raw_vvel & 0x1ff) != 0 {
|
||||||
// vvel_geo = (raw_vvel & 0x400) == 0
|
// vvel_geo = (raw_vvel & 0x400) == 0
|
||||||
vvel = ((raw_vvel & 0x1ff) - 1) * 64
|
vvel = ((raw_vvel & 0x1ff) - 1) * 64
|
||||||
if (raw_vvel & 0x200) != 0 {
|
if (raw_vvel & 0x200) != 0 {
|
||||||
vvel = 0 - vvel
|
vvel = 0 - vvel
|
||||||
|
@ -264,9 +268,9 @@ func parseDownlinkReport(s string) {
|
||||||
ti.speed_valid = speed_valid
|
ti.speed_valid = speed_valid
|
||||||
|
|
||||||
//OK.
|
//OK.
|
||||||
// fmt.Printf("ns_vel %d, ew_vel %d, track %d, speed_valid %t, speed %d, vvel_geo %t, vvel %d\n", ns_vel, ew_vel, track, speed_valid, speed, vvel_geo, vvel)
|
// fmt.Printf("ns_vel %d, ew_vel %d, track %d, speed_valid %t, speed %d, vvel_geo %t, vvel %d\n", ns_vel, ew_vel, track, speed_valid, speed, vvel_geo, vvel)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
utc_coupled := false
|
utc_coupled := false
|
||||||
tisb_site_id := uint8(0)
|
tisb_site_id := uint8(0)
|
||||||
|
|
||||||
|
@ -275,12 +279,82 @@ func parseDownlinkReport(s string) {
|
||||||
} else {
|
} else {
|
||||||
utc_coupled = (uint8(frame[16]) & 0x08) != 0
|
utc_coupled = (uint8(frame[16]) & 0x08) != 0
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//OK.
|
//OK.
|
||||||
// fmt.Printf("tisb_site_id %d, utc_coupled %t\n", tisb_site_id, utc_coupled)
|
// fmt.Printf("tisb_site_id %d, utc_coupled %t\n", tisb_site_id, utc_coupled)
|
||||||
|
|
||||||
ti.last_seen = time.Now()
|
ti.last_seen = time.Now()
|
||||||
|
|
||||||
|
trafficMutex.Lock()
|
||||||
traffic[ti.icao_addr] = ti
|
traffic[ti.icao_addr] = ti
|
||||||
|
trafficMutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
/*
|
||||||
|
// dump1090Addr = "127.0.0.1:30003"
|
||||||
|
inConn, err := net.Dial("tcp", dump1090Addr)
|
||||||
|
if err != nil {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rdr := bufio.NewReader(inConn)
|
||||||
|
for {
|
||||||
|
buf, err := rdr.ReadString('\n')
|
||||||
|
if err != nil { // Must have disconnected?
|
||||||
|
break
|
||||||
|
}
|
||||||
|
buf = strings.Trim(buf, "\r\n")
|
||||||
|
//log.Printf("%s\n", buf)
|
||||||
|
x := strings.Split(buf, ",")
|
||||||
|
//TODO: Add more sophisticated stuff that combines heading/speed updates with the location.
|
||||||
|
if len(x) < 22 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
icao := x[4]
|
||||||
|
icaoDec, err := strconv.ParseInt(icao, 16, 32)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mutex.Lock()
|
||||||
|
// Retrieve previous information on this ICAO code.
|
||||||
|
var pi PositionInfo
|
||||||
|
if _, ok := blips[icaoDec]; ok {
|
||||||
|
pi = blips[icaoDec]
|
||||||
|
}
|
||||||
|
|
||||||
|
if x[1] == "3" {
|
||||||
|
//MSG,3,111,11111,AC2BB7,111111,2015/07/28,03:59:12.363,2015/07/28,03:59:12.353,,5550,,,42.35847,-83.42212,,,,,,0
|
||||||
|
alt := x[11]
|
||||||
|
lat := x[14]
|
||||||
|
lng := x[15]
|
||||||
|
|
||||||
|
//log.Printf("icao=%s, icaoDec=%d, alt=%s, lat=%s, lng=%s\n", icao, icaoDec, alt, lat, lng)
|
||||||
|
pi.alt = alt
|
||||||
|
pi.lat = lat
|
||||||
|
pi.lng = lng
|
||||||
|
}
|
||||||
|
if x[1] == "4" {
|
||||||
|
// MSG,4,111,11111,A3B557,111111,2015/07/28,06:13:36.417,2015/07/28,06:13:36.398,,,414,278,,,-64,,,,,0
|
||||||
|
vel := x[12]
|
||||||
|
hdg := x[13]
|
||||||
|
vr := x[16]
|
||||||
|
|
||||||
|
//log.Printf("icao=%s, icaoDec=%d, vel=%s, hdg=%s, vr=%s\n", icao, icaoDec, vel, hdg, vr)
|
||||||
|
pi.vel = vel
|
||||||
|
pi.hdg = hdg
|
||||||
|
pi.vr = vr
|
||||||
|
}
|
||||||
|
if x[1] == "1" {
|
||||||
|
// MSG,1,,,%02X%02X%02X,,,,,,%s,,,,,,,,0,0,0,0
|
||||||
|
tail := x[10]
|
||||||
|
pi.tail = tail
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update "last seen" (any type of message).
|
||||||
|
pi.last_seen = time.Now()
|
||||||
|
|
||||||
|
blips[icaoDec] = pi // Update information on this ICAO code.
|
||||||
|
mutex.Unlock()
|
||||||
|
*/
|
||||||
|
|
Ładowanie…
Reference in New Issue