diff --git a/main/gps.go b/main/gps.go index 55397a70..4a1483c9 100644 --- a/main/gps.go +++ b/main/gps.go @@ -1932,7 +1932,6 @@ func makeAHRSGDL90Report() { palt := uint16(0xFFFF) vs := int16(0x7FFF) if isAHRSValid() { - // AHRS invalid magic number is ahrs.Invalid. if !isAHRSInvalidValue(mySituation.AHRSPitch) { pitch = roundToInt16(mySituation.AHRSPitch * 10) } @@ -2101,7 +2100,9 @@ func isGPSClockValid() bool { } func isAHRSValid() bool { - return stratuxClock.Since(mySituation.AHRSLastAttitudeTime) < 1*time.Second // If attitude information gets to be over 1 second old, declare invalid. + // If attitude information gets to be over 1 second old, declare invalid. + // If no GPS then we won't use or send attitude information. + return isGPSGroundTrackValid() && stratuxClock.Since(mySituation.AHRSLastAttitudeTime) < 1*time.Second } func isTempPressValid() bool { diff --git a/web/css/ahrs.css b/web/css/ahrs.css index c3c2a271..bc4f1b60 100644 --- a/web/css/ahrs.css +++ b/web/css/ahrs.css @@ -6,15 +6,16 @@ height: 100%; background-color: #ABFF00; border-radius: 10%; - box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #304701 0 -1px 9px, #89FF00 0 2px 12px; } .indicator.off { background-color: #F00; + box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #472001 0 -1px 9px, #FF4900 0 2px 12px; } .indicator.on { background-color: #ABFF00; + box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 7px 1px, inset #304701 0 -1px 9px, #89FF00 0 2px 12px; } .indicator.blink { @@ -151,10 +152,10 @@ stroke-width: 2px; } -.error .errText { +.msgText { font-size: 16px; font-family: sans-serif; - text-anchor: central; + text-anchor: middle; alignment-baseline: middle; stroke-width: 0px; fill: red; diff --git a/web/plates/gps.html b/web/plates/gps.html index 8dc94a49..fd09267e 100644 --- a/web/plates/gps.html +++ b/web/plates/gps.html @@ -1,35 +1,11 @@
-
-
-
GPS
-
-
- -
-
-
- -
-
-
-
-
-
-
- Location: - Track: -
-
- {{gps_lat}}, {{gps_lon}} ± {{gps_horizontal_accuracy}} m
- {{gps_alt}} ± {{gps_vertical_accuracy}} ft @ {{gps_vert_speed}} ft/min
- {{gps_track}}° @ {{gps_speed}} KTS -
-
-
-
-
AHRS
+
+ AHRS + {{ConnectState}} + {{ConnectState}} +
@@ -48,7 +24,10 @@
- Reset /
Level
+ +
@@ -80,6 +59,36 @@
+
+
+
+ GPS +
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+ Location: + Track: +
+
+ {{gps_lat}}, {{gps_lon}} ± {{gps_horizontal_accuracy}} m
+ {{gps_alt}} ± {{gps_vertical_accuracy}} ft @ {{gps_vert_speed}} ft/min
+ {{gps_track}}° @ {{gps_speed}} KTS +
+
+
+
diff --git a/web/plates/js/ahrs.js b/web/plates/js/ahrs.js index 7e73757a..77854041 100644 --- a/web/plates/js/ahrs.js +++ b/web/plates/js/ahrs.js @@ -1,9 +1,9 @@ function AHRSRenderer(locationId) { - this.width = -1; - this.height = -1; + this.width = -1; + this.height = -1; this.locationId = locationId; - this.canvas = document.getElementById(this.locationId); + this.canvas = document.getElementById(this.locationId); this.resize(); // State variables @@ -12,6 +12,7 @@ function AHRSRenderer(locationId) { this.heading = 0; this.slipSkid = 0; this.altitude = 0; + this.messages = []; var display = SVG(this.locationId).viewbox(-200, -200, 400, 400).group(); @@ -34,9 +35,9 @@ function AHRSRenderer(locationId) { var pitchMarks = this.card.group().addClass('marks').clipWith(this.pitchClip); var y; - for (var i = -1050; i <= 1050; i+=50) { + for (var i = -1050; i <= 1050; i += 50) { y = i * this.pitchScale; - if (i%100 === 0) { + 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'); @@ -48,7 +49,7 @@ function AHRSRenderer(locationId) { } this.rollMarks = this.ai.group().addClass('marks').clipWith(this.rollClip); - for (i=-180; i<180; i+=10) { + for (i = -180; i < 180; i += 10) { if (i === 0) { this.rollMarks.polygon('-10,-189 0,-175 10,-189').style('stroke-width', 0); } @@ -72,10 +73,10 @@ function AHRSRenderer(locationId) { pointer.line(0, 0, 0, 10); this.headingMarks = this.ai.group().addClass('marks'); - for (i=-200; i<=920; i+=20) { - if (i%60 === 0) { + for (i = -200; i <= 920; i += 20) { + if (i % 60 === 0) { this.headingMarks.line(i, 175, i, 178); - this.headingMarks.text(((i<0 ? (i/2+360) : i/2)%360).toString()).x(i).cy(185).addClass('markText'); + this.headingMarks.text(((i < 0 ? (i / 2 + 360) : i / 2) % 360).toString()).x(i).cy(185).addClass('markText'); this.headingMarks.line(i, 192, i, 195); } else { this.headingMarks.line(i, 175, i, 195).style('stroke-width', 1); @@ -86,13 +87,15 @@ function AHRSRenderer(locationId) { this.err.rect(400, 400).cx(0).cy(0); this.err.line(-200, -200, 200, +200); this.err.line(-200, +200, 200, -200); - this.errText = this.err.text("").cx(0).cy(0).addClass('errText'); - var tb = this.errText.bbox(); - this.errTextBg = this.err.rect(tb.x, tb.y, tb.w, tb.h).stroke({'width': 1}).after(this.errText); + + this.message = display.group().cy(-85); + this.msgText = this.message.text("").addClass('msgText').build(true); + var tb = this.msgText.bbox(); + this.msgTextBg = this.message.rect(tb.x, tb.y, tb.w, tb.h).stroke({'width': 1}).after(this.msgText); } AHRSRenderer.prototype = { - constructor: AHRSRenderer, + constructor: AHRSRenderer, resize: function () { var canvasWidth = this.canvas.parentElement.offsetWidth - 12; @@ -106,15 +109,32 @@ AHRSRenderer.prototype = { } }, - update: function (pitch, roll, heading, slipSkid) { + update: function (pitch, roll, heading, slipSkid) { this.pitch = pitch; this.roll = roll; this.heading = heading; - this.slipSkid = slipSkid; - if (this.slipSkid < -10) { - this.slipSkid = -10; + this.slipSkid = slipSkid; + if (this.slipSkid < -10) { + this.slipSkid = -10; } else if (this.slipSkid > 10) { - this.slipSkid = +10; + this.slipSkid = +10; + } + + if (this.messages.length > 0) { + this.message.hide(); + this.msgText.clear(); + var msgs = this.messages; + this.msgText.text(function (add) { + for (var i = 0; i < msgs.length; i++) { + add.tspan(msgs[i]).center(0, 20 * i).newLine(); + } + }); + var tb = this.msgText.bbox(); + this.msgTextBg.attr({x: tb.x - 3, y: tb.y - 2, width: tb.w + 6, height: tb.h + 4}); + this.message.show(); + } else { + this.message.hide(); + this.msgText.clear(); } this.pitchClip.translate(0, -10 * this.pitch * this.pitchScale); @@ -124,18 +144,16 @@ AHRSRenderer.prototype = { this.rollMarks.rotate(-this.roll, 0, 0); this.headingMarks.translate(-2 * (this.heading % 360), 0); this.skidBar.translate(-2 * this.slipSkid, 0); - }, - - turn_on: function() { - this.err.hide(); - this.ai.show(); - this.errText.clear(); }, - turn_off: function(message) { - this.errText.text(message).center(0, 0); - var tb = this.errText.bbox(); - this.errTextBg.attr({'x': tb.x, 'y': tb.y, 'width': tb.w, 'height': tb.h}); + turn_on: function () { + this.err.hide(); + this.ai.show(); + this.update(this.pitch, this.roll, this.heading, this.slipSkid); + }, + + turn_off: function () { + this.update(this.pitch, this.roll, this.heading, this.slipSkid); this.ai.hide(); this.err.show(); } @@ -149,7 +167,7 @@ function GMeterRenderer(locationId, nlim, plim, resetCallback) { this.nlim = nlim; this.plim = plim; } - this.nticks = Math.floor(this.plim+1) - Math.ceil(this.nlim-1) + 1; + this.nticks = Math.floor(this.plim + 1) - Math.ceil(this.nlim - 1) + 1; this.width = -1; this.height = -1; @@ -170,32 +188,32 @@ function GMeterRenderer(locationId, nlim, plim, resetCallback) { card.circle(390).cx(0).cy(0); card.line(-150, 0, -190, 0) .addClass('marks one'); - for (var i=Math.ceil(this.nlim-1); i<=Math.floor(this.plim+1); i++) { - if (i%2 === 0) { + for (var i = Math.ceil(this.nlim - 1); i <= Math.floor(this.plim + 1); i++) { + if (i % 2 === 0) { el = card.line(-150, 0, -190, 0).addClass('big'); card.text(i.toString()) .addClass('text') .cx(-105).cy(0) - .transform({ rotation: (i-1)/this.nticks*360, cx: 0, cy: 0, relative: true }) - .transform({ rotation: -(i-1)/this.nticks*360, relative: true }); + .transform({rotation: (i - 1) / this.nticks * 360, cx: 0, cy: 0, relative: true}) + .transform({rotation: -(i - 1) / this.nticks * 360, relative: true}); } else { el = card.line(-165, 0, -190, 0); } el.addClass('marks') - .rotate((i-1)/this.nticks*360, 0, 0); + .rotate((i - 1) / this.nticks * 360, 0, 0); } - card.line(-140, 0, -190, 0).addClass('marks limit').rotate((this.plim-1)/this.nticks*360, 0, 0); - card.line(-140, 0, -190, 0).addClass('marks limit').rotate((this.nlim-1)/this.nticks*360, 0, 0); + card.line(-140, 0, -190, 0).addClass('marks limit').rotate((this.plim - 1) / this.nticks * 360, 0, 0); + card.line(-140, 0, -190, 0).addClass('marks limit').rotate((this.nlim - 1) / this.nticks * 360, 0, 0); - var ax = -Math.cos(2*Math.PI/this.nticks), - ay = -Math.sin(2*Math.PI/this.nticks); - card.path('M -175 0, A 175 175 0 0 1 ' + 175*ax + ' ' + 175*ay) - .rotate(Math.floor(this.plim)/this.nticks*360, 0, 0) + var ax = -Math.cos(2 * Math.PI / this.nticks), + ay = -Math.sin(2 * Math.PI / this.nticks); + card.path('M -175 0, A 175 175 0 0 1 ' + 175 * ax + ' ' + 175 * ay) + .rotate(Math.floor(this.plim) / this.nticks * 360, 0, 0) .addClass('marks') .style('fill-opacity', '0'); - card.path('M -180 0, A 180 180 0 0 1 ' + 180*ax + ' ' + 180*ay) - .rotate(Math.floor(this.plim)/this.nticks*360, 0, 0) + card.path('M -180 0, A 180 180 0 0 1 ' + 180 * ax + ' ' + 180 * ay) + .rotate(Math.floor(this.plim) / this.nticks * 360, 0, 0) .addClass('marks') .style('fill-opacity', '0'); @@ -216,7 +234,7 @@ function GMeterRenderer(locationId, nlim, plim, resetCallback) { var reset = gMeter.group().cx(-165).cy(165).addClass('reset'); reset.circle(60).cx(0).cy(0).addClass('reset'); reset.text('RESET').cx(0).cy(0).addClass('text'); - reset.on('click', function() { + reset.on('click', function () { reset.animate(200).rotate(20, 0, 0); resetCallback(); reset.animate(200).rotate(0, 0, 0); @@ -243,8 +261,8 @@ GMeterRenderer.prototype = { this.min = gmin; this.max = gmax; - this.pointer_el.rotate((g-1)/this.nticks*360, 0, 0); - this.max_el.rotate((this.max-1)/this.nticks*360, 0, 0); - this.min_el.rotate((this.min-1)/this.nticks*360, 0, 0); + this.pointer_el.rotate((g - 1) / this.nticks * 360, 0, 0); + this.max_el.rotate((this.max - 1) / this.nticks * 360, 0, 0); + this.min_el.rotate((this.min - 1) / this.nticks * 360, 0, 0); } }; diff --git a/web/plates/js/gps.js b/web/plates/js/gps.js index f6a1ae4d..d7244f89 100644 --- a/web/plates/js/gps.js +++ b/web/plates/js/gps.js @@ -1,6 +1,11 @@ angular.module('appControllers').controller('GPSCtrl', GPSCtrl); // get the main module controllers set GPSCtrl.$inject = ['$rootScope', '$scope', '$state', '$http', '$interval']; // Inject my dependencies +const MSG_GROUND_TEST = ["GROUND TEST MODE - GPS REQUIRED", "DO NOT USE IN FLIGHT WITHOUT GPS"], + MSG_LEVELING = ["\n", "CALIBRATING", "FLY STRAIGHT AND DO NOT MOVE SENSOR"], + MSG_PSEUDO_AHRS = ["WARNING - USING GPS PSEUDO AHRS", "CONNECT AN AHRS BOARD TO USE TRUE AHRS"], + MSG_NO_AHRS = ["NO AHRS AVAILABLE", "MUST HAVE IMU AND/OR GPS FOR AHRS"]; + // create our controller function with all necessary logic function GPSCtrl($rootScope, $scope, $state, $http, $interval) { $scope.$parent.helppage = 'plates/gps-help.html'; @@ -116,14 +121,28 @@ function GPSCtrl($rootScope, $scope, $state, $http, $interval) { $scope.SolutionText = solutionText; $scope.gps_horizontal_accuracy = situation.GPSHorizontalAccuracy.toFixed(1); + var msg_ix = ahrs.messages.indexOf(MSG_GROUND_TEST[0]); + if (msg_ix < 0 && $scope.IMU_Sensor_Enabled && $scope.gps_horizontal_accuracy >= 30) { + ahrs.messages = ahrs.messages.concat(MSG_GROUND_TEST); + } else if (msg_ix >= 0 && $scope.gps_horizontal_accuracy < 30) { + ahrs.messages.splice(msg_ix, MSG_GROUND_TEST.length); + } + + var msg_ix = ahrs.messages.indexOf(MSG_NO_AHRS[0]); + if (msg_ix < 0 && !$scope.IMU_Sensor_Enabled && $scope.gps_horizontal_accuracy >= 30) { + ahrs.messages = ahrs.messages.concat(MSG_NO_AHRS); + ahrs.turn_off(); + } else if (msg_ix >= 0 && ($scope.IMU_Sensor_Enabled || $scope.gps_horizontal_accuracy < 30)) { + ahrs.messages.splice(msg_ix, MSG_NO_AHRS.length); + ahrs.turn_on(); + } + if ($scope.gps_horizontal_accuracy > 19999) { $scope.gps_horizontal_accuracy = "\u221e"; $scope.gps_lat = "--"; $scope.gps_lon = "--"; - $scope.gps_alt = "--"; $scope.gps_track = "--"; $scope.gps_speed = "--"; - $scope.gps_vert_speed = "--"; $scope.map_opacity = 0.2; $scope.map_mark_opacity = 0; } else { @@ -133,6 +152,8 @@ function GPSCtrl($rootScope, $scope, $state, $http, $interval) { $scope.gps_vertical_accuracy = (situation.GPSVerticalAccuracy*3.2808).toFixed(1); // accuracy is in meters, need to display in ft if ($scope.gps_vertical_accuracy > 9999) { $scope.gps_vertical_accuracy = "\u221e"; + $scope.gps_alt = "--"; + $scope.gps_vert_speed = "--"; } @@ -199,7 +220,7 @@ function GPSCtrl($rootScope, $scope, $state, $http, $interval) { $scope.ahrs_gload = situation.AHRSGLoad.toFixed(2); if ($scope.ahrs_gload > 360) { $scope.ahrs_gload = "--"; - } else { + } else if (gMeter !== undefined) { gMeter.update(situation.AHRSGLoad, situation.AHRSGLoadMin, situation.AHRSGLoadMax); } @@ -241,6 +262,17 @@ function GPSCtrl($rootScope, $scope, $state, $http, $interval) { statusBMP.classList.add("off"); statusBMP.classList.remove("on"); } + if (situation.AHRSStatus & 0x08) { + statusCal.classList.add("blink"); + statusCal.classList.remove("on"); + statusCal.innerText = "Caging"; + $scope.IsCaging = true; + } else { + statusCal.classList.remove("blink"); + statusCal.classList.add("on"); + statusCal.innerText = "Ready"; + $scope.IsCaging = false; + } if (situation.AHRSStatus & 0x10) { statusLog.classList.remove("off"); statusLog.classList.add("on"); @@ -248,16 +280,23 @@ function GPSCtrl($rootScope, $scope, $state, $http, $interval) { statusLog.classList.add("off"); statusLog.classList.remove("on"); } - if (situation.AHRSStatus & 0x08) { - statusCal.classList.add("blink"); - statusCal.classList.remove("on"); - statusCal.innerText = "Caging"; - } else { - statusCal.classList.remove("blink"); - statusCal.classList.add("on"); - statusCal.innerText = "Ready"; + + msg_ix = ahrs.messages.indexOf(MSG_LEVELING[0]); + if (msg_ix < 0 && $scope.IsCaging) { + ahrs.messages = ahrs.messages.concat(MSG_LEVELING); + ahrs.turn_off(); + } else if (msg_ix >= 0 && !$scope.IsCaging) { + ahrs.messages.splice(msg_ix, MSG_LEVELING.length); + ahrs.turn_on(); + } + + $scope.IsPseudoAHRS = (!$scope.IMU_Sensor_Enabled && $scope.gps_horizontal_accuracy < 30); + msg_ix = ahrs.messages.indexOf(MSG_PSEUDO_AHRS[0]); + if (msg_ix < 0 && $scope.IsPseudoAHRS) { + ahrs.messages = ahrs.messages.concat(MSG_PSEUDO_AHRS); + } else if (msg_ix >= 0 && !$scope.IsPseudoAHRS) { + ahrs.messages.splice(msg_ix, MSG_PSEUDO_AHRS.length); } - // "LastAttitudeTime":"2015-10-11T16:47:03.534615187Z" setGeoReferenceMap(situation.GPSLatitude, situation.GPSLongitude); } @@ -342,6 +381,7 @@ function GPSCtrl($rootScope, $scope, $state, $http, $interval) { // GPS/AHRS Controller tasks go here var ahrs = new AHRSRenderer("ahrs_display"); + ahrs.turn_on(); $scope.hideClick = function() { $scope.isHidden = !$scope.isHidden; @@ -360,23 +400,27 @@ function GPSCtrl($rootScope, $scope, $state, $http, $interval) { }; $scope.AHRSCage = function() { - if (!$scope.IsCaging()) { + if (!$scope.IsCaging) { $http.post(URL_AHRS_CAGE).then(function (response) { - // do nothing }, function (response) { - // do nothing + ahrs.messages = ahrs.messages.concat(response.data); + window.setTimeout(function() { + ahrs.messages.splice(ahrs.messages.indexOf(response.data), 1); + }, 1000); }); } }; - $scope.IsCaging = function() { - var caging = statusCal.innerText === "Caging"; - if (caging) { - ahrs.turn_off("Calibrating. Fly straight and do not move sensor."); - } else { - ahrs.turn_on(); + $scope.AHRSCalibrate = function() { + if (!$scope.IsCaging) { + $http.post(URL_AHRS_CAL).then(function (response) { + }, function (response) { + ahrs.messages = ahrs.messages.concat(response.data); + window.setTimeout(function() { + ahrs.messages.splice(ahrs.messages.indexOf(response.data), 1); + }, 1000); + }); } - return caging; }; $scope.GMeterReset = function() { @@ -392,6 +436,7 @@ function GPSCtrl($rootScope, $scope, $state, $http, $interval) { $http.get(URL_SETTINGS_GET). then(function (response) { settings = angular.fromJson(response.data); + $scope.IMU_Sensor_Enabled = settings.IMU_Sensor_Enabled; if (settings.GLimits === "" || settings.GLimits === undefined) { settings.GLimits = "-1.76 4.4"; } diff --git a/web/plates/js/settings.js b/web/plates/js/settings.js index d7713c22..2bffb268 100644 --- a/web/plates/js/settings.js +++ b/web/plates/js/settings.js @@ -257,18 +257,6 @@ function SettingsCtrl($rootScope, $scope, $state, $location, $window, $http) { }); }; - $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"); - }); - }; - $scope.updateWiFi = function(action) { $scope.WiFiErrors = { 'WiFiSSID': '', diff --git a/web/plates/js/towers.js b/web/plates/js/towers.js index b1fa07e6..81d9f4fd 100644 --- a/web/plates/js/towers.js +++ b/web/plates/js/towers.js @@ -49,8 +49,10 @@ function TowersCtrl($rootScope, $scope, $state, $http, $interval) { // Simple GET request example (note: responce is asynchronous) $http.get(URL_TOWERS_GET). then(function (response) { + $scope.ConnectState = "Connected"; loadTowers(response.data); }, function (response) { + $scope.ConnectState = "Disconnected"; $scope.raw_data = "error getting tower data"; }); }; @@ -68,4 +70,4 @@ function TowersCtrl($rootScope, $scope, $state, $http, $interval) { // stop any interval functions $interval.cancel(updateTowers); }; -}; \ No newline at end of file +} diff --git a/web/plates/settings.html b/web/plates/settings.html index 2316fcff..bd6b03bf 100644 --- a/web/plates/settings.html +++ b/web/plates/settings.html @@ -12,12 +12,6 @@ ng-disabled="!IMU_Sensor_Enabled">Set AHRS Sensor Orientation
-
-
- -
-
@@ -374,29 +368,6 @@
- -