Merge pull request #1 from cyoung/uavionix

Merge master changes. Some minor Ping-related fixes.
pull/470/head
Ryan Braun 2016-05-31 15:05:59 -05:00
commit c8244bf96f
18 zmienionych plików z 734 dodań i 97 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -1,6 +1,8 @@
[![stratux version](https://img.shields.io/github/tag/cyoung/stratux.svg?style=flat&label=stratux)](https://github.com/cyoung/stratux/releases)
[![Build Status](http://circleci-badges-max.herokuapp.com/img/cyoung/stratux/master?token=:circle-ci-token)](https://circleci.com/gh/cyoung/stratux/tree/master)
[![BSD3 License](http://img.shields.io/badge/license-BSD3-brightgreen.svg)](https://tldrlegal.com/license/bsd-3-clause-license-%28revised%29)
[![Stratux Slack](http://slack.stratux.me:3000/badge.svg)](http://slack.stratux.me/)
# stratux
RTL-SDR UAT tools

Wyświetl plik

@ -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"

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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)

Wyświetl plik

@ -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")

Wyświetl plik

@ -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()
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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/

Wyświetl plik

@ -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

Wyświetl plik

@ -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

134
test/es_dump_csv.go 100644
Wyświetl plik

@ -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
}
}

Wyświetl plik

@ -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";

Wyświetl plik

@ -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&deg; being off in the distance and 180&deg; 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&deg; is depicted with the nose of the airplane into the page; 180&deg; 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>

Wyświetl plik

@ -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}} &plusmn; {{gps_accuracy}} m <br> {{gps_alt}} &plusmn; {{gps_vert_accuracy}} ft @ {{gps_vert_speed}} ft/min</span>
<span class="col-xs-6 text-center">{{gps_track}}&deg; @ {{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">&nbsp;&#x2705;</span></span>
<!--<span class="col-xs-2 text-right">{{satellite.SatelliteNMEA}}</span>-->
<span class="col-xs-3 text-right">{{satellite.Elevation < -5 ? "---" : satellite.Elevation}}&deg;</span>
<span class="col-xs-3 text-right">{{satellite.Azimuth < 0 ? "---" : satellite.Azimuth}}&deg;</span>
<span class="col-xs-3 text-right">{{satellite.Signal < 1 ? "---" : satellite.Signal}}<span style="font-size:50%">&nbsp;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">

Wyświetl plik

@ -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 () {

Wyświetl plik

@ -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));