Merge pull request #15 from TomBric/master

WebRadar Performance Updates
pull/827/merge^2
Adrian Batzill 2019-06-28 21:04:10 +02:00 zatwierdzone przez GitHub
commit 13c93c47cf
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
10 zmienionych plików z 321 dodań i 271 usunięć

Wyświetl plik

@ -99,6 +99,8 @@ type SituationData struct {
AHRSGLoadMax float64
AHRSLastAttitudeTime time.Time
AHRSStatus uint8
RadarLimits int
RadarRange int
}
/*

Wyświetl plik

@ -39,6 +39,7 @@ type SettingMessage struct {
// Weather updates channel.
var weatherUpdate *uibroadcaster
var trafficUpdate *uibroadcaster
var radarUpdate *uibroadcaster
var gdl90Update *uibroadcaster
func handleGDL90WS(conn *websocket.Conn) {
@ -97,6 +98,7 @@ func handleJsonIo(conn *websocket.Conn) {
}
// Subscribe the socket to receive updates.
trafficUpdate.AddSocket(conn)
radarUpdate.AddSocket(conn)
weatherRawUpdate.AddSocket(conn)
situationUpdate.AddSocket(conn)
@ -145,6 +147,35 @@ func handleTrafficWS(conn *websocket.Conn) {
}
}
func handleRadarWS(conn *websocket.Conn) {
log.Printf("RadarWS client connected.\n")
trafficMutex.Lock()
for _, traf := range traffic {
if !traf.Position_valid { // Don't send unless a valid position exists.
continue
}
trafficJSON, _ := json.Marshal(&traf)
conn.Write(trafficJSON)
}
// Subscribe the socket to receive updates.
radarUpdate.AddSocket(conn)
trafficMutex.Unlock()
// Connection closes when function returns. Since uibroadcast is writing and we don't need to read anything (for now), just keep it busy.
for {
buf := make([]byte, 1024)
_, err := conn.Read(buf)
if err != nil {
break
}
if buf[0] != 0 { // Dummy.
continue
}
time.Sleep(1 * time.Second)
}
}
func handleStatusWS(conn *websocket.Conn) {
// log.Printf("Web client connected.\n")
@ -313,6 +344,12 @@ func handleSettingsSetRequest(w http.ResponseWriter, r *http.Request) {
}
case "PPM":
globalSettings.PPM = int(val.(float64))
case "RadarLimits":
mySituation.RadarLimits = int(val.(float64))
log.Printf("handleSettingsSetRequest RadarLimit:%d\n", mySituation.RadarLimits)
case "RadarRange":
mySituation.RadarRange = int(val.(float64))
log.Printf("handleSettingsSetRequest RadarRange:%d\n", mySituation.RadarRange)
case "Baud":
if serialOut, ok := globalSettings.SerialOutputs["/dev/serialout0"]; ok { //FIXME: Only one device for now.
newBaud := int(val.(float64))
@ -795,6 +832,7 @@ func viewLogs(w http.ResponseWriter, r *http.Request) {
func managementInterface() {
weatherUpdate = NewUIBroadcaster()
trafficUpdate = NewUIBroadcaster()
radarUpdate = NewUIBroadcaster()
situationUpdate = NewUIBroadcaster()
weatherRawUpdate = NewUIBroadcaster()
gdl90Update = NewUIBroadcaster()
@ -833,6 +871,13 @@ func managementInterface() {
Handler: websocket.Handler(handleTrafficWS)}
s.ServeHTTP(w, req)
})
http.HandleFunc("/radar",
func(w http.ResponseWriter, req *http.Request) {
s := websocket.Server{
Handler: websocket.Handler(handleRadarWS)}
s.ServeHTTP(w, req)
})
http.HandleFunc("/jsonio",
func(w http.ResponseWriter, req *http.Request) {

Wyświetl plik

@ -608,11 +608,11 @@ func configDevices(count int, esEnabled, uatEnabled, flarmEnabled bool) {
// dongles are set to the same stratux id and the unconsumed,
// non-anonymous, dongle makes it to this loop.
for i, s := range unusedIDs {
if uatEnabled && !globalStatus.UATRadio_connected && UATDev == nil && !rES.hasID(s) && !rFLARM.hasID(s) {
if uatEnabled && !globalStatus.UATRadio_connected && UATDev == nil && !rES.hasID(s) {
createUATDev(i, s, false)
} else if esEnabled && ESDev == nil && !rUAT.hasID(s) && !rFLARM.hasID(s) {
} else if esEnabled && ESDev == nil && !rUAT.hasID(s) {
createESDev(i, s, false)
} else if flarmEnabled && FLARMDev == nil {
} else if flarmEnabled && FLARMDev == nil && !rFLARM.hasID(s) {
createFLARMDev(i, s, false)
}
}
@ -702,8 +702,8 @@ func sdrWatcher() {
atomic.StoreUint32(&globalStatus.Devices, uint32(interfaceCount))
// support up to two dongles
if count > 3 {
count = 3
if count > 2 {
count = 2
}
if count == prevCount && prevESEnabled == esEnabled && prevUATEnabled == uatEnabled && prevFLARMEnabled == flarmEnabled {

Wyświetl plik

@ -223,6 +223,20 @@ func sendTrafficUpdates() {
//log.Printf("Traffic age of %X is %f seconds\n",icao,ti.Age)
if ti.Age > 2 { // if nothing polls an inactive ti, it won't push to the webUI, and its Age won't update.
trafficUpdate.SendJSON(ti)
var currAlt float32
currAlt = mySituation.BaroPressureAltitude
if currAlt == 99999 { // no valid BaroAlt, take GPS instead, better than nothing
currAlt = mySituation.GPSAltitudeMSL
}
if float32(ti.Alt) <= currAlt+float32(mySituation.RadarLimits)*1.3 { //take 30% more to see moving outs
// altitude lower than upper boundary
if float32(ti.Alt) >= currAlt-float32(mySituation.RadarLimits)*1.3 {
// altitude higher than upper boundary
if !ti.BearingDist_valid || ti.Distance<float64(mySituation.RadarRange)*1852.0*1.3 { //allow more so that aircraft moves out
radarUpdate.SendJSON(ti)
}
}
}
}
if ti.Position_valid && ti.Age < 6 { // ... but don't pass stale data to the EFB.
//TODO: Coast old traffic? Need to determine how FF, WingX, etc deal with stale targets.
@ -263,6 +277,21 @@ func registerTrafficUpdate(ti TrafficInfo) {
}
*/ // Send all traffic to the websocket and let JS sort it out. This will provide user indication of why they see 1000 ES messages and no traffic.
trafficUpdate.SendJSON(ti)
var currAlt float32
currAlt = mySituation.BaroPressureAltitude
if currAlt == 99999 { // no valid BaroAlt, take GPS instead, better than nothing
currAlt = mySituation.GPSAltitudeMSL
}
if float32(ti.Alt) <= currAlt+float32(mySituation.RadarLimits)*1.3 { //take 30% more to see moving outs
// altitude lower than upper boundary
if float32(ti.Alt) >= currAlt-float32(mySituation.RadarLimits)*1.3 {
// altitude higher than upper boundary
if !ti.BearingDist_valid || ti.Distance<float64(mySituation.RadarRange)*1852.0*1.3 { //allow more if aircraft moves out
radarUpdate.SendJSON(ti)
}
}
}
}
func isTrafficAlertable(ti TrafficInfo) bool {

Wyświetl plik

@ -1,9 +1,3 @@
*:-webkit-full-screen {
width: 100%;
height: 100%;
}
//for iOS,Safari
.radar .backRect {
fill: black;
}
@ -164,3 +158,23 @@
fill: #121512;
}
.radar .tSmall {
line-height: 0.8;
font-size: 0.9em;
font-family: sans-serif;
text-anchor: middle;
font-weight: bold;
stroke-width: 0;
fill: white;
}
.radar .tSmallInvert {
line-height: 0.8;
font-size: 0.9em;
font-family: sans-serif;
text-anchor: middle;
font-weight: bold;
stroke-width: 0;
fill: #121512;
}

