kopia lustrzana https://github.com/cyoung/stratux
commit
2d2d64a038
5
Makefile
5
Makefile
|
@ -1,7 +1,9 @@
|
||||||
|
|
||||||
ifeq "$(CIRCLECI)" "true"
|
ifeq "$(CIRCLECI)" "true"
|
||||||
BUILDINFO=
|
BUILDINFO=
|
||||||
else
|
else
|
||||||
BUILDINFO=-ldflags "-X main.stratuxVersion=`git describe --tags --abbrev=0` -X main.stratuxBuild=`git log -n 1 --pretty=%H`"
|
BUILDINFO=-ldflags "-X main.stratuxVersion=`git describe --tags --abbrev=0` -X main.stratuxBuild=`git log -n 1 --pretty=%H`"
|
||||||
|
$(if $(GOROOT),,$(error GOROOT is not set!))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all:
|
all:
|
||||||
|
@ -10,8 +12,9 @@ all:
|
||||||
go get -t -d -v ./...
|
go get -t -d -v ./...
|
||||||
go build $(BUILDINFO) main/gen_gdl90.go main/traffic.go main/ry835ai.go main/network.go main/managementinterface.go main/sdr.go main/uibroadcast.go
|
go build $(BUILDINFO) main/gen_gdl90.go main/traffic.go main/ry835ai.go main/network.go main/managementinterface.go main/sdr.go main/uibroadcast.go
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
sh -c true
|
make -C test
|
||||||
|
|
||||||
www:
|
www:
|
||||||
mkdir -p /var/www
|
mkdir -p /var/www
|
||||||
|
|
|
@ -12,6 +12,7 @@ Supported WiFi adapters:
|
||||||
* Edimax EW-7811Un
|
* Edimax EW-7811Un
|
||||||
|
|
||||||
Tested RTL-SDR:
|
Tested RTL-SDR:
|
||||||
|
* NooElec NESDR Nano 2 (best)
|
||||||
* NooElec NESDR Mini 2
|
* NooElec NESDR Mini 2
|
||||||
* Generic R820T (degraded performance)
|
* Generic R820T (degraded performance)
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,7 @@ apt-get install -y git cmake libusb-1.0-0.dev build-essential
|
||||||
|
|
||||||
cd /root
|
cd /root
|
||||||
rm -rf rtl-sdr
|
rm -rf rtl-sdr
|
||||||
git clone git://git.osmocom.org/rtl-sdr.git
|
git clone https://github.com/jpoirier/librtlsdr rtl-sdr
|
||||||
cd rtl-sdr
|
cd rtl-sdr
|
||||||
mkdir build
|
mkdir build
|
||||||
cd build
|
cd build
|
||||||
|
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"compress/gzip"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -23,15 +24,12 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
configLocation = "/etc/stratux.conf"
|
configLocation = "/etc/stratux.conf"
|
||||||
|
indexFilename = "/var/log/stratux/LOGINDEX"
|
||||||
managementAddr = ":80"
|
managementAddr = ":80"
|
||||||
debugLog = "/var/log/stratux.log"
|
debugLog = "/var/log/stratux.log"
|
||||||
maxDatagramSize = 8192
|
maxDatagramSize = 8192
|
||||||
maxUserMsgQueueSize = 25000 // About 10MB per port per connected client.
|
maxUserMsgQueueSize = 25000 // About 10MB per port per connected client.
|
||||||
uatReplayLog = "/var/log/stratux-uat.log"
|
logDirectory = "/var/log/stratux"
|
||||||
esReplayLog = "/var/log/stratux-es.log"
|
|
||||||
gpsReplayLog = "/var/log/stratux-gps.log"
|
|
||||||
ahrsReplayLog = "/var/log/stratux-ahrs.log"
|
|
||||||
dump1090ReplayLog = "/var/log/stratux-dump1090.log"
|
|
||||||
|
|
||||||
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)
|
||||||
|
@ -62,6 +60,12 @@ const (
|
||||||
TRACK_RESOLUTION = float32(360.0 / 256.0)
|
TRACK_RESOLUTION = float32(360.0 / 256.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var uatReplayLog string
|
||||||
|
var esReplayLog string
|
||||||
|
var gpsReplayLog string
|
||||||
|
var ahrsReplayLog string
|
||||||
|
var dump1090ReplayLog string
|
||||||
|
|
||||||
var stratuxBuild string
|
var stratuxBuild string
|
||||||
var stratuxVersion string
|
var stratuxVersion string
|
||||||
|
|
||||||
|
@ -72,11 +76,11 @@ var Crc16Table [256]uint16
|
||||||
var mySituation SituationData
|
var mySituation SituationData
|
||||||
|
|
||||||
// File handles for replay logging.
|
// File handles for replay logging.
|
||||||
var uatReplayfp *os.File
|
var uatReplayWriter *gzip.Writer
|
||||||
var esReplayfp *os.File
|
var esReplayWriter *gzip.Writer
|
||||||
var gpsReplayfp *os.File
|
var gpsReplayWriter *gzip.Writer
|
||||||
var ahrsReplayfp *os.File
|
var ahrsReplayWriter *gzip.Writer
|
||||||
var dump1090Replayfp *os.File
|
var dump1090ReplayWriter *gzip.Writer
|
||||||
|
|
||||||
type msg struct {
|
type msg struct {
|
||||||
MessageClass uint
|
MessageClass uint
|
||||||
|
@ -103,8 +107,49 @@ type ADSBTower struct {
|
||||||
Messages_total uint64
|
Messages_total uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var ADSBTowers map[string]ADSBTower // Running list of all towers seen. (lat,lng) -> ADSBTower
|
var ADSBTowers map[string]ADSBTower // Running list of all towers seen. (lat,lng) -> ADSBTower
|
||||||
|
|
||||||
|
func constructFilenames() {
|
||||||
|
// uatReplayLog = "/var/log/stratux-uat.log"
|
||||||
|
// esReplayLog = "/var/log/stratux-es.log"
|
||||||
|
// gpsReplayLog = "/var/log/stratux-gps.log"
|
||||||
|
// ahrsReplayLog = "/var/log/stratux-ahrs.log"
|
||||||
|
// dump1090ReplayLog = "/var/log/stratux-dump1090.log"
|
||||||
|
var fileIndexNumber uint
|
||||||
|
|
||||||
|
// First, create the log file directory if it does not exist
|
||||||
|
os.Mkdir(logDirectory,0644)
|
||||||
|
|
||||||
|
f, err := os.Open(indexFilename)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Unable to open index file %s using index of 0\n", indexFilename)
|
||||||
|
fileIndexNumber=0
|
||||||
|
} else {
|
||||||
|
_, err := fmt.Fscanf(f,"%d\n",&fileIndexNumber)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Unable to read index file %s using index of 0\n", indexFilename)
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
fileIndexNumber++
|
||||||
|
}
|
||||||
|
fo, err := os.Create(indexFilename)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error creating index file %s\n", indexFilename)
|
||||||
|
}
|
||||||
|
_, err2 := fmt.Fprintf(fo,"%d\n",fileIndexNumber)
|
||||||
|
if err2 != nil {
|
||||||
|
log.Printf("Error writing to index file %s\n", indexFilename)
|
||||||
|
}
|
||||||
|
fo.Sync()
|
||||||
|
fo.Close()
|
||||||
|
uatReplayLog = fmt.Sprintf("%s/%04d-uat.log.gz",logDirectory,fileIndexNumber)
|
||||||
|
esReplayLog = fmt.Sprintf("%s/%04d-es.log.gz",logDirectory,fileIndexNumber)
|
||||||
|
gpsReplayLog = fmt.Sprintf("%s/%04d-gps.log.gz",logDirectory,fileIndexNumber)
|
||||||
|
ahrsReplayLog = fmt.Sprintf("%s/%04d-ahrs.log.gz",logDirectory,fileIndexNumber)
|
||||||
|
dump1090ReplayLog = fmt.Sprintf("%s/%04d-dump1090.log.gz",logDirectory,fileIndexNumber)
|
||||||
|
}
|
||||||
|
|
||||||
// Construct the CRC table. Adapted from FAA ref above.
|
// Construct the CRC table. Adapted from FAA ref above.
|
||||||
func crcInit() {
|
func crcInit() {
|
||||||
var i uint16
|
var i uint16
|
||||||
|
@ -506,22 +551,22 @@ func replayLog(msg string, msgclass int) {
|
||||||
if len(msg) == 0 { // Blank message.
|
if len(msg) == 0 { // Blank message.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var fp *os.File
|
var wt *gzip.Writer
|
||||||
switch msgclass {
|
switch msgclass {
|
||||||
case MSGCLASS_UAT:
|
case MSGCLASS_UAT:
|
||||||
fp = uatReplayfp
|
wt = uatReplayWriter
|
||||||
case MSGCLASS_ES:
|
case MSGCLASS_ES:
|
||||||
fp = esReplayfp
|
wt = esReplayWriter
|
||||||
case MSGCLASS_GPS:
|
case MSGCLASS_GPS:
|
||||||
fp = gpsReplayfp
|
wt = gpsReplayWriter
|
||||||
case MSGCLASS_AHRS:
|
case MSGCLASS_AHRS:
|
||||||
fp = ahrsReplayfp
|
wt = ahrsReplayWriter
|
||||||
case MSGCLASS_DUMP1090:
|
case MSGCLASS_DUMP1090:
|
||||||
fp = dump1090Replayfp
|
wt = dump1090ReplayWriter
|
||||||
}
|
}
|
||||||
if fp != nil {
|
if wt != nil {
|
||||||
s := makeReplayLogEntry(msg)
|
s := makeReplayLogEntry(msg)
|
||||||
fp.Write([]byte(s))
|
wt.Write([]byte(s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -816,37 +861,41 @@ func replayMark(active bool) {
|
||||||
t = fmt.Sprintf("UNPAUSE,%d\n", time.Since(timeStarted).Nanoseconds())
|
t = fmt.Sprintf("UNPAUSE,%d\n", time.Since(timeStarted).Nanoseconds())
|
||||||
}
|
}
|
||||||
|
|
||||||
if uatReplayfp != nil {
|
if uatReplayWriter != nil {
|
||||||
uatReplayfp.Write([]byte(t))
|
uatReplayWriter.Write([]byte(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
if esReplayfp != nil {
|
if esReplayWriter != nil {
|
||||||
esReplayfp.Write([]byte(t))
|
esReplayWriter.Write([]byte(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
if gpsReplayfp != nil {
|
if gpsReplayWriter != nil {
|
||||||
gpsReplayfp.Write([]byte(t))
|
gpsReplayWriter.Write([]byte(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
if ahrsReplayfp != nil {
|
if ahrsReplayWriter != nil {
|
||||||
ahrsReplayfp.Write([]byte(t))
|
ahrsReplayWriter.Write([]byte(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
if dump1090Replayfp != nil {
|
if dump1090ReplayWriter != nil {
|
||||||
dump1090Replayfp.Write([]byte(t))
|
dump1090ReplayWriter.Write([]byte(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func openReplay(fn string) (*os.File, error) {
|
func openReplay(fn string) (*gzip.Writer, error) {
|
||||||
ret, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
fp, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Failed to open log file '%s': %s\n", fn, err.Error())
|
log.Printf("Failed to open log file '%s': %s\n", fn, err.Error())
|
||||||
} else {
|
return nil, err
|
||||||
timeFmt := "Mon Jan 2 15:04:05 -0700 MST 2006"
|
|
||||||
fmt.Fprintf(ret, "START,%s,%s\n", timeStarted.Format(timeFmt), time.Now().Format(timeFmt)) // Start time marker.
|
|
||||||
}
|
}
|
||||||
return ret, err
|
|
||||||
|
gzw := gzip.NewWriter(fp) //FIXME: Close() on the gzip.Writer will not close the underlying file.
|
||||||
|
timeFmt := "Mon Jan 2 15:04:05 -0700 MST 2006"
|
||||||
|
s := fmt.Sprintf("START,%s,%s\n", timeStarted.Format(timeFmt), time.Now().Format(timeFmt)) // Start time marker.
|
||||||
|
gzw.Write([]byte(s))
|
||||||
|
return gzw, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func printStats() {
|
func printStats() {
|
||||||
|
@ -879,6 +928,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Stratux %s (%s) starting.\n", stratuxVersion, stratuxBuild)
|
log.Printf("Stratux %s (%s) starting.\n", stratuxVersion, stratuxBuild)
|
||||||
|
constructFilenames()
|
||||||
|
|
||||||
ADSBTowers = make(map[string]ADSBTower)
|
ADSBTowers = make(map[string]ADSBTower)
|
||||||
MsgLog = make([]msg, 0)
|
MsgLog = make([]msg, 0)
|
||||||
|
@ -894,39 +944,39 @@ func main() {
|
||||||
// Set up the replay logs. Keep these files open in any case, even if replay logging is disabled.
|
// Set up the replay logs. Keep these files open in any case, even if replay logging is disabled.
|
||||||
|
|
||||||
// UAT replay log.
|
// UAT replay log.
|
||||||
if uatfp, err := openReplay(uatReplayLog); err != nil {
|
if uatwt, err := openReplay(uatReplayLog); err != nil {
|
||||||
globalSettings.ReplayLog = false
|
globalSettings.ReplayLog = false
|
||||||
} else {
|
} else {
|
||||||
uatReplayfp = uatfp
|
uatReplayWriter = uatwt
|
||||||
defer uatReplayfp.Close()
|
defer uatReplayWriter.Close()
|
||||||
}
|
}
|
||||||
// 1090ES replay log.
|
// 1090ES replay log.
|
||||||
if esfp, err := openReplay(esReplayLog); err != nil {
|
if eswt, err := openReplay(esReplayLog); err != nil {
|
||||||
globalSettings.ReplayLog = false
|
globalSettings.ReplayLog = false
|
||||||
} else {
|
} else {
|
||||||
esReplayfp = esfp
|
esReplayWriter = eswt
|
||||||
defer esReplayfp.Close()
|
defer esReplayWriter.Close()
|
||||||
}
|
}
|
||||||
// GPS replay log.
|
// GPS replay log.
|
||||||
if gpsfp, err := openReplay(gpsReplayLog); err != nil {
|
if gpswt, err := openReplay(gpsReplayLog); err != nil {
|
||||||
globalSettings.ReplayLog = false
|
globalSettings.ReplayLog = false
|
||||||
} else {
|
} else {
|
||||||
gpsReplayfp = gpsfp
|
gpsReplayWriter = gpswt
|
||||||
defer gpsReplayfp.Close()
|
defer gpsReplayWriter.Close()
|
||||||
}
|
}
|
||||||
// AHRS replay log.
|
// AHRS replay log.
|
||||||
if ahrsfp, err := openReplay(ahrsReplayLog); err != nil {
|
if ahrswt, err := openReplay(ahrsReplayLog); err != nil {
|
||||||
globalSettings.ReplayLog = false
|
globalSettings.ReplayLog = false
|
||||||
} else {
|
} else {
|
||||||
ahrsReplayfp = ahrsfp
|
ahrsReplayWriter = ahrswt
|
||||||
defer ahrsReplayfp.Close()
|
defer ahrsReplayWriter.Close()
|
||||||
}
|
}
|
||||||
// Dump1090 replay log.
|
// Dump1090 replay log.
|
||||||
if dump1090fp, err := openReplay(dump1090ReplayLog); err != nil {
|
if dump1090wt, err := openReplay(dump1090ReplayLog); err != nil {
|
||||||
globalSettings.ReplayLog = false
|
globalSettings.ReplayLog = false
|
||||||
} else {
|
} else {
|
||||||
dump1090Replayfp = dump1090fp
|
dump1090ReplayWriter = dump1090wt
|
||||||
defer dump1090Replayfp.Close()
|
defer dump1090ReplayWriter.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark the files (whether we're logging or not).
|
// Mark the files (whether we're logging or not).
|
||||||
|
|
|
@ -101,6 +101,21 @@ func handleStatusWS(conn *websocket.Conn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleSituationWS(conn *websocket.Conn) {
|
||||||
|
timer := time.NewTicker(100 * time.Millisecond)
|
||||||
|
for {
|
||||||
|
<-timer.C
|
||||||
|
situationJSON, _ := json.Marshal(&mySituation)
|
||||||
|
_, err := conn.Write(situationJSON)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// AJAX call - /getStatus. Responds with current global status
|
// AJAX call - /getStatus. Responds with current global status
|
||||||
// a webservice call for the same data available on the websocket but when only a single update is needed
|
// a webservice call for the same data available on the websocket but when only a single update is needed
|
||||||
func handleStatusRequest(w http.ResponseWriter, r *http.Request) {
|
func handleStatusRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -225,6 +240,12 @@ func managementInterface() {
|
||||||
Handler: websocket.Handler(handleStatusWS)}
|
Handler: websocket.Handler(handleStatusWS)}
|
||||||
s.ServeHTTP(w, req)
|
s.ServeHTTP(w, req)
|
||||||
})
|
})
|
||||||
|
http.HandleFunc("/situation",
|
||||||
|
func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
s := websocket.Server{
|
||||||
|
Handler: websocket.Handler(handleSituationWS)}
|
||||||
|
s.ServeHTTP(w, req)
|
||||||
|
})
|
||||||
http.HandleFunc("/weather",
|
http.HandleFunc("/weather",
|
||||||
func(w http.ResponseWriter, req *http.Request) {
|
func(w http.ResponseWriter, req *http.Request) {
|
||||||
s := websocket.Server{
|
s := websocket.Server{
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
ifeq "$(CIRCLECI)" "true"
|
||||||
|
#
|
||||||
|
else
|
||||||
|
$(if $(GOROOT),,$(error GOROOT is not set!))
|
||||||
|
endif
|
||||||
|
|
||||||
|
SRCS = $(wildcard *.go)
|
||||||
|
DEST = $(patsubst %.go,%,$(SRCS))
|
||||||
|
|
||||||
|
all: $(DEST)
|
||||||
|
|
||||||
|
%: %.go
|
||||||
|
go build $<
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kidoman/embd"
|
||||||
|
_ "github.com/kidoman/embd/host/all"
|
||||||
|
"github.com/kidoman/embd/sensor/bmp180"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
var i2cbus embd.I2CBus
|
||||||
|
var myBMP180 *bmp180.BMP180
|
||||||
|
|
||||||
|
func readBMP180() (float64, float64, error) { // ºCelsius, Meters
|
||||||
|
temp, err := myBMP180.Temperature()
|
||||||
|
if err != nil {
|
||||||
|
return temp, 0.0, err
|
||||||
|
}
|
||||||
|
altitude, err := myBMP180.Altitude()
|
||||||
|
altitude = float64(1/0.3048) * altitude // Convert meters to feet.
|
||||||
|
if err != nil {
|
||||||
|
return temp, altitude, err
|
||||||
|
}
|
||||||
|
return temp, altitude, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func initBMP180() error {
|
||||||
|
myBMP180 = bmp180.New(i2cbus) //TODO: error checking.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func initI2C() error {
|
||||||
|
i2cbus = embd.NewI2CBus(1) //TODO: error checking.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unused at the moment. 5 second update, since read functions in bmp180 are slow.
|
||||||
|
func tempAndPressureReader() {
|
||||||
|
// Read temperature and pressure altitude.
|
||||||
|
temp, alt, err_bmp180 := readBMP180()
|
||||||
|
// Process.
|
||||||
|
if err_bmp180 != nil {
|
||||||
|
fmt.Printf("readBMP180(): %s\n", err_bmp180.Error())
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Temp %f Alt %f\n",temp,alt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
func initAHRS() error {
|
||||||
|
if err := initI2C(); err != nil { // I2C bus.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := initBMP180(); err != nil { // I2C temperature and pressure altitude.
|
||||||
|
i2cbus.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go tempAndPressureReader()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Printf("Hello world!\n")
|
||||||
|
initAHRS()
|
||||||
|
for {
|
||||||
|
tempAndPressureReader()
|
||||||
|
}
|
||||||
|
}
|
Ładowanie…
Reference in New Issue