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
|
cp -f gen_gdl90 /usr/bin/gen_gdl90
|
||||||
chmod 755 /usr/bin/gen_gdl90
|
chmod 755 /usr/bin/gen_gdl90
|
||||||
cp init.d-stratux /etc/init.d/stratux
|
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
|
chmod 755 /etc/init.d/stratux
|
||||||
ln -sf /etc/init.d/stratux /etc/rc2.d/S01stratux
|
ln -sf /etc/init.d/stratux /etc/rc2.d/S01stratux
|
||||||
ln -sf /etc/init.d/stratux /etc/rc6.d/K01stratux
|
ln -sf /etc/init.d/stratux /etc/rc6.d/K01stratux
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
[](https://github.com/cyoung/stratux/releases)
|
[](https://github.com/cyoung/stratux/releases)
|
||||||
[](https://circleci.com/gh/cyoung/stratux/tree/master)
|
[](https://circleci.com/gh/cyoung/stratux/tree/master)
|
||||||
[](https://tldrlegal.com/license/bsd-3-clause-license-%28revised%29)
|
[](https://tldrlegal.com/license/bsd-3-clause-license-%28revised%29)
|
||||||
|
[](http://slack.stratux.me/)
|
||||||
|
|
||||||
# stratux
|
# stratux
|
||||||
RTL-SDR UAT tools
|
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
|
#sshd config
|
||||||
cp -f sshd_config mnt/etc/ssh/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
|
#stratux files
|
||||||
cp -f ../libdump978.so mnt/usr/lib/libdump978.so
|
cp -f ../libdump978.so mnt/usr/lib/libdump978.so
|
||||||
cp -f ../linux-mpu9150/libimu.so mnt/usr/lib/libimu.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.
|
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.Satellites = 0
|
||||||
mySituation.SatellitesSeen = 0
|
mySituation.SatellitesSeen = 0
|
||||||
mySituation.SatellitesTracked = 0
|
mySituation.SatellitesTracked = 0
|
||||||
|
|
|
@ -160,6 +160,19 @@ func handleTowersRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Fprintf(w, "%s\n", towersJSON)
|
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.
|
// AJAX call - /getSettings. Responds with all stratux.conf data.
|
||||||
func handleSettingsGetRequest(w http.ResponseWriter, r *http.Request) {
|
func handleSettingsGetRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
setNoCache(w)
|
setNoCache(w)
|
||||||
|
@ -460,6 +473,7 @@ func managementInterface() {
|
||||||
http.HandleFunc("/getStatus", handleStatusRequest)
|
http.HandleFunc("/getStatus", handleStatusRequest)
|
||||||
http.HandleFunc("/getSituation", handleSituationRequest)
|
http.HandleFunc("/getSituation", handleSituationRequest)
|
||||||
http.HandleFunc("/getTowers", handleTowersRequest)
|
http.HandleFunc("/getTowers", handleTowersRequest)
|
||||||
|
http.HandleFunc("/getSatellites", handleSatellitesRequest)
|
||||||
http.HandleFunc("/getSettings", handleSettingsGetRequest)
|
http.HandleFunc("/getSettings", handleSettingsGetRequest)
|
||||||
http.HandleFunc("/setSettings", handleSettingsSetRequest)
|
http.HandleFunc("/setSettings", handleSettingsSetRequest)
|
||||||
http.HandleFunc("/shutdown", handleShutdownRequest)
|
http.HandleFunc("/shutdown", handleShutdownRequest)
|
||||||
|
|
18
main/ping.go
18
main/ping.go
|
@ -12,14 +12,14 @@ package main
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
//"sync/atomic"
|
//"sync/atomic"
|
||||||
"time"
|
|
||||||
"net"
|
"net"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"time"
|
||||||
|
|
||||||
// Using forked version of tarm/serial to force Linux
|
// Using forked version of tarm/serial to force Linux
|
||||||
// instead of posix code, allowing for higher baud rates
|
// instead of posix code, allowing for higher baud rates
|
||||||
|
@ -38,9 +38,7 @@ func initPingSerial() bool {
|
||||||
|
|
||||||
log.Printf("Configuring Ping ADS-B\n")
|
log.Printf("Configuring Ping ADS-B\n")
|
||||||
|
|
||||||
if _, err := os.Stat("/dev/ttyUSB0"); err == nil {
|
if _, err := os.Stat("/dev/ping"); err == nil {
|
||||||
device = "/dev/ttyUSB0"
|
|
||||||
} else if _, err := os.Stat("/dev/ping"); err == nil {
|
|
||||||
device = "/dev/ping"
|
device = "/dev/ping"
|
||||||
} else {
|
} else {
|
||||||
log.Printf("No suitable Ping device found.\n")
|
log.Printf("No suitable Ping device found.\n")
|
||||||
|
@ -150,7 +148,7 @@ func pingSerialReader() {
|
||||||
s := scanner.Text()
|
s := scanner.Text()
|
||||||
// Trimspace removes newlines as well as whitespace
|
// Trimspace removes newlines as well as whitespace
|
||||||
s = strings.TrimSpace(s)
|
s = strings.TrimSpace(s)
|
||||||
logString := fmt.Sprintf("Ping received: %s", s);
|
logString := fmt.Sprintf("Ping received: %s", s)
|
||||||
log.Println(logString)
|
log.Println(logString)
|
||||||
if s[0] == '*' {
|
if s[0] == '*' {
|
||||||
// 1090ES report
|
// 1090ES report
|
||||||
|
@ -169,19 +167,19 @@ func pingSerialReader() {
|
||||||
log.Println("Starting dump1090 network connection")
|
log.Println("Starting dump1090 network connection")
|
||||||
pingNetworkConnection()
|
pingNetworkConnection()
|
||||||
}
|
}
|
||||||
if (len(report[0]) != 0 && dump1090Connection != nil) {
|
if len(report[0]) != 0 && dump1090Connection != nil {
|
||||||
dump1090Connection.Write([]byte(report[0] + ";\r\n"))
|
dump1090Connection.Write([]byte(report[0] + ";\r\n"))
|
||||||
//log.Println("Relaying 1090ES message")
|
//log.Println("Relaying 1090ES message")
|
||||||
//logString := fmt.Sprintf("Relaying 1090ES: %s;", report[0]);
|
//logString := fmt.Sprintf("Relaying 1090ES: %s;", report[0]);
|
||||||
//log.Println(logString)
|
//log.Println(logString)
|
||||||
}
|
}
|
||||||
} else if (s[0] == '+' || s[0] == '-') {
|
} else if s[0] == '+' || s[0] == '-' {
|
||||||
// UAT report
|
// UAT report
|
||||||
// Ping appends a signal strength and RS bit errors corrected
|
// Ping appends a signal strength and RS bit errors corrected
|
||||||
// at the end of the message
|
// at the end of the message
|
||||||
// e.g. -08A5DFDF3907E982585F029B00040080105C3AB4BC5C240700A206000000000000003A13C82F96C80A63191F05FCB231;rs=1;ss=A2;
|
// e.g. -08A5DFDF3907E982585F029B00040080105C3AB4BC5C240700A206000000000000003A13C82F96C80A63191F05FCB231;rs=1;ss=A2;
|
||||||
// We need to rescale the signal strength for interpretation by dump978,
|
// 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
|
// 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
|
// 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
|
// 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)
|
//logString = fmt.Sprintf("Relaying message, type=%d", msgtype)
|
||||||
//log.Println(logString)
|
//log.Println(logString)
|
||||||
relayMessage(msgtype, o)
|
relayMessage(msgtype, o)
|
||||||
} else if (o == nil) {
|
} else if o == nil {
|
||||||
//log.Println("Not relaying message, o == nil")
|
//log.Println("Not relaying message, o == nil")
|
||||||
} else {
|
} else {
|
||||||
//log.Println("Not relaying message, msgtype == 0")
|
//log.Println("Not relaying message, msgtype == 0")
|
||||||
|
|
490
main/ry835ai.go
490
main/ry835ai.go
|
@ -30,6 +30,28 @@ import (
|
||||||
"../mpu6050"
|
"../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 {
|
type SituationData struct {
|
||||||
mu_GPS *sync.Mutex
|
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 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
|
u-blox5_Referenzmanual.pdf
|
||||||
Platform settings
|
Platform settings
|
||||||
|
@ -130,12 +155,17 @@ func initGPSSerial() bool {
|
||||||
baudrate := int(9600)
|
baudrate := int(9600)
|
||||||
isSirfIV := bool(false)
|
isSirfIV := bool(false)
|
||||||
|
|
||||||
if _, err := os.Stat("/dev/ttyACM0"); err == nil { // u-blox receivers on native USB connection
|
if _, err := os.Stat("/dev/ublox8"); err == nil { // u-blox 8 (RY83xAI over USB).
|
||||||
device = "/dev/ttyACM0"
|
device = "/dev/ublox8"
|
||||||
} 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)
|
} 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
|
isSirfIV = true
|
||||||
baudrate = 4800
|
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.
|
} else if _, err := os.Stat("/dev/ttyAMA0"); err == nil { // ttyAMA0 is PL011 UART (GPIO pins 8 and 10) on all RPi.
|
||||||
device = "/dev/ttyAMA0"
|
device = "/dev/ttyAMA0"
|
||||||
} else {
|
} else {
|
||||||
|
@ -225,15 +255,16 @@ func initGPSSerial() bool {
|
||||||
p.Write(makeNMEACmd("PSRF103,04,00,01,01"))
|
p.Write(makeNMEACmd("PSRF103,04,00,01,01"))
|
||||||
// Enable VTG.
|
// Enable VTG.
|
||||||
p.Write(makeNMEACmd("PSRF103,05,00,01,01"))
|
p.Write(makeNMEACmd("PSRF103,05,00,01,01"))
|
||||||
// Disable GSV.
|
// Enable GSV (once every 5 position updates)
|
||||||
p.Write(makeNMEACmd("PSRF103,03,00,00,01"))
|
p.Write(makeNMEACmd("PSRF103,03,00,05,01"))
|
||||||
|
|
||||||
if globalSettings.DEBUG {
|
if globalSettings.DEBUG {
|
||||||
log.Printf("Finished writing SiRF GPS config to %s. Opening port to test connection.\n", device)
|
log.Printf("Finished writing SiRF GPS config to %s. Opening port to test connection.\n", device)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Set 10Hz update. Little endian order.
|
// Set 5 Hz update. Little endian order.
|
||||||
p.Write(makeUBXCFG(0x06, 0x08, 6, []byte{0x64, 0x00, 0x01, 0x00, 0x01, 0x00}))
|
//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.
|
// Set navigation settings.
|
||||||
nav := make([]byte, 36)
|
nav := make([]byte, 36)
|
||||||
|
@ -246,17 +277,20 @@ func initGPSSerial() bool {
|
||||||
p.Write(makeUBXCFG(0x06, 0x24, 36, nav))
|
p.Write(makeUBXCFG(0x06, 0x24, 36, nav))
|
||||||
|
|
||||||
// GNSS configuration CFG-GNSS for ublox 7 higher, p. 125 (v8)
|
// GNSS configuration CFG-GNSS for ublox 7 higher, p. 125 (v8)
|
||||||
//
|
|
||||||
// NOTE: Max position rate = 5 Hz if GPS+GLONASS used.
|
// 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
|
// Disable GLONASS to enable 10 Hz solution rate. GLONASS is not used
|
||||||
// for SBAS (WAAS), so little real-world impact.
|
// for SBAS (WAAS), so little real-world impact.
|
||||||
|
|
||||||
cfgGnss := []byte{0x00, 0x20, 0x20, 0x05}
|
cfgGnss := []byte{0x00, 0x20, 0x20, 0x05}
|
||||||
gps := []byte{0x00, 0x08, 0x10, 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}
|
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}
|
beidou := []byte{0x03, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x01}
|
||||||
qzss := []byte{0x05, 0x00, 0x03, 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, gps...)
|
||||||
cfgGnss = append(cfgGnss, sbas...)
|
cfgGnss = append(cfgGnss, sbas...)
|
||||||
cfgGnss = append(cfgGnss, beidou...)
|
cfgGnss = append(cfgGnss, beidou...)
|
||||||
|
@ -267,12 +301,16 @@ func initGPSSerial() bool {
|
||||||
// SBAS configuration for ublox 6 and higher
|
// SBAS configuration for ublox 6 and higher
|
||||||
p.Write(makeUBXCFG(0x06, 0x16, 8, []byte{0x01, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}))
|
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
|
// 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, 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
|
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
|
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, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})) // GSV
|
//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, 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, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01})) // VGT
|
||||||
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF0, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})) // GRS
|
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, 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, 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{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, 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
|
p.Write(makeUBXCFG(0x06, 0x01, 8, []byte{0xF1, 0x04, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x00})) // Ublox,4
|
||||||
|
|
||||||
// Reconfigure serial port.
|
// Reconfigure serial port.
|
||||||
|
@ -586,44 +623,147 @@ func processNMEALine(l string) (sentenceUsed bool) {
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
return false
|
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.
|
// We've made it this far, so that means we've processed "everything" and can now make the change to mySituation.
|
||||||
mySituation = tmpSituation
|
mySituation = tmpSituation
|
||||||
return true
|
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
|
// field 2 = number of satellites tracked
|
||||||
satSeen := 0 // satellites seen (signal present)
|
//satSeen := 0 // satellites seen (signal present)
|
||||||
satTracked, err := strconv.Atoi(x[2])
|
satTracked, err := strconv.Atoi(x[2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
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
|
// 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++ {
|
for i := 0; i < satTracked; i++ {
|
||||||
j := 7 + 6*i
|
//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.
|
||||||
if j < len(x) {
|
sv, err = strconv.Atoi(x[3+6*i]) // sv number
|
||||||
if x[j] != "" {
|
if err != nil {
|
||||||
satSeen++
|
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
|
return true
|
||||||
} else if x[1] == "04" { // clock message
|
} 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
|
// 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
|
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.
|
// Altitude.
|
||||||
alt, err1 := strconv.ParseFloat(x[9], 32)
|
alt, err1 := strconv.ParseFloat(x[9], 32)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
|
@ -940,27 +1057,83 @@ func processNMEALine(l string) (sentenceUsed bool) {
|
||||||
// field 1: operation mode
|
// field 1: operation mode
|
||||||
// M: manual forced to 2D or 3D mode
|
// M: manual forced to 2D or 3D mode
|
||||||
// A: automatic switching between 2D and 3D modes
|
// 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.
|
tmpSituation.Quality = 0 // Just a note.
|
||||||
return false
|
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
|
// 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
|
sat := 0
|
||||||
|
|
||||||
for _, svtxt := range x[3:15] {
|
for _, svtxt := range x[3:15] {
|
||||||
_, err := strconv.Atoi(svtxt)
|
sv, err := strconv.Atoi(svtxt)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
sat++
|
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)
|
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)
|
||||||
// Satellites tracked / seen should be parsed from GSV message (TO-DO) ... since we don't have it, just use satellites from solution
|
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.SatellitesTracked = uint16(sat)
|
tmpSituation.Satellites++
|
||||||
tmpSituation.SatellitesSeen = uint16(sat)
|
}
|
||||||
|
}
|
||||||
|
//log.Printf("There are %d satellites in solution from this GSA message\n", sat) // TESTING - DEBUG
|
||||||
|
|
||||||
// field 16: HDOP
|
// field 16: HDOP
|
||||||
// Accuracy estimate
|
// Accuracy estimate
|
||||||
|
@ -990,6 +1163,142 @@ func processNMEALine(l string) (sentenceUsed bool) {
|
||||||
return true
|
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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1160,6 +1469,39 @@ func attitudeReaderSender() {
|
||||||
globalStatus.RY835AI_connected = false
|
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 {
|
func isGPSConnected() bool {
|
||||||
return stratuxClock.Since(mySituation.LastValidNMEAMessageTime) < 5*time.Second
|
return stratuxClock.Since(mySituation.LastValidNMEAMessageTime) < 5*time.Second
|
||||||
}
|
}
|
||||||
|
@ -1244,6 +1586,8 @@ func pollRY835AI() {
|
||||||
func initRY835AI() {
|
func initRY835AI() {
|
||||||
mySituation.mu_GPS = &sync.Mutex{}
|
mySituation.mu_GPS = &sync.Mutex{}
|
||||||
mySituation.mu_Attitude = &sync.Mutex{}
|
mySituation.mu_Attitude = &sync.Mutex{}
|
||||||
|
satelliteMutex = &sync.Mutex{}
|
||||||
|
Satellites = make(map[string]SatelliteInfo)
|
||||||
|
|
||||||
go pollRY835AI()
|
go pollRY835AI()
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ func (e *ES) read() {
|
||||||
default:
|
default:
|
||||||
n, err := stderr.Read(stderrBuf)
|
n, err := stderr.Read(stderrBuf)
|
||||||
if err == nil && n > 0 {
|
if err == nil && n > 0 {
|
||||||
m := Dump1090TermMessage{Text: string(stdoutBuf[:n]), Source: "stderr"}
|
m := Dump1090TermMessage{Text: string(stderrBuf[:n]), Source: "stderr"}
|
||||||
logDump1090TermMessage(m)
|
logDump1090TermMessage(m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,6 +229,9 @@ func (u *UAT) sdrConfig() (err error) {
|
||||||
}
|
}
|
||||||
log.Printf("\tSetTunerGain Successful\n")
|
log.Printf("\tSetTunerGain Successful\n")
|
||||||
|
|
||||||
|
tgain := u.dev.GetTunerGain()
|
||||||
|
log.Printf("\tGetTunerGain: %d\n", tgain)
|
||||||
|
|
||||||
//---------- Get/Set Sample Rate ----------
|
//---------- Get/Set Sample Rate ----------
|
||||||
err = u.dev.SetSampleRate(SampleRate)
|
err = u.dev.SetSampleRate(SampleRate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -426,6 +429,8 @@ func configDevices(count int, esEnabled, uatEnabled bool) {
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
_, _, s, err := rtl.GetDeviceUsbStrings(i)
|
_, _, s, err := rtl.GetDeviceUsbStrings(i)
|
||||||
if err == nil {
|
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
|
// no need to check if createXDev returned an error; if it
|
||||||
// failed to config the error is logged and we can ignore
|
// failed to config the error is logged and we can ignore
|
||||||
// it here so it doesn't get queued up again
|
// 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/modules.txt work/bin/
|
||||||
cp image/stxAliases.txt work/bin/
|
cp image/stxAliases.txt work/bin/
|
||||||
cp image/hostapd_manager.sh 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.
|
#TODO: librtlsdr.
|
||||||
cd work/
|
cd work/
|
||||||
|
|
|
@ -8,7 +8,11 @@ ssh -i ~/.ssh/id_rsa.updates stratux-updates@updates.stratux.me 'ls -1 queue/' |
|
||||||
cd selfupdate
|
cd selfupdate
|
||||||
./makeupdate.sh
|
./makeupdate.sh
|
||||||
cd ..
|
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 ..
|
cd ..
|
||||||
ssh -i ~/.ssh/id_rsa.updates stratux-updates@updates.stratux.me "rm -f queue/${git_hash}"
|
ssh -i ~/.ssh/id_rsa.updates stratux-updates@updates.stratux.me "rm -f queue/${git_hash}"
|
||||||
done
|
done
|
|
@ -11,6 +11,7 @@ ln -fs /etc/init.d/stratux /etc/rc6.d/K01stratux
|
||||||
|
|
||||||
#wifi config
|
#wifi config
|
||||||
cp -f hostapd.conf /etc/hostapd/hostapd.conf
|
cp -f hostapd.conf /etc/hostapd/hostapd.conf
|
||||||
|
cp -f hostapd-edimax.conf /etc/hostapd/hostapd-edimax.conf
|
||||||
|
|
||||||
#WiFi Config Manager
|
#WiFi Config Manager
|
||||||
cp -f hostapd_manager.sh /usr/sbin/
|
cp -f hostapd_manager.sh /usr/sbin/
|
||||||
|
@ -21,6 +22,10 @@ cp -f config.txt /boot/config.txt
|
||||||
#modprobe.d blacklist
|
#modprobe.d blacklist
|
||||||
cp -f rtl-sdr-blacklist.conf /etc/modprobe.d/
|
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
|
#go setup
|
||||||
cp -f bashrc.txt /root/.bashrc
|
cp -f bashrc.txt /root/.bashrc
|
||||||
cp -f stxAliases /root/.stxAliases
|
cp -f stxAliases /root/.stxAliases
|
||||||
|
@ -35,4 +40,4 @@ cd web/ && make stratuxBuild=${stratuxBuild}
|
||||||
|
|
||||||
# Remove old Wi-Fi watcher script.
|
# Remove old Wi-Fi watcher script.
|
||||||
rm -f /usr/sbin/wifi_watch.sh
|
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_GPS_GET = "http://" + URL_HOST_BASE + "/getSituation";
|
||||||
var URL_TOWERS_GET = "http://" + URL_HOST_BASE + "/getTowers"
|
var URL_TOWERS_GET = "http://" + URL_HOST_BASE + "/getTowers"
|
||||||
var URL_STATUS_GET = "http://" + URL_HOST_BASE + "/getStatus"
|
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_STATUS_WS = "ws://" + URL_HOST_BASE + "/status"
|
||||||
var URL_TRAFFIC_WS = "ws://" + URL_HOST_BASE + "/traffic";
|
var URL_TRAFFIC_WS = "ws://" + URL_HOST_BASE + "/traffic";
|
||||||
var URL_WEATHER_WS = "ws://" + URL_HOST_BASE + "/weather";
|
var URL_WEATHER_WS = "ws://" + URL_HOST_BASE + "/weather";
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
<div class="section text-left help-page">
|
<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 / 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><strong>GPS</strong> shows position with estimated accuracy, ground track, ground speed, and geometric altitude. Location is displayed 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><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>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>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>
|
<p class="text-warning">NOTE: This page is for reference only and must not be used for flight operations.</p>
|
||||||
</div>
|
</div>
|
|
@ -18,7 +18,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<strong class="col-xs-6 text-center">Location:</strong>
|
<strong class="col-xs-6 text-center">Location:</strong>
|
||||||
<strong class="col-xs-6 text-center">Track:</strong>
|
<strong class="col-xs-6 text-center">Track:</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<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_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>
|
<span class="col-xs-6 text-center">{{gps_track}}° @ {{gps_speed}} KTS</span>
|
||||||
|
@ -54,6 +54,46 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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="col-sm-12">
|
||||||
<div class="panel panel-default">
|
<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
|
// create our controller function with all necessary logic
|
||||||
function GPSCtrl($rootScope, $scope, $state, $http, $interval) {
|
function GPSCtrl($rootScope, $scope, $state, $http, $interval) {
|
||||||
$scope.$parent.helppage = 'plates/gps-help.html';
|
$scope.$parent.helppage = 'plates/gps-help.html';
|
||||||
|
$scope.data_list = [];
|
||||||
|
|
||||||
var status = {};
|
var status = {};
|
||||||
var display_area_size = -1;
|
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);
|
status = angular.fromJson(data);
|
||||||
// consider using angular.extend()
|
// consider using angular.extend()
|
||||||
$scope.raw_data = angular.toJson(data, true); // makes it pretty
|
$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_accuracy = status.Accuracy.toFixed(1);
|
||||||
$scope.gps_vert_accuracy = (status.AccuracyVert*3.2808).toFixed(1); // accuracy is in meters, need to display in ft
|
$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 () {
|
var updateStatus = $interval(function () {
|
||||||
// refresh GPS/AHRS status once each half second (aka polling)
|
// refresh GPS/AHRS status once each half second (aka polling)
|
||||||
getStatus();
|
getStatus();
|
||||||
|
getSatellites();
|
||||||
}, (1 * 500), 0, false);
|
}, (1 * 500), 0, false);
|
||||||
|
|
||||||
$state.get('gps').onEnter = function () {
|
$state.get('gps').onEnter = function () {
|
||||||
|
|
|
@ -92,9 +92,12 @@ function SettingsCtrl($rootScope, $scope, $state, $location, $window, $http) {
|
||||||
|
|
||||||
$scope.updatewatchlist = function () {
|
$scope.updatewatchlist = function () {
|
||||||
if ($scope.WatchList !== settings["WatchList"]) {
|
if ($scope.WatchList !== settings["WatchList"]) {
|
||||||
settings["WatchList"] = $scope.WatchList.toUpperCase();
|
settings["WatchList"] = "";
|
||||||
|
if ($scope.WatchList !== undefined) {
|
||||||
|
settings["WatchList"] = $scope.WatchList.toUpperCase();
|
||||||
|
}
|
||||||
newsettings = {
|
newsettings = {
|
||||||
"WatchList": $scope.WatchList.toUpperCase()
|
"WatchList": settings["WatchList"]
|
||||||
};
|
};
|
||||||
// console.log(angular.toJson(newsettings));
|
// console.log(angular.toJson(newsettings));
|
||||||
setSettings(angular.toJson(newsettings));
|
setSettings(angular.toJson(newsettings));
|
||||||
|
|
Ładowanie…
Reference in New Issue