Wyświetl plik

@ -1,166 +0,0 @@
*:-webkit-full-screen {
width: 100%;
height: 100%;
}
//for iOS,Safari
.radar .backRect {
fill: black;
}
.radar .card {
fill: #ffffff ;
stroke: #000000;
}
.radar .textDir {
font-size: 1em;
font-family: sans-serif;
font-weight: normal;
text-anchor: left;
fill: #000000;
}
.radar .textOutside {
font-size: 1.3em;
font-family: sans-serif;
font-weight: normal;
text-anchor: left;
fill: white;
stroke: none;
}
.radar .textOutsideRight {
font-size: 1.3em;
font-family: sans-serif;
font-weight: normal;
text-anchor: end;
fill: white;
stroke: none;
}
.radar .textCirc {
font-size: 1.3em;
font-family: sans-serif;
text-anchor: center;
fill: #7B1AB9;
}
.radar .textCircReg {
font-size: 1.0em;
font-family: sans-serif;
text-anchor: left;
fill: #7B1AB9;
}
.radar .textRegOut {
font-size: 1.0em;
font-family: sans-serif;
text-anchor: left;
fill: white;
stroke: white;
stroke-width: 5px;
}
.radar .textCOut {
font-size: 1.3em;
font-family: sans-serif;
text-anchor: left;
fill: white;
stroke: white;
stroke-width: 5px;
}
.radar .greenCirc {
fill: none;
stroke: #7B1AB9;
stroke-width: 3px;
}
.radar .trace {
fill: none;
stroke: black;
stroke-width: 2px;
stroke-dasharray: 2px;
}
.radar .textPlane {
font-size: 1.3em;
font-family: sans-serif;
text-anchor: start;
fill: black;
stroke-width: 0px;
}
.radar .textPlaneSmall {
font-size: 0.8em;
font-family: sans-serif;
text-anchor: start;
fill: black;
stroke-width: 0px;
}
.radar .textPlaneReg {
font-size: 0.8em;
font-family: sans-serif;
text-anchor: start;
fill: black;
stroke-width: 0px;
}
.radar .textPlaneVerySmall {
font-size: 0.5em;
font-family: sans-serif;
text-anchor: start;
fill: black;
}
.radar .textSmall {
font-size: 1em;
font-family: sans-serif;
text-anchor: left;
fill: #1122EE;
}
.radar .plane {
fill: black;
stroke: none;
}
.radar .centerplane {
fill: #1122EE;
stroke: none;
}
.radar .planeRotationPoint {
fill: white;
stroke-width: 1px;
stroke: white;
}
.radar .zoom {
fill: #121512;
stroke: white;
stroke-width: 2;
}
.radar .zoomInvert {
fill: white;
stroke: #121512;
stroke-width: 2;
}
.radar .textZoom {
font-size: 1.1em;
font-family: sans-serif;
text-anchor: middle;
font-weight: bold;
stroke-width: 0;
fill: white;
}
.radar .textZoomInvert {
font-size: 1.1em;
font-family: sans-serif;
text-anchor: middle;
font-weight: bold;
stroke-width: 0;
fill: #121512;
}

