kopia lustrzana https://github.com/cyoung/stratux
commit
9c1e679d16
|
@ -0,0 +1,150 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"./uatparse"
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/gansidui/geohash"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UATFrame struct {
|
||||
FISB_month uint32
|
||||
FISB_day uint32
|
||||
FISB_hours uint32
|
||||
FISB_minutes uint32
|
||||
FISB_seconds uint32
|
||||
|
||||
Product_id uint32
|
||||
// Text data, if applicable.
|
||||
Text_data []string
|
||||
|
||||
// For AIRMET/NOTAM.
|
||||
//FIXME: Temporary.
|
||||
Points map[string][]uatparse.GeoPoint
|
||||
ReportNumber uint16
|
||||
ReportYear uint16
|
||||
LocationIdentifier string
|
||||
RecordFormat uint8
|
||||
ReportStart string
|
||||
ReportEnd string
|
||||
}
|
||||
|
||||
var reports map[string]UATFrame
|
||||
|
||||
func groupPoints(f *uatparse.UATFrame) map[string][]uatparse.GeoPoint {
|
||||
// Index all of the points by GeoHash. Group points together.
|
||||
res := make(map[string][]uatparse.GeoPoint)
|
||||
precision := 5 // 6 maybe, 0.000687.
|
||||
for _, p := range f.Points {
|
||||
hash, _ := geohash.Encode(p.Lat, p.Lon, precision)
|
||||
if r, ok := res[hash]; ok {
|
||||
r = append(r, p)
|
||||
res[hash] = r
|
||||
} else {
|
||||
res[hash] = []uatparse.GeoPoint{p}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func updateReport(f *uatparse.UATFrame) {
|
||||
if f.ReportNumber == 0 || f.ReportYear == 0 || f.RecordFormat == 0 {
|
||||
return
|
||||
}
|
||||
s := strconv.Itoa(int(f.ReportNumber)) + "-" + strconv.Itoa(int(f.ReportYear))
|
||||
f.LocationIdentifier = strings.Replace(f.LocationIdentifier, "\x03", "", -1)
|
||||
if len(f.Points) == 0 && len(f.Text_data) == 0 {
|
||||
return
|
||||
}
|
||||
if p, ok := reports[s]; ok {
|
||||
if len(f.Points) > 0 {
|
||||
p.Points = groupPoints(f)
|
||||
reports[s] = p
|
||||
}
|
||||
if len(f.Text_data) > 0 {
|
||||
p.Text_data = f.Text_data
|
||||
reports[s] = p
|
||||
}
|
||||
} else {
|
||||
var z UATFrame
|
||||
z.FISB_month = f.FISB_month
|
||||
z.FISB_day = f.FISB_day
|
||||
z.FISB_hours = f.FISB_hours
|
||||
z.FISB_minutes = f.FISB_minutes
|
||||
z.FISB_seconds = f.FISB_seconds
|
||||
z.Product_id = f.Product_id
|
||||
z.Points = groupPoints(f)
|
||||
z.ReportNumber = f.ReportNumber
|
||||
z.ReportYear = f.ReportYear
|
||||
z.LocationIdentifier = f.LocationIdentifier
|
||||
z.RecordFormat = f.RecordFormat
|
||||
z.ReportStart = f.ReportStart
|
||||
z.ReportEnd = f.ReportEnd
|
||||
|
||||
reports[s] = z
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
reports = make(map[string]UATFrame)
|
||||
s := "+3cc0978aa66ca3a02100002d3f29688210000000ff0dc45e1e00000000efd305071c142d071d0300bef1e3f1900abdf823bc440abe9ee394a80ac088439a980abfefa3e45c0abef1e3f1900a248000353f6a002210000000ff003e51987c4d5060cb9cb1c30833df2c78cf87f2d74c307d77cf7c10893053857f1d70df2e72c70c1fc75c37cb9cb2cf07f3c707f3c707c17d97df7df780260000353f6a002210000000ff004146247c4d5060cb9cb1c30833df2cf3df07f2d35c307d77cf7d7b71e3881420f3417f1d70df2e72c70c1fc75c37cf0c35d797f0c307f2d707c17d97df7df780648000213c66b022102c45170000bec0487c38f50136d1202c4517bb0defcf0da0c77c79cb26a0844517830defcf0da01145e05176605f1b205f3b205f4b205f6b205176605e00943a0497660e52bf2dcc8013848145d9817c6c8145d980a80250e8145d98178013848125d98334afcb132c8145d981680250e8145d981780138480140322014e120497660cb74ac8145d981780250e8145d9810d2004e1205176605f68033131203075048013848020524890c1105120c75c37c77c79cb2b71d71c31df0cf0c054d47800;rs=31;"
|
||||
// s := "+3c2643887cdca4802100002d3f29688210000000ff0dc45e1e00000000efd305071c142d071d0300bef1e3f1900abdf823bc440abe9ee394a80ac088439a980abfefa3e45c0abef1e3f1900a248000353f6a002210000000ff003e51987c4d5060cb9cb1c30833df2c78cf87f2d74c307d77cf7c10893053857f1d70df2e72c70c1fc75c37cb9cb2cf07f3c707f3c707c17d97df7df7806c80002d3f29682210000000ff00ce11787c04948d15480b0c8260cb8cb0d358032094e05c1832e32c34d5e04948d1548132454920605501148348063d280919281604c24481539424c832e70cf0c1e04948d154809192baeb8d3a024180d3e05c980931e1923cd833c138050558143e0cb039780430c8143e0d304d31600841a050f833c0538580948b8143e0d703858044cd7943e0cf04e014155e0c91e008c5e0c31c2f5894e008c5e0cd336040340ebc24ae8033ce11380458c407830c2dc336ae8033ce1138033ce507782644830cda814212560c3969e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;rs=17;"
|
||||
|
||||
// s := "+3bb40f8953d8b1b0360000353f54002210000000ff006185947c4d5060cb9c70c30833df0d78d707f2e72d5f5df49fcf4c3105fc75c37cb9c70c307f1d70df3c30df0c1fc30c1fcb2c1f05f65f7f3d30c417d2cf4c3105f0545054825526604854c549616018f48315381448e1e0052141b2024e78006c80002d3f29682210000000ff00ce11787c04948d15480b0c8260cb8cb0d358032094e05c1832e32c34d5e04948d1548132454920605501148348063d280919281604c24481539424c832e70cf0c1e04948d154809192baeb8d3a024180d3e05c980931e1923cd833c138050558143e0cb039780430c8143e0d304d31600841a050f833c0538580948b8143e0d703858044cd7943e0cf04e014155e0c91e008c5e0c31c2f5894e008c5e0cd336040340ebc24ae8033ce11380458c407830c2dc336ae8033ce1138033ce507782644830cda814212560c3969e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;rs=29;"
|
||||
|
||||
// s := "+3c2643887cdcb780480000213e955822102cc5c1000085bb887c38f50136d1202cc5c1bb0defc30ca0cb6c70d336a084c5c1830defc30ca03170603c24d48143d715280c1d48280534a0c72c34e30ca9834cb2c33c2ec303b0e36c38d38bb1c17828d2ee4e360160317069831c31d6ec46520a33cf1bb01948011cca603d55203c68131525890c5831d70df2db1c34cedc75c38c70c70d3378005700002d3f29688210000000ff28c4631e00000000efd317071c142d071d0300ce1a242695a4ce03a3e63da4cc01039cbda4ca5f633345a4c93ca31db1a4c899034aa1a4c8328379fda4c73fe39e15a4c75fa3d24da4cac703fe89a4ccf3840731a4ce1a242695a4ce1a24269540ce03a3e63d40cc01039cbd40ca5f63334540c93ca31db140c899034aa140c8328379fd40c73fe39e1540c75fa3d24d40cac703fe8940ccf384073140ce1a24269540000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000;"
|
||||
|
||||
replayUATFilename := flag.Bool("stdin", false, "Read from stdin")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *replayUATFilename == true {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
text, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
msg, err := uatparse.New(text)
|
||||
if err != nil {
|
||||
// fmt.Printf("err: %s\n", err.Error())
|
||||
// return
|
||||
continue
|
||||
}
|
||||
msg.DecodeUplink()
|
||||
for _, frame := range msg.Frames {
|
||||
updateReport(frame)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
msg, err := uatparse.New(s)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("err: %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
msg.DecodeUplink()
|
||||
|
||||
for _, frame := range msg.Frames {
|
||||
updateReport(frame)
|
||||
}
|
||||
}
|
||||
|
||||
r := make([]UATFrame, 0)
|
||||
for _, p := range reports {
|
||||
if len(p.Points) > 0 && len(p.Text_data) > 0 {
|
||||
r = append(r, p)
|
||||
}
|
||||
}
|
||||
|
||||
j, _ := json.Marshal(&r)
|
||||
fmt.Printf("%s\n", j)
|
||||
|
||||
}
|
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
Plik binarny nie jest wyświetlany.
|
@ -0,0 +1,30 @@
|
|||
package uatparse
|
||||
|
||||
// AIRMET = AIRMET/SIGMET/ (TFR?)
|
||||
|
||||
const (
|
||||
UATMSG_TEXT = 1
|
||||
UATMSG_NEXRAD = 2
|
||||
UATMSG_AIRMET = 3 // AIRMET. Decoded.
|
||||
|
||||
// How the coordinates should be used in a graphical AIRMET.
|
||||
AIRMET_POLYGON = 1
|
||||
AIRMET_ELLIPSE = 2
|
||||
AIRMET_PRISM = 3
|
||||
AIRMET_3D = 4
|
||||
)
|
||||
|
||||
// Points can be in 3D - take care that altitude is used correctly.
|
||||
type GeoPoint struct {
|
||||
Lat float64
|
||||
Lon float64
|
||||
Alt int32
|
||||
}
|
||||
|
||||
type UATAirmet struct {
|
||||
Points []GeoPoint // Points
|
||||
}
|
||||
|
||||
type UATMsgDecoded struct {
|
||||
Type int
|
||||
}
|
|
@ -29,8 +29,13 @@ const (
|
|||
type UATFrame struct {
|
||||
Raw_data []byte
|
||||
FISB_data []byte
|
||||
FISB_month uint32
|
||||
FISB_day uint32
|
||||
FISB_hours uint32
|
||||
FISB_minutes uint32
|
||||
FISB_seconds uint32
|
||||
|
||||
FISB_length uint32
|
||||
|
||||
frame_length uint32
|
||||
Frame_type uint32
|
||||
|
@ -38,6 +43,22 @@ type UATFrame struct {
|
|||
Product_id uint32
|
||||
// Text data, if applicable.
|
||||
Text_data []string
|
||||
|
||||
// Flags.
|
||||
a_f bool
|
||||
g_f bool
|
||||
p_f bool
|
||||
s_f bool //TODO: Segmentation.
|
||||
|
||||
// For AIRMET/NOTAM.
|
||||
//FIXME: Temporary.
|
||||
Points []GeoPoint
|
||||
ReportNumber uint16
|
||||
ReportYear uint16
|
||||
LocationIdentifier string
|
||||
RecordFormat uint8
|
||||
ReportStart string
|
||||
ReportEnd string
|
||||
}
|
||||
|
||||
type UATMsg struct {
|
||||
|
@ -82,37 +103,65 @@ func dlac_decode(data []byte, data_len uint32) string {
|
|||
return ret
|
||||
}
|
||||
|
||||
func (f *UATFrame) decodeInfoFrame() {
|
||||
|
||||
f.Product_id = ((uint32(f.Raw_data[0]) & 0x1f) << 6) | (uint32(f.Raw_data[1]) >> 2)
|
||||
|
||||
if f.Frame_type != 0 {
|
||||
return // Not FIS-B.
|
||||
}
|
||||
if f.frame_length < 4 {
|
||||
return // Too short for FIS-B.
|
||||
}
|
||||
|
||||
// Decodes the time format and aligns 'FISB_data' accordingly.
|
||||
//TODO: Make a new "FISB Time" structure that also encodes the type of timestamp received.
|
||||
//TODO: pass up error.
|
||||
func (f *UATFrame) decodeTimeFormat() {
|
||||
t_opt := ((uint32(f.Raw_data[1]) & 0x01) << 1) | (uint32(f.Raw_data[2]) >> 7)
|
||||
|
||||
if t_opt != 0 { //FIXME.
|
||||
// fmt.Printf("don't know time format %d\n", t_opt)
|
||||
// panic("time format")
|
||||
return
|
||||
var fisb_data []byte
|
||||
switch t_opt {
|
||||
case 0: // Hours, Minutes.
|
||||
if f.frame_length < 4 {
|
||||
return
|
||||
}
|
||||
f.FISB_hours = (uint32(f.Raw_data[2]) & 0x7c) >> 2
|
||||
f.FISB_minutes = ((uint32(f.Raw_data[2]) & 0x03) << 4) | (uint32(f.Raw_data[3]) >> 4)
|
||||
f.FISB_length = f.frame_length - 4
|
||||
fisb_data = f.Raw_data[4:]
|
||||
case 1: // Hours, Minutes, Seconds.
|
||||
if f.frame_length < 5 {
|
||||
return
|
||||
}
|
||||
f.FISB_hours = (uint32(f.Raw_data[2]) & 0x7c) >> 2
|
||||
f.FISB_minutes = ((uint32(f.Raw_data[2]) & 0x03) << 4) | (uint32(f.Raw_data[3]) >> 4)
|
||||
f.FISB_seconds = ((uint32(f.Raw_data[3]) & 0x0f) << 2) | (uint32(f.Raw_data[4]) >> 6)
|
||||
f.FISB_length = f.frame_length - 5
|
||||
fisb_data = f.Raw_data[5:]
|
||||
case 2: // Month, Day, Hours, Minutes.
|
||||
if f.frame_length < 5 {
|
||||
return
|
||||
}
|
||||
f.FISB_month = (uint32(f.Raw_data[2]) & 0x78) >> 3
|
||||
f.FISB_day = ((uint32(f.Raw_data[2]) & 0x07) << 2) | (uint32(f.Raw_data[3]) >> 6)
|
||||
f.FISB_hours = (uint32(f.Raw_data[3]) & 0x3e) >> 1
|
||||
f.FISB_minutes = ((uint32(f.Raw_data[3]) & 0x01) << 5) | (uint32(f.Raw_data[4]) >> 3)
|
||||
f.FISB_length = f.frame_length - 5
|
||||
fisb_data = f.Raw_data[5:]
|
||||
case 3: // Month, Day, Hours, Minutes, Seconds.
|
||||
if f.frame_length < 6 {
|
||||
return
|
||||
}
|
||||
f.FISB_month = (uint32(f.Raw_data[2]) & 0x78) >> 3
|
||||
f.FISB_day = ((uint32(f.Raw_data[2]) & 0x07) << 2) | (uint32(f.Raw_data[3]) >> 6)
|
||||
f.FISB_hours = (uint32(f.Raw_data[3]) & 0x3e) >> 1
|
||||
f.FISB_minutes = ((uint32(f.Raw_data[3]) & 0x01) << 5) | (uint32(f.Raw_data[4]) >> 3)
|
||||
f.FISB_seconds = ((uint32(f.Raw_data[4]) & 0x03) << 3) | (uint32(f.Raw_data[5]) >> 5)
|
||||
f.FISB_length = f.frame_length - 6
|
||||
fisb_data = f.Raw_data[6:]
|
||||
default:
|
||||
return // Should never reach this.
|
||||
}
|
||||
|
||||
f.FISB_hours = (uint32(f.Raw_data[2]) & 0x7c) >> 2
|
||||
f.FISB_minutes = ((uint32(f.Raw_data[2]) & 0x03) << 4) | (uint32(f.Raw_data[3]) >> 4)
|
||||
|
||||
fisb_length := f.frame_length - 4
|
||||
fisb_data := f.Raw_data[4:]
|
||||
f.FISB_data = fisb_data
|
||||
|
||||
if f.Product_id != 413 { // Doesn't have text data that we'd be interested in, so we're done.
|
||||
return
|
||||
if (uint16(f.Raw_data[1]) & 0x02) != 0 {
|
||||
f.s_f = true // Default false.
|
||||
}
|
||||
}
|
||||
|
||||
p := dlac_decode(fisb_data, fisb_length)
|
||||
// Format newlines.
|
||||
func formatDLACData(p string) []string {
|
||||
ret := make([]string, 0)
|
||||
for {
|
||||
pos := strings.Index(p, "\x1E")
|
||||
|
@ -126,8 +175,307 @@ func (f *UATFrame) decodeInfoFrame() {
|
|||
ret = append(ret, p[:pos])
|
||||
p = p[pos+1:]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
f.Text_data = ret
|
||||
// Whole frame contents is DLAC encoded text.
|
||||
func (f *UATFrame) decodeTextFrame() {
|
||||
p := dlac_decode(f.FISB_data, f.FISB_length)
|
||||
|
||||
f.Text_data = formatDLACData(p)
|
||||
}
|
||||
|
||||
// Gets month, day, hours, minutes.
|
||||
// Formats into a string.
|
||||
func airmetParseDate(b []byte, date_time_format uint8) string {
|
||||
switch date_time_format {
|
||||
case 0: // No date/time used.
|
||||
return ""
|
||||
case 1: // Month, Day, Hours, Minutes.
|
||||
month := uint8(b[0])
|
||||
day := uint8(b[1])
|
||||
hours := uint8(b[2])
|
||||
minutes := uint8(b[3])
|
||||
return fmt.Sprintf("%02d-%02d %02d:%02d", month, day, hours, minutes)
|
||||
case 2: // Day, Hours, Minutes.
|
||||
day := uint8(b[0])
|
||||
hours := uint8(b[1])
|
||||
minutes := uint8(b[2])
|
||||
return fmt.Sprintf("%02d %02d:%02d", day, hours, minutes)
|
||||
case 3: // Hours, Minutes.
|
||||
hours := uint8(b[0])
|
||||
minutes := uint8(b[1])
|
||||
return fmt.Sprintf("%02d:%02d", hours, minutes)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func airmetLatLng(lat_raw, lng_raw int32, alt bool) (float64, float64) {
|
||||
fct := float64(0.000687)
|
||||
if alt {
|
||||
fct = float64(0.001373)
|
||||
}
|
||||
lat := fct * float64(lat_raw)
|
||||
lng := fct * float64(lng_raw)
|
||||
if lat > 90.0 {
|
||||
lat = lat - 180.0
|
||||
}
|
||||
if lng > 180.0 {
|
||||
lng = lng - 360.0
|
||||
}
|
||||
return lat, lng
|
||||
}
|
||||
|
||||
//TODO: Ignoring flags (segmentation, etc.)
|
||||
// Aero_FISB_ProdDef_Rev4.pdf
|
||||
// Decode product IDs 8-13.
|
||||
func (f *UATFrame) decodeAirmet() {
|
||||
// APDU header: 48 bits (3-3) - assume no segmentation.
|
||||
|
||||
record_format := (uint8(f.FISB_data[0]) & 0xF0) >> 4
|
||||
f.RecordFormat = record_format
|
||||
fmt.Printf("record_format=%d\n", record_format)
|
||||
product_version := (uint8(f.FISB_data[0]) & 0x0F)
|
||||
fmt.Printf("product_version=%d\n", product_version)
|
||||
record_count := (uint8(f.FISB_data[1]) & 0xF0) >> 4
|
||||
fmt.Printf("record_count=%d\n", record_count)
|
||||
location_identifier := dlac_decode(f.FISB_data[2:], 3)
|
||||
fmt.Printf("%s\n", hex.Dump(f.FISB_data))
|
||||
f.LocationIdentifier = location_identifier
|
||||
fmt.Printf("location_identifier=%s\n", location_identifier)
|
||||
record_reference := (uint8(f.FISB_data[5])) //FIXME: Special values. 0x00 means "use location_identifier". 0xFF means "use different reference". (4-3).
|
||||
fmt.Printf("record_reference=%d\n", record_reference)
|
||||
// Not sure when this is even used.
|
||||
// rwy_designator := (record_reference & FC) >> 4
|
||||
// parallel_rwy_designator := record_reference & 0x03 // 0 = NA, 1 = R, 2 = L, 3 = C (Figure 4-2).
|
||||
|
||||
//FIXME: Assume one record.
|
||||
if record_count != 1 {
|
||||
fmt.Printf("record_count=%d, != 1\n", record_count)
|
||||
return
|
||||
}
|
||||
/*
|
||||
0 - No data
|
||||
1 - Unformatted ASCII Text
|
||||
2 - Unformatted DLAC Text
|
||||
3 - Unformatted DLAC Text w/ dictionary
|
||||
4 - Formatted Text using ASN.1/PER
|
||||
5-7 - Future Use
|
||||
8 - Graphical Overlay
|
||||
9-15 - Future Use
|
||||
*/
|
||||
switch record_format {
|
||||
case 2:
|
||||
record_length := (uint16(f.FISB_data[6]) << 8) | uint16(f.FISB_data[7])
|
||||
if len(f.FISB_data)-int(record_length) < 6 {
|
||||
fmt.Printf("FISB record not long enough: record_length=%d, len(f.FISB_data)=%d\n", record_length, len(f.FISB_data))
|
||||
return
|
||||
}
|
||||
fmt.Printf("record_length=%d\n", record_length)
|
||||
// Report identifier = report number + report year.
|
||||
report_number := (uint16(f.FISB_data[8]) << 6) | ((uint16(f.FISB_data[9]) & 0xFC) >> 2)
|
||||
f.ReportNumber = report_number
|
||||
fmt.Printf("report_number=%d\n", report_number)
|
||||
report_year := ((uint16(f.FISB_data[9]) & 0x03) << 5) | ((uint16(f.FISB_data[10]) & 0xF8) >> 3)
|
||||
f.ReportYear = report_year
|
||||
fmt.Printf("report_year=%d\n", report_year)
|
||||
report_status := (uint8(f.FISB_data[10]) & 0x04) >> 2 //TODO: 0 = cancelled, 1 = active.
|
||||
fmt.Printf("report_status=%d\n", report_status)
|
||||
fmt.Printf("record_length=%d,len=%d\n", record_length, len(f.FISB_data))
|
||||
text_data_len := record_length - 5
|
||||
text_data := dlac_decode(f.FISB_data[11:], uint32(text_data_len))
|
||||
fmt.Printf("text_data=%s\n", text_data)
|
||||
f.Text_data = formatDLACData(text_data)
|
||||
case 8:
|
||||
// (6-1). (6.22 - Graphical Overlay Record Format).
|
||||
record_data := f.FISB_data[6:] // Start after the record header.
|
||||
record_length := (uint16(record_data[0]) << 2) | ((uint16(record_data[1]) & 0xC0) >> 6)
|
||||
fmt.Printf("record_length=%d\n", record_length)
|
||||
// Report identifier = report number + report year.
|
||||
report_number := ((uint16(record_data[1]) & 0x3F) << 8) | uint16(record_data[2])
|
||||
f.ReportNumber = report_number
|
||||
fmt.Printf("report_number=%d\n", report_number)
|
||||
report_year := (uint16(record_data[3]) & 0xFE) >> 1
|
||||
f.ReportYear = report_year
|
||||
fmt.Printf("report_year=%d\n", report_year)
|
||||
overlay_record_identifier := ((uint8(record_data[4]) & 0x1E) >> 1) + 1 // Document instructs to add 1.
|
||||
fmt.Printf("overlay_record_identifier=%d\n", overlay_record_identifier)
|
||||
object_label_flag := uint8(record_data[4] & 0x01)
|
||||
fmt.Printf("object_label_flag=%d\n", object_label_flag)
|
||||
|
||||
if object_label_flag == 0 { // Numeric index.
|
||||
object_label := (uint8(record_data[5]) << 8) | uint8(record_data[6])
|
||||
record_data = record_data[7:]
|
||||
fmt.Printf("object_label=%d\n", object_label)
|
||||
} else {
|
||||
object_label := dlac_decode(record_data[5:], 9)
|
||||
record_data = record_data[14:]
|
||||
fmt.Printf("object_label=%s\n", object_label)
|
||||
}
|
||||
|
||||
element_flag := (uint8(record_data[0]) & 0x80) >> 7
|
||||
fmt.Printf("element_flag=%d\n", element_flag)
|
||||
qualifier_flag := (uint8(record_data[0]) & 0x40) >> 6
|
||||
fmt.Printf("qualifier_flag=%d\n", qualifier_flag)
|
||||
param_flag := (uint8(record_data[0]) & 0x20) >> 5
|
||||
fmt.Printf("param_flag=%d\n", param_flag)
|
||||
object_element := uint8(record_data[0]) & 0x1F
|
||||
fmt.Printf("object_element=%d\n", object_element)
|
||||
|
||||
object_type := (uint8(record_data[1]) & 0xF0) >> 4
|
||||
fmt.Printf("object_type=%d\n", object_type)
|
||||
|
||||
object_status := uint8(record_data[1]) & 0x0F
|
||||
fmt.Printf("object_status=%d\n", object_status)
|
||||
|
||||
//FIXME
|
||||
if qualifier_flag == 0 { //TODO: Check.
|
||||
record_data = record_data[2:]
|
||||
} else {
|
||||
object_qualifier := (uint32(record_data[2]) << 16) | (uint32(record_data[3]) << 8) | uint32(record_data[4])
|
||||
fmt.Printf("object_qualifier=%d\n", object_qualifier)
|
||||
fmt.Printf("%02x%02x%02x\n", record_data[2], record_data[3], record_data[4])
|
||||
record_data = record_data[5:]
|
||||
}
|
||||
//FIXME
|
||||
//if param_flag == 0 { //TODO: Check.
|
||||
// record_data = record_data[2:]
|
||||
//} else {
|
||||
// //TODO.
|
||||
// // record_data = record_data[4:]
|
||||
//}
|
||||
|
||||
record_applicability_options := (uint8(record_data[0]) & 0xC0) >> 6
|
||||
fmt.Printf("record_applicability_options=%d\n", record_applicability_options)
|
||||
date_time_format := (uint8(record_data[0]) & 0x30) >> 4
|
||||
fmt.Printf("date_time_format=%d\n", date_time_format)
|
||||
geometry_overlay_options := uint8(record_data[0]) & 0x0F
|
||||
fmt.Printf("geometry_overlay_options=%d\n", geometry_overlay_options)
|
||||
|
||||
overlay_operator := (uint8(record_data[1]) & 0xC0) >> 6
|
||||
fmt.Printf("overlay_operator=%d\n", overlay_operator)
|
||||
|
||||
overlay_vertices_count := (uint8(record_data[1]) & 0x3F) + 1 // Document instructs to add 1. (6.20).
|
||||
fmt.Printf("overlay_vertices_count=%d\n", overlay_vertices_count)
|
||||
|
||||
// Parse all of the dates.
|
||||
switch record_applicability_options {
|
||||
case 0: // No times given. UFN.
|
||||
record_data = record_data[2:]
|
||||
case 1: // Start time only. WEF.
|
||||
f.ReportStart = airmetParseDate(record_data[2:], date_time_format)
|
||||
record_data = record_data[6:]
|
||||
case 2: // End time only. TIL.
|
||||
f.ReportEnd = airmetParseDate(record_data[2:], date_time_format)
|
||||
record_data = record_data[6:]
|
||||
case 3: // Both start and end times. WEF.
|
||||
f.ReportStart = airmetParseDate(record_data[2:], date_time_format)
|
||||
f.ReportEnd = airmetParseDate(record_data[6:], date_time_format)
|
||||
record_data = record_data[10:]
|
||||
}
|
||||
|
||||
// Now we have the vertices.
|
||||
switch geometry_overlay_options {
|
||||
case 3: // Extended Range 3D Polygon (MSL).
|
||||
points := make([]GeoPoint, 0) // Slice containing all of the points.
|
||||
fmt.Printf("%d\n", len(record_data))
|
||||
for i := 0; i < int(overlay_vertices_count); i++ {
|
||||
lng_raw := (int32(record_data[6*i]) << 11) | (int32(record_data[6*i+1]) << 3) | (int32(record_data[6*i+2]) & 0xE0 >> 5)
|
||||
lat_raw := ((int32(record_data[6*i+2]) & 0x1F) << 14) | (int32(record_data[6*i+3]) << 6) | ((int32(record_data[6*i+4]) & 0xFC) >> 2)
|
||||
alt_raw := ((int32(record_data[6*i+4]) & 0x03) << 8) | int32(record_data[6*i+5])
|
||||
|
||||
fmt.Printf("lat_raw=%d, lng_raw=%d, alt_raw=%d\n", lat_raw, lng_raw, alt_raw)
|
||||
lat, lng := airmetLatLng(lat_raw, lng_raw, false)
|
||||
|
||||
alt := alt_raw * 100
|
||||
fmt.Printf("lat=%f,lng=%f,alt=%d\n", lat, lng, alt)
|
||||
fmt.Printf("coord:%f,%f\n", lat, lng)
|
||||
var point GeoPoint
|
||||
point.Lat = lat
|
||||
point.Lon = lng
|
||||
point.Alt = alt
|
||||
points = append(points, point)
|
||||
f.Points = points
|
||||
}
|
||||
case 9: // Extended Range 3D Point (AGL). p.47.
|
||||
lng_raw := (int32(record_data[0]) << 11) | (int32(record_data[1]) << 3) | (int32(record_data[2]) & 0xE0 >> 5)
|
||||
lat_raw := ((int32(record_data[2]) & 0x1F) << 14) | (int32(record_data[3]) << 6) | ((int32(record_data[4]) & 0xFC) >> 2)
|
||||
alt_raw := ((int32(record_data[4]) & 0x03) << 8) | int32(record_data[5])
|
||||
|
||||
fmt.Printf("lat_raw=%d, lng_raw=%d, alt_raw=%d\n", lat_raw, lng_raw, alt_raw)
|
||||
lat, lng := airmetLatLng(lat_raw, lng_raw, false)
|
||||
|
||||
alt := alt_raw * 100
|
||||
fmt.Printf("lat=%f,lng=%f,alt=%d\n", lat, lng, alt)
|
||||
fmt.Printf("coord:%f,%f\n", lat, lng)
|
||||
var point GeoPoint
|
||||
point.Lat = lat
|
||||
point.Lon = lng
|
||||
point.Alt = alt
|
||||
f.Points = []GeoPoint{point}
|
||||
case 7, 8: // Extended Range Circular Prism (MSL). (8 = AGL)
|
||||
lng_bot_raw := (int32(record_data[0]) << 10) | (int32(record_data[1]) << 2) | (int32(record_data[2]) & 0xC0 >> 6)
|
||||
lat_bot_raw := ((int32(record_data[2]) & 0x3F) << 12) | (int32(record_data[3]) << 4) | ((int32(record_data[4]) & 0xF0) >> 4)
|
||||
lng_top_raw := ((int32(record_data[4]) & 0x0F) << 14) | (int32(record_data[5]) << 6) | ((int32(record_data[6]) & 0xFC) >> 2)
|
||||
lat_top_raw := ((int32(record_data[6]) & 0x03) << 16) | (int32(record_data[7]) << 8) | int32(record_data[8])
|
||||
|
||||
alt_bot_raw := (int32(record_data[9]) & 0xFE) >> 1
|
||||
alt_top_raw := ((int32(record_data[9]) & 0x01) << 6) | ((int32(record_data[10]) & 0xFC) >> 2)
|
||||
|
||||
r_lng_raw := ((int32(record_data[10]) & 0x03) << 7) | ((int32(record_data[11]) & 0xFE) >> 1)
|
||||
r_lat_raw := ((int32(record_data[11]) & 0x01) << 8) | int32(record_data[12])
|
||||
alpha := int32(record_data[13])
|
||||
|
||||
lat_bot, lng_bot := airmetLatLng(lat_bot_raw, lng_bot_raw, true)
|
||||
lat_top, lng_top := airmetLatLng(lat_top_raw, lng_top_raw, true)
|
||||
|
||||
alt_bot := alt_bot_raw * 5
|
||||
alt_top := alt_top_raw * 500
|
||||
|
||||
r_lng := float64(r_lng_raw) * float64(0.2)
|
||||
r_lat := float64(r_lat_raw) * float64(0.2)
|
||||
|
||||
fmt.Printf("lat_bot, lng_bot = %f, %f\n", lat_bot, lng_bot)
|
||||
fmt.Printf("lat_top, lng_top = %f, %f\n", lat_top, lng_top)
|
||||
|
||||
if geometry_overlay_options == 8 {
|
||||
fmt.Printf("alt_bot, alt_top = %d AGL, %d AGL\n", alt_bot, alt_top)
|
||||
} else {
|
||||
fmt.Printf("alt_bot, alt_top = %d MSL, %d MSL\n", alt_bot, alt_top)
|
||||
}
|
||||
fmt.Printf("r_lng, r_lat = %f, %f\n", r_lng, r_lat)
|
||||
|
||||
fmt.Printf("alpha=%d\n", alpha)
|
||||
|
||||
default:
|
||||
fmt.Printf("unknown geometry: %d\n", geometry_overlay_options)
|
||||
}
|
||||
//case 1: // Unformatted ASCII Text.
|
||||
default:
|
||||
fmt.Printf("unknown record format: %d\n", record_format)
|
||||
}
|
||||
fmt.Printf("\n\n\n")
|
||||
}
|
||||
|
||||
func (f *UATFrame) decodeInfoFrame() {
|
||||
|
||||
f.Product_id = ((uint32(f.Raw_data[0]) & 0x1f) << 6) | (uint32(f.Raw_data[1]) >> 2)
|
||||
|
||||
if f.Frame_type != 0 {
|
||||
return // Not FIS-B.
|
||||
}
|
||||
|
||||
f.decodeTimeFormat()
|
||||
|
||||
switch f.Product_id {
|
||||
case 413:
|
||||
f.decodeTextFrame()
|
||||
case 8, 11, 13:
|
||||
f.decodeAirmet()
|
||||
default:
|
||||
fmt.Printf("don't know what to do with product id: %d\n", f.Product_id)
|
||||
}
|
||||
|
||||
// logger.Printf("pos=%d,len=%d,t_opt=%d,product_id=%d, time=%d:%d\n", frame_start, frame_len, t_opt, product_id, fisb_hours, fisb_minutes)
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue