2015-08-04 05:44:55 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2015-08-08 23:05:19 +00:00
|
|
|
"bufio"
|
|
|
|
"encoding/hex"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"time"
|
2015-08-04 05:44:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// http://www.faa.gov/nextgen/programs/adsb/wsa/media/GDL90_Public_ICD_RevA.PDF
|
|
|
|
|
|
|
|
const (
|
2015-08-10 16:41:27 +00:00
|
|
|
ipadAddr = "255.255.255.255:4000" // Port 4000 for FreeFlight RANGR.
|
2015-08-08 23:05:19 +00:00
|
|
|
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)
|
2015-08-04 05:44:55 +00:00
|
|
|
|
|
|
|
// assume 6 byte frames: 2 header bytes, 4 byte payload
|
|
|
|
// (TIS-B heartbeat with one address, or empty FIS-B APDU)
|
2015-08-08 23:05:19 +00:00
|
|
|
UPLINK_MAX_INFO_FRAMES = (424 / 6)
|
2015-08-09 16:10:44 +00:00
|
|
|
|
2015-08-09 22:51:23 +00:00
|
|
|
MSGTYPE_UPLINK = 0x07
|
|
|
|
MSGTYPE_BASIC_REPORT = 0x1E
|
|
|
|
MSGTYPE_LONG_REPORT = 0x1F
|
2015-08-04 05:44:55 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
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++ {
|
2015-08-08 23:05:19 +00:00
|
|
|
ret = Crc16Table[ret>>8] ^ (ret << 8) ^ uint16(data[i])
|
2015-08-04 05:44:55 +00:00
|
|
|
}
|
|
|
|
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.
|
2015-08-08 23:05:19 +00:00
|
|
|
tmp = append(tmp, byte(crc&0xFF))
|
|
|
|
tmp = append(tmp, byte(crc>>8))
|
2015-08-04 05:44:55 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2015-08-09 16:10:44 +00:00
|
|
|
func relayMessage(msgtype uint16, msg []byte) {
|
2015-08-08 23:05:19 +00:00
|
|
|
ret := make([]byte, len(msg)+4)
|
2015-08-04 05:44:55 +00:00
|
|
|
// See p.15.
|
2015-08-09 16:10:44 +00:00
|
|
|
ret[0] = byte(msgtype) // Uplink message ID.
|
2015-08-09 22:51:23 +00:00
|
|
|
ret[1] = 0x00 //TODO: Time.
|
|
|
|
ret[2] = 0x00 //TODO: Time.
|
|
|
|
ret[3] = 0x00 //TODO: Time.
|
2015-08-04 05:44:55 +00:00
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-09 16:10:44 +00:00
|
|
|
func parseInput(buf string) ([]byte, uint16) {
|
2015-08-09 22:51:23 +00:00
|
|
|
x := strings.Split(buf, ";") // Discard everything after the first ';'.
|
2015-08-04 05:44:55 +00:00
|
|
|
if len(x) == 0 {
|
2015-08-09 16:10:44 +00:00
|
|
|
return nil, 0
|
2015-08-04 05:44:55 +00:00
|
|
|
}
|
|
|
|
s := x[0]
|
|
|
|
if len(s) == 0 {
|
2015-08-09 16:10:44 +00:00
|
|
|
return nil, 0
|
2015-08-04 05:44:55 +00:00
|
|
|
}
|
2015-08-09 16:10:44 +00:00
|
|
|
msgtype := uint16(0)
|
2015-08-04 05:44:55 +00:00
|
|
|
|
|
|
|
s = s[1:]
|
2015-08-09 22:51:23 +00:00
|
|
|
msglen := len(s) / 2
|
2015-08-04 05:44:55 +00:00
|
|
|
|
2015-08-08 23:05:19 +00:00
|
|
|
if len(s)%2 != 0 { // Bad format.
|
2015-08-09 16:10:44 +00:00
|
|
|
return nil, 0
|
|
|
|
}
|
|
|
|
|
|
|
|
if msglen == UPLINK_FRAME_DATA_BYTES {
|
|
|
|
msgtype = MSGTYPE_UPLINK
|
|
|
|
} else if msglen == 34 {
|
|
|
|
msgtype = MSGTYPE_LONG_REPORT
|
|
|
|
} else if msglen == 18 {
|
|
|
|
msgtype = MSGTYPE_BASIC_REPORT
|
|
|
|
} else {
|
|
|
|
msgtype = 0
|
2015-08-04 05:44:55 +00:00
|
|
|
}
|
|
|
|
|
2015-08-09 16:10:44 +00:00
|
|
|
if msgtype == 0 {
|
|
|
|
fmt.Printf("UNKNOWN MESSAGE TYPE: %s - msglen=%d\n", s, msglen)
|
2015-08-04 05:44:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now, begin converting the string into a byte array.
|
|
|
|
frame := make([]byte, UPLINK_FRAME_DATA_BYTES)
|
|
|
|
hex.Decode(frame, []byte(s))
|
|
|
|
|
2015-08-09 16:10:44 +00:00
|
|
|
return frame, msgtype
|
2015-08-04 05:44:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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')
|
2015-08-09 16:10:44 +00:00
|
|
|
o, msgtype := parseInput(buf)
|
|
|
|
if o != nil && msgtype != 0 {
|
|
|
|
relayMessage(msgtype, o)
|
2015-08-04 05:44:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-08 23:05:19 +00:00
|
|
|
}
|