Wyświetl plik

@ -3,9 +3,17 @@ RadarCtrl.$inject = ['$rootScope', '$scope', '$state', '$http', '$interval']; //
var Lat;
var Long;
var GPSCourse = 0;
var OldGPSCourse = 0; //old value
var GPSTime; // general time variable for cutoff
var BaroAltitude; // Barometric Altitude if availabe, else set to GPS Altitude, invalid is -100.000
var OldBaroAltitude = 0; // Old Value
var DisplayRadius = 10; // Radius in NM, below this radius targets are displayed
var OldDisplayRadius = 0;
var MaxAlarms = 5; // number of times an alarm sound is played, if airplane enters AlarmRadius
var MaxSpeechAlarms = 1; // number of times the aircraft is announced, MaxSpeedAlarms needs to be less than MaxAlarms
@ -14,15 +22,11 @@ var minimalCircle = 25; //minimal circle in pixel around center ist distance
var radar; // global RadarRenderer
var posangle = Math.PI; //global var for angle position of text
var zoom = [2,5,10,20,40]; // different zooms in nm
var zoomfactor = 2; // start with 10 nm
var speechOn = false; // speech output
var soundType = 0; // speech and sound output, 0=beep+speech (default) 1=Beep 2=Speech 3=Snd Off
var synth; // global speechSynthesis variable
var altDiff = [5,10,20,50,100,500]; // Threshold to display other planes within altitude difference in 100 ft
var altindex = 2; // start with 2000 ft
var AltDiffThreshold; // in 100 feet display value
var storageDiff = 20; // altitude difference in 100 ft below airplane is stored in list (otherwise do not even consider, performance optimization
var OldAltDiffThreshold = 0; // in 100 feet display value
var situation = {};
@ -62,9 +66,12 @@ function RadarCtrl($rootScope, $scope, $state, $http, $interval) {
situation = angular.fromJson(data);
// consider using angular.extend()
$scope.raw_data = angular.toJson(data, true); // makes it pretty
GPSTime = Date.parse(situation.GPSTime); //set global time variable
Lat = situation.GPSLatitude;
Long = situation.GPSLongitude;
GPSCourse = situation.GPSTrueCourse;
AltDiffThreshold = situation.RadarLimits/100;
DisplayRadius = situation.RadarRange;
var press_time = Date.parse(situation.BaroLastMeasurementTime);
var gps_time = Date.parse(situation.GPSLastGPSTimeStratuxTime);
if (gps_time - press_time < 1000) { //pressure is ok
@ -90,13 +97,13 @@ function RadarCtrl($rootScope, $scope, $state, $http, $interval) {
}
function speaktraffic(altitudeDiff, direction) {
if ( speechOn ) {
if ( (soundType == 0) || (soundType == 2)) {
var feet = altitudeDiff * 100;
var sign = "plus";
if (altitudeDiff <= 0 ) sign = "minus";
if (altitudeDiff < 0 ) sign = "minus";
var txt = "Traffic ";
if ( direction) txt += direction +" o'clock ";
txt += sign + " " + feet + " feet";
txt += sign + " " + Math.abs(feet) + " feet";
var utterOn = new SpeechSynthesisUtterance(txt);
utterOn.lang="en-US";
utterOn.rate=1.1;
@ -130,8 +137,8 @@ function RadarCtrl($rootScope, $scope, $state, $http, $interval) {
doUpdate=1;
if ( distcirc<=(DisplayRadius/2) ) {
if (!traffic.alarms) traffic.alarms = 0;
if ( speechOn && (traffic.alarms <MaxSpeechAlarms) && (altDiffValid == 1)) speaktraffic(altDiff, null);
if (traffic.alarms <MaxAlarms ) sound_alert.play(); // play alarmtone max times
if ((traffic.alarms <MaxSpeechAlarms) && (altDiffValid == 1)) speaktraffic(altDiff, null);
if ((traffic.alarms <MaxAlarms) && ((soundType==0)||(soundType==1))) sound_alert.play(); // play alarmtone max times
traffic.alarms = traffic.alarms + 1;
} else {
if ( distcirc >= (DisplayRadius*0.75) ) { // implement hysteresis, play tone again only if 3/4 of DisplayRadius outside
@ -210,7 +217,7 @@ function RadarCtrl($rootScope, $scope, $state, $http, $interval) {
doUpdate = 1;
if ( distradius <=(DisplayRadius/2) ) {
if (!traffic.alarms) traffic.alarms = 0;
if ( speechOn && (traffic.alarms <MaxSpeechAlarms) ) {
if (((soundType==0) || (soundType==2)) && (traffic.alarms <MaxSpeechAlarms) ) {
var alpha = 0;
if ( disty >=0 ) {
alpha = Math.PI - Math.atan(distx/disty);
@ -222,10 +229,10 @@ function RadarCtrl($rootScope, $scope, $state, $http, $interval) {
if ( alpha<0) alpha +=360;
var oclock = Math.round(alpha/30);
if (oclock <= 0 ) oclock += 12;
console.log("Distx %d Disty %d GPSCourse %f alpha-Course %f oclock %f\n", distx, disty, GPSCourse, alpha, oclock);
//console.log("Distx %d Disty %d GPSCourse %f alpha-Course %f oclock %f\n", distx, disty, GPSCourse, alpha, oclock);
speaktraffic(altDiff, oclock);
}
if (traffic.alarms <=MaxAlarms ) sound_alert.play(); // play alarmtone max 5 times
if ((traffic.alarms <MaxAlarms) && ((soundType==0)||(soundType==1))) sound_alert.play(); // play alarmtone max times
traffic.alarms = traffic.alarms + 1;
} else {
traffic.alarms = 0; // reset counter ones outside alarm circle
@ -286,14 +293,9 @@ function RadarCtrl($rootScope, $scope, $state, $http, $interval) {
new_traffic.targettype = obj.TargetType;
var timestamp = Date.parse(obj.Timestamp);
var timeLack = -1;
if (new_traffic.timeVal >0 ) {
if (new_traffic.timeVal >0 && timestamp) {
timeLack = timestamp - new_traffic.timeVal;
}
new_traffic.timeVal = timestamp;
new_traffic.time = utcTimeString(timestamp);
new_traffic.signal = obj.SignalLevel;
new_traffic.ema = expMovingAverage(new_traffic.ema, new_traffic.signal, timeLack);
new_traffic.lat = obj.Lat;
new_traffic.lon = obj.Lng;
var n = Math.round(obj.Alt / 25) * 25;
@ -309,8 +311,8 @@ function RadarCtrl($rootScope, $scope, $state, $http, $interval) {
new_traffic.vspeed = Math.round(obj.Vvel / 100) * 100
new_traffic.age = obj.Age;
new_traffic.ageLastAlt = obj.AgeLastAlt;
new_traffic.Last_seen = Date.parse(obj.Last_seen);
new_traffic.Last_alt = Date.parse(obj.Last_alt);
new_traffic.dist = (obj.Distance/1852);
new_traffic.tail = obj.Tail; //registration No
}
@ -362,16 +364,16 @@ function RadarCtrl($rootScope, $scope, $state, $http, $interval) {
}
if ((invalidIdx < 0) && (!message.Position_valid)) { // new aircraft without position
if ( altDiffValid && (Math.abs(altDiff) <= (AltDiffThreshold + storageDiff)) ) {
setAircraft(message, new_traffic); //store in any case, since EMA needs history of dB
if ( altDiffValid && (Math.abs(altDiff) <= AltDiffThreshold ) ) {
setAircraft(message, new_traffic);
checkCollisionVector(new_traffic);
$scope.data_list_invalid.unshift(new_traffic); // add to start of invalid array.
} // else not added in list, since not relevant
}
// Handle the negative cases of those above - where an aircraft moves from "valid" to "invalid" or vice-versa.
if ((validIdx >= 0) && (!message.Position_valid)) { //known valid aircraft now with invalid position
// Position is not valid any more. Remove from "valid" table.
if ((validIdx >= 0) && !message.Position_valid ) {
// Position is not valid any more or outside Threshold. Remove from "valid" table.
if ( $scope.data_list[validIdx].planeimg ) {
$scope.data_list[validIdx].planeimg.remove().forget(); // remove plane image
$scope.data_list[validIdx].planetext.remove().forget(); // remove plane image
@ -403,7 +405,7 @@ function RadarCtrl($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_TRAFFIC_WS);
socket = new WebSocket(URL_RADAR_WS);
$scope.socket = socket; // store socket in scope for enter/exit usage
sit_socket = new WebSocket(URL_GPS_WS); // socket for situation
$scope.sit_socket = sit_socket;
@ -465,6 +467,7 @@ function RadarCtrl($rootScope, $scope, $state, $http, $interval) {
var tempUptimeClock = new Date(Date.parse(globalStatus.UptimeClock));
var uptimeClockString = tempUptimeClock.toUTCString();
$scope.UptimeClock = uptimeClockString;
$scope.StratuxClock = Date.parse(globalStatus.UptimeClock);
var tempLocalClock = new Date;
$scope.LocalClock = tempLocalClock.toUTCString();
@ -487,12 +490,14 @@ function RadarCtrl($rootScope, $scope, $state, $http, $interval) {
// perform cleanup every 10 seconds
var clearStaleTraffic = $interval(function () {
// remove stale aircraft = anything more than 20 seconds without a position update
// remove stale aircraft = anything more than x seconds without a position update
var cutoff = 59;
var cutTime = $scope.StratuxClock-cutoff*1000;
// Clean up "valid position" table.
for (var i = $scope.data_list.length; i > 0; i--) {
if ($scope.data_list[i - 1].age >= cutoff) {
if ($scope.data_list[i - 1].Last_seen < cutTime) {
if ( $scope.data_list[i-1].planeimg ) {
$scope.data_list[i-1].planeimg.remove().forget(); // remove plane image
$scope.data_list[i-1].planetext.remove().forget(); // remove plane image
@ -509,13 +514,15 @@ function RadarCtrl($rootScope, $scope, $state, $http, $interval) {
// Clean up "invalid position" table.
for (var i = $scope.data_list_invalid.length; i > 0; i--) {
if (($scope.data_list_invalid[i - 1].age >= cutoff) || ($scope.data_list_invalid[i - 1].ageLastAlt >= cutoff)) {
//if (($scope.data_list_invalid[i - 1].timeVal < cutTime) || ($scope.data_list_invalid[i - 1].ageLastAlt < cutTime)) {
if ($scope.data_list_invalid[i - 1].Last_alt < cutTime) {
if ( $scope.data_list_invalid[i-1].circ ) { // is displayed
$scope.data_list_invalid[i-1].circ.remove().forget();
}
$scope.data_list_invalid.splice(i - 1, 1);
}
}
radar.update();
}, (1000 * 10), 0, false);
@ -533,7 +540,7 @@ function RadarCtrl($rootScope, $scope, $state, $http, $interval) {
$interval.cancel(clearStaleTraffic);
};
radar = new RadarRenderer ("radar_display",$scope);
radar = new RadarRenderer ("radar_display",$scope,$http);
// Traffic Controller tasks
connect($scope); // connect - opens a socket and listens for messages
@ -553,6 +560,11 @@ function clearRadarTraces ($scope) {
}
}
}
for (var i = $scope.data_list_invalid.length; i > 0; i--) { //clear circles
if ( $scope.data_list_invalid[i-1].circ ) { // is displayed
$scope.data_list_invalid[i-1].circ.remove().forget();
}
}
}
function requestFullScreen(el) {
@ -574,7 +586,63 @@ function cancelFullScreen(el) {
}
}
function RadarRenderer(locationId,$scope) {
function displaySoundStatus(speech, soundMode) {
switch (soundMode) {
case 0:
if ( synth ) {
var utterOn = new SpeechSynthesisUtterance("Beep and Speech on");
utterOn.lang="en-US";
synth.speak(utterOn);
}
speech.get(0).removeClass('zoom').addClass('zoomInvert');
speech.get(1).removeClass('tSmall').addClass('tSmallInvert').text('BpSp').cx(16).cy(0);
break;
case 1:
if ( synth ) {
var utterOn = new SpeechSynthesisUtterance("Beep only");
utterOn.lang="en-US";
synth.speak(utterOn);
}
speech.get(0).removeClass('zoom').addClass('zoomInvert');
speech.get(1).removeClass('tSmall').addClass('tSmallInvert').text('Beep').cx(16).cy(0);
break;
case 2:
if ( synth ) {
var utterOn = new SpeechSynthesisUtterance("Speech only");
utterOn.lang="en-US";
synth.speak(utterOn);
}
speech.get(0).removeClass('zoom').addClass('zoomInvert');
speech.get(1).removeClass('tSmall').addClass('tSmallInvert').text('Spch').cx(16).cy(0);
break;
default:
if ( synth ) {
var utterOn = new SpeechSynthesisUtterance("Sound off");
utterOn.lang="en-US";
synth.speak(utterOn);
}
speech.get(0).removeClass('zoomInvert').addClass('zoom');
speech.get(1).removeClass('tSmallInvert').addClass('tSmall').text('SnOff').cx(18).cy(0);
}
}
function communicateLimits (threshold,radarrange,$http) { //tell raspi the limits for callback
var newsettings = {
"RadarLimits": threshold,
"RadarRange": radarrange
};
msg = angular.toJson(newsettings);
// Simple POST request example (note: response is asynchronous)
$http.post(URL_SETTINGS_SET, msg).
then(function (response) {
// do nothing
}, function (response) {
// do nothing
});
}
function RadarRenderer(locationId,$scope,$http) {
this.$scope = $scope;
this.width = -1;
this.height = -1;
@ -582,8 +650,8 @@ function RadarRenderer(locationId,$scope) {
this.canvas = document.getElementById(this.locationId);
this.resize();
AltDiffThreshold = altDiff[altindex];
DisplayRadius = zoom[zoomfactor];
AltDiffThreshold = 20;
DisplayRadius = 10;
// Draw the radar using the svg.js library
var radarAll = SVG(this.locationId).viewbox(-201, -201, 402, 302).group().addClass('radar');
@ -593,6 +661,8 @@ function RadarRenderer(locationId,$scope) {
card.circle(200).cx(0).cy(0);
this.displayText = radarAll.text(DisplayRadius+' nm').addClass('textOutside').x(-200).cy(-158); //not rotated
this.altText = radarAll.text('\xB1'+AltDiffThreshold+'00ft').addClass('textOutsideRight').x(200).cy(-158); //not rotated
communicateLimits(100*AltDiffThreshold,DisplayRadius,$http); // initially sent Thresholds
this.fl = radarAll.text("FL"+Math.round(BaroAltitude/100)).addClass('textSmall').move(7,5);
card.text("N").addClass('textDir').center(0,-190);
card.text("S").addClass('textDir').center(0,190);
card.text("W").addClass('textDir').center(-190,0);
@ -607,70 +677,113 @@ function RadarRenderer(locationId,$scope) {
zoomin.text('Ra-').cx(12).cy(2).addClass('textZoom');
zoomin.on('click', function () {
var animateTime= 200;
if (zoomfactor > 0 ) {
zoomfactor--;
} else {
animateTime = 20;
var newval = DisplayRadius;
switch (DisplayRadius) {
case 40:
newval = 20;
break;
case 20:
newval = 10;
break;
case 10:
newval = 5;
break;
case 5:
newval = 2;
break;
default: // keep 2
animateTime = 20;
}
DisplayRadius = zoom[zoomfactor];
communicateLimits(100*AltDiffThreshold,newval,$http);
zoomin.animate(animateTime).rotate(90, 0, 0);
this.displayText.text(DisplayRadius+' nm');
//update();
zoomin.animate(animateTime).rotate(0, 0, 0);
clearRadarTraces($scope);
}, this);
var zoomout = radarAll.group().cx(-177).cy(-190).addClass('zoom');
zoomout.circle(45).cx(0).cy(0).addClass('zoom');
zoomout.text('Ra+').cx(12).cy(2).addClass('textZoom');
zoomout.on('click', function () {
var animateTime= 200;
if (zoomfactor < (zoom.length-1) ) {
zoomfactor++;
} else {
animateTime = 20;
var animateTime = 200;
var newval = DisplayRadius;
switch (DisplayRadius) {
case 2:
newval = 5;
break;
case 5:
newval = 10;
break;
case 10:
newval = 20;
break;
case 20:
newval = 40;
break;
default: // keep 40
animateTime = 20;
}
DisplayRadius = zoom[zoomfactor];
communicateLimits(100*AltDiffThreshold,newval,$http);
zoomout.animate(animateTime).rotate(90, 0, 0);
this.displayText.text(DisplayRadius+' nm');
zoomout.animate(animateTime).rotate(0, 0, 0);
clearRadarTraces($scope);
}, this);
var altmore = radarAll.group().cx(120).cy(-190).addClass('zoom');
altmore.circle(45).cx(0).cy(0).addClass('zoom');
altmore.text('Alt+').cx(12).cy(2).addClass('textZoom');
altmore.on('click', function () {
var newval = AltDiffThreshold;
var animateTime= 200;
if (altindex < (altDiff.length-1) ) {
altindex++;
} else {
animateTime = 20;
}
AltDiffThreshold = altDiff[altindex];
switch (AltDiffThreshold) {
case 5:
newval = 10;
break;
case 10:
newval = 20;
break;
case 20:
newval = 50;
break;
case 50:
newval = 100;
break;
case 100:
newval = 500;
break;
default:
animateTime = 20;
}
communicateLimits(100*newval,DisplayRadius,$http);
altmore.animate(animateTime).rotate(90, 0, 0);
this.altText.text('\xB1'+AltDiffThreshold+'00ft');
//update();
altmore.animate(animateTime).rotate(0, 0, 0);
clearRadarTraces($scope);
}, this);
var altless = radarAll.group().cx(177).cy(-190).addClass('zoom');
altless.circle(45).cx(0).cy(0).addClass('zoom');
altless.text('Alt-').cx(12).cy(2).addClass('textZoom');
altless.on('click', function () {
var newval = AltDiffThreshold;
var animateTime= 200;
if (altindex > 0 ) {
altindex--;
} else {
animateTime = 20;
}
AltDiffThreshold = altDiff[altindex];
switch (AltDiffThreshold) {
case 500:
newval = 100;
break;
case 100:
newval = 50;
break;
case 50:
newval = 20;
break;
case 20:
newval = 10;
break;
case 10:
newval = 5;
break;
default: //5 stays 5
animateTime = 20;
}
communicateLimits(100*newval,DisplayRadius,$http);
altless.animate(animateTime).rotate(90, 0, 0);
//update();
this.altText.text('\xB1'+AltDiffThreshold+'00ft');
altless.animate(animateTime).rotate(0, 0, 0);
clearRadarTraces($scope);
}, this);
var fullscreen = radarAll.group().cx(185).cy(-125).addClass('zoom');
@ -692,30 +805,28 @@ function RadarRenderer(locationId,$scope) {
}, this);
var speech = radarAll.group().cx(-185).cy(-125).addClass('zoom');
var speech = radarAll.group().cx(-185).cy(-125);
speech.rect(40,35).radius(10).cx(0).cy(0).addClass('zoom');
speech.text('Spk').cx(12).cy(2).addClass('textZoom');
speech.text('Undef').cx(16).cy(0).addClass('tSmall');
synth = window.speechSynthesis;
if (!synth) soundType=1; // speech function not working, default now beep
displaySoundStatus(speech,soundType);
speech.on('click', function () {
if (!synth) return; // speech function not working
if ( ! speechOn ) {
var utterOn = new SpeechSynthesisUtterance("Speech on");
utterOn.lang="en-US";
utterOn.rate=1.1;
speech.get(0).removeClass('zoom').addClass('zoomInvert');
speech.get(1).removeClass('textZoom').addClass('textZoomInvert');
synth.speak(utterOn);
speechOn = true;
} else {
var utterOff = new SpeechSynthesisUtterance("Speech off");
utterOff.lang="en-US";
utterOff.rate=1.1;
speech.get(0).removeClass('zoomInvert').addClass('zoom');
speech.get(1).removeClass('textZoomInvert').addClass('textZoom');
synth.speak(utterOff);
speechOn = false;
switch (soundType) {
case 0: //speech and beep
soundType = 1; // beep only
break;
case 1: //beep only
if (synth) { soundType=2; } else { soundType=3 };
break;
case 2: //speech only
soundType = 3; //Sound off
break;
default:
soundType = 0; //speech and beep
}
displaySoundStatus(speech,soundType);
}, this);
@ -740,8 +851,23 @@ RadarRenderer.prototype = {
},
update: function () {
if (this.fl) this.fl.remove();
this.rScreen.rotate(-GPSCourse,0,0); // rotate conforming to GPSCourse
this.fl = this.allScreen.text("FL"+Math.round(BaroAltitude/100)).addClass('textSmall').move(7,5);
if ( BaroAltitude != OldBaroAltitude ) {
this.fl.text("FL"+Math.round(BaroAltitude/100)); // just update text
OldBaroAltitude = BaroAltitude;
}
if ( AltDiffThreshold != OldAltDiffThreshold ) {
this.altText.text('\xB1'+AltDiffThreshold+'00ft');
clearRadarTraces(this.$scope);
OldAltDiffThreshold = AltDiffThreshold;
}
if ( DisplayRadius != OldDisplayRadius ) {
this.displayText.text(DisplayRadius+' nm');
clearRadarTraces(this.$scope);
OldDisplayRadius = DisplayRadius;
}
if ( GPSCourse != OldGPSCourse ) {
this.rScreen.rotate(-GPSCourse,0,0); // rotate conforming to GPSCourse
OldGPSCourse = GPSCourse;
}
}
};

Wyświetl plik

@ -37,7 +37,7 @@ function StatusCtrl($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)
// Update Status

Wyświetl plik

@ -119,7 +119,7 @@ function TrafficCtrl($rootScope, $scope, $state, $http, $interval) {
socket.onmessage = function (msg) {
console.log('Received traffic update.')
//console.log('Received traffic update.')
var message = JSON.parse(msg.data);
$scope.raw_data = angular.toJson(msg.data, true);
@ -238,4 +238,4 @@ function TrafficCtrl($rootScope, $scope, $state, $http, $interval) {
// Traffic Controller tasks
connect($scope); // connect - opens a socket and listens for messages
};
};

Wyświetl plik

@ -6,6 +6,6 @@
<p>An aircraft is removed from the radar, if it is outside the range, or if there is no transmission received within the last 60 seconds.</p>
<p>The aircraft in the <strong>middle</strong> is a symbol for your own aircraft. Right below the own aircraft the current flight-level is displayed. This level is received from barometric measurement, if your stratux is equipped with it. If not, the flight level is depending on the GPS altitude. Be aware that in this case the altitude difference to other aircraft may be wrong because their position reports are based on barometric measurement. A barometric sensor is therefore absolutely recommended!</p>
<p>A short <strong>beep sound</strong> is played whenever an aircraft is within the inner circle range.</p>
<p><strong>"Spk"</strong> toogles speech output, if your web browser supports it. If switched on, a warning e.g. "Traffic at 10 o'clock plus 500 feet" is spoken, whenever an aircraft enters the inner circle.</p>
<p><strong>"BpSp/Beep/Spch/SnOff"</strong> toogles between different sound outputs. Default is Beep (5x) and a spoken warning, alternatively only Beep, only Speech or silence. If speech is switched on, a warning e.g. "Traffic at 10 o'clock plus 500 feet" is spoken, whenever an aircraft enters the inner circle.</p>
<p><strong>"F/S"</strong> toggles full screen mode, which displays the radar in maximum screen size (landscape format recommended). </p>
</div>