kopia lustrzana https://github.com/cyoung/stratux
Initial commit.
rodzic
5fe81f14d0
commit
0fc0b1e14f
|
@ -0,0 +1,143 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"fmt"
|
||||
"bufio"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"sync"
|
||||
"github.com/sevlyar/go-daemon"
|
||||
)
|
||||
|
||||
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.
|
||||
//fmt.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)
|
||||
//fmt.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)
|
||||
|
||||
context := new(daemon.Context)
|
||||
child, _ := context.Reborn()
|
||||
if child != nil {
|
||||
fmt.Printf("going into background.\n")
|
||||
} else {
|
||||
defer context.Release()
|
||||
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")
|
||||
//fmt.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]
|
||||
|
||||
//fmt.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]
|
||||
|
||||
//fmt.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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
package main
|
||||
|
||||
|
||||
import (
|
||||
"net"
|
||||
"encoding/hex"
|
||||
"time"
|
||||
"strings"
|
||||
"fmt"
|
||||
"bufio"
|
||||
"os"
|
||||
)
|
||||
|
||||
// http://www.faa.gov/nextgen/programs/adsb/wsa/media/GDL90_Public_ICD_RevA.PDF
|
||||
|
||||
const (
|
||||
ipadAddr = "192.168.10.255:4000" // Port 4000 for FreeFlight RANGR.
|
||||
maxDatagramSize = 8192
|
||||
UPLINK_BLOCK_DATA_BITS = 576
|
||||
UPLINK_BLOCK_BITS = (UPLINK_BLOCK_DATA_BITS+160)
|
||||
UPLINK_BLOCK_DATA_BYTES = (UPLINK_BLOCK_DATA_BITS/8)
|
||||
UPLINK_BLOCK_BYTES = (UPLINK_BLOCK_BITS/8)
|
||||
|
||||
UPLINK_FRAME_BLOCKS = 6
|
||||
UPLINK_FRAME_DATA_BITS = (UPLINK_FRAME_BLOCKS * UPLINK_BLOCK_DATA_BITS)
|
||||
UPLINK_FRAME_BITS = (UPLINK_FRAME_BLOCKS * UPLINK_BLOCK_BITS)
|
||||
UPLINK_FRAME_DATA_BYTES = (UPLINK_FRAME_DATA_BITS/8)
|
||||
UPLINK_FRAME_BYTES = (UPLINK_FRAME_BITS/8)
|
||||
|
||||
// assume 6 byte frames: 2 header bytes, 4 byte payload
|
||||
// (TIS-B heartbeat with one address, or empty FIS-B APDU)
|
||||
UPLINK_MAX_INFO_FRAMES = (424/6)
|
||||
)
|
||||
|
||||
|
||||
var Crc16Table [256]uint16
|
||||
var outConn *net.UDPConn
|
||||
|
||||
// Construct the CRC table. Adapted from FAA ref above.
|
||||
func crcInit() {
|
||||
var i uint16
|
||||
var bitctr uint16
|
||||
var crc uint16
|
||||
for i = 0; i < 256; i++ {
|
||||
crc = (i << 8)
|
||||
for bitctr = 0; bitctr < 8; bitctr++ {
|
||||
z := uint16(0)
|
||||
if (crc & 0x8000) != 0 {
|
||||
z = 0x1021
|
||||
}
|
||||
crc = (crc << 1) ^ z
|
||||
}
|
||||
Crc16Table[i] = crc
|
||||
}
|
||||
}
|
||||
|
||||
// Compute CRC. Adapted from FAA ref above.
|
||||
func crcCompute(data []byte) uint16 {
|
||||
ret := uint16(0)
|
||||
for i := 0; i < len(data); i++ {
|
||||
ret = Crc16Table[ret >> 8] ^ (ret << 8) ^ uint16(data[i])
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func prepareMessage(data []byte) []byte {
|
||||
tmp := []byte{0x7E} // Flag start.
|
||||
// Compute CRC before modifying the message.
|
||||
crc := crcCompute(data)
|
||||
|
||||
// Copy the message over, escaping 0x7E (Flag Byte) and 0x7D (Control-Escape).
|
||||
for i := 0; i < len(data); i++ {
|
||||
mv := data[i]
|
||||
if (mv == 0x7E) || (mv == 0x7D) {
|
||||
mv = mv ^ 0x20
|
||||
tmp = append(tmp, 0x7D)
|
||||
}
|
||||
tmp = append(tmp, mv)
|
||||
}
|
||||
|
||||
// Add the two CRC16 bytes.
|
||||
tmp = append(tmp, byte(crc & 0xFF))
|
||||
tmp = append(tmp, byte(crc >> 8))
|
||||
|
||||
tmp = append(tmp, 0x7E) // Flag end.
|
||||
|
||||
return tmp
|
||||
}
|
||||
|
||||
func makeHeartbeat() []byte {
|
||||
msg := make([]byte, 7)
|
||||
// See p.10.
|
||||
msg[0] = 0x00 // Message type "Heartbeat".
|
||||
msg[1] = 0x01 // "UAT Initialized".
|
||||
nowUTC := time.Now().UTC()
|
||||
// Seconds since 0000Z.
|
||||
midnightUTC := time.Date(nowUTC.Year(), nowUTC.Month(), nowUTC.Day(), 0, 0, 0, 0, time.UTC)
|
||||
secondsSinceMidnightUTC := uint32(nowUTC.Sub(midnightUTC).Seconds())
|
||||
|
||||
msg[2] = byte((secondsSinceMidnightUTC >> 16) << 7)
|
||||
msg[3] = byte((secondsSinceMidnightUTC & 0xFF))
|
||||
msg[4] = byte((secondsSinceMidnightUTC & 0xFFFF) >> 8)
|
||||
|
||||
// TODO. Number of uplink messages. See p.12.
|
||||
// msg[5]
|
||||
// msg[6]
|
||||
|
||||
return prepareMessage(msg)
|
||||
}
|
||||
|
||||
func relayUplinkMessage(msg []byte) {
|
||||
ret := make([]byte, len(msg) + 4)
|
||||
// See p.15.
|
||||
ret[0] = 0x07 // Uplink message ID.
|
||||
ret[1] = 0x00 //TODO: Time.
|
||||
ret[2] = 0x00 //TODO: Time.
|
||||
ret[3] = 0x00 //TODO: Time.
|
||||
|
||||
for i := 0; i < len(msg); i++ {
|
||||
ret[i+4] = msg[i]
|
||||
}
|
||||
|
||||
outConn.Write(prepareMessage(ret))
|
||||
}
|
||||
|
||||
func heartBeatSender() {
|
||||
for {
|
||||
outConn.Write(makeHeartbeat())
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func parseInput(buf string) []byte {
|
||||
buf = strings.Trim(buf, "\r\n") // Remove newlines.
|
||||
x := strings.Split(buf, ";") // We want to discard everything before the first ';'.
|
||||
if len(x) == 0 {
|
||||
return nil
|
||||
}
|
||||
s := x[0]
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
if s[0] != '+' {
|
||||
return nil // Only want + ("Uplink") messages currently. - (Downlink) or messages that start with other are discarded.
|
||||
}
|
||||
|
||||
s = s[1:]
|
||||
|
||||
if len(s) % 2 != 0 { // Bad format.
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(s)/2 != UPLINK_FRAME_DATA_BYTES {
|
||||
fmt.Printf("UPLINK_FRAME_DATA_BYTES=%d, len(s)=%d\n", UPLINK_FRAME_DATA_BYTES, len(s))
|
||||
// panic("Error")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Now, begin converting the string into a byte array.
|
||||
frame := make([]byte, UPLINK_FRAME_DATA_BYTES)
|
||||
hex.Decode(frame, []byte(s))
|
||||
|
||||
return frame
|
||||
}
|
||||
|
||||
|
||||
func main() {
|
||||
crcInit() // Initialize CRC16 table.
|
||||
|
||||
// Open UDP port to send out the messages.
|
||||
addr, err := net.ResolveUDPAddr("udp", ipadAddr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
outConn, err = net.DialUDP("udp", nil, addr)
|
||||
|
||||
// Start the heartbeat message loop in the background, once per second.
|
||||
go heartBeatSender()
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
for {
|
||||
buf, _ := reader.ReadString('\n')
|
||||
o := parseInput(buf)
|
||||
if o != nil {
|
||||
relayUplinkMessage(o)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Ładowanie…
Reference in New Issue