kopia lustrzana https://github.com/cyoung/stratux
Merge pull request #1 from cyoung/uavionix
Merge master changes. Some minor Ping-related fixes.pull/470/head
commit
c8244bf96f
2
Makefile
2
Makefile
|
@ -39,6 +39,8 @@ install:
|
|||
cp -f gen_gdl90 /usr/bin/gen_gdl90
|
||||
chmod 755 /usr/bin/gen_gdl90
|
||||
cp init.d-stratux /etc/init.d/stratux
|
||||
cp image/10-stratux.rules /etc/udev/rules.d/10-stratux.rules
|
||||
cp image/99-uavionix.rules /etc/udev/rules.d/99-uavionix.rules
|
||||
chmod 755 /etc/init.d/stratux
|
||||
ln -sf /etc/init.d/stratux /etc/rc2.d/S01stratux
|
||||
ln -sf /etc/init.d/stratux /etc/rc6.d/K01stratux
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
[](https://github.com/cyoung/stratux/releases)
|
||||
[](https://circleci.com/gh/cyoung/stratux/tree/master)
|
||||
[](https://tldrlegal.com/license/bsd-3-clause-license-%28revised%29)
|
||||
[](http://slack.stratux.me/)
|
||||
|
||||
# stratux
|
||||
RTL-SDR UAT tools
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# To be placed in /etc/udev/rules.d.
|
||||
# Auto-detect common USB stratux peripherals.
|
||||
|
||||
# u-blox devices. Known devices include
|
||||
# ublox8: RY835AI, RY836AI
|
||||
# ublox7: VK-172, RY725AI
|
||||
# ublox6: VK-162
|
||||
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a8", SYMLINK+="ublox8"
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a7", SYMLINK+="ublox7"
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a6", SYMLINK+="ublox6"
|
||||
#SUBSYSTEMS=="usb", ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a7", SYMLINK+="vk172"
|
||||
#SUBSYSTEMS=="usb", ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a6", SYMLINK+="vk162"
|
||||
|
||||
|
||||
|
||||
# pl2303 devices are indistinguishable using idVendor and idProduct.
|
||||
# Currently the BU-353-S4 and the TU-S9 (serialout) use the pl2303.
|
||||
|
||||
SUBSYSTEMS=="usb", ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", SYMLINK+="prolific%n"
|
||||
|
||||
#SUBSYSTEMS=="usb", ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", SYMLINK+="bu353s4"
|
||||
#SUBSYSTEMS=="usb", ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", SYMLINK+="tu-s9"
|
|
@ -59,6 +59,9 @@ cp -f isc-dhcp-server mnt/etc/default/isc-dhcp-server
|
|||
#sshd config
|
||||
cp -f sshd_config mnt/etc/ssh/sshd_config
|
||||
|
||||
#udev config
|
||||
cp -f 10-stratux.rules mnt/etc/udev/rules.d
|
||||
|
||||
#stratux files
|
||||
cp -f ../libdump978.so mnt/usr/lib/libdump978.so
|
||||
cp -f ../linux-mpu9150/libimu.so mnt/usr/lib/libimu.so
|
||||
|
|
|
@ -738,6 +738,11 @@ func updateStatus() {
|
|||
}
|
||||
|
||||
if !(globalStatus.GPS_connected) || !(isGPSConnected()) { // isGPSConnected looks for valid NMEA messages. GPS_connected is set by gpsSerialReader and will immediately fail on disconnected USB devices, or in a few seconds after "blocked" comms on ttyAMA0.
|
||||
|
||||
satelliteMutex.Lock()
|
||||
Satellites = make(map[string]SatelliteInfo)
|
||||
satelliteMutex.Unlock()
|
||||
|
||||
mySituation.Satellites = 0
|
||||
mySituation.SatellitesSeen = 0
|
||||
mySituation.SatellitesTracked = 0
|
||||
|
|
|
@ -160,6 +160,19 @@ func handleTowersRequest(w http.ResponseWriter, r *http.Request) {
|
|||
fmt.Fprintf(w, "%s\n", towersJSON)
|
||||
}
|
||||
|
||||
// AJAX call - /getSatellites. Responds with all GNSS satellites that are being tracked, along with status information.
|
||||
func handleSatellitesRequest(w http.ResponseWriter, r *http.Request) {
|
||||
setNoCache(w)
|
||||
setJSONHeaders(w)
|
||||
satelliteMutex.Lock()
|
||||
satellitesJSON, err := json.Marshal(&Satellites)
|
||||
if err != nil {
|
||||
log.Printf("Error sending GNSS satellite JSON data: %s\n", err.Error())
|
||||
}
|
||||
fmt.Fprintf(w, "%s\n", satellitesJSON)
|
||||
satelliteMutex.Unlock()
|
||||
}
|
||||
|
||||
// AJAX call - /getSettings. Responds with all stratux.conf data.
|
||||
func handleSettingsGetRequest(w http.ResponseWriter, r *http.Request) {
|
||||
setNoCache(w)
|
||||
|
@ -460,6 +473,7 @@ func managementInterface() {
|
|||
http.HandleFunc("/getStatus", handleStatusRequest)
|
||||
http.HandleFunc("/getSituation", handleSituationRequest)
|
||||
http.HandleFunc("/getTowers", handleTowersRequest)
|
||||
http.HandleFunc("/getSatellites", handleSatellitesRequest)
|
||||
http.HandleFunc("/getSettings", handleSettingsGetRequest)
|
||||
http.HandleFunc("/setSettings", handleSettingsSetRequest)
|
||||
http.HandleFunc("/shutdown", handleShutdownRequest)
|
||||
|
|
18
main/ping.go
18
main/ping.go
|
@ -12,14 +12,14 @@ package main
|
|||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"strings"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
//"sync/atomic"
|
||||
"time"
|
||||
"net"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
// Using forked version of tarm/serial to force Linux
|
||||
// instead of posix code, allowing for higher baud rates
|
||||
|
@ -38,9 +38,7 @@ func initPingSerial() bool {
|
|||
|
||||
log.Printf("Configuring Ping ADS-B\n")
|
||||
|
||||
if _, err := os.Stat("/dev/ttyUSB0"); err == nil {
|
||||
device = "/dev/ttyUSB0"
|
||||
} else if _, err := os.Stat("/dev/ping"); err == nil {
|
||||
if _, err := os.Stat("/dev/ping"); err == nil {
|
||||
device = "/dev/ping"
|
||||
} else {
|
||||
log.Printf("No suitable Ping device found.\n")
|
||||
|
@ -150,7 +148,7 @@ func pingSerialReader() {
|
|||
s := scanner.Text()
|
||||
// Trimspace removes newlines as well as whitespace
|
||||
s = strings.TrimSpace(s)
|
||||
logString := fmt.Sprintf("Ping received: %s", s);
|
||||
logString := fmt.Sprintf("Ping received: %s", s)
|
||||
log.Println(logString)
|
||||
if s[0] == '*' {
|
||||
// 1090ES report
|
||||
|
@ -169,19 +167,19 @@ func pingSerialReader() {
|
|||
log.Println("Starting dump1090 network connection")
|
||||
pingNetworkConnection()
|
||||
}
|
||||
if (len(report[0]) != 0 && dump1090Connection != nil) {
|
||||
if len(report[0]) != 0 && dump1090Connection != nil {
|
||||
dump1090Connection.Write([]byte(report[0] + ";\r\n"))
|
||||
//log.Println("Relaying 1090ES message")
|
||||
//logString := fmt.Sprintf("Relaying 1090ES: %s;", report[0]);
|
||||
//log.Println(logString)
|
||||
}
|
||||
} else if (s[0] == '+' || s[0] == '-') {
|
||||
} else if s[0] == '+' || s[0] == '-' {
|
||||
// UAT report
|
||||
// Ping appends a signal strength and RS bit errors corrected
|
||||
// at the end of the message
|
||||
// e.g. -08A5DFDF3907E982585F029B00040080105C3AB4BC5C240700A206000000000000003A13C82F96C80A63191F05FCB231;rs=1;ss=A2;
|
||||
// We need to rescale the signal strength for interpretation by dump978,
|
||||
// which expects a 0-1000 base 10 (linear?) scale
|
||||
// which expects a 0-1000 base 10 (linear?) scale
|
||||
// RSSI is in hex and represents an int8 with -128 (0x80) representing an
|
||||
// errored measurement. There will be some offset from actual due to loss
|
||||
// in the path. In one example we measured 0x93 (-98) when injecting a
|
||||
|
@ -191,7 +189,7 @@ func pingSerialReader() {
|
|||
//logString = fmt.Sprintf("Relaying message, type=%d", msgtype)
|
||||
//log.Println(logString)
|
||||
relayMessage(msgtype, o)
|
||||
} else if (o == nil) {
|
||||
} else if o == nil {
|
||||
//log.Println("Not relaying message, o == nil")
|
||||
} else {
|
||||
//log.Println("Not relaying message, msgtype == 0")
|
||||
|
|
490
main/ry835ai.go
490
main/ry835ai.go
|
@ -30,6 +30,28 @@ import (
|
|||
"../mpu6050"
|
||||
)
|
||||
|
||||
const (
|
||||
SAT_TYPE_UNKNOWN = 0 // default type
|
||||
SAT_TYPE_GPS = 1 // GPxxx; NMEA IDs 1-32
|
||||
SAT_TYPE_GLONASS = 2 // GLxxx; NMEA IDs 65-88
|
||||
SAT_TYPE_GALILEO = 3 // GAxxx; NMEA IDs unknown
|
||||
SAT_TYPE_BEIDOU = 4 // GBxxx; NMEA IDs 201-235
|
||||
SAT_TYPE_SBAS = 10 // NMEA IDs 33-54
|
||||
)
|
||||
|
||||
type SatelliteInfo struct {
|
||||
SatelliteNMEA uint8 // NMEA ID of the satellite. 1-32 is GPS, 33-54 is SBAS, 65-88 is Glonass.
|
||||
SatelliteID string // Formatted code indicating source and PRN code. e.g. S138==WAAS satellite 138, G2==GPS satellites 2
|
||||
Elevation int16 // Angle above local horizon, -xx to +90
|
||||
Azimuth int16 // Bearing (degrees true), 0-359
|
||||
Signal int8 // Signal strength, 0 - 99; -99 indicates no reception
|
||||
Type uint8 // Type of satellite (GPS, GLONASS, Galileo, SBAS)
|
||||
TimeLastSolution time.Time // Time (system ticker) a solution was last calculated using this satellite
|
||||
TimeLastSeen time.Time // Time (system ticker) a signal was last received from this satellite
|
||||
TimeLastTracked time.Time // Time (system ticker) this satellite was tracked (almanac data)
|
||||
InSolution bool // True if satellite is used in the position solution (reported by GSA message or PUBX,03)
|
||||
}
|
||||
|
||||
type SituationData struct {
|
||||
mu_GPS *sync.Mutex
|
||||
|
||||
|
@ -76,6 +98,9 @@ var serialPort *serial.Port
|
|||
|
||||
var readyToInitGPS bool // TO-DO: replace with channel control to terminate goroutine when complete
|
||||
|
||||
var satelliteMutex *sync.Mutex
|
||||
var Satellites map[string]SatelliteInfo
|
||||
|
||||
/*
|
||||
u-blox5_Referenzmanual.pdf
|
||||
Platform settings
|
||||
|
@ -130,12 +155,17 @@ func initGPSSerial() bool {
|
|||
baudrate := int(9600)
|
||||
isSirfIV := bool(false)
|
||||
|
||||
if _, err := os.Stat("/dev/ttyACM0"); err == nil { // u-blox receivers on native USB connection
|
||||
device = "/dev/ttyACM0"
|
||||
} else if _, err := os.Stat("/dev/ttyUSB0"); err == nil { // USB-to-serial bridge. Typical use is BU-353-S4 SIRF IV receivers, but could also be for other devices or serial-out (better detection is TODO)
|
||||
if _, err := os.Stat("/dev/ublox8"); err == nil { // u-blox 8 (RY83xAI over USB).
|
||||
device = "/dev/ublox8"
|
||||
} else if _, err := os.Stat("/dev/ublox7"); err == nil { // u-blox 7 (VK-172, RY725AI over USB).
|
||||
device = "/dev/ublox7"
|
||||
} else if _, err := os.Stat("/dev/ublox6"); err == nil { // u-blox 6 (VK-162).
|
||||
device = "/dev/ublox6"
|
||||
} else if _, err := os.Stat("/dev/prolific0"); err == nil { // Assume it's a BU-353-S4 SIRF IV.
|
||||
//TODO: Check a "serialout" flag and/or deal with multiple prolific devices.
|
||||
isSirfIV = true
|
||||
baudrate = 4800
|
||||
device = "/dev/ttyUSB0"
|
||||
device = "/dev/prolific0"
|
||||
} else if _, err := os.Stat("/dev/ttyAMA0"); err == nil { // ttyAMA0 is PL011 UART (GPIO pins 8 and 10) on all RPi.
|
||||
device = "/dev/ttyAMA0"
|
||||
} else {
|
||||
|
@ -225,15 +255,16 @@ func initGPSSerial() bool {
|
|||
p.Write(makeNMEACmd("PSRF103,04,00,01,01"))
|
||||
// Enable VTG.
|
||||
p.Write(makeNMEACmd("PSRF103,05,00,01,01"))
|
||||
// Disable GSV.
|
||||
p.Write(makeNMEACmd("PSRF103,03,00,00,01"))
|
||||
// Enable GSV (once every 5 position updates)
|
||||
p.Write(makeNMEACmd("PSRF103,03,00,05,01"))
|
||||
|
||||
if globalSettings.DEBUG {
|
||||
log.Printf("Finished writing SiRF GPS config to %s. Opening port to test connection.\n", device)
|
||||
}
|
||||
} else {
|
||||
// Set 10Hz update. Little endian order.
|
||||
p.Write(makeUBXCFG(0x06, 0x08, 6, []byte{0x64, 0x00, 0x01, 0x00, 0x01, 0x00}))
|
||||
// Set 5 Hz update. Little endian order.
|
||||
//p.Write(makeUBXCFG(0x06, 0x08, 6, []byte{0x64, 0x00, 0x01, 0x00, 0x01, 0x00})) // 10 Hz
|
||||
p.Write(makeUBXCFG(0x06, 0x08, 6, []byte{0xc8, 0x00, 0x01, 0x00, 0x01, 0x00})) // 5 Hz
|
||||
|
||||
// Set navigation settings.
|
||||
nav := make([]byte, 36)
|
||||
|
@ -246,17 +277,20 @@ func initGPSSerial() bool {
|
|||
p.Write(makeUBXCFG(0x06, 0x24, 36, nav))
|
||||
|
||||
// GNSS configuration CFG-GNSS for ublox 7 higher, p. 125 (v8)
|
||||
//
|
||||
// NOTE: Max position rate = 5 Hz if GPS+GLONASS used.
|
||||
|
||||
// TESTING: 5Hz unified GPS + GLONASS
|
||||
|
||||
// Disable GLONASS to enable 10 Hz solution rate. GLONASS is not used
|
||||
// for SBAS (WAAS), so little real-world impact.
|
||||
|
||||
cfgGnss := []byte{0x00, 0x20, 0x20, 0x05}
|
||||
gps := []byte{0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01}
|
||||
sbas := []byte{0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x01, 0x01}
|
||||
gps := []byte{0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01} // enable GPS with 8-16 tracking channels
|
||||
sbas := []byte{0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x01, 0x01} // enable SBAS (WAAS) with 2-3 tracking channels
|
||||
beidou := []byte{0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x01}
|
||||
qzss := []byte{0x05, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x01}
|
||||
glonass := []byte{0x06, 0x04, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x01}
|
||||
//glonass := []byte{0x06, 0x04, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x01} // this disables GLONASS
|
||||
glonass := []byte{0x06, 0x08, 0x0E, 0x00, 0x01, 0x00, 0x01, 0x01} // this enables GLONASS with 8-14 tracking channels
|
||||
cfgGnss = append(cfgGnss, gps...)
|
||||
cfgGnss = append(cfgGnss, sbas...)
|
||||
cfgGnss = append(cfgGnss, beidou...)
|
||||
|
@ -267,12 +301,16 @@ func initGPSSerial() bool {
|
|||
// SBAS configuration for ublox 6 and higher
|
||||
p.Write(makeUBXCFG(0x06, 0x16, 8, []byte{0x01, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}))
|
||||
|
||||
// Message output configuration -- disable standard NMEA messages except 1Hz GGA
|
||||
// Message output configuration: UBX,00 (position) on each calculated fix; UBX,03 (satellite info) every 5th fix,
|
||||
// UBX,04 (timing) every 10th, GGA (NMEA position) every 5th. All other NMEA messages disabled.
|
||||
|
||||
// Msg DDC UART1 UART2 USB I2C Res
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x00, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0x01})) // GGA
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})) // GLL
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})) // GSA
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})) // GSV
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x01})) // GGA enabled every 5th message
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})) // GLL disabled
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})) // GSA disabled
|
||||
//p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x02, 0x00, 0x05, 0x00, 0x05, 0x00, 0x01})) // GSA enabled disabled every 5th position (used for testing only)
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})) // GSV disabled
|
||||
//p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x03, 0x00, 0x05, 0x00, 0x05, 0x00, 0x01})) // GSV enabled for every 5th position (used for testing only)
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})) // RMC
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})) // VGT
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})) // GRS
|
||||
|
@ -283,9 +321,8 @@ func initGPSSerial() bool {
|
|||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})) // GNS
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})) // ???
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})) // VLW
|
||||
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF1, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00})) // Ublox,0
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF1, 0x03, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x00})) // Ublox,3
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF1, 0x03, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00})) // Ublox,3
|
||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF1, 0x04, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x00})) // Ublox,4
|
||||
|
||||
// Reconfigure serial port.
|
||||
|
@ -586,44 +623,147 @@ func processNMEALine(l string) (sentenceUsed bool) {
|
|||
if err1 != nil {
|
||||
return false
|
||||
}
|
||||
tmpSituation.Satellites = uint16(sat)
|
||||
tmpSituation.Satellites = uint16(sat) // this seems to be reliable. UBX,03 handles >12 satellites solutions correctly.
|
||||
|
||||
// We've made it this far, so that means we've processed "everything" and can now make the change to mySituation.
|
||||
mySituation = tmpSituation
|
||||
return true
|
||||
} else if x[1] == "03" { // satellite status message
|
||||
} else if x[1] == "03" { // satellite status message. Only the first 20 satellites will be reported in this message for UBX firmware older than v3.0. Order seems to be GPS, then SBAS, then GLONASS.
|
||||
|
||||
if len(x) < 3 { // malformed UBX,03 message that somehow passed checksum verification but is missing all of its fields
|
||||
return false
|
||||
}
|
||||
|
||||
// field 2 = number of satellites tracked
|
||||
satSeen := 0 // satellites seen (signal present)
|
||||
//satSeen := 0 // satellites seen (signal present)
|
||||
satTracked, err := strconv.Atoi(x[2])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
mySituation.SatellitesTracked = uint16(satTracked)
|
||||
|
||||
if globalSettings.DEBUG {
|
||||
log.Printf("GPS PUBX,03 message with %d satellites is %d fields long. (Should be %d fields long)\n", satTracked, len(x), satTracked*6+3)
|
||||
}
|
||||
|
||||
if len(x) < (satTracked*6 + 3) { // malformed UBX,03 message that somehow passed checksum verification but is missing some of its fields
|
||||
if globalSettings.DEBUG {
|
||||
log.Printf("GPS PUBX,03 message is missing fields\n")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
mySituation.SatellitesTracked = uint16(satTracked) // requires UBX M8N firmware v3.01 or later to report > 20 satellites
|
||||
|
||||
// fields 3-8 are repeated block
|
||||
|
||||
var sv, elev, az, cno int
|
||||
var svType uint8
|
||||
var svStr string
|
||||
|
||||
/* Reference for constellation tracking
|
||||
for i:= 0; i < satTracked; i++ {
|
||||
x[3+6*i] // sv number
|
||||
x[4+6*i] // status [ U | e | - ] indicates [U]sed in solution, [e]phemeris data only, [-] not used
|
||||
x[5+6*i] // azimuth, deg, 0-359
|
||||
x[6+6*i] // elevation, deg, 0-90
|
||||
x[7+6*i] // signal strength dB-Hz
|
||||
x[8+6*i] // lock time, sec, 0-64
|
||||
}
|
||||
*/
|
||||
|
||||
for i := 0; i < satTracked; i++ {
|
||||
j := 7 + 6*i
|
||||
if j < len(x) {
|
||||
if x[j] != "" {
|
||||
satSeen++
|
||||
}
|
||||
//field 3+6i is sv number. GPS NMEA = PRN. GLONASS NMEA = PRN + 65. SBAS is PRN; needs to be converted to NMEA for compatiblity with GSV messages.
|
||||
sv, err = strconv.Atoi(x[3+6*i]) // sv number
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if sv < 33 { // indicates GPS
|
||||
svType = SAT_TYPE_GPS
|
||||
svStr = fmt.Sprintf("G%d", sv)
|
||||
} else if sv < 65 { // indicates SBAS: WAAS, EGNOS, MSAS, etc.
|
||||
svType = SAT_TYPE_SBAS
|
||||
svStr = fmt.Sprintf("S%d", sv+87) // add 87 to convert from NMEA to PRN.
|
||||
} else if sv < 97 { // GLONASS
|
||||
svType = SAT_TYPE_GLONASS
|
||||
svStr = fmt.Sprintf("R%d", sv-64) // subtract 64 to convert from NMEA to PRN.
|
||||
} else if (sv >= 120) && (sv < 162) { // indicates SBAS: WAAS, EGNOS, MSAS, etc.
|
||||
svType = SAT_TYPE_SBAS
|
||||
svStr = fmt.Sprintf("S%d", sv)
|
||||
sv -= 87 // subtract 87 to convert to NMEA from PRN.
|
||||
} else { // TO-DO: Galileo
|
||||
svType = SAT_TYPE_UNKNOWN
|
||||
svStr = fmt.Sprintf("U%d", sv)
|
||||
}
|
||||
|
||||
var thisSatellite SatelliteInfo
|
||||
|
||||
// START OF PROTECTED BLOCK
|
||||
satelliteMutex.Lock()
|
||||
|
||||
// Retrieve previous information on this satellite code.
|
||||
if val, ok := Satellites[svStr]; ok { // if we've already seen this satellite identifier, copy it in to do updates
|
||||
thisSatellite = val
|
||||
//log.Printf("UBX,03: Satellite %s already seen. Retrieving from 'Satellites'.\n", svStr) // DEBUG
|
||||
} else { // this satellite isn't in the Satellites data structure
|
||||
thisSatellite.SatelliteID = svStr
|
||||
thisSatellite.SatelliteNMEA = uint8(sv)
|
||||
thisSatellite.Type = uint8(svType)
|
||||
//log.Printf("UBX,03: Creating new satellite %s\n", svStr) // DEBUG
|
||||
}
|
||||
thisSatellite.TimeLastTracked = stratuxClock.Time
|
||||
|
||||
// Field 6+6*i is elevation, deg, 0-90
|
||||
elev, err = strconv.Atoi(x[6+6*i]) // elevation
|
||||
if err != nil { // could be blank if no position fix. Represent as -999.
|
||||
elev = -999
|
||||
}
|
||||
thisSatellite.Elevation = int16(elev)
|
||||
|
||||
// Field 5+6*i is azimuth, deg, 0-359
|
||||
az, err = strconv.Atoi(x[5+6*i]) // azimuth
|
||||
if err != nil { // could be blank if no position fix. Represent as -999.
|
||||
az = -999
|
||||
}
|
||||
thisSatellite.Azimuth = int16(az)
|
||||
|
||||
// Field 7+6*i is signal strength dB-Hz
|
||||
cno, err = strconv.Atoi(x[7+6*i]) // signal
|
||||
if err != nil { // will be blank if satellite isn't being received. Represent as -99.
|
||||
cno = -99
|
||||
} else if cno > 0 {
|
||||
thisSatellite.TimeLastSeen = stratuxClock.Time // Is this needed?
|
||||
}
|
||||
thisSatellite.Signal = int8(cno)
|
||||
|
||||
// Field 4+6*i is status: [ U | e | - ]: [U]sed in solution, [e]phemeris data only, [-] not used
|
||||
if x[4+6*i] == "U" {
|
||||
thisSatellite.InSolution = true
|
||||
thisSatellite.TimeLastSolution = stratuxClock.Time
|
||||
} else if x[4+6*i] == "e" {
|
||||
thisSatellite.InSolution = false
|
||||
//log.Printf("Satellite %s is no longer in solution but has ephemeris - UBX,03\n", svStr) // DEBUG
|
||||
// do anything that needs to be done for ephemeris
|
||||
} else {
|
||||
thisSatellite.InSolution = false
|
||||
//log.Printf("Satellite %s is no longer in solution and has no ephemeris - UBX,03\n", svStr) // DEBUG
|
||||
}
|
||||
|
||||
if globalSettings.DEBUG {
|
||||
inSolnStr := " "
|
||||
if thisSatellite.InSolution {
|
||||
inSolnStr = "+"
|
||||
}
|
||||
log.Printf("UBX: Satellite %s%s at index %d. Type = %d, NMEA-ID = %d, Elev = %d, Azimuth = %d, Cno = %d\n", inSolnStr, svStr, i, svType, sv, elev, az, cno) // remove later?
|
||||
}
|
||||
|
||||
Satellites[thisSatellite.SatelliteID] = thisSatellite // Update constellation with this satellite
|
||||
updateConstellation()
|
||||
satelliteMutex.Unlock()
|
||||
// END OF PROTECTED BLOCK
|
||||
|
||||
// end of satellite iteration loop
|
||||
}
|
||||
|
||||
mySituation.SatellitesSeen = uint16(satSeen)
|
||||
// log.Printf("Satellites with signal: %v\n",mySituation.SatellitesSeen)
|
||||
|
||||
/* Reference for future constellation tracking
|
||||
for i:= 0; i < satTracked; i++ {
|
||||
x[3+6*i] // sv number
|
||||
x[4+6*i] // status [ U | e | - ] for used / ephemeris / not used
|
||||
x[5+6*i] // azimuth, deg, 0-359
|
||||
x[6+6*i] // elevation, deg, 0-90
|
||||
x[7+6*i] // signal strength dB-Hz
|
||||
x[8+6*i] // lock time, sec, 0-64
|
||||
*/
|
||||
return true
|
||||
} else if x[1] == "04" { // clock message
|
||||
// field 5 is UTC week (epoch = 1980-JAN-06). If this is invalid, do not parse date / time
|
||||
|
@ -767,29 +907,6 @@ func processNMEALine(l string) (sentenceUsed bool) {
|
|||
tmpSituation.Lng = -tmpSituation.Lng
|
||||
}
|
||||
|
||||
/* Satellite count and horizontal accuracy deprecated. Using PUBX,00 with fallback to GSA.
|
||||
// Satellites.
|
||||
sat, err1 := strconv.Atoi(x[7])
|
||||
if err1 != nil {
|
||||
return false
|
||||
}
|
||||
tmpSituation.Satellites = uint16(sat)
|
||||
|
||||
// Accuracy.
|
||||
hdop, err1 := strconv.ParseFloat(x[8], 32)
|
||||
if err1 != nil {
|
||||
return false
|
||||
}
|
||||
if tmpSituation.Quality == 2 {
|
||||
tmpSituation.Accuracy = float32(hdop * 4.0) //Estimate for WAAS / DGPS solution
|
||||
} else {
|
||||
tmpSituation.Accuracy = float32(hdop * 8.0) //Estimate for 3D non-WAAS solution
|
||||
}
|
||||
|
||||
// NACp estimate.
|
||||
tmpSituation.NACp = calculateNACp(tmpSituation.Accuracy)
|
||||
*/
|
||||
|
||||
// Altitude.
|
||||
alt, err1 := strconv.ParseFloat(x[9], 32)
|
||||
if err1 != nil {
|
||||
|
@ -940,27 +1057,83 @@ func processNMEALine(l string) (sentenceUsed bool) {
|
|||
// field 1: operation mode
|
||||
// M: manual forced to 2D or 3D mode
|
||||
// A: automatic switching between 2D and 3D modes
|
||||
if (x[1] != "A") && (x[1] != "M") { // invalid fix
|
||||
|
||||
/*
|
||||
if (x[1] != "A") && (x[1] != "M") { // invalid fix ... but x[2] is a better indicator of fix quality. Deprecating this.
|
||||
tmpSituation.Quality = 0 // Just a note.
|
||||
return false
|
||||
}
|
||||
*/
|
||||
|
||||
// field 2: solution type
|
||||
// 1 = no solution; 2 = 2D fix, 3 = 3D fix. WAAS status is parsed from GGA message, so no need to get here
|
||||
if (x[2] == "") || (x[2] == "1") { // missing or no solution
|
||||
tmpSituation.Quality = 0 // Just a note.
|
||||
return false
|
||||
}
|
||||
|
||||
// field 2: solution type
|
||||
// 1 = no solution; 2 = 2D fix, 3 = 3D fix. WAAS status is parsed from GGA message, so no need to get here
|
||||
|
||||
// fields 3-14: satellites in solution
|
||||
var svStr string
|
||||
var svType uint8
|
||||
var svSBAS bool // used to indicate whether this GSA message contains a SBAS satellite
|
||||
var svGLONASS bool // used to indicate whether this GSA message contains GLONASS satellites
|
||||
sat := 0
|
||||
|
||||
for _, svtxt := range x[3:15] {
|
||||
_, err := strconv.Atoi(svtxt)
|
||||
sv, err := strconv.Atoi(svtxt)
|
||||
if err == nil {
|
||||
sat++
|
||||
|
||||
if sv < 33 { // indicates GPS
|
||||
svType = SAT_TYPE_GPS
|
||||
svStr = fmt.Sprintf("G%d", sv)
|
||||
} else if sv < 65 { // indicates SBAS: WAAS, EGNOS, MSAS, etc.
|
||||
svType = SAT_TYPE_SBAS
|
||||
svStr = fmt.Sprintf("S%d", sv+87) // add 87 to convert from NMEA to PRN.
|
||||
svSBAS = true
|
||||
} else if sv < 97 { // GLONASS
|
||||
svType = SAT_TYPE_GLONASS
|
||||
svStr = fmt.Sprintf("R%d", sv-64) // subtract 64 to convert from NMEA to PRN.
|
||||
svGLONASS = true
|
||||
} else { // TO-DO: Galileo
|
||||
svType = SAT_TYPE_UNKNOWN
|
||||
svStr = fmt.Sprintf("U%d", sv)
|
||||
}
|
||||
|
||||
var thisSatellite SatelliteInfo
|
||||
|
||||
// START OF PROTECTED BLOCK
|
||||
satelliteMutex.Lock()
|
||||
|
||||
// Retrieve previous information on this satellite code.
|
||||
if val, ok := Satellites[svStr]; ok { // if we've already seen this satellite identifier, copy it in to do updates
|
||||
thisSatellite = val
|
||||
//log.Printf("Satellite %s already seen. Retrieving from 'Satellites'.\n", svStr)
|
||||
} else { // this satellite isn't in the Satellites data structure, so create it
|
||||
thisSatellite.SatelliteID = svStr
|
||||
thisSatellite.SatelliteNMEA = uint8(sv)
|
||||
thisSatellite.Type = uint8(svType)
|
||||
//log.Printf("Creating new satellite %s from GSA message\n", svStr) // DEBUG
|
||||
}
|
||||
thisSatellite.InSolution = true
|
||||
thisSatellite.TimeLastSolution = stratuxClock.Time
|
||||
thisSatellite.TimeLastSeen = stratuxClock.Time // implied, since this satellite is used in the position solution
|
||||
thisSatellite.TimeLastTracked = stratuxClock.Time // implied, since this satellite is used in the position solution
|
||||
|
||||
Satellites[thisSatellite.SatelliteID] = thisSatellite // Update constellation with this satellite
|
||||
updateConstellation()
|
||||
satelliteMutex.Unlock()
|
||||
// END OF PROTECTED BLOCK
|
||||
|
||||
}
|
||||
}
|
||||
tmpSituation.Satellites = uint16(sat)
|
||||
|
||||
// Satellites tracked / seen should be parsed from GSV message (TO-DO) ... since we don't have it, just use satellites from solution
|
||||
tmpSituation.SatellitesTracked = uint16(sat)
|
||||
tmpSituation.SatellitesSeen = uint16(sat)
|
||||
if sat < 12 || tmpSituation.Satellites < 13 { // GSA only reports up to 12 satellites in solution, so we don't want to overwrite higher counts based on updateConstellation().
|
||||
tmpSituation.Satellites = uint16(sat)
|
||||
if (tmpSituation.Quality == 2) && !svSBAS && !svGLONASS { // add one to the satellite count if we have a SBAS solution, but the GSA message doesn't track a SBAS satellite
|
||||
tmpSituation.Satellites++
|
||||
}
|
||||
}
|
||||
//log.Printf("There are %d satellites in solution from this GSA message\n", sat) // TESTING - DEBUG
|
||||
|
||||
// field 16: HDOP
|
||||
// Accuracy estimate
|
||||
|
@ -990,6 +1163,142 @@ func processNMEALine(l string) (sentenceUsed bool) {
|
|||
return true
|
||||
|
||||
}
|
||||
|
||||
if (x[0] == "GPGSV") || (x[0] == "GLGSV") { // GPS + SBAS or GLONASS satellites in view message. Galileo is TBD.
|
||||
if len(x) < 4 {
|
||||
return false
|
||||
}
|
||||
|
||||
// field 1 = number of GSV messages of this type
|
||||
msgNum, err := strconv.Atoi(x[2])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// field 2 = index of this GSV message
|
||||
|
||||
msgIndex, err := strconv.Atoi(x[2])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// field 3 = number of GPS satellites tracked
|
||||
/* Is this redundant if parsing from full constellation?
|
||||
satTracked, err := strconv.Atoi(x[3])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
*/
|
||||
|
||||
//mySituation.SatellitesTracked = uint16(satTracked) // Replaced with parsing of 'Satellites' data structure
|
||||
|
||||
// field 4-7 = repeating block with satellite id, elevation, azimuth, and signal strengh (Cno)
|
||||
|
||||
lenGSV := len(x)
|
||||
satsThisMsg := (lenGSV - 4) / 4
|
||||
|
||||
if globalSettings.DEBUG {
|
||||
log.Printf("%s message [%d of %d] is %v fields long and describes %v satellites\n", x[0], msgIndex, msgNum, lenGSV, satsThisMsg)
|
||||
}
|
||||
|
||||
var sv, elev, az, cno int
|
||||
var svType uint8
|
||||
var svStr string
|
||||
|
||||
for i := 0; i < satsThisMsg; i++ {
|
||||
|
||||
sv, err = strconv.Atoi(x[4+4*i]) // sv number
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if sv < 33 { // indicates GPS
|
||||
svType = SAT_TYPE_GPS
|
||||
svStr = fmt.Sprintf("G%d", sv)
|
||||
} else if sv < 65 { // indicates SBAS: WAAS, EGNOS, MSAS, etc.
|
||||
svType = SAT_TYPE_SBAS
|
||||
svStr = fmt.Sprintf("S%d", sv+87) // add 87 to convert from NMEA to PRN.
|
||||
} else if sv < 97 { // GLONASS
|
||||
svType = SAT_TYPE_GLONASS
|
||||
svStr = fmt.Sprintf("R%d", sv-64) // subtract 64 to convert from NMEA to PRN.
|
||||
} else { // TO-DO: Galileo
|
||||
svType = SAT_TYPE_UNKNOWN
|
||||
svStr = fmt.Sprintf("U%d", sv)
|
||||
}
|
||||
|
||||
var thisSatellite SatelliteInfo
|
||||
|
||||
// START OF PROTECTED BLOCK
|
||||
satelliteMutex.Lock()
|
||||
|
||||
// Retrieve previous information on this satellite code.
|
||||
if val, ok := Satellites[svStr]; ok { // if we've already seen this satellite identifier, copy it in to do updates
|
||||
thisSatellite = val
|
||||
//log.Printf("Satellite %s already seen. Retrieving from 'Satellites'.\n", svStr) // DEBUG
|
||||
} else { // this satellite isn't in the Satellites data structure, so create it new
|
||||
thisSatellite.SatelliteID = svStr
|
||||
thisSatellite.SatelliteNMEA = uint8(sv)
|
||||
thisSatellite.Type = uint8(svType)
|
||||
//log.Printf("Creating new satellite %s\n", svStr) // DEBUG
|
||||
}
|
||||
thisSatellite.TimeLastTracked = stratuxClock.Time
|
||||
|
||||
elev, err = strconv.Atoi(x[5+4*i]) // elevation
|
||||
if err != nil { // some firmwares leave this blank if there's no position fix. Represent as -999.
|
||||
elev = -999
|
||||
}
|
||||
thisSatellite.Elevation = int16(elev)
|
||||
|
||||
az, err = strconv.Atoi(x[6+4*i]) // azimuth
|
||||
if err != nil { // UBX allows tracking up to 5(?) degrees below horizon. Some firmwares leave this blank if no position fix. Represent invalid as -999.
|
||||
az = -999
|
||||
}
|
||||
thisSatellite.Azimuth = int16(az)
|
||||
|
||||
cno, err = strconv.Atoi(x[7+4*i]) // signal
|
||||
if err != nil { // will be blank if satellite isn't being received. Represent as -99.
|
||||
cno = -99
|
||||
thisSatellite.InSolution = false // resets the "InSolution" status if the satellite disappears out of solution due to no signal. FIXME
|
||||
//log.Printf("Satellite %s is no longer in solution due to cno parse error - GSV\n", svStr) // DEBUG
|
||||
} else if cno > 0 {
|
||||
thisSatellite.TimeLastSeen = stratuxClock.Time // Is this needed?
|
||||
}
|
||||
if cno > 127 { // make sure strong signals don't overflow. Normal range is 0-99 so it shouldn't, but take no chances.
|
||||
cno = 127
|
||||
}
|
||||
thisSatellite.Signal = int8(cno)
|
||||
|
||||
// hack workaround for GSA 12-sv limitation... if this is a SBAS satellite, we have a SBAS solution, and signal is greater than some arbitrary threshold, set InSolution
|
||||
// drawback is this will show all tracked SBAS satellites as being in solution.
|
||||
if thisSatellite.Type == SAT_TYPE_SBAS {
|
||||
if mySituation.Quality == 2 {
|
||||
if thisSatellite.Signal > 16 {
|
||||
thisSatellite.InSolution = true
|
||||
thisSatellite.TimeLastSolution = stratuxClock.Time
|
||||
}
|
||||
} else { // quality == 0 or 1
|
||||
thisSatellite.InSolution = false
|
||||
//log.Printf("WAAS satellite %s is marked as out of solution GSV\n", svStr) // DEBUG
|
||||
}
|
||||
}
|
||||
|
||||
if globalSettings.DEBUG {
|
||||
inSolnStr := " "
|
||||
if thisSatellite.InSolution {
|
||||
inSolnStr = "+"
|
||||
}
|
||||
log.Printf("GSV: Satellite %s%s at index %d. Type = %d, NMEA-ID = %d, Elev = %d, Azimuth = %d, Cno = %d\n", inSolnStr, svStr, i, svType, sv, elev, az, cno) // remove later?
|
||||
}
|
||||
|
||||
Satellites[thisSatellite.SatelliteID] = thisSatellite // Update constellation with this satellite
|
||||
updateConstellation()
|
||||
satelliteMutex.Unlock()
|
||||
// END OF PROTECTED BLOCK
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// if we've gotten this far, the message isn't one that we want to parse
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -1160,6 +1469,39 @@ func attitudeReaderSender() {
|
|||
globalStatus.RY835AI_connected = false
|
||||
}
|
||||
|
||||
/*
|
||||
updateConstellation(): Periodic cleanup and statistics calculation for 'Satellites'
|
||||
data structure. Calling functions must protect this in a satelliteMutex.
|
||||
|
||||
*/
|
||||
|
||||
func updateConstellation() {
|
||||
var sats, tracked, seen uint8
|
||||
for svStr, thisSatellite := range Satellites {
|
||||
if stratuxClock.Since(thisSatellite.TimeLastTracked) > 10*time.Second { // remove stale satellites if they haven't been tracked for 10 seconds
|
||||
delete(Satellites, svStr)
|
||||
} else { // satellite almanac data is "fresh" even if it isn't being received.
|
||||
tracked++
|
||||
if thisSatellite.Signal > 0 {
|
||||
seen++
|
||||
}
|
||||
if stratuxClock.Since(thisSatellite.TimeLastSolution) > 5*time.Second {
|
||||
thisSatellite.InSolution = false
|
||||
Satellites[svStr] = thisSatellite
|
||||
}
|
||||
if thisSatellite.InSolution { // TESTING: Determine "In solution" from structure (fix for multi-GNSS overflow)
|
||||
sats++
|
||||
}
|
||||
// do any other calculations needed for this satellite
|
||||
}
|
||||
}
|
||||
//log.Printf("Satellite counts: %d tracking channels, %d with >0 dB-Hz signal\n", tracked, seen) // DEBUG - REMOVE
|
||||
//log.Printf("Satellite struct: %v\n", Satellites) // DEBUG - REMOVE
|
||||
mySituation.Satellites = uint16(sats)
|
||||
mySituation.SatellitesTracked = uint16(tracked)
|
||||
mySituation.SatellitesSeen = uint16(seen)
|
||||
}
|
||||
|
||||
func isGPSConnected() bool {
|
||||
return stratuxClock.Since(mySituation.LastValidNMEAMessageTime) < 5*time.Second
|
||||
}
|
||||
|
@ -1244,6 +1586,8 @@ func pollRY835AI() {
|
|||
func initRY835AI() {
|
||||
mySituation.mu_GPS = &sync.Mutex{}
|
||||
mySituation.mu_Attitude = &sync.Mutex{}
|
||||
satelliteMutex = &sync.Mutex{}
|
||||
Satellites = make(map[string]SatelliteInfo)
|
||||
|
||||
go pollRY835AI()
|
||||
}
|
||||
|
|
|
@ -120,7 +120,7 @@ func (e *ES) read() {
|
|||
default:
|
||||
n, err := stderr.Read(stderrBuf)
|
||||
if err == nil && n > 0 {
|
||||
m := Dump1090TermMessage{Text: string(stdoutBuf[:n]), Source: "stderr"}
|
||||
m := Dump1090TermMessage{Text: string(stderrBuf[:n]), Source: "stderr"}
|
||||
logDump1090TermMessage(m)
|
||||
}
|
||||
}
|
||||
|
@ -229,6 +229,9 @@ func (u *UAT) sdrConfig() (err error) {
|
|||
}
|
||||
log.Printf("\tSetTunerGain Successful\n")
|
||||
|
||||
tgain := u.dev.GetTunerGain()
|
||||
log.Printf("\tGetTunerGain: %d\n", tgain)
|
||||
|
||||
//---------- Get/Set Sample Rate ----------
|
||||
err = u.dev.SetSampleRate(SampleRate)
|
||||
if err != nil {
|
||||
|
@ -426,6 +429,8 @@ func configDevices(count int, esEnabled, uatEnabled bool) {
|
|||
for i := 0; i < count; i++ {
|
||||
_, _, s, err := rtl.GetDeviceUsbStrings(i)
|
||||
if err == nil {
|
||||
//FIXME: Trim NULL from the serial. Best done in gortlsdr, but putting this here for now.
|
||||
s = strings.Trim(s, "\x00")
|
||||
// no need to check if createXDev returned an error; if it
|
||||
// failed to config the error is logged and we can ignore
|
||||
// it here so it doesn't get queued up again
|
||||
|
|
|
@ -28,6 +28,8 @@ cp image/bashrc.txt work/bin/
|
|||
cp image/modules.txt work/bin/
|
||||
cp image/stxAliases.txt work/bin/
|
||||
cp image/hostapd_manager.sh work/bin/
|
||||
cp image/10-stratux.rules work/bin/
|
||||
cp image/99-uavionix.rules work/bin/
|
||||
|
||||
#TODO: librtlsdr.
|
||||
cd work/
|
||||
|
|
|
@ -8,7 +8,11 @@ ssh -i ~/.ssh/id_rsa.updates stratux-updates@updates.stratux.me 'ls -1 queue/' |
|
|||
cd selfupdate
|
||||
./makeupdate.sh
|
||||
cd ..
|
||||
scp -i ~/.ssh/id_rsa.updates work/update*.sh stratux-updates@updates.stratux.me:finished/
|
||||
for fl in `ls -1 work/update*.sh | cut -d/ -f2`
|
||||
do
|
||||
scp -i ~/.ssh/id_rsa.updates work/${fl} stratux-updates@updates.stratux.me:uploading/
|
||||
ssh -i ~/.ssh/id_rsa.updates stratux-updates@updates.stratux.me "mv uploading/${fl} finished/"
|
||||
done
|
||||
cd ..
|
||||
ssh -i ~/.ssh/id_rsa.updates stratux-updates@updates.stratux.me "rm -f queue/${git_hash}"
|
||||
done
|
|
@ -11,6 +11,7 @@ ln -fs /etc/init.d/stratux /etc/rc6.d/K01stratux
|
|||
|
||||
#wifi config
|
||||
cp -f hostapd.conf /etc/hostapd/hostapd.conf
|
||||
cp -f hostapd-edimax.conf /etc/hostapd/hostapd-edimax.conf
|
||||
|
||||
#WiFi Config Manager
|
||||
cp -f hostapd_manager.sh /usr/sbin/
|
||||
|
@ -21,6 +22,10 @@ cp -f config.txt /boot/config.txt
|
|||
#modprobe.d blacklist
|
||||
cp -f rtl-sdr-blacklist.conf /etc/modprobe.d/
|
||||
|
||||
#udev config
|
||||
cp -f 10-stratux.rules /etc/udev/rules.d
|
||||
cp -f 99-uavionix.rules /etc/udev/rules.d
|
||||
|
||||
#go setup
|
||||
cp -f bashrc.txt /root/.bashrc
|
||||
cp -f stxAliases /root/.stxAliases
|
||||
|
@ -35,4 +40,4 @@ cd web/ && make stratuxBuild=${stratuxBuild}
|
|||
|
||||
# Remove old Wi-Fi watcher script.
|
||||
rm -f /usr/sbin/wifi_watch.sh
|
||||
sed -i "/\bwifi_watch\b/d" /etc/rc.local
|
||||
sed -i "/\bwifi_watch\b/d" /etc/rc.local
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type dump1090Data struct {
|
||||
Icao_addr uint32
|
||||
DF int // Mode S downlink format.
|
||||
CA int // Lowest 3 bits of first byte of Mode S message (DF11 and DF17 capability; DF18 control field, zero for all other DF types)
|
||||
TypeCode int // Mode S type code
|
||||
SubtypeCode int // Mode S subtype code
|
||||
SBS_MsgType int // type of SBS message (used in "old" 1090 parsing)
|
||||
SignalLevel float64 // Decimal RSSI (0-1 nominal) as reported by dump1090-mutability. Convert to dB RSSI before setting in TrafficInfo.
|
||||
Tail *string
|
||||
Squawk *int // 12-bit squawk code in octal format
|
||||
Emitter_category *int
|
||||
OnGround *bool
|
||||
Lat *float32
|
||||
Lng *float32
|
||||
Position_valid bool
|
||||
NACp *int
|
||||
Alt *int
|
||||
AltIsGNSS bool //
|
||||
GnssDiffFromBaroAlt *int16 // GNSS height above baro altitude in feet; valid range is -3125 to 3125. +/- 3138 indicates larger difference.
|
||||
Vvel *int16
|
||||
Speed_valid bool
|
||||
Speed *uint16
|
||||
Track *uint16
|
||||
Timestamp time.Time // time traffic last seen, UTC
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Printf("es_dump_csv <sqlite file>\n")
|
||||
return
|
||||
}
|
||||
db, err := sql.Open("sqlite3", os.Args[1])
|
||||
if err != nil {
|
||||
fmt.Printf("sql.Open(): %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
defer db.Close()
|
||||
rows, err := db.Query("SELECT Data FROM es_messages")
|
||||
if err != nil {
|
||||
fmt.Printf("db.Exec(): %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
csvOut := make([][]string, 0)
|
||||
|
||||
for rows.Next() {
|
||||
var Data string
|
||||
if err := rows.Scan(&Data); err != nil {
|
||||
fmt.Printf("rows.Scan(): %s\n", err.Error())
|
||||
continue
|
||||
}
|
||||
var d dump1090Data
|
||||
err := json.Unmarshal([]byte(Data), &d)
|
||||
if err != nil {
|
||||
fmt.Printf("json.Unmarshal(): %s\n", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
r := make([]string, 23)
|
||||
r[0] = fmt.Sprintf("%06x", d.Icao_addr)
|
||||
r[1] = fmt.Sprintf("%d", d.DF)
|
||||
r[2] = fmt.Sprintf("%d", d.CA)
|
||||
r[3] = fmt.Sprintf("%d", d.TypeCode)
|
||||
r[4] = fmt.Sprintf("%d", d.SubtypeCode)
|
||||
r[5] = fmt.Sprintf("%d", d.SBS_MsgType)
|
||||
|
||||
r[6] = fmt.Sprintf("%f", d.SignalLevel)
|
||||
if d.Tail != nil {
|
||||
r[7] = fmt.Sprintf("%s", *d.Tail)
|
||||
}
|
||||
if d.Squawk != nil {
|
||||
r[8] = fmt.Sprintf("%d", *d.Squawk)
|
||||
}
|
||||
if d.Emitter_category != nil {
|
||||
r[9] = fmt.Sprintf("%d", *d.Emitter_category)
|
||||
}
|
||||
if d.OnGround != nil {
|
||||
r[10] = fmt.Sprintf("%t", *d.OnGround)
|
||||
}
|
||||
if d.Lat != nil {
|
||||
r[11] = fmt.Sprintf("%f", *d.Lat)
|
||||
}
|
||||
if d.Lng != nil {
|
||||
r[12] = fmt.Sprintf("%f", *d.Lng)
|
||||
}
|
||||
|
||||
r[13] = fmt.Sprintf("%t", d.Position_valid)
|
||||
if d.NACp != nil {
|
||||
r[14] = fmt.Sprintf("%d", *d.NACp)
|
||||
}
|
||||
if d.Alt != nil {
|
||||
r[15] = fmt.Sprintf("%d", *d.Alt)
|
||||
}
|
||||
|
||||
r[16] = fmt.Sprintf("%t", d.AltIsGNSS)
|
||||
if d.GnssDiffFromBaroAlt != nil {
|
||||
r[17] = fmt.Sprintf("%d", *d.GnssDiffFromBaroAlt)
|
||||
}
|
||||
if d.Vvel != nil {
|
||||
r[18] = fmt.Sprintf("%d", *d.Vvel)
|
||||
}
|
||||
r[19] = fmt.Sprintf("%t", d.Speed_valid)
|
||||
if d.Speed != nil {
|
||||
r[20] = fmt.Sprintf("%d", *d.Speed)
|
||||
}
|
||||
if d.Track != nil {
|
||||
r[21] = fmt.Sprintf("%d", *d.Track)
|
||||
}
|
||||
r[22] = fmt.Sprintf("%s", d.Timestamp)
|
||||
|
||||
csvOut = append(csvOut, r)
|
||||
}
|
||||
|
||||
w := csv.NewWriter(os.Stdout)
|
||||
w.WriteAll(csvOut)
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
fmt.Printf("rows.Scan(): %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ var URL_SETTINGS_SET = "http://" + URL_HOST_BASE + "/setSettings";
|
|||
var URL_GPS_GET = "http://" + URL_HOST_BASE + "/getSituation";
|
||||
var URL_TOWERS_GET = "http://" + URL_HOST_BASE + "/getTowers"
|
||||
var URL_STATUS_GET = "http://" + URL_HOST_BASE + "/getStatus"
|
||||
var URL_SATELLITES_GET = "http://" + URL_HOST_BASE + "/getSatellites"
|
||||
var URL_STATUS_WS = "ws://" + URL_HOST_BASE + "/status"
|
||||
var URL_TRAFFIC_WS = "ws://" + URL_HOST_BASE + "/traffic";
|
||||
var URL_WEATHER_WS = "ws://" + URL_HOST_BASE + "/weather";
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<div class="section text-left help-page">
|
||||
<p>The <strong>GPS / AHRS</strong> page provides a view on the current status of GPS data and AHRS orientation. The Satellite count is located on the <strong>Status</strong> page.</p>
|
||||
<p>The <strong>GPS</strong> reports position with estimated accuracy, ground track, ground speed, and GPS derived altitude are displayed along with the location depicted on a world map.</p>
|
||||
<p>The <strong>AHRS</strong> reports magnetic heading, pressure altitude, pitch and roll are displayed along with a graphical representation of movement.</p>
|
||||
<p>The AHR graphical depiction is a 3-dimentional paper airplane which rotates about a heading with 000° being off in the distance and 180° being down in front. The airplane will pitch up/down and left/right based on the data from the AHRS. To aid with recognizing orientation, the <span class="paperairplane_left">left wing is blue</span> and the <span class="paperairplane_right">right wing is tan</span>.</p>
|
||||
<p><strong>GPS</strong> shows position with estimated accuracy, ground track, ground speed, and geometric altitude. Location is displayed on a world map.</p>
|
||||
<p><strong>Satellites</strong> shows the status of GNSS constellations, and lists all satellites that your receiver is tracking. Stratux uses Satellite Based Augmentation System (SBAS) and multi-GNSS solutions on supported receivers. GPS satellites are prefixed with "G", SBAS satellites such as WAAS or EGNOS are prefixed with "S", and Russian GLONASS satellites are prefixed with "R". A checkmark shows if each satellite is used in the current position solution. For each satellite, the elevation, azimuth, and signal strength are provided. A summary of total satellites is presented at the bottom of the table.</p>
|
||||
<p><strong>AHRS</strong> reports heading, pressure altitude, pitch and roll, along with a graphical representation of movement. As of version v0.8, heading is derived from GPS track, and is provided in degrees true.</p>
|
||||
<p>The AHRS graphical depiction is a 3-dimensional paper airplane. Heading of 000° is depicted with the nose of the airplane into the page; 180° is depicted with the nose pointing out of the page.The airplane will pitch up/down and left/right based on the data from the AHRS. To aid with recognizing orientation, the <span class="paperairplane_left">left wing is blue</span> and the <span class="paperairplane_right">right wing is tan</span>.</p>
|
||||
<p class="text-warning">NOTE: This page is for reference only and must not be used for flight operations.</p>
|
||||
</div>
|
|
@ -18,7 +18,7 @@
|
|||
<div class="row">
|
||||
<strong class="col-xs-6 text-center">Location:</strong>
|
||||
<strong class="col-xs-6 text-center">Track:</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="col-xs-6 text-center">{{gps_lat}}, {{gps_lon}} ± {{gps_accuracy}} m <br> {{gps_alt}} ± {{gps_vert_accuracy}} ft @ {{gps_vert_speed}} ft/min</span>
|
||||
<span class="col-xs-6 text-center">{{gps_track}}° @ {{gps_speed}} KTS</span>
|
||||
|
@ -54,6 +54,46 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12">
|
||||
<div class="col-sm-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<span class="panel_label">Satellites</span>
|
||||
</div>
|
||||
<div class="panel-body towers-page">
|
||||
<div class="row">
|
||||
<span class="col-xs-3"><strong>Satellite</strong></span>
|
||||
<!--<span class="col-xs-2 text-right"><strong>NMEA Code</strong></span>-->
|
||||
<span class="col-xs-3 text-right"><strong>Elevation</strong></span>
|
||||
<span class="col-xs-3 text-right"><strong>Azimuth</strong></span>
|
||||
<span class="col-xs-3 text-right"><strong>Signal</strong></span>
|
||||
</div>
|
||||
|
||||
<div class="row" ng-repeat="satellite in data_list | orderBy: 'SatelliteNMEA'">
|
||||
<div class="separator"></div>
|
||||
<span class="col-xs-3">{{satellite.SatelliteID}}<span ng-show="satellite.InSolution"> ✅</span></span>
|
||||
<!--<span class="col-xs-2 text-right">{{satellite.SatelliteNMEA}}</span>-->
|
||||
<span class="col-xs-3 text-right">{{satellite.Elevation < -5 ? "---" : satellite.Elevation}}°</span>
|
||||
<span class="col-xs-3 text-right">{{satellite.Azimuth < 0 ? "---" : satellite.Azimuth}}°</span>
|
||||
<span class="col-xs-3 text-right">{{satellite.Signal < 1 ? "---" : satellite.Signal}}<span style="font-size:50%"> dB-Hz</span></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="separator"></div>
|
||||
<div class="panel-footer">
|
||||
<div class="row">
|
||||
<label class="col-xs-6">GPS solution:</label>
|
||||
<span class="col-xs-6">{{SolutionText}}</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-xs-6">Summary:</label>
|
||||
<span class="col-xs-6">{{Satellites}} in solution; {{GPS_satellites_seen}} seen; {{GPS_satellites_tracked}} tracked</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="col-sm-12">
|
||||
<div class="panel panel-default">
|
||||
|
|
|
@ -4,7 +4,8 @@ GPSCtrl.$inject = ['$rootScope', '$scope', '$state', '$http', '$interval']; // I
|
|||
// create our controller function with all necessary logic
|
||||
function GPSCtrl($rootScope, $scope, $state, $http, $interval) {
|
||||
$scope.$parent.helppage = 'plates/gps-help.html';
|
||||
|
||||
$scope.data_list = [];
|
||||
|
||||
var status = {};
|
||||
var display_area_size = -1;
|
||||
|
||||
|
@ -52,14 +53,25 @@ function GPSCtrl($rootScope, $scope, $state, $http, $interval) {
|
|||
};
|
||||
|
||||
|
||||
function loadStatus(data) {
|
||||
function loadStatus(data) { // mySituation
|
||||
status = angular.fromJson(data);
|
||||
// consider using angular.extend()
|
||||
$scope.raw_data = angular.toJson(data, true); // makes it pretty
|
||||
|
||||
/* not currently used
|
||||
$scope.gps_satellites = status.Satellites;
|
||||
*/
|
||||
|
||||
$scope.Satellites = status.Satellites;
|
||||
$scope.GPS_satellites_tracked = status.SatellitesTracked;
|
||||
$scope.GPS_satellites_seen = status.SatellitesSeen;
|
||||
$scope.Quality = status.Quality;
|
||||
|
||||
var solutionText = "No Fix";
|
||||
if (status.Quality == 2) {
|
||||
solutionText = "GPS + SBAS (WAAS / EGNOS)";
|
||||
} else if (status.Quality == 1) {
|
||||
solutionText = "3D GPS"
|
||||
}
|
||||
$scope.SolutionText = solutionText;
|
||||
|
||||
$scope.gps_accuracy = status.Accuracy.toFixed(1);
|
||||
$scope.gps_vert_accuracy = (status.AccuracyVert*3.2808).toFixed(1); // accuracy is in meters, need to display in ft
|
||||
|
||||
|
@ -106,9 +118,48 @@ function GPSCtrl($rootScope, $scope, $state, $http, $interval) {
|
|||
});
|
||||
};
|
||||
|
||||
function getSatellites() {
|
||||
// Simple GET request example (note: response is asynchronous)
|
||||
$http.get(URL_SATELLITES_GET).
|
||||
then(function (response) {
|
||||
loadSatellites(response.data);
|
||||
}, function (response) {
|
||||
$scope.raw_data = "error getting satellite data";
|
||||
});
|
||||
};
|
||||
|
||||
function setSatellite(obj, new_satellite) {
|
||||
new_satellite.SatelliteNMEA = obj.SatelliteNMEA;
|
||||
new_satellite.SatelliteID = obj.SatelliteID; // Formatted code indicating source and PRN code. e.g. S138==WAAS satellite 138, G2==GPS satellites 2
|
||||
new_satellite.Elevation = obj.Elevation; // Angle above local horizon, -xx to +90
|
||||
new_satellite.Azimuth = obj.Azimuth; // Bearing (degrees true), 0-359
|
||||
new_satellite.Signal = obj.Signal; // Signal strength, 0 - 99; -99 indicates no reception
|
||||
new_satellite.InSolution = obj.InSolution; // is this satellite in the position solution
|
||||
}
|
||||
|
||||
function loadSatellites(data) {
|
||||
if (($scope === undefined) || ($scope === null))
|
||||
return; // we are getting called once after clicking away from the status page
|
||||
|
||||
var satellites = data; // it seems the json was already converted to an object list by the http request
|
||||
$scope.raw_data = angular.toJson(data, true);
|
||||
|
||||
$scope.data_list.length = 0; // clear array
|
||||
// we need to use an array so AngularJS can perform sorting; it also means we need to loop to find a tower in the towers set
|
||||
for (var key in satellites) {
|
||||
//if (satellites[key].Messages_last_minute > 0) {
|
||||
var new_satellite = {};
|
||||
setSatellite(satellites[key], new_satellite);
|
||||
$scope.data_list.push(new_satellite); // add to start of array
|
||||
//}
|
||||
}
|
||||
// $scope.$apply();
|
||||
}
|
||||
|
||||
var updateStatus = $interval(function () {
|
||||
// refresh GPS/AHRS status once each half second (aka polling)
|
||||
getStatus();
|
||||
getSatellites();
|
||||
}, (1 * 500), 0, false);
|
||||
|
||||
$state.get('gps').onEnter = function () {
|
||||
|
|
|
@ -92,9 +92,12 @@ function SettingsCtrl($rootScope, $scope, $state, $location, $window, $http) {
|
|||
|
||||
$scope.updatewatchlist = function () {
|
||||
if ($scope.WatchList !== settings["WatchList"]) {
|
||||
settings["WatchList"] = $scope.WatchList.toUpperCase();
|
||||
settings["WatchList"] = "";
|
||||
if ($scope.WatchList !== undefined) {
|
||||
settings["WatchList"] = $scope.WatchList.toUpperCase();
|
||||
}
|
||||
newsettings = {
|
||||
"WatchList": $scope.WatchList.toUpperCase()
|
||||
"WatchList": settings["WatchList"]
|
||||
};
|
||||
// console.log(angular.toJson(newsettings));
|
||||
setSettings(angular.toJson(newsettings));
|
||||
|
|
Ładowanie…
Reference in New Issue