Add copyright and examples.

fisb_service
Christopher Young 2016-04-01 10:28:07 -04:00
rodzic 522d74c22e
commit cfd23d54eb
13 zmienionych plików z 217 dodań i 76 usunięć

Wyświetl plik

@ -1,3 +1,13 @@
/*
Copyright (c) 2015-2016 Christopher Young
Distributable under the terms of The "BSD New"" License
that can be found in the LICENSE file, herein included
as part of this header.
gen_gdl90.go: Input demodulated UAT and 1090ES information, output GDL90. Heartbeat,
ownship, status messages, stats collection.
*/
package main package main
import ( import (
@ -7,6 +17,25 @@ import (
"uatparse" "uatparse"
) )
/*
Examples:
METAR KIND 261654Z 10005KT 10SM SCT250 11/02 A3015 RMK AO2 SLP212
T01060017=
SPECI KTDZ 261738Z AUTO 04006KT 8SM SCT020 07/00 A3025 RMK AO2
T00670000=
TAF KCMI 261131Z 2612/2712 12007KT P6SM SCT250
FM261600 13013KT P6SM SCT100
FM270000 15006KT P6SM BKN200=
TAF.AMD KMKG 261725Z 2617/2718 VRB05KT P6SM SCT250
FM270100 11006KT P6SM BKN150
FM271200 13008KT P6SM VCSH BKN150=
WINDS COU 271200Z FT 3000 6000 9000 12000 18000 24000 30000 34000 39000
2109 2019+05 2134+01 2347-04 2353-21 2454-33 257949 259857 257955
PIREP HNN 261618Z CRW UA /OV HNN/TM 1618/FL360/TP B737/TB OCNL LGT CHOP 360/RM OVER HNN AWC-WEB:SWA
*/
const ( const (
FISB_METAR = iota FISB_METAR = iota
FISB_SPECI FISB_SPECI

Wyświetl plik

@ -1,3 +1,13 @@
/*
Copyright (c) 2015-2016 Christopher Young
Distributable under the terms of The "BSD New"" License
that can be found in the LICENSE file, herein included
as part of this header.
gen_gdl90.go: Input demodulated UAT and 1090ES information, output GDL90. Heartbeat,
ownship, status messages, stats collection.
*/
package main package main
import ( import (

Wyświetl plik

@ -1,3 +1,13 @@
/*
Copyright (c) 2015-2016 Christopher Young
Distributable under the terms of The "BSD New"" License
that can be found in the LICENSE file, herein included
as part of this header.
gen_gdl90.go: Input demodulated UAT and 1090ES information, output GDL90. Heartbeat,
ownship, status messages, stats collection.
*/
package main package main
import ( import (
@ -25,7 +35,7 @@ func decodeUplink(frame []byte) {
if lon > 180 { if lon > 180 {
lon = lon - 360 lon = lon - 360
} }
fmt.Printf("%.04f, %.04f\n", lat, lon) fmt.Printf("%.04f, %.04f\n", lat, lon)
} }

Wyświetl plik

@ -1,3 +1,13 @@
/*
Copyright (c) 2015-2016 Christopher Young
Distributable under the terms of The "BSD New"" License
that can be found in the LICENSE file, herein included
as part of this header.
gen_gdl90.go: Input demodulated UAT and 1090ES information, output GDL90. Heartbeat,
ownship, status messages, stats collection.
*/
package main package main
import ( import (

Wyświetl plik

@ -1,3 +1,13 @@
/*
Copyright (c) 2015-2016 Christopher Young
Distributable under the terms of The "BSD New"" License
that can be found in the LICENSE file, herein included
as part of this header.
gen_gdl90.go: Input demodulated UAT and 1090ES information, output GDL90. Heartbeat,
ownship, status messages, stats collection.
*/
package main package main
import ( import (

Wyświetl plik

@ -1,27 +1,35 @@
package main /*
Copyright (c) 2015-2016 Christopher Young
Distributable under the terms of The "BSD New"" License
that can be found in the LICENSE file, herein included
as part of this header.
gen_gdl90.go: Input demodulated UAT and 1090ES information, output GDL90. Heartbeat,
ownship, status messages, stats collection.
*/
package main
import ( import (
"fmt" "fmt"
// "time" // "time"
"../uatparse" "../uatparse"
"os"
"bufio" "bufio"
"strings"
"unicode"
"strconv"
"github.com/gonum/plot" "github.com/gonum/plot"
"github.com/gonum/plot/plotter" "github.com/gonum/plot/plotter"
"github.com/gonum/plot/plotutil" "github.com/gonum/plot/plotutil"
"github.com/gonum/plot/vg" "github.com/gonum/plot/vg"
"os"
"sort" "sort"
"strconv"
"strings"
"unicode"
) )
const ( const (
UPLINK_FRAME_DATA_BYTES = 432 UPLINK_FRAME_DATA_BYTES = 432
) )
/* /*
From AC 00-45G [http://www.faa.gov/documentLibrary/media/Advisory_Circular/AC_00-45G_CHG_1-2.pdf] From AC 00-45G [http://www.faa.gov/documentLibrary/media/Advisory_Circular/AC_00-45G_CHG_1-2.pdf]
@ -51,29 +59,28 @@ Winds Aloft 12 hours 10 minutes
*/ */
func append_metars(rawUplinkMessage string, curMetars []string) []string { func append_metars(rawUplinkMessage string, curMetars []string) []string {
ret := curMetars ret := curMetars
uatMsg, err := uatparse.New(rawUplinkMessage) uatMsg, err := uatparse.New(rawUplinkMessage)
if err != nil { if err != nil {
return ret return ret
} }
//fmt.Printf("*************************\n") //fmt.Printf("*************************\n")
metars, _ := uatMsg.GetTextReports() metars, _ := uatMsg.GetTextReports()
for _, v := range metars { for _, v := range metars {
//fmt.Printf("EE: %s\n", v) //fmt.Printf("EE: %s\n", v)
vSplit := strings.Split(v, " ") vSplit := strings.Split(v, " ")
if vSplit[0] != "METAR" || len(vSplit) < 3 { // Only looking for METARs. if vSplit[0] != "METAR" || len(vSplit) < 3 { // Only looking for METARs.
continue continue
} }
ret = append(ret, v) ret = append(ret, v)
} }
//fmt.Printf("=========================\n") //fmt.Printf("=========================\n")
return ret return ret
} }
/* /*
Average number of METARs received for an airport for which you first received a METAR in the first 5 minutes, over 10 minutes. Divided by two. Average number of METARs received for an airport for which you first received a METAR in the first 5 minutes, over 10 minutes. Divided by two.
*/ */
@ -97,7 +104,7 @@ func metar_qos_one_period(a, b []string) float64 {
ret += float64(num) ret += float64(num)
} }
if len(numMetarByIdent) > 0 { if len(numMetarByIdent) > 0 {
ret = ret / float64(2 * len(numMetarByIdent)) ret = ret / float64(2*len(numMetarByIdent))
} }
return ret return ret
} }
@ -124,7 +131,7 @@ func main() {
if err != nil { if err != nil {
break break
} }
buf = strings.TrimFunc(buf, func(r rune) bool {return unicode.IsControl(r)}) buf = strings.TrimFunc(buf, func(r rune) bool { return unicode.IsControl(r) })
linesplit := strings.Split(buf, ",") linesplit := strings.Split(buf, ",")
if len(linesplit) < 2 { // Blank line or invalid. if len(linesplit) < 2 { // Blank line or invalid.
continue continue
@ -132,9 +139,9 @@ func main() {
if linesplit[0] == "START" { // Reset ticker, new start. if linesplit[0] == "START" { // Reset ticker, new start.
//TODO: Support multiple sessions. //TODO: Support multiple sessions.
// Reset the counters, new session. // Reset the counters, new session.
// qos = make(map[uint]float64) // qos = make(map[uint]float64)
// curWindowMetars = make([]string, 0) // curWindowMetars = make([]string, 0)
// curWindow = 0 // curWindow = 0
windowOffset = curWindow windowOffset = curWindow
} else { // If it's not "START", then it's a tick count. } else { // If it's not "START", then it's a tick count.
i, err := strconv.ParseInt(linesplit[0], 10, 64) i, err := strconv.ParseInt(linesplit[0], 10, 64)
@ -145,17 +152,17 @@ func main() {
// Window number in current session. // Window number in current session.
wnum := int64(i / (5 * 60 * 1000000000)) wnum := int64(i / (5 * 60 * 1000000000))
// fmt.Printf("%d\n", curWindow) // fmt.Printf("%d\n", curWindow)
if wnum + windowOffset != curWindow { // Switched over. if wnum+windowOffset != curWindow { // Switched over.
curWindow = wnum + windowOffset curWindow = wnum + windowOffset
beforeLastWindowMetars, ok := metarsByWindow[curWindow - 2] beforeLastWindowMetars, ok := metarsByWindow[curWindow-2]
lastWindowMetars, ok2 := metarsByWindow[curWindow - 1] lastWindowMetars, ok2 := metarsByWindow[curWindow-1]
if ok && ok2 { if ok && ok2 {
// fmt.Printf("%v\n\n\nheyy\n\n%v\n", beforeLastWindowMetars, lastWindowMetars) // fmt.Printf("%v\n\n\nheyy\n\n%v\n", beforeLastWindowMetars, lastWindowMetars)
qos[curWindow - 1] = metar_qos_one_period(beforeLastWindowMetars, lastWindowMetars) qos[curWindow-1] = metar_qos_one_period(beforeLastWindowMetars, lastWindowMetars)
fmt.Printf("qos=%f\n", qos[curWindow - 1]) fmt.Printf("qos=%f\n", qos[curWindow-1])
delete(metarsByWindow, curWindow - 2) delete(metarsByWindow, curWindow-2)
delete(metarsByWindow, curWindow - 1) delete(metarsByWindow, curWindow-1)
} }
} }
metarsByWindow[curWindow] = append_metars(linesplit[1], metarsByWindow[curWindow]) metarsByWindow[curWindow] = append_metars(linesplit[1], metarsByWindow[curWindow])
@ -180,7 +187,7 @@ func main() {
pts := make(plotter.XYs, len(qos)) pts := make(plotter.XYs, len(qos))
i := 0 i := 0
for _,k := range keys { for _, k := range keys {
v := qos[int64(k)] v := qos[int64(k)]
fmt.Printf("%d, %f\n", k, v) fmt.Printf("%d, %f\n", k, v)
pts[i].X = float64(k) pts[i].X = float64(k)
@ -192,7 +199,7 @@ func main() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
if err := p.Save(4 * vg.Inch, 4 * vg.Inch, "qos.png"); err != nil { if err := p.Save(4*vg.Inch, 4*vg.Inch, "qos.png"); err != nil {
panic(err) panic(err)
} }
} }

Wyświetl plik

@ -1,40 +1,48 @@
/*
Copyright (c) 2015-2016 Christopher Young
Distributable under the terms of The "BSD New"" License
that can be found in the LICENSE file, herein included
as part of this header.
gen_gdl90.go: Input demodulated UAT and 1090ES information, output GDL90. Heartbeat,
ownship, status messages, stats collection.
*/
package main package main
import ( import (
"fmt"
"../uatparse" "../uatparse"
"strconv"
"os"
"bufio" "bufio"
"fmt"
"github.com/kellydunn/golang-geo" "github.com/kellydunn/golang-geo"
"math" "math"
) "os"
"strconv"
)
// Most adapted from extract_nexrad.c // Most adapted from extract_nexrad.c
const ( const (
BLOCK_WIDTH = float64(48.0/60.0) BLOCK_WIDTH = float64(48.0 / 60.0)
WIDE_BLOCK_WIDTH = float64(96.0/60.0) WIDE_BLOCK_WIDTH = float64(96.0 / 60.0)
BLOCK_HEIGHT = float64(4.0/60.0) BLOCK_HEIGHT = float64(4.0 / 60.0)
BLOCK_THRESHOLD = 405000 BLOCK_THRESHOLD = 405000
BLOCKS_PER_RING = 450 BLOCKS_PER_RING = 450
WARN_DIST = float64(18.52) // kilometers (10 nm). WARN_DIST = float64(18.52) // kilometers (10 nm).
) )
type NEXRADFrame struct { type NEXRADFrame struct {
radar_type uint32 radar_type uint32
ts string ts string
scale int scale int
latNorth float64 latNorth float64
lonWest float64 lonWest float64
height float64 height float64
width float64 width float64
intensity []uint8 // Really only 4-bit values. intensity []uint8 // Really only 4-bit values.
} }
func block_location(block_num int, ns_flag bool, scale_factor int) (float64, float64, float64, float64) { func block_location(block_num int, ns_flag bool, scale_factor int) (float64, float64, float64, float64) {
var realScale float64 var realScale float64
if scale_factor == 1 { if scale_factor == 1 {
@ -49,8 +57,8 @@ func block_location(block_num int, ns_flag bool, scale_factor int) (float64, flo
block_num = block_num & ^1 block_num = block_num & ^1
} }
raw_lat := float64(BLOCK_HEIGHT * float64(int(float64(block_num) / float64(BLOCKS_PER_RING)))) raw_lat := float64(BLOCK_HEIGHT * float64(int(float64(block_num)/float64(BLOCKS_PER_RING))))
raw_lon := float64(block_num % BLOCKS_PER_RING) * BLOCK_WIDTH raw_lon := float64(block_num%BLOCKS_PER_RING) * BLOCK_WIDTH
var lonSize float64 var lonSize float64
if block_num >= BLOCK_THRESHOLD { if block_num >= BLOCK_THRESHOLD {
@ -66,7 +74,7 @@ func block_location(block_num int, ns_flag bool, scale_factor int) (float64, flo
} else { } else {
raw_lat = raw_lat + BLOCK_HEIGHT raw_lat = raw_lat + BLOCK_HEIGHT
} }
if raw_lon > 180.0 { if raw_lon > 180.0 {
raw_lon = raw_lon - 360.0 raw_lon = raw_lon - 360.0
} }
@ -97,7 +105,7 @@ func decode_nexrad(f *uatparse.UATFrame) []NEXRADFrame {
intensityData := f.FISB_data[3:] intensityData := f.FISB_data[3:]
for _, v := range intensityData { for _, v := range intensityData {
intensity := uint8(v) & 0x7; intensity := uint8(v) & 0x7
runlength := (uint8(v) >> 3) + 1 runlength := (uint8(v) >> 3) + 1
for runlength > 0 { for runlength > 0 {
tmp.intensity = append(tmp.intensity, intensity) tmp.intensity = append(tmp.intensity, intensity)
@ -128,7 +136,7 @@ func decode_nexrad(f *uatparse.UATFrame) []NEXRADFrame {
} }
for j := 0; j < 8; j++ { for j := 0; j < 8; j++ {
if bb & (1 << uint(j)) != 0 { if bb&(1<<uint(j)) != 0 {
row_x := (row_offset + 8*i + j - 3) % row_size row_x := (row_offset + 8*i + j - 3) % row_size
bn := row_start + row_x bn := row_start + row_x
lat, lon, h, w := block_location(bn, ns_flag, scale_factor) lat, lon, h, w := block_location(bn, ns_flag, scale_factor)
@ -236,9 +244,9 @@ func scanNEXRAD(poly *geo.Polygon, frame NEXRADFrame) (*geo.Point, uint8) {
var maxIntensity uint8 var maxIntensity uint8
for y := 0; y < 4; y++ { for y := 0; y < 4; y++ {
for x := 0; x < 32; x++ { for x := 0; x < 32; x++ {
intensity := frame.intensity[x + 32*y] intensity := frame.intensity[x+32*y]
lat := frame.latNorth - (float64(y) * (frame.height)/float64(4.0)) lat := frame.latNorth - (float64(y) * (frame.height) / float64(4.0))
lon := frame.lonWest + (float64(x) * (frame.width)/float64(32.0)) lon := frame.lonWest + (float64(x) * (frame.width) / float64(32.0))
pt := geo.NewPoint(lat, lon) pt := geo.NewPoint(lat, lon)
if !poly.Contains(pt) { // Doesn't contain this point - skip. if !poly.Contains(pt) { // Doesn't contain this point - skip.
continue continue
@ -303,12 +311,12 @@ func main() {
nineOClock := fixHeading(hdgFloat - 90.0) nineOClock := fixHeading(hdgFloat - 90.0)
threeOClock := fixHeading(hdgFloat + 90.0) threeOClock := fixHeading(hdgFloat + 90.0)
// fmt.Printf("myPos=%v\n", myPos) // fmt.Printf("myPos=%v\n", myPos)
leftBottom := myPos.PointAtDistanceAndBearing(WARN_DIST, nineOClock) leftBottom := myPos.PointAtDistanceAndBearing(WARN_DIST, nineOClock)
rightBottom := myPos.PointAtDistanceAndBearing(WARN_DIST, threeOClock) rightBottom := myPos.PointAtDistanceAndBearing(WARN_DIST, threeOClock)
// fmt.Printf("nineOClock=%f [leftBottom=%v], threeOClock=%f [rightBottom=%v]\n", nineOClock, leftBottom, threeOClock, rightBottom) // fmt.Printf("nineOClock=%f [leftBottom=%v], threeOClock=%f [rightBottom=%v]\n", nineOClock, leftBottom, threeOClock, rightBottom)
hypDist := math.Sqrt2 * WARN_DIST hypDist := math.Sqrt2 * WARN_DIST
leftTopHdg := fixHeading(hdgFloat - 45.0) leftTopHdg := fixHeading(hdgFloat - 45.0)
@ -317,12 +325,11 @@ func main() {
leftTop := myPos.PointAtDistanceAndBearing(hypDist, leftTopHdg) leftTop := myPos.PointAtDistanceAndBearing(hypDist, leftTopHdg)
rightTop := myPos.PointAtDistanceAndBearing(hypDist, rightTopHdg) rightTop := myPos.PointAtDistanceAndBearing(hypDist, rightTopHdg)
// fmt.Printf("leftTopHdg=%f [leftTop=%v], rightTopHdg=%f [rightTop=%v]\n", leftTopHdg, leftTop, rightTopHdg, rightTop) // fmt.Printf("leftTopHdg=%f [leftTop=%v], rightTopHdg=%f [rightTop=%v]\n", leftTopHdg, leftTop, rightTopHdg, rightTop)
points := []*geo.Point{leftTop, rightTop, rightBottom, leftBottom, leftTop} points := []*geo.Point{leftTop, rightTop, rightBottom, leftBottom, leftTop}
poly := geo.NewPolygon(points) poly := geo.NewPolygon(points)
var maxpt *geo.Point var maxpt *geo.Point
var maxIntensity uint8 var maxIntensity uint8
@ -335,14 +342,13 @@ func main() {
} }
} }
// fmt.Printf("maxes: %d %v\n", maxIntensity, maxpt) // fmt.Printf("maxes: %d %v\n", maxIntensity, maxpt)
if maxIntensity > 0 && maxpt != nil { if maxIntensity > 0 && maxpt != nil {
desc := intensityToText(maxIntensity) desc := intensityToText(maxIntensity)
direction := fixHeading(myPos.BearingTo(maxpt)) direction := fixHeading(myPos.BearingTo(maxpt))
relativeDirection := fixHeading(direction - hdgFloat) relativeDirection := fixHeading(direction - hdgFloat)
// fmt.Printf("direction=%f, relativeDirection=%f\n", direction, relativeDirection) // fmt.Printf("direction=%f, relativeDirection=%f\n", direction, relativeDirection)
directionDesc := oclock(relativeDirection) directionDesc := oclock(relativeDirection)
dist := myPos.GreatCircleDistance(maxpt) * float64(0.539957) // Convert km -> nm. dist := myPos.GreatCircleDistance(maxpt) * float64(0.539957) // Convert km -> nm.
fmt.Printf("%s precip %d o'clock, %0.1f nm.\n", desc, directionDesc, dist) fmt.Printf("%s precip %d o'clock, %0.1f nm.\n", desc, directionDesc, dist)

Wyświetl plik

@ -1,19 +1,28 @@
package main /*
Copyright (c) 2015-2016 Christopher Young
Distributable under the terms of The "BSD New"" License
that can be found in the LICENSE file, herein included
as part of this header.
gen_gdl90.go: Input demodulated UAT and 1090ES information, output GDL90. Heartbeat,
ownship, status messages, stats collection.
*/
package main
import ( import (
"fmt" "fmt"
// "time" // "time"
"os"
"bufio" "bufio"
"strings"
"unicode"
"strconv"
"github.com/gonum/plot" "github.com/gonum/plot"
"github.com/gonum/plot/plotter" "github.com/gonum/plot/plotter"
"github.com/gonum/plot/plotutil" "github.com/gonum/plot/plotutil"
"github.com/gonum/plot/vg" "github.com/gonum/plot/vg"
"os"
"sort" "sort"
"strconv"
"strings"
"unicode"
) )
func main() { func main() {
@ -37,7 +46,7 @@ func main() {
if err != nil { if err != nil {
break break
} }
buf = strings.TrimFunc(buf, func(r rune) bool {return unicode.IsControl(r)}) buf = strings.TrimFunc(buf, func(r rune) bool { return unicode.IsControl(r) })
linesplit := strings.Split(buf, ",") linesplit := strings.Split(buf, ",")
if len(linesplit) < 2 { // Blank line or invalid. if len(linesplit) < 2 { // Blank line or invalid.
continue continue
@ -53,10 +62,10 @@ func main() {
// Window number in current session. // Window number in current session.
wnum := int64(i / (60 * 1000000000)) wnum := int64(i / (60 * 1000000000))
// fmt.Printf("%d\n", curWindow) // fmt.Printf("%d\n", curWindow)
if wnum + windowOffset != curWindow { // Switched over. if wnum+windowOffset != curWindow { // Switched over.
curWindow = wnum + windowOffset curWindow = wnum + windowOffset
fmt.Printf("ppm=%d\n", ppm[curWindow - 1]) fmt.Printf("ppm=%d\n", ppm[curWindow-1])
} }
ppm[curWindow]++ ppm[curWindow]++
} }
@ -80,7 +89,7 @@ func main() {
pts := make(plotter.XYs, len(ppm)) pts := make(plotter.XYs, len(ppm))
i := 0 i := 0
for _,k := range keys { for _, k := range keys {
v := ppm[int64(k)] v := ppm[int64(k)]
fmt.Printf("%d, %d\n", k, v) fmt.Printf("%d, %d\n", k, v)
pts[i].X = float64(k) pts[i].X = float64(k)
@ -92,7 +101,7 @@ func main() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
if err := p.Save(4 * vg.Inch, 4 * vg.Inch, "ppm.png"); err != nil { if err := p.Save(4*vg.Inch, 4*vg.Inch, "ppm.png"); err != nil {
panic(err) panic(err)
} }
} }

Wyświetl plik

@ -1,3 +1,13 @@
/*
Copyright (c) 2015-2016 Christopher Young
Distributable under the terms of The "BSD New"" License
that can be found in the LICENSE file, herein included
as part of this header.
gen_gdl90.go: Input demodulated UAT and 1090ES information, output GDL90. Heartbeat,
ownship, status messages, stats collection.
*/
package main package main
import ( import (

Wyświetl plik

@ -1,3 +1,13 @@
/*
Copyright (c) 2015-2016 Christopher Young
Distributable under the terms of The "BSD New"" License
that can be found in the LICENSE file, herein included
as part of this header.
gen_gdl90.go: Input demodulated UAT and 1090ES information, output GDL90. Heartbeat,
ownship, status messages, stats collection.
*/
package main package main
import ( import (

Wyświetl plik

@ -1,3 +1,13 @@
/*
Copyright (c) 2015-2016 Christopher Young
Distributable under the terms of The "BSD New"" License
that can be found in the LICENSE file, herein included
as part of this header.
gen_gdl90.go: Input demodulated UAT and 1090ES information, output GDL90. Heartbeat,
ownship, status messages, stats collection.
*/
// +build ignore // +build ignore
package main package main

Wyświetl plik

@ -1,3 +1,13 @@
/*
Copyright (c) 2015-2016 Christopher Young
Distributable under the terms of The "BSD New"" License
that can be found in the LICENSE file, herein included
as part of this header.
gen_gdl90.go: Input demodulated UAT and 1090ES information, output GDL90. Heartbeat,
ownship, status messages, stats collection.
*/
package main package main
import ( import (

Wyświetl plik

@ -1,3 +1,13 @@
/*
Copyright (c) 2015-2016 Christopher Young
Distributable under the terms of The "BSD New"" License
that can be found in the LICENSE file, herein included
as part of this header.
gen_gdl90.go: Input demodulated UAT and 1090ES information, output GDL90. Heartbeat,
ownship, status messages, stats collection.
*/
package uatparse package uatparse
import ( import (