Merge remote-tracking branch 'origin/master' into ahrs_dev_protocolfun

adsbexchange
Christopher Young 2017-08-03 23:08:45 -04:00
commit fcf524b03c
16 zmienionych plików z 785 dodań i 651 usunięć

Wyświetl plik

@ -13,9 +13,6 @@ Raspberry Pi 2 with the Edimax EW-7811Un Wi-Fi dongle is supported but not recom
Tested and works well with most common R820T and R820T2 RTL-SDR devices.
Tested with and preliminary support added for [uAvionix pingEFB dual-link ADS-B receiver](http://www.uavionix.com/products/pingefb/).
Apps with stratux recognition/support:
* Seattle Avionics FlyQ EFB 2.1.1+.
* AvNav EFB 2.0.0+.
@ -31,10 +28,17 @@ Tested weather/traffic displays:
* ForeFlight 7+ - weather, traffic. AHRS not functional.
* Avare
Other EFBs? See the [app vendor integration guide](https://github.com/cyoung/stratux/blob/master/notes/app-vendor-integration.md).
Dangerzone builds (AHRS display):
* ForeFlight 7+ - weather, traffic, AHRS.
Questions? [See the FAQ](https://github.com/cyoung/stratux/wiki/FAQ)
http://stratux.me/
http://slack.stratux.me/
https://www.reddit.com/r/stratux
Jet tests (high gain antennas):

Wyświetl plik

@ -18,3 +18,6 @@ deployment:
branch: master
commands:
- yes | ssh -i ~/.ssh/id_updates.stratux.me stratux-updates@updates.stratux.me "touch queue/`git log -n 1 --pretty=%H`"
branch: ahrs_dev
commands:
- yes | ssh -i ~/.ssh/id_updates.stratux.me stratux-updates@updates.stratux.me "touch queue/`git log -n 1 --pretty=%H`"

@ -1 +1 @@
Subproject commit 083dd940f35eed0505f3cded7d8b6aa5f70b4e35
Subproject commit a9f32832e8fd8cac2b2e10cf673f0b79e1cec67d

Wyświetl plik

@ -1,6 +1,8 @@
auto lo
iface lo inet loopback
allow-hotplug eth0
iface eth0 inet dhcp
allow-hotplug wlan0

Wyświetl plik

@ -108,6 +108,65 @@ var maxSignalStrength int
var stratuxBuild string
var stratuxVersion string
var product_name_map = map[int]string{
0: "METAR",
1: "TAF",
2: "SIGMET",
3: "Conv SIGMET",
4: "AIRMET",
5: "PIREP",
6: "Severe Wx",
7: "Winds Aloft",
8: "NOTAM", //"NOTAM (Including TFRs) and Service Status";
9: "D-ATIS", //"Aerodrome and Airspace – D-ATIS";
10: "Terminal Wx", //"Aerodrome and Airspace - TWIP";
11: "AIRMET", //"Aerodrome and Airspace - AIRMET";
12: "SIGMET", //"Aerodrome and Airspace - SIGMET/Convective SIGMET";
13: "SUA", //"Aerodrome and Airspace - SUA Status";
20: "METAR", //"METAR and SPECI";
21: "TAF", //"TAF and Amended TAF";
22: "SIGMET", //"SIGMET";
23: "Conv SIGMET", //"Convective SIGMET";
24: "AIRMET", //"AIRMET";
25: "PIREP", //"PIREP";
26: "Severe Wx", //"AWW";
27: "Winds Aloft", //"Winds and Temperatures Aloft";
51: "NEXRAD", //"National NEXRAD, Type 0 - 4 level";
52: "NEXRAD", //"National NEXRAD, Type 1 - 8 level (quasi 6-level VIP)";
53: "NEXRAD", //"National NEXRAD, Type 2 - 8 level";
54: "NEXRAD", //"National NEXRAD, Type 3 - 16 level";
55: "NEXRAD", //"Regional NEXRAD, Type 0 - low dynamic range";
56: "NEXRAD", //"Regional NEXRAD, Type 1 - 8 level (quasi 6-level VIP)";
57: "NEXRAD", //"Regional NEXRAD, Type 2 - 8 level";
58: "NEXRAD", //"Regional NEXRAD, Type 3 - 16 level";
59: "NEXRAD", //"Individual NEXRAD, Type 0 - low dynamic range";
60: "NEXRAD", //"Individual NEXRAD, Type 1 - 8 level (quasi 6-level VIP)";
61: "NEXRAD", //"Individual NEXRAD, Type 2 - 8 level";
62: "NEXRAD", //"Individual NEXRAD, Type 3 - 16 level";
63: "NEXRAD Regional", //"Global Block Representation - Regional NEXRAD, Type 4 – 8 level";
64: "NEXRAD CONUS", //"Global Block Representation - CONUS NEXRAD, Type 4 - 8 level";
81: "Tops", //"Radar echo tops graphic, scheme 1: 16-level";
82: "Tops", //"Radar echo tops graphic, scheme 2: 8-level";
83: "Tops", //"Storm tops and velocity";
101: "Lightning", //"Lightning strike type 1 (pixel level)";
102: "Lightning", //"Lightning strike type 2 (grid element level)";
151: "Lightning", //"Point phenomena, vector format";
201: "Surface", //"Surface conditions/winter precipitation graphic";
202: "Surface", //"Surface weather systems";
254: "G-AIRMET", //"AIRMET, SIGMET: Bitmap encoding";
351: "Time", //"System Time";
352: "Status", //"Operational Status";
353: "Status", //"Ground Station Status";
401: "Imagery", //"Generic Raster Scan Data Product APDU Payload Format Type 1";
402: "Text",
403: "Vector Imagery", //"Generic Vector Data Product APDU Payload Format Type 1";
404: "Symbols",
405: "Text",
411: "Text", //"Generic Textual Data Product APDU Payload Format Type 1";
412: "Symbols", //"Generic Symbolic Product APDU Payload Format Type 1";
413: "Text", //"Generic Textual Data Product APDU Payload Format Type 2";
}
// CRC16 table generated to use to work with GDL90 messages.
var Crc16Table [256]uint16
@ -795,6 +854,15 @@ func updateStatus() {
if err == nil {
globalStatus.Logfile_Size = fileInfo.Size()
}
var ahrsLogSize int64
ahrsLogFiles, _ := ioutil.ReadDir("/var/log")
for _, f := range ahrsLogFiles {
if v, _ := filepath.Match("sensors_*.csv", f.Name()); v {
ahrsLogSize += f.Size()
}
}
globalStatus.AHRS_LogFiles_Size = ahrsLogSize
}
type WeatherMessage struct {
@ -963,65 +1031,6 @@ func parseInput(buf string) ([]byte, uint16) {
return frame, msgtype
}
var product_name_map = map[int]string{
0: "METAR",
1: "TAF",
2: "SIGMET",
3: "Conv SIGMET",
4: "AIRMET",
5: "PIREP",
6: "Severe Wx",
7: "Winds Aloft",
8: "NOTAM", //"NOTAM (Including TFRs) and Service Status";
9: "D-ATIS", //"Aerodrome and Airspace – D-ATIS";
10: "Terminal Wx", //"Aerodrome and Airspace - TWIP";
11: "AIRMET", //"Aerodrome and Airspace - AIRMET";
12: "SIGMET", //"Aerodrome and Airspace - SIGMET/Convective SIGMET";
13: "SUA", //"Aerodrome and Airspace - SUA Status";
20: "METAR", //"METAR and SPECI";
21: "TAF", //"TAF and Amended TAF";
22: "SIGMET", //"SIGMET";
23: "Conv SIGMET", //"Convective SIGMET";
24: "AIRMET", //"AIRMET";
25: "PIREP", //"PIREP";
26: "Severe Wx", //"AWW";
27: "Winds Aloft", //"Winds and Temperatures Aloft";
51: "NEXRAD", //"National NEXRAD, Type 0 - 4 level";
52: "NEXRAD", //"National NEXRAD, Type 1 - 8 level (quasi 6-level VIP)";
53: "NEXRAD", //"National NEXRAD, Type 2 - 8 level";
54: "NEXRAD", //"National NEXRAD, Type 3 - 16 level";
55: "NEXRAD", //"Regional NEXRAD, Type 0 - low dynamic range";
56: "NEXRAD", //"Regional NEXRAD, Type 1 - 8 level (quasi 6-level VIP)";
57: "NEXRAD", //"Regional NEXRAD, Type 2 - 8 level";
58: "NEXRAD", //"Regional NEXRAD, Type 3 - 16 level";
59: "NEXRAD", //"Individual NEXRAD, Type 0 - low dynamic range";
60: "NEXRAD", //"Individual NEXRAD, Type 1 - 8 level (quasi 6-level VIP)";
61: "NEXRAD", //"Individual NEXRAD, Type 2 - 8 level";
62: "NEXRAD", //"Individual NEXRAD, Type 3 - 16 level";
63: "NEXRAD Regional", //"Global Block Representation - Regional NEXRAD, Type 4 – 8 level";
64: "NEXRAD CONUS", //"Global Block Representation - CONUS NEXRAD, Type 4 - 8 level";
81: "Tops", //"Radar echo tops graphic, scheme 1: 16-level";
82: "Tops", //"Radar echo tops graphic, scheme 2: 8-level";
83: "Tops", //"Storm tops and velocity";
101: "Lightning", //"Lightning strike type 1 (pixel level)";
102: "Lightning", //"Lightning strike type 2 (grid element level)";
151: "Lightning", //"Point phenomena, vector format";
201: "Surface", //"Surface conditions/winter precipitation graphic";
202: "Surface", //"Surface weather systems";
254: "G-AIRMET", //"AIRMET, SIGMET: Bitmap encoding";
351: "Time", //"System Time";
352: "Status", //"Operational Status";
353: "Status", //"Ground Station Status";
401: "Imagery", //"Generic Raster Scan Data Product APDU Payload Format Type 1";
402: "Text",
403: "Vector Imagery", //"Generic Vector Data Product APDU Payload Format Type 1";
404: "Symbols",
405: "Text",
411: "Text", //"Generic Textual Data Product APDU Payload Format Type 1";
412: "Symbols", //"Generic Symbolic Product APDU Payload Format Type 1";
413: "Text", //"Generic Textual Data Product APDU Payload Format Type 2";
}
func getProductNameFromId(product_id int) string {
name, present := product_name_map[product_id]
if present {
@ -1036,26 +1045,27 @@ func getProductNameFromId(product_id int) string {
}
type settings struct {
UAT_Enabled bool
ES_Enabled bool
Ping_Enabled bool
GPS_Enabled bool
BMP_Sensor_Enabled bool
IMU_Sensor_Enabled bool
NetworkOutputs []networkConnection
SerialOutputs map[string]serialConnection
DisplayTrafficSource bool
DEBUG bool
ReplayLog bool
AHRSLog bool
IMUMapping [2]int // Map from aircraft axis to sensor axis: accelerometer
SensorQuaternion [4]float64 // Quaternion mapping from sensor frame to aircraft frame
PPM int
OwnshipModeS string
WatchList string
DeveloperMode bool
GLimits string
StaticIps []string
UAT_Enabled bool
ES_Enabled bool
Ping_Enabled bool
GPS_Enabled bool
BMP_Sensor_Enabled bool
IMU_Sensor_Enabled bool
NetworkOutputs []networkConnection
SerialOutputs map[string]serialConnection
DisplayTrafficSource bool
DEBUG bool
ReplayLog bool
AHRSLog bool
IMUMapping [2]int // Map from aircraft axis to sensor axis: accelerometer
SensorQuaternion [4]float64 // Quaternion mapping from sensor frame to aircraft frame
C, D [3]float64 // IMU Accel, Gyro zero bias
PPM int
OwnshipModeS string
WatchList string
DeveloperMode bool
GLimits string
StaticIps []string
}
type status struct {
@ -1100,6 +1110,7 @@ type status struct {
UAT_OTHER_total uint32
Errors []string
Logfile_Size int64
AHRS_LogFiles_Size int64
BMPConnected bool
IMUConnected bool
}
@ -1122,7 +1133,7 @@ func defaultSettings() {
globalSettings.DisplayTrafficSource = false
globalSettings.ReplayLog = false //TODO: 'true' for debug builds.
globalSettings.AHRSLog = false
globalSettings.IMUMapping = [2]int{-1, -3} // OpenFlightBox AHRS normal mapping
globalSettings.IMUMapping = [2]int{-1, 0}
globalSettings.OwnshipModeS = "F00000"
globalSettings.DeveloperMode = false
globalSettings.StaticIps = make([]string, 0)

Wyświetl plik

@ -26,6 +26,8 @@ import (
"syscall"
"text/template"
"time"
"archive/zip"
"path/filepath"
)
type SettingMessage struct {
@ -387,10 +389,28 @@ func doReboot() {
}
func handleDeleteLogFile(w http.ResponseWriter, r *http.Request) {
log.Printf("handleDeleteLogFile called!!!\n")
log.Println("handleDeleteLogFile called!!!")
clearDebugLogFile()
}
func handleDeleteAHRSLogFiles(w http.ResponseWriter, r *http.Request) {
files, err := ioutil.ReadDir("/var/log")
if err != nil {
http.Error(w, fmt.Sprintf("error deleting AHRS logs: %s", err), http.StatusNotFound)
return
}
var fn string
for _, f := range files {
fn = f.Name()
if v, _ := filepath.Match("sensors_*.csv", fn) ; v {
os.Remove("/var/log/" + fn)
log.Printf("Deleting AHRS log file %s\n", fn)
}
analysisLogger = nil
}
}
func handleDevelModeToggle(w http.ResponseWriter, r *http.Request) {
log.Printf("handleDevelModeToggle called!!!\n")
globalSettings.DeveloperMode = true
@ -410,8 +430,6 @@ func handleRebootRequest(w http.ResponseWriter, r *http.Request) {
go delayReboot()
}
var f int // We need this to be global to handle successive calls to handleOrientAHRS.
func handleOrientAHRS(w http.ResponseWriter, r *http.Request) {
// define header in support of cross-domain AJAX
setNoCache(w)
@ -425,7 +443,6 @@ func handleOrientAHRS(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" {
var (
action []byte = make([]byte, 1)
u int
err error
)
@ -436,38 +453,22 @@ func handleOrientAHRS(w http.ResponseWriter, r *http.Request) {
switch action[0] {
case 'f': // Set sensor "forward" direction (toward nose of airplane).
if f, err = getMinAccelDirection(); err != nil {
f, err := getMinAccelDirection()
if err != nil {
log.Printf("AHRS Error: sensor orientation: couldn't read accelerometer: %s\n", err)
http.Error(w, fmt.Sprintf("couldn't read accelerometer: %s\n", err), http.StatusBadRequest)
return
}
log.Printf("AHRS Info: sensor orientation: received forward direction %d\n", f)
case 'u': // Set sensor "up" direction (toward top of airplane).
if u, err = getMinAccelDirection(); err != nil {
log.Printf("AHRS Error: sensor orientation: couldn't read accelerometer: %s\n", err)
http.Error(w, fmt.Sprintf("couldn't read accelerometer: %s\n", err), http.StatusBadRequest)
return
}
log.Printf("AHRS Info: sensor orientation: received up direction %d\n", u)
if f == u || f == -u {
log.Printf("AHRS Error: sensor orientation: up (%d) and forward (%d) axes cannot be the same\n", u, f)
http.Error(w, fmt.Sprintf("up (%d) and forward (%d) axes cannot be the same", u, f),
http.StatusBadRequest)
return
}
globalSettings.IMUMapping = [2]int{f, u}
log.Printf("AHRS Info: sensor orientation success! forward axis is %d\n", f)
globalSettings.IMUMapping = [2]int{f, 0}
case 'd': // Set sensor "up" direction (toward top of airplane).
globalSettings.SensorQuaternion = [4]float64{0, 0, 0, 0}
saveSettings()
myIMUReader.Close()
globalStatus.IMUConnected = false // restart the processes depending on the orientation
CageAHRS()
log.Printf("AHRS Info: sensor orientation success! forward: %d; up: %d\n", f, u)
default: // Cancel the sensor calibration.
f = 0
log.Println("AHRS Info: sensor orientation: canceled")
ResetAHRSGLoad()
time.Sleep(2000 * time.Millisecond)
}
}
}
@ -486,6 +487,21 @@ func handleCageAHRS(w http.ResponseWriter, r *http.Request) {
}
}
func handleCalibrateAHRS(w http.ResponseWriter, r *http.Request) {
// define header in support of cross-domain AJAX
setNoCache(w)
w.Header().Set("Content-Type", "text/plain")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Method", "GET, POST, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
// For an OPTION method request, we return header without processing.
// This ensures we are recognized as supporting cross-domain AJAX REST calls.
if r.Method == "POST" {
CalibrateAHRS()
}
}
func handleResetGMeter(w http.ResponseWriter, r *http.Request) {
// define header in support of cross-domain AJAX
setNoCache(w)
@ -528,13 +544,63 @@ func delayReboot() {
}
func handleDownloadLogRequest(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "applicaiton/zip")
w.Header().Set("Content-Type", "application/zip")
w.Header().Set("Content-Disposition", "attachment; filename='stratux.log'")
http.ServeFile(w, r, "/var/log/stratux.log")
}
func handleDownloadAHRSLogsRequest(w http.ResponseWriter, r *http.Request) {
// Common error handler
httpErr := func(w http.ResponseWriter, e error) {
http.Error(w, fmt.Sprintf("error zipping AHRS logs: %s", e), http.StatusNotFound)
}
files, err := ioutil.ReadDir("/var/log")
if err != nil {
httpErr(w, err)
return
}
z := zip.NewWriter(w)
defer z.Close()
for _, f := range files {
fn := f.Name()
v1, _ := filepath.Match("sensors_*.csv", fn)
v2, _ := filepath.Match("stratux.log", fn)
if !(v1 || v2) {
continue
}
unzippedFile, err := os.Open("/var/log/" + fn)
if err != nil {
httpErr(w, err)
return
}
fh, err := zip.FileInfoHeader(f)
if err != nil {
httpErr(w, err)
return
}
zippedFile, err := z.CreateHeader(fh)
if err != nil {
httpErr(w, err)
return
}
_, err = io.Copy(zippedFile, unzippedFile)
if err != nil {
httpErr(w, err)
return
}
}
w.Header().Set("Content-Type", "application/zip")
w.Header().Set("Content-Disposition", "attachment; filename=\"ahrs_logs.zip\"")
}
func handleDownloadDBRequest(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "applicaiton/zip")
w.Header().Set("Content-Type", "application/zip")
w.Header().Set("Content-Disposition", "attachment; filename='stratux.sqlite'")
http.ServeFile(w, r, "/var/log/stratux.sqlite")
}
@ -747,13 +813,14 @@ func managementInterface() {
http.HandleFunc("/updateUpload", handleUpdatePostRequest)
http.HandleFunc("/roPartitionRebuild", handleroPartitionRebuild)
http.HandleFunc("/develmodetoggle", handleDevelModeToggle)
http.HandleFunc("/orientAHRS", handleOrientAHRS)
http.HandleFunc("/calibrateAHRS", handleCalibrateAHRS)
http.HandleFunc("/cageAHRS", handleCageAHRS)
http.HandleFunc("/resetGMeter", handleResetGMeter)
http.HandleFunc("/deletelogfile", handleDeleteLogFile)
http.HandleFunc("/downloadlog", handleDownloadLogRequest)
http.HandleFunc("/deleteahrslogfiles", handleDeleteAHRSLogFiles)
http.HandleFunc("/downloadahrslogs", handleDownloadAHRSLogsRequest)
http.HandleFunc("/downloaddb", handleDownloadDBRequest)
err := http.ListenAndServe(managementAddr, nil)

Wyświetl plik

@ -5,6 +5,7 @@ import (
"log"
"math"
"path/filepath"
"strings"
"time"
"../goflying/ahrs"
@ -16,16 +17,17 @@ import (
const (
numRetries uint8 = 5
calCLimit = 0.15
calDLimit = 10.0
)
var (
i2cbus embd.I2CBus
myPressureReader sensors.PressureReader
myIMUReader sensors.IMUReader
s ahrs.AHRSProvider
cal chan (bool)
cal chan (string)
analysisLogger *ahrs.AHRSLogger
ahrsCalibrating, needsCage bool // Does the sensor orientation matrix need to be recalculated?
ahrsCalibrating bool
logMap map[string]interface{}
)
@ -44,7 +46,6 @@ func pollSensors() {
// If it's not currently connected, try connecting to pressure sensor
if globalSettings.BMP_Sensor_Enabled && !globalStatus.BMPConnected {
log.Println("AHRS Info: attempting pressure sensor connection.")
globalStatus.BMPConnected = initPressureSensor() // I2C temperature and pressure altitude.
go tempAndPressureSender()
}
@ -125,39 +126,31 @@ func tempAndPressureSender() {
}
func initIMU() (ok bool) {
log.Println("AHRS Info: attempting to connect to MPU9250")
imu, err := sensors.NewMPU9250()
if err == nil {
myIMUReader = imu
time.Sleep(200 * time.Millisecond)
log.Println("AHRS Info: Successfully connected MPU9250")
return true
}
// TODO westphae: try to connect to MPU9150 or other IMUs.
log.Println("AHRS Error: couldn't initialize MPU9250")
log.Println("AHRS Error: couldn't initialize an IMU")
return false
}
func sensorAttitudeSender() {
var (
roll, pitch, heading float64
t time.Time
m *ahrs.Measurement
a, b, c, d, mm [3]float64 // IMU measurements: accel, gyro, accel bias, gyro bias, magnetometer
f [4]float64 // Sensor orientation quaternion
ff [3][3]float64 // Sensor orientation matrix
cc float64
roll, pitch, heading float64
mpuError, magError error
failNum uint8
)
log.Println("AHRS Info: initializing new Simple AHRS")
s = ahrs.InitializeSimple()
m = ahrs.NewMeasurement()
cal = make(chan (bool), 1)
needsCage = true
s := ahrs.NewSimpleAHRS()
m := ahrs.NewMeasurement()
cal = make(chan (string), 1)
// Set up loggers for analysis
ahrswebListener, err := ahrsweb.NewKalmanListener()
@ -171,74 +164,81 @@ func sensorAttitudeSender() {
// Need a sampling freq faster than 10Hz
timer := time.NewTicker(50 * time.Millisecond) // ~20Hz update.
for {
// Do an initial calibration
select { // Don't block if cal isn't receiving: only need one calibration in the queue at a time.
case cal <- true:
default:
// Set sensor gyro calibrations
if c, d := &globalSettings.C, &globalSettings.D; d[0]*d[0] + d[1]*d[1] + d[2]*d[2] > 0 {
s.SetCalibrations(c, d)
log.Printf("AHRS Info: IMU Calibrations read from settings: accel %6f %6f %6f; gyro %6f %6f %6f\n",
c[0], c[1], c[2], d[0], d[1], d[2])
} else {
// Do an initial calibration
select { // Don't block if cal isn't receiving: only need one calibration in the queue at a time.
case cal <- "cal":
default:
}
}
// Set sensor rotation matrix / quaternion
f[0] = globalSettings.SensorQuaternion[0]
f[1] = globalSettings.SensorQuaternion[1]
f[2] = globalSettings.SensorQuaternion[2]
f[3] = globalSettings.SensorQuaternion[3]
if f[0]*f[0]+f[1]*f[1]+f[2]*f[2]+f[3]*f[3] > 0.5 {
// Use the sensor rotation quaternion from config.
ff = *ahrs.QuaternionToRotationMatrix(f[0], f[1], f[2], f[3])
needsCage = false
// Set sensor quaternion
if f := &globalSettings.SensorQuaternion; f[0]*f[0] + f[1]*f[1] + f[2]*f[2] + f[3]*f[3] > 0 {
s.SetSensorQuaternion(f)
} else {
select { // Don't block if cal isn't receiving: only need one calibration in the queue at a time.
case cal <- "level":
default:
}
}
failNum = 0
<-timer.C
time.Sleep(950 * time.Millisecond)
for globalSettings.IMU_Sensor_Enabled && globalStatus.IMUConnected {
<-timer.C
// Process calibration and level requests
select {
case <-cal:
log.Println("AHRS Info: Calibrating IMU")
case action := <-cal:
log.Printf("AHRS Info: cal received action %s\n", action)
ahrsCalibrating = true
//TODO westphae: check for errors when reading IMU
myIMUReader.Read() // Clear out the averages
time.Sleep(1 * time.Second)
_, d[0], d[1], d[2], c[0], c[1], c[2], _, _, _, _, _ = myIMUReader.Read()
log.Printf("AHRS Info: IMU Calibrated: accel %6f %6f %6f; gyro %6f %6f %6f\n",
c[0], c[1], c[2], d[0], d[1], d[2])
var (
nTries uint8
cc, dd float64
)
for (math.Abs(cc-1) > calCLimit || dd > calDLimit) && nTries < numRetries {
time.Sleep(1 * time.Second)
_, d1, d2, d3, c1, c2, c3, _, _, _, mpuError, _ := myIMUReader.Read()
cc = math.Sqrt(c1*c1 + c2*c2 + c3*c3)
dd = math.Sqrt(d1*d1 + d2*d2 + d3*d3)
nTries++
log.Printf("AHRS Info: IMU calibration attempt #%d\n", nTries)
if mpuError != nil {
log.Printf("AHRS Info: Error reading IMU while calibrating: %s\n", mpuError)
} else {
if strings.Contains(action, "cal") { // Calibrate gyros
globalSettings.D = [3]float64{d1, d2, d3}
s.SetCalibrations(nil, &globalSettings.D)
log.Printf("AHRS Info: IMU gyro calibration: %3f %3f %3f\n", d1, d2, d3)
}
if strings.Contains(action, "level") { // Calibrate accel / level
globalSettings.C = [3]float64{c1, c2, c3}
s.SetCalibrations(&globalSettings.C, nil)
globalSettings.SensorQuaternion = *makeOrientationQuaternion(globalSettings.C)
s.SetSensorQuaternion(&globalSettings.SensorQuaternion)
s.Reset()
log.Printf("AHRS Info: IMU accel calibration: %3f %3f %3f\n", c1, c2, c3)
log.Printf("AHRS Info: Caged to quaternion %v\n", globalSettings.SensorQuaternion)
}
saveSettings()
}
}
ahrsCalibrating = false
cc = math.Sqrt(c[0]*c[0] + c[1]*c[1] + c[2]*c[2])
s.Reset()
<-timer.C // Make sure we get data for the actual algorithm
default:
}
if needsCage {
log.Println("AHRS Info: Caging")
ff = *makeSensorRotationMatrix([3]float64{c[0], c[1], c[2]})
f[0], f[1], f[2], f[3] = ahrs.RotationMatrixToQuaternion(ff)
globalSettings.SensorQuaternion[0] = f[0]
globalSettings.SensorQuaternion[1] = f[1]
globalSettings.SensorQuaternion[2] = f[2]
globalSettings.SensorQuaternion[3] = f[3]
saveSettings()
needsCage = false
}
// Make the IMU sensor measurements.
t = stratuxClock.Time
m.T = float64(t.UnixNano()/1000) / 1e6
_, b[0], b[1], b[2], a[0], a[1], a[2], mm[0], mm[1], mm[2], mpuError, magError = myIMUReader.Read()
a[0] /= cc
a[1] /= cc
a[2] /= cc
b[0] -= d[0]
b[1] -= d[1]
b[2] -= d[2]
m.A1 = -(ff[0][0]*a[0] + ff[0][1]*a[1] + ff[0][2]*a[2])
m.A2 = -(ff[1][0]*a[0] + ff[1][1]*a[1] + ff[1][2]*a[2])
m.A3 = -(ff[2][0]*a[0] + ff[2][1]*a[1] + ff[2][2]*a[2])
m.B1 = ff[0][0]*b[0] + ff[0][1]*b[1] + ff[0][2]*b[2]
m.B2 = ff[1][0]*b[0] + ff[1][1]*b[1] + ff[1][2]*b[2]
m.B3 = ff[2][0]*b[0] + ff[2][1]*b[1] + ff[2][2]*b[2]
m.M1 = ff[0][0]*mm[0] + ff[0][1]*mm[1] + ff[0][2]*mm[2]
m.M2 = ff[1][0]*mm[0] + ff[1][1]*mm[1] + ff[1][2]*mm[2]
m.M3 = ff[2][0]*mm[0] + ff[2][1]*mm[1] + ff[2][2]*mm[2]
_, m.B1, m.B2, m.B3, m.A1, m.A2, m.A3, m.M1, m.M2, m.M3, mpuError, magError = myIMUReader.Read()
m.SValid = mpuError == nil
m.MValid = magError == nil
if mpuError != nil {
@ -254,11 +254,13 @@ func sensorAttitudeSender() {
}
failNum = 0
if magError != nil {
log.Printf("AHRS Magnetometer Error, not using for this run: %s\n", magError)
if globalSettings.DEBUG {
log.Printf("AHRS Magnetometer Error, not using for this run: %s\n", magError)
}
m.MValid = false
// Don't necessarily disconnect here, unless AHRSProvider deeply depends on magnetometer
}
// Make the GPS measurements.
m.TW = float64(mySituation.GPSLastGroundTrackTime.UnixNano()/1000) / 1e6
m.WValid = isGPSGroundTrackValid()
if m.WValid {
@ -271,10 +273,10 @@ func sensorAttitudeSender() {
}
}
// Run the AHRS calcs
// Run the AHRS calculations.
s.Compute(m)
// If we have valid AHRS info, then update mySituation
// If we have valid AHRS info, then update mySituation.
mySituation.muAttitude.Lock()
if s.Valid() {
roll, pitch, heading = s.RollPitchHeading()
@ -296,7 +298,6 @@ func sensorAttitudeSender() {
mySituation.AHRSLastAttitudeTime = t
} else {
s.Reset()
mySituation.AHRSRoll = ahrs.Invalid
mySituation.AHRSPitch = ahrs.Invalid
mySituation.AHRSGyroHeading = ahrs.Invalid
@ -307,28 +308,27 @@ func sensorAttitudeSender() {
mySituation.AHRSGLoadMin = ahrs.Invalid
mySituation.AHRSGLoadMax = 0
mySituation.AHRSLastAttitudeTime = time.Time{}
s.Reset()
}
mySituation.muAttitude.Unlock()
makeAHRSGDL90Report() // Send whether or not valid - the function will invalidate the values as appropriate
// makeFFAHRSSimReport() // Simultaneous use of GDL90 and FFSIM not supported in FF 7.5.1 or later. Function definition will be kept for AHRS debugging and future workarounds.
// Send to AHRS debugging server:
// Send to AHRS debugging server.
if ahrswebListener != nil {
if err = ahrswebListener.Send(s.GetState(), m); err != nil {
log.Printf("Error writing to ahrsweb: %s\n", err)
log.Printf("AHRS Error: couldn't write to ahrsweb: %s\n", err)
ahrswebListener = nil
}
}
// Log it to csv for analysis
// Log it to csv for later analysis.
if globalSettings.AHRSLog && usage.Usage() < 0.95 {
if analysisLogger == nil {
analysisFilename := filepath.Join(logDirf, fmt.Sprintf("sensors_%s.csv",
time.Now().Format("20060102_150405")))
analysisFilename := fmt.Sprintf("sensors_%s.csv", time.Now().Format("20060102_150405"))
logMap = s.GetLogMap()
updateExtraLogging()
analysisLogger = ahrs.NewAHRSLogger(analysisFilename, logMap)
analysisLogger = ahrs.NewAHRSLogger(filepath.Join(logDirf, analysisFilename), logMap)
}
if analysisLogger != nil {
@ -353,10 +353,9 @@ func updateExtraLogging() {
logMap["BaroVerticalSpeed"] = float64(mySituation.BaroVerticalSpeed)
}
func makeSensorRotationMatrix(g [3]float64) (rotmat *[3][3]float64) {
func makeOrientationQuaternion(g [3]float64) (f *[4]float64) {
if globalSettings.IMUMapping[0] == 0 { // if unset, default to some standard orientation
globalSettings.IMUMapping[0] = -1 // +2 for RY836AI
globalSettings.IMUMapping[1] = -3 // +3 for RY836AI
}
// This is the "forward direction" chosen during the orientation process.
@ -370,8 +369,10 @@ func makeSensorRotationMatrix(g [3]float64) (rotmat *[3][3]float64) {
// Normalize the gravity vector to be 1 G.
z, _ := ahrs.MakeUnitVector(g)
rotmat, _ = ahrs.MakeHardSoftRotationMatrix(*z, *x, [3]float64{0, 0, 1}, [3]float64{1, 0, 0})
return rotmat
rotmat, _ := ahrs.MakeHardSoftRotationMatrix(*z, *x, [3]float64{0, 0, 1}, [3]float64{1, 0, 0})
f = new([4]float64)
f[0], f[1], f[2], f[3] = ahrs.RotationMatrixToQuaternion(*rotmat)
return
}
// This is used in the orientation process where the user specifies the forward and up directions.
@ -407,10 +408,14 @@ func getMinAccelDirection() (i int, err error) {
return
}
// CageAHRS sends a signal to the AHRSProvider that it should be reset.
// CageAHRS sends a signal to the AHRSProvider that it should recalibrate and reset its level orientation.
func CageAHRS() {
needsCage = true
cal <- true
cal <- "level"
}
// CageAHRS sends a signal to the AHRSProvider that it should recalibrate and reset its level orientation.
func CalibrateAHRS() {
cal <- "cal"
}
// ResetAHRSGLoad resets the min and max to the current G load value.

Wyświetl plik

@ -91,7 +91,7 @@ Stratux makes available a webserver to retrieve statistics which may be useful t
* `http://192.168.10.1/getStatus` - device status and statistics. Example output (commented JSON):
```json
```javascript
{
"Version": "v0.5b1", // Software version.
"Devices": 0, // Number of radios connected.

Wyświetl plik

@ -2,9 +2,6 @@
package sensors
import (
"log"
"time"
"../goflying/mpu9250"
)
@ -29,21 +26,16 @@ func NewMPU9250() (*MPU9250, error) {
err error
)
log.Println("AHRS Info: Making new MPU9250")
mpu, err = mpu9250.NewMPU9250(gyroRange, accelRange, updateFreq, true, false)
if err != nil {
return nil, err
}
// Set Gyro (Accel) LPFs to 20 (21) Hz to filter out prop/glareshield vibrations above 1200 (1260) RPM
log.Println("AHRS Info: Setting MPU9250 LPF")
mpu.SetGyroLPF(21)
mpu.SetAccelLPF(21)
time.Sleep(100 * time.Millisecond)
m.mpu = mpu
log.Println("AHRS Info: monitoring IMU")
return &m, nil
}

Wyświetl plik

@ -1,26 +1,29 @@
// application constants
var URL_HOST_BASE = window.location.hostname;
var URL_SETTINGS_GET = "http://" + URL_HOST_BASE + "/getSettings";
var URL_SETTINGS_SET = "http://" + URL_HOST_BASE + "/setSettings";
var URL_GPS_WS = "ws://" + URL_HOST_BASE + "/situation";
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";
var URL_DEVELOPER_GET = "ws://" + URL_HOST_BASE + "/developer";
var URL_UPDATE_UPLOAD = "http://" + URL_HOST_BASE + "/updateUpload";
var URL_REBOOT = "http://" + URL_HOST_BASE + "/reboot";
var URL_SHUTDOWN = "http://" + URL_HOST_BASE + "/shutdown";
var URL_RESTARTAPP = "http://" + URL_HOST_BASE + "/restart";
var URL_DEV_TOGGLE_GET = "http://" + URL_HOST_BASE + "/develmodetoggle";
var URL_AHRS_ORIENT = "http://" + URL_HOST_BASE + "/orientAHRS";
var URL_AHRS_CAGE = "http://" + URL_HOST_BASE + "/cageAHRS";
var URL_GMETER_RESET = "http://" + URL_HOST_BASE + "/resetGMeter";
var URL_DELETELOGFILE = "http://" + URL_HOST_BASE + "/deletelogfile";
var URL_DOWNLOADLOGFILE = "http://" + URL_HOST_BASE + "/downloadlog";
var URL_DOWNLOADDB = "http://" + URL_HOST_BASE + "/downloaddb";
var URL_HOST_BASE = window.location.hostname;
var URL_SETTINGS_GET = "http://" + URL_HOST_BASE + "/getSettings";
var URL_SETTINGS_SET = "http://" + URL_HOST_BASE + "/setSettings";
var URL_GPS_WS = "ws://" + URL_HOST_BASE + "/situation";
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";
var URL_DEVELOPER_GET = "ws://" + URL_HOST_BASE + "/developer";
var URL_UPDATE_UPLOAD = "http://" + URL_HOST_BASE + "/updateUpload";
var URL_REBOOT = "http://" + URL_HOST_BASE + "/reboot";
var URL_SHUTDOWN = "http://" + URL_HOST_BASE + "/shutdown";
var URL_RESTARTAPP = "http://" + URL_HOST_BASE + "/restart";
var URL_DEV_TOGGLE_GET = "http://" + URL_HOST_BASE + "/develmodetoggle";
var URL_AHRS_ORIENT = "http://" + URL_HOST_BASE + "/orientAHRS";
var URL_AHRS_CAL = "http://" + URL_HOST_BASE + "/calibrateAHRS";
var URL_AHRS_CAGE = "http://" + URL_HOST_BASE + "/cageAHRS";
var URL_GMETER_RESET = "http://" + URL_HOST_BASE + "/resetGMeter";
var URL_DELETELOGFILE = "http://" + URL_HOST_BASE + "/deletelogfile";
var URL_DOWNLOADLOGFILE = "http://" + URL_HOST_BASE + "/downloadlog";
var URL_DELETEAHRSLOGFILES = "http://" + URL_HOST_BASE + "/deleteahrslogfiles";
var URL_DOWNLOADAHRSLOGFILES = "http://" + URL_HOST_BASE + "/downloadahrslogs";
var URL_DOWNLOADDB = "http://" + URL_HOST_BASE + "/downloaddb";
// define the module with dependency on mobile-angular-ui
//var app = angular.module('stratux', ['ngRoute', 'mobile-angular-ui', 'mobile-angular-ui.gestures', 'appControllers']);
@ -91,7 +94,7 @@ app.controller('MainCtrl', function ($scope, $http) {
// any logic global logic
$http.get(URL_SETTINGS_GET)
.then(function(response) {
settings = angular.fromJson(response.data);
var settings = angular.fromJson(response.data);
$scope.DeveloperMode = settings.DeveloperMode;
}, function(response) {
//Second function handles error

Wyświetl plik

@ -1,59 +1,78 @@
<div class="col-sm-12">
<div class="col-sm-12">
<div class="panel-group col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">
Restart Stratux application
</div>
<div class="panel-body">
<div class="form-group reset-flow" align="center">
<div class="col-xs-12">
<a ng-click="postRestart()" class=
"btn btn-primary btn-block" style=
"margin-bottom:1em;">Restart Application</a>
<div class="panel panel-default">
<div class="panel-heading">
Restart Stratux application
</div>
<div class="panel-body">
<div class="form-group reset-flow" align="center">
<div class="col-xs-12">
<a ng-click="postRestart()" class="btn btn-primary btn-block"
style="margin-bottom:1em;">Restart Application</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="panel-group col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">
Logfile (Size: {{Logfile_Size}} bytes)
</div>
<div class="panel panel-default">
<div class="panel-heading">
Logfile (Size: {{Logfile_Size}})
</div>
<div class="panel-body">
<div class="col-xs-12">
<a href="./downloadlog" ng-click="postDownloadLog()"
class="btn btn-primary btn-block" style=
"margin-bottom:0.5em;">Download Logfile</a>
</div>
<div class="panel-body">
<div class="col-xs-12">
<a href="./downloadlog" ng-click="postDownloadLog()"
class="btn btn-primary btn-block"
style="margin-bottom:0.5em">Download Logfile</a>
</div>
<div class="col-xs-12">
<a ng-click="postDeleteLog()" class=
"btn btn-primary btn-block" style=
"margin-bottom:0.5em;">Delete Logfile</a>
</div>
<div class="col-xs-12">
<a ng-click="postDeleteLog()"
class="btn btn-primary btn-block"
style="margin-bottom:0.5em">Delete Logfile</a>
</div>
</div>
</div>
</div>
</div>
<div class="panel-group col-sm-6" style="float:right;">
<div class="panel panel-default">
<div class="panel-heading">
Database
</div>
<div class="panel-group col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">
AHRS Log Files (Size: {{AHRS_LogFiles_Size}})
</div>
<div class="panel-body">
<div class="col-xs-12">
<a href="./downloaddb" ng-click="postDownloadDB()"
class="btn btn-primary btn-block" style=
"margin-bottom:0.5em;">Download Database</a>
</div>
<div class="panel-body">
<div class="col-xs-12">
<a href="./downloadahrslogs" download="ahrs_logs.zip" ng-click="postDownloadAHRSLogs()"
class="btn btn-primary btn-block"
style="margin-bottom:0.5em">Download AHRS Logs</a>
</div>
<div class="col-xs-12">
<a ng-click="postDeleteAHRSLogs()"
class="btn btn-primary btn-block"
style="margin-bottom:0.5em">Delete AHRS Logs</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="panel-group col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">
Database
</div>
<div class="panel-body">
<div class="col-xs-12">
<a href="./downloaddb" ng-click="postDownloadDB()"
class="btn btn-primary btn-block"
style="margin-bottom:0.5em;">Download Database</a>
</div>
</div>
</div>
</div>
</div>

Wyświetl plik

@ -3,7 +3,7 @@ function AHRSRenderer(locationId) {
this.height = -1;
this.locationId = locationId;
this.canvas = document.getElementById(locationId);
this.canvas = document.getElementById(this.locationId);
this.resize();
// State variables
@ -13,7 +13,7 @@ function AHRSRenderer(locationId) {
this.slipSkid = 0;
this.altitude = 0;
var display = SVG(locationId).viewbox(-200, -200, 400, 400).group();
var display = SVG(this.locationId).viewbox(-200, -200, 400, 400).group();
this.ai = display.group().addClass('ai');
@ -26,26 +26,24 @@ function AHRSRenderer(locationId) {
this.ai = this.ai.clipWith(screenClip).group();
// card is the earth+sky+pitch marks, moves with both pitch and roll.
this.pitchScale = 0.5;
this.card = this.ai.group();
this.card.circle(2400).cx(0).cy(0).addClass('sky'); // Sky
this.card.line(-1200, 0, 1200, 0).addClass('marks'); // Horizon line
this.card.circle(2400).cx(0).cy(0).addClass('earth').clipWith(earthClip); // Earth
var pitchMarks = this.card.group().addClass('marks').clipWith(this.pitchClip);
for (i = -1050; i <= 1050; i+=25) {
switch (i%100) {
case 0:
pitchMarks.line(-40, i, 40, i);
if (i !== 0) {
pitchMarks.text(Math.abs(i) <= 900 ? Math.abs(i / 10).toString() : '80').x(-55).cy(i).addClass('markText');
pitchMarks.text(Math.abs(i) <= 900 ? Math.abs(i / 10).toString() : '80').x(+55).cy(i).addClass('markText');
}
break;
case 50:
pitchMarks.line(-20, i, 20, i);
break;
default:
pitchMarks.line(-10, i, 10, i);
var y;
for (var i = -1050; i <= 1050; i+=50) {
y = i * this.pitchScale;
if (i%100 === 0) {
pitchMarks.line(-30, y, 30, y);
if (i !== 0) {
pitchMarks.text(Math.abs(i) <= 900 ? Math.abs(i / 10).toString() : '80').x(-55).cy(y).addClass('markText');
pitchMarks.text(Math.abs(i) <= 900 ? Math.abs(i / 10).toString() : '80').x(+55).cy(y).addClass('markText');
}
} else {
pitchMarks.line(-15, y, 15, y);
}
}
@ -119,10 +117,10 @@ AHRSRenderer.prototype = {
this.slipSkid = +10;
}
this.pitchClip.translate(0, -10 * this.pitch);
this.pitchClip.translate(0, -10 * this.pitch * this.pitchScale);
this.rollClip.rotate(this.roll, 0, 0);
this.card.rotate(0, 0, 0).translate(0, 10 * this.pitch);
this.card.rotate(-this.roll, 0, -10 * this.pitch);
this.card.rotate(0, 0, 0).translate(0, 10 * this.pitch * this.pitchScale);
this.card.rotate(-this.roll, 0, -10 * this.pitch * this.pitchScale);
this.rollMarks.rotate(-this.roll, 0, 0);
this.headingMarks.translate(-2 * (this.heading % 360), 0);
this.skidBar.translate(-2 * this.slipSkid, 0);
@ -157,7 +155,7 @@ function GMeterRenderer(locationId, nlim, plim, resetCallback) {
this.height = -1;
this.locationId = locationId;
this.canvas = document.getElementById(locationId);
this.canvas = document.getElementById(this.locationId);
this.resize();
// State variables
@ -166,7 +164,7 @@ function GMeterRenderer(locationId, nlim, plim, resetCallback) {
this.max = 1;
// Draw the G Meter using the svg.js library
var gMeter = SVG(locationId).viewbox(-200, -200, 400, 400).group().addClass('gMeter');
var gMeter = SVG(this.locationId).viewbox(-200, -200, 400, 400).group().addClass('gMeter');
var el, card = gMeter.group().addClass('card');
card.circle(390).cx(0).cy(0);

Wyświetl plik

@ -10,7 +10,7 @@ function DeveloperCtrl($rootScope, $scope, $state, $http, $interval) {
return; // we are getting called once after clicking away from the status page
if (($scope.socket === undefined) || ($scope.socket === null)) {
socket = new WebSocket(URL_STATUS_WS);
var socket = new WebSocket(URL_STATUS_WS);
$scope.socket = socket; // store socket in scope for enter/exit usage
}
@ -36,9 +36,9 @@ function DeveloperCtrl($rootScope, $scope, $state, $http, $interval) {
};
socket.onmessage = function (msg) {
console.log('Received status update.')
console.log('Received status update.');
var status = JSON.parse(msg.data)
var status = JSON.parse(msg.data);
// Update Status
$scope.Version = status.Version;
$scope.Build = status.Build.substr(0, 10);
@ -62,7 +62,8 @@ function DeveloperCtrl($rootScope, $scope, $state, $http, $interval) {
$scope.UAT_PIREP_total = status.UAT_PIREP_total;
$scope.UAT_NOTAM_total = status.UAT_NOTAM_total;
$scope.UAT_OTHER_total = status.UAT_OTHER_total;
$scope.Logfile_Size = status.Logfile_Size;
$scope.Logfile_Size = humanFileSize(status.Logfile_Size);
$scope.AHRS_LogFiles_Size = humanFileSize(status.AHRS_LogFiles_Size);
// Errors array.
if (status.Errors.length > 0) {
$scope.visible_errors = true;
@ -70,7 +71,7 @@ function DeveloperCtrl($rootScope, $scope, $state, $http, $interval) {
}
var uptime = status.Uptime;
if (uptime != undefined) {
if (uptime !== undefined) {
var up_d = parseInt((uptime/1000) / 86400),
up_h = parseInt((uptime/1000 - 86400*up_d) / 3600),
up_m = parseInt((uptime/1000 - 86400*up_d - 3600*up_h) / 60),
@ -80,7 +81,7 @@ function DeveloperCtrl($rootScope, $scope, $state, $http, $interval) {
// $('#Uptime').text('unavailable');
}
var boardtemp = status.CPUTemp;
if (boardtemp != undefined) {
if (boardtemp !== undefined) {
/* boardtemp is celcius to tenths */
$scope.CPUTemp = String(boardtemp.toFixed(1) + 'C / ' + ((boardtemp * 9 / 5) + 32.0).toFixed(1) + 'F');
} else {
@ -125,6 +126,26 @@ function DeveloperCtrl($rootScope, $scope, $state, $http, $interval) {
});
};
$scope.postDeleteAHRSLogs = function () {
$http.post(URL_DELETEAHRSLOGFILES).
then(function (response) {
// do nothing
// $scope.$apply();
}, function (response) {
// do nothing
});
};
$scope.postDownloadAHRSLogs = function () {
$http.post(URL_DOWNLOADAHRSLOGFILES).
then(function (response) {
// do nothing
// $scope.$apply();
}, function (response) {
// do nothing
});
};
$scope.postDownloadDB = function () {
$http.post(URL_DOWNLOADDB).
then(function (response) {
@ -137,4 +158,13 @@ function DeveloperCtrl($rootScope, $scope, $state, $http, $interval) {
connect($scope); // connect - opens a socket and listens for messages
};
}
function humanFileSize(size) {
if (size === 0) {
return '0 B'
} else {
var i = Math.floor(Math.log(size) / Math.log(1024));
return ( size / Math.pow(1024, i) ).toFixed(2) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i];
}
}

Wyświetl plik

@ -1,4 +1,4 @@
angular.module('appControllers').controller('GPSCtrl', GPSCtrl); // get the main module contollers set
angular.module('appControllers').controller('GPSCtrl', GPSCtrl); // get the main module controllers set
GPSCtrl.$inject = ['$rootScope', '$scope', '$state', '$http', '$interval']; // Inject my dependencies
// create our controller function with all necessary logic

Wyświetl plik

@ -156,7 +156,7 @@ function SettingsCtrl($rootScope, $scope, $state, $location, $window, $http) {
newsettings = {
"GLimits": settings["GLimits"]
};
console.log(angular.toJson(newsettings));
// console.log(angular.toJson(newsettings));
setSettings(angular.toJson(newsettings));
}
};
@ -228,22 +228,29 @@ function SettingsCtrl($rootScope, $scope, $state, $location, $window, $http) {
};
$scope.setOrientation = function(action) {
console.log("sending " + action + " message.");
// console.log("sending " + action + " message.");
$http.post(URL_AHRS_ORIENT, action).
then(function (response) {
console.log("sent " + action + " message.");
// console.log("sent " + action + " message.");
}, function(response) {
// failure: cancel the calibration
console.log(response.data);
// console.log(response.data);
$scope.Orientation_Failure_Message = response.data;
switch (action) {
case "forward":
$scope.Ui.turnOff("modalCalibrateUp");
break;
case "up":
$scope.Ui.turnOff('modalCalibrateDone');
}
$scope.Ui.turnOff('modalCalibrateDone');
$scope.Ui.turnOn("modalCalibrateFailed");
});
};
}
$scope.calibrateGyros = function() {
console.log("sending calibrate message.");
$http.post(URL_AHRS_CAL).
then(function(response) {
console.log("Sent calibrate message.");
}, function(response) {
console.log(response.data);
$scope.Calibration_Failure_Message = response.data;
$scope.Ui.turnOff("modalCalibrateGyros");
$scope.Ui.turnOn("modalCalibrateGyrosFailed");
});
};
}

Wyświetl plik

@ -1,355 +1,348 @@
<div class="col-sm-12">
<div class="panel-group col-sm-6">
<div class="panel-group col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">Hardware</div>
<div class="panel-body">
<div class="form-group">
<label class="control-label col-xs-7">978 MHz</label>
<div class="col-xs-5">
<ui-switch ng-model='UAT_Enabled' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">1090 MHz</label>
<div class="col-xs-5">
<ui-switch ng-model='ES_Enabled' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">Ping ADS-B</label>
<div class="col-xs-5">
<ui-switch ng-model='Ping_Enabled' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">GPS</label>
<div class="col-xs-5">
<ui-switch ng-model='GPS_Enabled' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">Attitude Sensor</label>
<div class="col-xs-5">
<ui-switch ng-model='IMU_Sensor_Enabled' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">Altitude Sensor</label>
<div class="col-xs-5">
<ui-switch ng-model='BMP_Sensor_Enabled' settings-change></ui-switch>
</div>
</div>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">Hardware</div>
<div class="panel-body">
<div class="form-group">
<label class="control-label col-xs-7">978 MHz</label>
<div class="col-xs-5">
<ui-switch ng-model='UAT_Enabled' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">1090 MHz</label>
<div class="col-xs-5">
<ui-switch ng-model='ES_Enabled' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">Ping ADS-B</label>
<div class="col-xs-5">
<ui-switch ng-model='Ping_Enabled' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">GPS</label>
<div class="col-xs-5">
<ui-switch ng-model='GPS_Enabled' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">Attitude Sensor</label>
<div class="col-xs-5">
<ui-switch ng-model='IMU_Sensor_Enabled' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">Altitude Sensor</label>
<div class="col-xs-5">
<ui-switch ng-model='BMP_Sensor_Enabled' settings-change></ui-switch>
</div>
</div>
</div>
</div>
</div>
<div class="panel-group col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">Diagnostics</div>
<div class="panel-body">
<div class="form-group">
<label class="control-label col-xs-7">Show Traffic Source in Callsign</label>
<div class="col-xs-5">
<ui-switch ng-model='DisplayTrafficSource' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">Verbose Message Log</label>
<div class="col-xs-5">
<ui-switch ng-model='DEBUG' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">Record Replay Logs</label>
<div class="col-xs-5">
<ui-switch ng-model='ReplayLog' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">Record AHRS Logs</label>
<div class="col-xs-5">
<ui-switch ng-model='AHRSLog' settings-change></ui-switch>
</div>
</div>
</div>
</div>
</div>
<div class="panel-group col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">Diagnostics</div>
<div class="panel-body">
<div class="form-group">
<label class="control-label col-xs-7">Show Traffic Source in Callsign</label>
<div class="col-xs-5">
<ui-switch ng-model='DisplayTrafficSource' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">Verbose Message Log</label>
<div class="col-xs-5">
<ui-switch ng-model='DEBUG' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">Record Replay Logs</label>
<div class="col-xs-5">
<ui-switch ng-model='ReplayLog' settings-change></ui-switch>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-7">Record AHRS Logs</label>
<div class="col-xs-5">
<ui-switch ng-model='AHRSLog' settings-change></ui-switch>
</div>
</div>
</div>
</div>
</div>
<div class="panel-group col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">AHRS</div>
<div class="panel-body">
<div class="col-xs-12">
<span style="position:relative; overflow: hidden;">
<button class="btn btn-primary btn-block" ui-turn-on="modalCalibrateForward">
Set AHRS Sensor Orientation
</button>
</span>
<div class="form-group reset-flow">
<label class="control-label col-xs-5">G Limits</label>
<form name="GLimitForm" ng-submit="updateGLimits()" novalidate>
<input class="col-xs-7" type="string" required ng-model="GLimits" placeholder="Space-separated negative and positive G meter limits" ng-blur="updateGLimits()" />
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-12">
<div class="panel-group col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">Configuration</div>
<div class="panel-body">
<div class="form-group reset-flow">
<label class="control-label col-xs-5">Mode S Code (Hex)</label>
<form name="modeForm" ng-submit="updatemodes()" novalidate>
<!-- type="number" not supported except on mobile -->
<!-- RegEx for validation: ^[A-Fa-f0-9]{6}$ -->
<input class="col-xs-7" type="string" required ng-model="OwnshipModeS" placeholder="FAA HEX code" ng-blur="updatemodes()" />
</form>
</div>
<div class="form-group reset-flow">
<label class="control-label col-xs-5">Watch List</label>
<form name="watchForm" ng-submit="updatewatchlist()" novalidate>
<!-- type="number" not supported except on mobile -->
<input class="col-xs-7" type="string" required ng-model="WatchList" placeholder="space-delimited identifiers" ng-blur="updatewatchlist()" />
</form>
</div>
<div class="form-group reset-flow">
<label class="control-label col-xs-5">PPM Correction</label>
<form name="ppmForm" ng-submit="updateppm()" novalidate>
<!-- type="number" not supported except on mobile -->
<input class="col-xs-7" type="number_format" required ng-model="PPM" placeholder="integer" ng-blur="updateppm()" />
</form>
</div>
<div class="form-group reset-flow" ng-class="{ 'section_invisible': (!visible_serialout)}">
<label class="control-label col-xs-5">Serial Output Baudrate</label>
<form name="ppmForm" ng-submit="updateBaud()" novalidate>
<!-- type="number" not supported except on mobile -->
<input class="col-xs-7" type="number_format" required ng-model="Baud" placeholder="integer" ng-blur="updateBaud()" />
</form>
</div>
</div>
</div>
</div>
<div class="panel-group col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">Commands</div>
<div class="panel-body">
<!-- Upload. Temporary. -->
<div class="col-xs-12">
<span ng-show="update_files == ''">
<span style="position:relative; overflow: hidden;">
<span class="fake-btn fake-btn-block">Click to select System Update file</span>
<input style="opacity:0.0; position: absolute; top: 0; right: 0;" class="col-xs-12" type="file" name="update_file" onchange="angular.element(this).scope().setUploadFile(this.files)"/>
</span>
</span>
<span ng-hide="update_files == ''">
<button class="btn btn-block" onclick="angular.element(this).scope().uploadFile()">Install {{update_files[0].name}}</button>
</span>
</div>
<div class="form-group reset-flow">
<div class="col-xs-12">
<button class="btn btn-primary btn-block" ui-turn-on="modalReboot">Reboot</button>
</div>
</div>
<div class="form-group reset-flow">
<div class="col-xs-12">
<button class="btn btn-primary btn-block"ui-turn-on="modalShutdown">Shutdown</button>
</div>
</div>
</div>
</div>
</div>
<div class="panel-group col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">AHRS</div>
<div class="panel-body">
<div class="col-xs-12">
<span style="position:relative; overflow: hidden;">
<button class="btn btn-primary btn-block" ui-turn-on="modalCalibrateForward"
ng-disabled="!IMU_Sensor_Enabled">Set AHRS Sensor Orientation</button>
</span>
</div>
<div class="form-group reset-flow">
<div class="col-xs-12">
<button class="btn btn-primary btn-block" ui-turn-on="modalCalibrateGyros"
ng-disabled="!IMU_Sensor_Enabled">Calibrate Gyros</button>
</div>
</div>
<div class="form-group reset-flow">
<label class="control-label col-xs-5">G Limits</label>
<form name="GLimitForm" ng-submit="updateGLimits()" novalidate ng-disabled="!IMU_Sensor_Enabled">
<input class="col-xs-7" type="string" required ng-model="GLimits" ng-blur="updateGLimits()"
placeholder="Space-separated negative and positive G meter limits"/>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- Developer mode area -->
<div class="col-sm-12">
<div ng-show="DeveloperMode" class="panel-group col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">Developer Options</div>
<div class="panel-body">
<div class="col-xs-12">
<div class="form-group reset-flow">
<label class="control-label col-xs-5">Static IPs</label>
<form name="staticipForm" ng-submit="updatestaticips()" novalidate>
<input class="col-xs-7" type="string" required ng-model="StaticIps" ng-list=" " ng-trim="false" placeholder="space-delimited ip's to send network data" ng-blur="updatestaticips()" />
</form>
</div>
</div>
</div>
</div>
</div>
<div class="panel-group col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">Configuration</div>
<div class="panel-body">
<div class="form-group reset-flow">
<label class="control-label col-xs-5">Mode S Code (Hex)</label>
<form name="modeForm" ng-submit="updatemodes()" novalidate>
<!-- type="number" not supported except on mobile -->
<!-- RegEx for validation: ^[A-Fa-f0-9]{6}$ -->
<input class="col-xs-7" type="string" required ng-model="OwnshipModeS" placeholder="FAA HEX code" ng-blur="updatemodes()" />
</form>
</div>
<div class="form-group reset-flow">
<label class="control-label col-xs-5">Watch List</label>
<form name="watchForm" ng-submit="updatewatchlist()" novalidate>
<!-- type="number" not supported except on mobile -->
<input class="col-xs-7" type="string" required ng-model="WatchList" placeholder="space-delimited identifiers" ng-blur="updatewatchlist()" />
</form>
</div>
<div class="form-group reset-flow">
<label class="control-label col-xs-5">PPM Correction</label>
<form name="ppmForm" ng-submit="updateppm()" novalidate>
<!-- type="number" not supported except on mobile -->
<input class="col-xs-7" type="number_format" required ng-model="PPM" placeholder="integer" ng-blur="updateppm()" />
</form>
</div>
<div class="form-group reset-flow" ng-class="{ 'section_invisible': (!visible_serialout)}">
<label class="control-label col-xs-5">Serial Output Baudrate</label>
<form name="ppmForm" ng-submit="updateBaud()" novalidate>
<!-- type="number" not supported except on mobile -->
<input class="col-xs-7" type="number_format" required ng-model="Baud" placeholder="integer" ng-blur="updateBaud()" />
</form>
</div>
</div>
</div>
</div>
<div class="panel-group col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">Commands</div>
<div class="panel-body">
<!-- Upload. Temporary. -->
<div class="col-xs-12">
<span ng-show="update_files == ''">
<span style="position:relative; overflow: hidden;">
<span class="fake-btn fake-btn-block">Click to select System Update file</span>
<input style="opacity:0.0; position: absolute; top: 0; right: 0;" class="col-xs-12" type="file" name="update_file" onchange="angular.element(this).scope().setUploadFile(this.files)"/>
</span>
</span>
<span ng-hide="update_files == ''">
<button class="btn btn-block" onclick="angular.element(this).scope().uploadFile()">Install {{update_files[0].name}}</button>
</span>
</div>
<div class="form-group reset-flow">
<div class="col-xs-12">
<button class="btn btn-primary btn-block" ui-turn-on="modalReboot">Reboot</button>
</div>
</div>
<div class="form-group reset-flow">
<div class="col-xs-12">
<button class="btn btn-primary btn-block"ui-turn-on="modalShutdown">Shutdown</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Developer mode area -->
<div class="col-sm-12">
<div ng-show="DeveloperMode" class="panel-group col-sm-6">
<div class="panel panel-default">
<div class="panel-heading">Developer Options</div>
<div class="panel-body">
<div class="col-xs-12">
<div class="form-group reset-flow">
<label class="control-label col-xs-5">Static IPs</label>
<form name="staticipForm" ng-submit="updatestaticips()" novalidate>
<input class="col-xs-7" type="string" required ng-model="StaticIps" ng-list=" " ng-trim="false" placeholder="space-delimited ip's to send network data" ng-blur="updatestaticips()" />
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-12" ng-show="DeveloperMode" >
<div class="panel panel-default">
<div class="panel-heading">Raw Configuration</div>
<div class="panel-body">
<p>stratux.conf:</p>
<pre>{{rawSettings}}</pre>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">Raw Configuration</div>
<div class="panel-body">
<p>stratux.conf:</p>
<pre>{{rawSettings}}</pre>
</div>
</div>
</div>
<div class="col-sm-12">
<h3 ui-if="rebooting" ui-state="rebooting">Stratux is rebooting. You may need to reconnect WiFi once it reboots.</h3>
<h3 ui-if="shuttingdown" ui-state="shuttingdown">Stratux is shutting down. You may disconnect power.</h3>
<h3 ui-if="rebooting" ui-state="rebooting">Stratux is rebooting. You may need to reconnect WiFi once it reboots.</h3>
<h3 ui-if="shuttingdown" ui-state="shuttingdown">Stratux is shutting down. You may disconnect power.</h3>
</div>
<div ui-content-for="modals">
<div class="modal" ui-if="modalReboot" ui-state="modalReboot">
<div class="modal-overlay "></div>
<div class="vertical-alignment-helper center-block">
<div class="modal-dialog vertical-align-center">
<div class="modal" ui-if="modalReboot" ui-state="modalReboot">
<div class="modal-overlay "></div>
<div class="vertical-alignment-helper center-block">
<div class="modal-dialog vertical-align-center">
<div class="modal-content">
<div class="modal-header">
<button class="close"
ui-turn-off="modalReboot"></button>
<h4 class="modal-title">Are you really sure?</h4>
<div class="modal-content">
<div class="modal-header">
<button class="close"
ui-turn-off="modalReboot"></button>
<h4 class="modal-title">Are you really sure?</h4>
</div>
<div class="modal-body">
<p>Do you wish to reboot the Stratux?</p>
<p>Note that the system will reboot immediately and it will stop responding during the reboot</p>
</div>
<div class="modal-footer">
<a ui-turn-off="modalReboot" class="btn btn-default">Cancel</a>
<a ng-click="postReboot()" ui-turn-off="modalReboot" ui-turn-on="rebooting" class="btn btn-primary">Reboot</a>
</div>
</div>
</div>
</div>
<div class="modal-body">
<p>Do you wish to reboot the Stratux?</p>
<p>Note that the system will reboot immediately and it will stop responding during the reboot</p>
</div>
<div class="modal-footer">
<a ui-turn-off="modalReboot" class="btn btn-default">Cancel</a>
<a ng-click="postReboot()" ui-turn-off="modalReboot" ui-turn-on="rebooting" class="btn btn-primary">Reboot</a>
</div>
</div>
</div>
</div>
</div>
<div class="modal" ui-if="modalShutdown" ui-state="modalShutdown">
<div class="modal-overlay "></div>
<div class="vertical-alignment-helper center-block">
<div class="modal-dialog vertical-align-center">
<div class="modal" ui-if="modalShutdown" ui-state="modalShutdown">
<div class="modal-overlay "></div>
<div class="vertical-alignment-helper center-block">
<div class="modal-dialog vertical-align-center">
<div class="modal-content">
<div class="modal-header">
<button class="close"
ui-turn-off="modalShutdown"></button>
<h4 class="modal-title">Are you really sure?</h4>
<div class="modal-content">
<div class="modal-header">
<button class="close"
ui-turn-off="modalShutdown"></button>
<h4 class="modal-title">Are you really sure?</h4>
</div>
<div class="modal-body">
<p>Do you wish to shutdown the Stratux?</p>
<p>Note that the system will shutdown immediately. Please disconnect the power after the shutdown.</p>
</div>
<div class="modal-footer">
<a ui-turn-off="modalShutdown" class="btn btn-default">Cancel</a>
<a ng-click="postShutdown()" ui-turn-off="modalShutdown" ui-turn-on="shuttingdown" class="btn btn-primary">Shutdown</a>
</div>
</div>
</div>
</div>
<div class="modal-body">
<p>Do you wish to shutdown the Stratux?</p>
<p>Note that the system will shutdown immediately. Please disconnect the power after the shutdown.</p>
</div>
<div class="modal-footer">
<a ui-turn-off="modalShutdown" class="btn btn-default">Cancel</a>
<a ng-click="postShutdown()" ui-turn-off="modalShutdown" ui-turn-on="shuttingdown" class="btn btn-primary">Shutdown</a>
</div>
</div>
</div>
</div>
</div>
<div class="modal" ui-if="modalCalibrateForward" ui-state="modalCalibrateForward">
<div class="modal-overlay "></div>
<div class="vertical-alignment-helper center-block">
<div class="modal-dialog vertical-align-center">
<div class="modal-content">
<div class="modal-header">
<button class="close" ui-turn-off="modalCalibrateForward"></button>
<h4 class="modal-title">Set AHRS Sensor Orientation: Forward Direction</h4>
</div>
<div class="modal-body">
<p>Point the Stratux/sensor box so that the end that will be pointing toward the <strong>nose</strong> of the airplane is pointing toward the sky and press the <strong>Set Forward Direction</strong> button.</p>
</div>
<div class="modal-footer">
<a ui-turn-off="modalCalibrateForward" ui-turn-on="modalCalibrateCanceled" class="btn btn-default">Cancel</a>
<a ng-click="setOrientation('forward')" ui-turn-off="modalCalibrateForward" ui-turn-on="modalCalibrateUp" class="btn btn-default btn-primary">Set Forward Direction</a>
</div>
</div>
</div>
</div>
</div>
<div class="modal" ui-if="modalCalibrateUp" ui-state="modalCalibrateUp">
<div class="modal-overlay "></div>
<div class="vertical-alignment-helper center-block">
<div class="modal-dialog vertical-align-center">
<div class="modal-content">
<div class="modal-header">
<button class="close" ui-turn-off="modalCalibrateUp"></button>
<h4 class="modal-title">Set AHRS Sensor Orientation: Up Direction</h4>
</div>
<div class="modal-body">
<p>Place the Stratux/sensor box in the orientation it will be in during flight and press the <strong>Set Up Direction</strong> button.</p>
</div>
<div class="modal-footer">
<a ui-turn-off="modalCalibrateUp" ui-turn-on="modalCalibrateCanceled" class="btn btn-default">Cancel</a>
<a ng-click="setOrientation('up')" ui-turn-off="modalCalibrateUp" ui-turn-on="modalCalibrateDone" class="btn btn-default btn-primary">Set Up Direction</a>
</div>
</div>
</div>
</div>
</div>
<div class="modal" ui-if="modalCalibrateDone" ui-state="modalCalibrateDone">
<div class="modal-overlay "></div>
<div class="vertical-alignment-helper center-block">
<div class="modal-dialog vertical-align-center">
<div class="modal-content">
<div class="modal-header">
<button class="close" ui-turn-off="modalCalibrateDone"></button>
<h4 class="modal-title">Set AHRS Sensor Orientation: Finished</h4>
</div>
<div class="modal-body">
<p>The sensors are calibrated. These settings will be saved for future flights.</p>
</div>
<div class="modal-footer">
<a ui-turn-off="modalCalibrateDone" class="btn btn-default btn-primary">OK</a>
</div>
</div>
</div>
</div>
</div>
<div class="modal" ui-if="modalCalibrateFailed" ui-state="modalCalibrateFailed">
<div class="modal-overlay "></div>
<div class="vertical-alignment-helper center-block">
<div class="modal-dialog vertical-align-center">
<div class="modal-content">
<div class="modal-header">
<button class="close" ui-turn-off="modalCalibrateFailed"></button>
<h4 class="modal-title">Set AHRS Sensor Orientation: Failed!</h4>
</div>
<div class="modal-body">
<p>There was an error: {{Orientation_Failure_Message}}</p>
<p><div id="orientationFailureMessage"></div></p>
<p>The calibration failed.</p>
</div>
<div class="modal-footer">
<a ui-turn-off="modalCalibrateFailed" class="btn btn-default btn-primary">OK</a>
</div>
</div>
</div>
</div>
</div>
<div class="modal" ui-if="modalCalibrateCanceled" ui-state="modalCalibrateCanceled">
<div class="modal-overlay "></div>
<div class="vertical-alignment-helper center-block">
<div class="modal-dialog vertical-align-center">
<div class="modal-content">
<div class="modal-header">
<button class="close" ui-turn-off="modalCalibrateCanceled"></button>
<h4 class="modal-title">Set AHRS Sensor Orientation: Canceled</h4>
</div>
<div class="modal-body">
<p>Calibration canceled.</p>
</div>
<div class="modal-footer">
<a ui-turn-off="modalCalibrateCanceled" class="btn btn-default btn-primary">OK</a>
</div>
</div>
</div>
</div>
</div>
<div class="modal" ui-if="modalCalibrateForward" ui-state="modalCalibrateForward">
<div class="modal-overlay "></div>
<div class="vertical-alignment-helper center-block">
<div class="modal-dialog vertical-align-center">
<div class="modal-content">
<div class="modal-header">
<button class="close" ui-turn-off="modalCalibrateForward"></button>
<h4 class="modal-title">Set AHRS Sensor Orientation: Forward Direction</h4>
</div>
<div class="modal-body">
<p>Point the Stratux so that the end that will be pointing toward the
<strong>nose</strong> of the airplane is pointing toward the sky and press the
<strong>Set Forward Direction</strong> button.</p>
</div>
<div class="modal-footer">
<a ui-turn-off="modalCalibrateForward" class="btn btn-default">Cancel</a>
<a ng-click="setOrientation('forward')" ui-turn-off="modalCalibrateForward"
ui-turn-on="modalCalibrateDone" class="btn btn-default btn-primary">Set Forward Direction</a>
</div>
</div>
</div>
</div>
</div>
<div class="modal" ui-if="modalCalibrateDone" ui-state="modalCalibrateDone">
<div class="modal-overlay "></div>
<div class="vertical-alignment-helper center-block">
<div class="modal-dialog vertical-align-center">
<div class="modal-content">
<div class="modal-header">
<button class="close" ui-turn-off="modalCalibrateDone"></button>
<h4 class="modal-title">Set AHRS Sensor Orientation: Finished</h4>
</div>
<div class="modal-body">
<p>The sensor orientation is set. These settings will be saved for future flights.
Place the Stratux in its in-flight orientation and keep it stationary for 5 seconds
after you press the <strong>Done</strong> button.</p>
</div>
<div class="modal-footer">
<a ng-click="setOrientation('done')" ui-turn-off="modalCalibrateDone"
class="btn btn-default btn-primary">Done</a>
</div>
</div>
</div>
</div>
</div>
<div class="modal" ui-if="modalCalibrateFailed" ui-state="modalCalibrateFailed">
<div class="modal-overlay "></div>
<div class="vertical-alignment-helper center-block">
<div class="modal-dialog vertical-align-center">
<div class="modal-content">
<div class="modal-header">
<button class="close" ui-turn-off="modalCalibrateFailed"></button>
<h4 class="modal-title">Set AHRS Sensor Orientation: Failed!</h4>
</div>
<div class="modal-body">
<p>There was an error: {{Orientation_Failure_Message}}</p>
<p><div id="orientationFailureMessage"></div></p>
<p>The orientation failed. Please try again.</p>
</div>
<div class="modal-footer">
<a ui-turn-off="modalCalibrateFailed" class="btn btn-default btn-primary">OK</a>
</div>
</div>
</div>
</div>
</div>
<div class="modal" ui-if="modalCalibrateGyros" ui-state="modalCalibrateGyros">
<div class="modal-overlay "></div>
<div class="vertical-alignment-helper center-block">
<div class="modal-dialog vertical-align-center">
<div class="modal-content">
<div class="modal-header">
<button class="close" ui-turn-off="modalCalibrateGyros"></button>
<h4 class="modal-title">Calibrate MPU Gyros</h4>
</div>
<div class="modal-body">
<p>Press <b>Calibrate</b> and keep the Stratux as stationary as possible for the next second.
You should only do this in calm air as turbulence will throw off the calibrations.</p>
</div>
<div class="modal-footer">
<a ui-turn-off="modalCalibrateGyros" class="btn btn-default">Cancel</a>
<a ng-click="calibrateGyros()" ui-turn-off="modalCalibrateGyros"
class="btn btn-default btn-primary">Calibrate</a>
</div>
</div>
</div>
</div>
</div>
</div>