diff --git a/main/gen_gdl90.go b/main/gen_gdl90.go index d22aafb1..e9f39643 100644 --- a/main/gen_gdl90.go +++ b/main/gen_gdl90.go @@ -211,7 +211,7 @@ func makeOwnshipReport() bool { msg[12] = msg[12] | 0x0B // "Airborne" + "True Heading" } - msg[13] = 0xBB // NIC and NACp. + msg[13] = byte(0x80 | (mySituation.NACp & 0x0F)) //Set NIC = 8 and use NACp from ry835ai.go. gdSpeed := uint16(0) // 1kt resolution. if isGPSGroundTrackValid() { @@ -239,6 +239,15 @@ func makeOwnshipReport() bool { msg[18] = 0x01 // "Light (ICAO) < 15,500 lbs" + // Create callsign "Stratux". + msg[19] = 0x53 + msg[20] = 0x74 + msg[21] = 0x72 + msg[22] = 0x61 + msg[23] = 0x74 + msg[24] = 0x75 + msg[25] = 0x78 + sendGDL90(prepareMessage(msg), false) return true } @@ -264,6 +273,33 @@ func makeOwnshipGeometricAltitudeReport() bool { return true } +/* + + "Stratux" GDL90 message. + + Message ID 0xCC. + Byte1: p p p p p p GPS AHRS + First 6 bytes are protocol version codes. + Protocol 1: GPS on/off | AHRS on/off. +*/ + +func makeStratuxHeartbeat() []byte { + msg := make([]byte, 2) + msg[0] = 0xCC // Message type "Stratux". + msg[1] = 0 + if isGPSValid() { + msg[1] = 0x02 + } + if isAHRSValid() { + msg[1] = msg[1] | 0x01 + } + + protocolVers := int8(1) + msg[1] = msg[1] | byte(protocolVers << 2) + + return prepareMessage(msg) +} + func makeHeartbeat() []byte { msg := make([]byte, 7) // See p.10. @@ -312,6 +348,7 @@ func heartBeatSender() { select { case <-timer.C: sendGDL90(makeHeartbeat(), false) + sendGDL90(makeStratuxHeartbeat(), false) // sendGDL90(makeTrafficReport()) makeOwnshipReport() makeOwnshipGeometricAltitudeReport() @@ -738,7 +775,7 @@ func printStats() { log.Printf("stats [up since: %s]\n", humanize.Time(timeStarted)) log.Printf(" - CPUTemp=%.02f deg C, MemStats.Alloc=%s, MemStats.Sys=%s, totalNetworkMessagesSent=%s\n", globalStatus.CPUTemp, humanize.Bytes(uint64(memstats.Alloc)), humanize.Bytes(uint64(memstats.Sys)), humanize.Comma(int64(totalNetworkMessagesSent))) log.Printf(" - UAT/min %s/%s [maxSS=%.02f%%], ES/min %s/%s\n", humanize.Comma(int64(globalStatus.UAT_messages_last_minute)), humanize.Comma(int64(globalStatus.UAT_messages_max)), float64(maxSignalStrength)/10.0, humanize.Comma(int64(globalStatus.ES_messages_last_minute)), humanize.Comma(int64(globalStatus.ES_messages_max))) - log.Printf(" - Total traffic targets tracked=%s, last GPS fix: %s\n", humanize.Comma(int64(len(seenTraffic))), humanize.Time(mySituation.LastFixLocalTime)) + log.Printf(" - Total traffic targets tracked=%s, last GPS fix: %s, GPS solution type: %d, NACp: %d, est accuracy %.02f m\n", humanize.Comma(int64(len(seenTraffic))), humanize.Time(mySituation.LastFixLocalTime), mySituation.quality, mySituation.NACp, mySituation.Accuracy) } } diff --git a/main/ry835ai.go b/main/ry835ai.go index 334c1ca2..d58d2d68 100644 --- a/main/ry835ai.go +++ b/main/ry835ai.go @@ -31,6 +31,7 @@ type SituationData struct { quality uint8 Satellites uint16 Accuracy float32 // Meters. + NACp uint8 // NACp categories are defined in AC 20-165A Alt float32 // Feet. alt_accuracy float32 LastFixLocalTime time.Time @@ -266,7 +267,7 @@ func processNMEALine(l string) bool { if err1 != nil { return false } - mySituation.quality = uint8(q) + mySituation.quality = uint8(q) // 1 = 3D GPS; 2 = DGPS (SBAS /WAAS) // Satellites. sat, err1 := strconv.Atoi(x[7]) @@ -280,7 +281,28 @@ func processNMEALine(l string) bool { if err1 != nil { return false } - mySituation.Accuracy = float32(hdop * 5.0) //FIXME: 5 meters ~ 1.0 HDOP? + if mySituation.quality == 2 { + mySituation.Accuracy = float32(hdop * 4.0) //Estimate for WAAS / DGPS solution + } else { + mySituation.Accuracy = float32(hdop * 8.0) //Estimate for 3D non-WAAS solution + } + + // NACp estimate. + if mySituation.Accuracy < 3 { + mySituation.NACp = 11 + } else if mySituation.Accuracy < 10 { + mySituation.NACp = 10 + } else if mySituation.Accuracy < 30 { + mySituation.NACp = 9 + } else if mySituation.Accuracy < 92.6 { + mySituation.NACp = 8 + } else if mySituation.Accuracy < 185.2 { + mySituation.NACp = 7 + } else if mySituation.Accuracy < 555.6 { + mySituation.NACp = 6 + } else { + mySituation.NACp = 0 + } // Altitude. alt, err1 := strconv.ParseFloat(x[9], 32) diff --git a/web/plates/js/status.js b/web/plates/js/status.js index d1a8ea03..4f9504e5 100755 --- a/web/plates/js/status.js +++ b/web/plates/js/status.js @@ -1,108 +1,108 @@ -angular.module('appControllers').controller('StatusCtrl', StatusCtrl); // get the main module contollers set -StatusCtrl.$inject = ['$rootScope', '$scope', '$state', '$http']; // Inject my dependencies - -// create our controller function with all necessary logic -function StatusCtrl($rootScope, $scope, $state, $http) { - - $scope.$parent.helppage = 'plates/status-help.html'; - - function connect($scope) { - if (($scope === undefined) || ($scope === null)) - return; // we are getting called once after clicking away from the status page - - if (($scope.socket === undefined) || ($scope.socket === null)) { - socket = new WebSocket('ws://' + URL_HOST_BASE + '/status'); - $scope.socket = socket; // store socket in scope for enter/exit usage - } - - $scope.ConnectState = "Disconnected"; - - socket.onopen = function (msg) { - // $scope.ConnectStyle = "label-success"; - $scope.ConnectState = "Connected"; - }; - - socket.onclose = function (msg) { - // $scope.ConnectStyle = "label-danger"; - $scope.ConnectState = "Closed"; - $scope.$apply(); - setTimeout(connect, 1000); - }; - - socket.onerror = function (msg) { - // $scope.ConnectStyle = "label-danger"; - $scope.ConnectState = "Error"; - $scope.$apply(); - }; - - socket.onmessage = function (msg) { - console.log('Received status update.') - - var status = JSON.parse(msg.data) - // Update Status - $scope.Version = status.Version; - $scope.Devices = status.Devices; - $scope.Connected_Users = status.Connected_Users; - $scope.UAT_messages_last_minute = status.UAT_messages_last_minute; - // $scope.UAT_products_last_minute = JSON.stringify(status.UAT_products_last_minute); - $scope.UAT_messages_max = status.UAT_messages_max; - $scope.ES_messages_last_minute = status.ES_messages_last_minute; - $scope.ES_messages_max = status.ES_messages_max; - $scope.GPS_satellites_locked = status.GPS_satellites_locked; - $scope.RY835AI_connected = status.RY835AI_connected; - - var uptime = status.Uptime; - if (uptime != undefined) { - var up_s = parseInt((uptime / 1000) % 60), - up_m = parseInt((uptime / (1000 * 60)) % 60), - up_h = parseInt((uptime / (1000 * 60 * 60)) % 24); - $scope.Uptime = String(((up_h < 10) ? "0" + up_h : up_h) + "h" + ((up_m < 10) ? "0" + up_m : up_m) + "m" + ((up_s < 10) ? "0" + up_s : up_s) + "s"); - } else { - // $('#Uptime').text('unavailable'); - } - var boardtemp = status.CPUTemp; - if (boardtemp != undefined) { - /* boardtemp is celcius to tenths */ - $scope.CPUTemp = String(boardtemp.toFixed(1) + 'C / ' + ((boardtemp * 9 / 5) + 32.0).toFixed(1) + 'F'); - } else { - // $('#CPUTemp').text('unavailable'); - } - - $scope.$apply(); // trigger any needed refreshing of data - }; - } - - function setHardwareVisibility() { - $scope.visible_uat = true; - $scope.visible_es = true; - $scope.visible_gps = true; - $scope.visible_ahrs = true; - - // Simple GET request example (note: responce is asynchronous) - $http.get(URL_SETTINGS_GET). - then(function (response) { - settings = angular.fromJson(response.data); - $scope.visible_uat = settings.UAT_Enabled; - $scope.visible_es = settings.ES_Enabled; - $scope.visible_gps = settings.GPS_Enabled; - $scope.visible_ahrs = settings.AHRS_Enabled; - }, function (response) { - // nop - }); - }; - - $state.get('home').onEnter = function () { - // everything gets handled correctly by the controller - }; - $state.get('home').onExit = function () { - if (($scope.socket !== undefined) && ($scope.socket !== null)) { - $scope.socket.close(); - $scope.socket = null; - } - }; - - - // Status Controller tasks - setHardwareVisibility(); - connect($scope); // connect - opens a socket and listens for messages -}; \ No newline at end of file +angular.module('appControllers').controller('StatusCtrl', StatusCtrl); // get the main module contollers set +StatusCtrl.$inject = ['$rootScope', '$scope', '$state', '$http']; // Inject my dependencies + +// create our controller function with all necessary logic +function StatusCtrl($rootScope, $scope, $state, $http) { + + $scope.$parent.helppage = 'plates/status-help.html'; + + function connect($scope) { + if (($scope === undefined) || ($scope === null)) + return; // we are getting called once after clicking away from the status page + + if (($scope.socket === undefined) || ($scope.socket === null)) { + socket = new WebSocket('ws://' + URL_HOST_BASE + '/status'); + $scope.socket = socket; // store socket in scope for enter/exit usage + } + + $scope.ConnectState = "Disconnected"; + + socket.onopen = function (msg) { + // $scope.ConnectStyle = "label-success"; + $scope.ConnectState = "Connected"; + }; + + socket.onclose = function (msg) { + // $scope.ConnectStyle = "label-danger"; + $scope.ConnectState = "Closed"; + $scope.$apply(); + setTimeout(connect, 1000); + }; + + socket.onerror = function (msg) { + // $scope.ConnectStyle = "label-danger"; + $scope.ConnectState = "Error"; + $scope.$apply(); + }; + + socket.onmessage = function (msg) { + console.log('Received status update.') + + var status = JSON.parse(msg.data) + // Update Status + $scope.Version = status.Version; + $scope.Devices = status.Devices; + $scope.Connected_Users = status.Connected_Users; + $scope.UAT_messages_last_minute = status.UAT_messages_last_minute; + // $scope.UAT_products_last_minute = JSON.stringify(status.UAT_products_last_minute); + $scope.UAT_messages_max = status.UAT_messages_max; + $scope.ES_messages_last_minute = status.ES_messages_last_minute; + $scope.ES_messages_max = status.ES_messages_max; + $scope.GPS_satellites_locked = status.GPS_satellites_locked; + $scope.RY835AI_connected = status.RY835AI_connected; + + var uptime = status.Uptime; + if (uptime != undefined) { + var up_s = parseInt((uptime / 1000) % 60), + up_m = parseInt((uptime / (1000 * 60)) % 60), + up_h = parseInt((uptime / (1000 * 60 * 60)) % 24); + $scope.Uptime = String(((up_h < 10) ? "0" + up_h : up_h) + "h" + ((up_m < 10) ? "0" + up_m : up_m) + "m" + ((up_s < 10) ? "0" + up_s : up_s) + "s"); + } else { + // $('#Uptime').text('unavailable'); + } + var boardtemp = status.CPUTemp; + if (boardtemp != undefined) { + /* boardtemp is celcius to tenths */ + $scope.CPUTemp = String(boardtemp.toFixed(1) + 'C / ' + ((boardtemp * 9 / 5) + 32.0).toFixed(1) + 'F'); + } else { + // $('#CPUTemp').text('unavailable'); + } + + $scope.$apply(); // trigger any needed refreshing of data + }; + } + + function setHardwareVisibility() { + $scope.visible_uat = true; + $scope.visible_es = true; + $scope.visible_gps = true; + $scope.visible_ahrs = true; + + // Simple GET request example (note: responce is asynchronous) + $http.get(URL_SETTINGS_GET). + then(function (response) { + settings = angular.fromJson(response.data); + $scope.visible_uat = settings.UAT_Enabled; + $scope.visible_es = settings.ES_Enabled; + $scope.visible_gps = settings.GPS_Enabled; + $scope.visible_ahrs = settings.AHRS_Enabled; + }, function (response) { + // nop + }); + }; + + $state.get('home').onEnter = function () { + // everything gets handled correctly by the controller + }; + $state.get('home').onExit = function () { + if (($scope.socket !== undefined) && ($scope.socket !== null)) { + $scope.socket.close(); + $scope.socket = null; + } + }; + + + // Status Controller tasks + setHardwareVisibility(); + connect($scope); // connect - opens a socket and listens for messages +};