From fb9ebccb416e6952e9b58f2dd5f1703e26e1ad68 Mon Sep 17 00:00:00 2001
From: Flaparoo <629229+flaparoo@users.noreply.github.com>
Date: Tue, 28 Nov 2023 17:54:28 +0800
Subject: [PATCH] New app: flightdash
---
apps/flightdash/ChangeLog | 1 +
apps/flightdash/README.md | 76 ++++
apps/flightdash/flightdash-icon.js | 1 +
apps/flightdash/flightdash.app.js | 527 +++++++++++++++++++++++++
apps/flightdash/flightdash.png | Bin 0 -> 1020 bytes
apps/flightdash/flightdash.settings.js | 344 ++++++++++++++++
apps/flightdash/interface.html | 186 +++++++++
apps/flightdash/jquery-csv.min.js | 1 +
apps/flightdash/metadata.json | 21 +
apps/flightdash/screenshot.png | Bin 0 -> 4334 bytes
10 files changed, 1157 insertions(+)
create mode 100644 apps/flightdash/ChangeLog
create mode 100644 apps/flightdash/README.md
create mode 100644 apps/flightdash/flightdash-icon.js
create mode 100644 apps/flightdash/flightdash.app.js
create mode 100644 apps/flightdash/flightdash.png
create mode 100644 apps/flightdash/flightdash.settings.js
create mode 100644 apps/flightdash/interface.html
create mode 100644 apps/flightdash/jquery-csv.min.js
create mode 100644 apps/flightdash/metadata.json
create mode 100644 apps/flightdash/screenshot.png
diff --git a/apps/flightdash/ChangeLog b/apps/flightdash/ChangeLog
new file mode 100644
index 000000000..971e5b97e
--- /dev/null
+++ b/apps/flightdash/ChangeLog
@@ -0,0 +1 @@
+1.00: initial release
diff --git a/apps/flightdash/README.md b/apps/flightdash/README.md
new file mode 100644
index 000000000..07b753178
--- /dev/null
+++ b/apps/flightdash/README.md
@@ -0,0 +1,76 @@
+# Flight Dashboard
+
+Shows basic flight and navigation instruments.
+
+
+
+Basic flight data includes:
+
+- Ground speed
+- Track
+- Altimeter
+- VSI
+- Local time
+
+You can also set a destination to get nav guidance:
+
+- Distance from destination
+- Bearing to destination
+- Estimated Time En-route (minutes and seconds)
+- Estimated Time of Arrival (in UTC)
+
+The speed/distance and altitude units are configurable.
+
+Altitude data can be derived from GPS or the Bangle's barometer.
+
+
+## DISCLAIMER
+
+Remember to Aviate - Navigate - Communicate! Do NOT get distracted by your
+gadgets, keep your eyes looking outside and do NOT rely on this app for actual
+navigation!
+
+
+## Usage
+
+After installing the app, use the "interface" page (floppy disk icon) in the
+App Loader to filter and upload a list of airports (to be used as navigation
+destinations). Due to memory constraints, only up to about 500 airports can be
+stored on the Bangle itself (recommended is around 100 - 150 airports max.).
+
+Then, on the Bangle, access the Flight-Dash settings, either through the
+Settings app (Settings -> Apps -> Flight-Dash) or a tap anywhere in the
+Flight-Dash app itself. The following settings are available:
+
+- **Nav Dest.**: Choose the navigation destination:
+ - Nearest airports (from the uploaded list)
+ - Search the uploaded list of airports
+ - User waypoints (which can be set/edited through the settings)
+ - Nearest airports (queried online through AVWX - requires Internet connection at the time)
+- **Speed** and **Altitude**: Set the preferred units of measurements.
+- **Use Baro**: If enabled, altitude information is derived from the Bangle's barometer (instead of using GPS altitude).
+
+If the barometer is used for altitude information, the current QNH value is
+also displayed. It can be adjusted by swiping up/down in the app.
+
+To query the nearest airports online through AVWX, you have to install - and
+configure - the [avwx](?id=avwx) module.
+
+The app requires a text input method (to set user waypoint names, and search
+for airports), and if not already installed will automatically install the
+default "textinput" app as a dependency.
+
+
+## Hint
+
+Under the bearing "band", the current nav destination is displayed. Next to
+that, you'll also find the cardinal direction you are approaching **from**.
+This can be useful for inbound radio calls. Together with the distance, the
+current altitude and the ETA, you have all the information required to make
+radio calls like a pro!
+
+
+## Author
+
+Flaparoo [github](https://github.com/flaparoo)
+
diff --git a/apps/flightdash/flightdash-icon.js b/apps/flightdash/flightdash-icon.js
new file mode 100644
index 000000000..3a2e2757c
--- /dev/null
+++ b/apps/flightdash/flightdash-icon.js
@@ -0,0 +1 @@
+require("heatshrink").decompress(atob("mEwwhC/AH4A/AHcCkAX/C/4X9kUiC/4XcgczmcwRSArBkYWBAAMyA4KUMC4QWDAAIXOAAUziMTmMRmZdRmQXDkZhIHQJ1IAAYXGBgoNDgQJFLoQhFDQ84wQFDlGDBwxBInGIDAUoxAXFJosDOIIXDAAgXCPoJkGBAKfBmc6C4ujBIINBiYXIEIMK1AWDxWgHoQXMgGqC4eqKoYXHL4QFChQYC1QuBEwbcHZo7hHBpYA/AH4A/AH4"))
diff --git a/apps/flightdash/flightdash.app.js b/apps/flightdash/flightdash.app.js
new file mode 100644
index 000000000..f612836c6
--- /dev/null
+++ b/apps/flightdash/flightdash.app.js
@@ -0,0 +1,527 @@
+/*
+ * Flight Dashboard - Bangle.js
+ */
+
+const COLOUR_BLACK = 0x0000; // same as: g.setColor(0, 0, 0)
+const COLOUR_WHITE = 0xffff; // same as: g.setColor(1, 1, 1)
+const COLOUR_GREEN = 0x07e0; // same as: g.setColor(0, 1, 0)
+const COLOUR_YELLOW = 0xffe0; // same as: g.setColor(1, 1, 0)
+const COLOUR_MAGENTA = 0xf81f; // same as: g.setColor(1, 0, 1)
+const COLOUR_CYAN = 0x07ff; // same as: g.setColor(0, 1, 1)
+const COLOUR_LIGHT_BLUE = 0x841f; // same as: g.setColor(0.5, 0.5, 1)
+
+const APP_NAME = 'flightdash';
+
+const horizontalCenter = g.getWidth() / 2;
+const verticalCenter = g.getHeight() / 2;
+
+const dataFontHeight = 22;
+const secondaryFontHeight = 18;
+const labelFontHeight = 12;
+
+
+//globals
+var settings = {};
+
+var updateInterval;
+
+var speed = '-'; var speedPrev = -1;
+var track = '-'; var trackPrev = -1;
+var lat = 0; var lon = 0;
+var distance = '-'; var distancePrev = -1;
+var bearing = '-'; var bearingPrev = -1;
+var relativeBearing = 0; var relativeBearingPrev = -1;
+var fromCardinal = '-';
+var ETAdate = new Date();
+var ETA = '-'; var ETAPrev = '';
+
+var QNH = Math.round(Bangle.getOptions().seaLevelPressure); var QNHPrev = -1;
+
+var altitude = '-'; var altitudePrev = -1;
+
+var VSI = '-'; var VSIPrev = -1;
+var VSIraw = 0;
+var VSIprevTimestamp = Date.now();
+var VSIprevAltitude;
+var VSIsamples = 0; var VSIsamplesCount = 0;
+
+var speedUnit = 'N/A';
+var distanceUnit = 'N/A';
+var altUnit = 'N/A';
+
+
+// date object to time string in format (HH:MM[:SS])
+function timeStr(date, seconds) {
+ let timeStr = date.getHours().toString();
+ if (timeStr.length == 1) timeStr = '0' + timeStr;
+ let minutes = date.getMinutes().toString();
+ if (minutes.length == 1) minutes = '0' + minutes;
+ timeStr += ':' + minutes;
+ if (seconds) {
+ let seconds = date.getSeconds().toString();
+ if (seconds.length == 1) seconds = '0' + seconds;
+ timeStr += ':' + seconds;
+ }
+ return timeStr;
+}
+
+// add thousands separator to number
+function addThousandSeparator(n) {
+ let s = n.toString();
+ if (s.length > 3) {
+ return s.substr(0, s.length - 3) + ',' + s.substr(s.length - 3, 3);
+ } else {
+ return s;
+ }
+}
+
+
+// update VSI
+function updateVSI(alt) {
+ VSIsamples += alt; VSIsamplesCount += 1;
+ let VSInewTimestamp = Date.now();
+ if (VSIprevTimestamp + 1000 <= VSInewTimestamp) { // update VSI every 1 second
+ let VSInewAltitude = VSIsamples / VSIsamplesCount;
+ if (VSIprevAltitude) {
+ let VSIinterval = (VSInewTimestamp - VSIprevTimestamp) / 1000;
+ VSIraw = (VSInewAltitude - VSIprevAltitude) * 60 / VSIinterval; // extrapolate to change / minute
+ }
+ VSIprevTimestamp = VSInewTimestamp;
+ VSIprevAltitude = VSInewAltitude;
+ VSIsamples = 0; VSIsamplesCount = 0;
+ }
+
+ VSI = Math.floor(VSIraw / 10) * 10; // "smooth" VSI value
+ if (settings.altimeterUnits == 0) { // Feet
+ VSI = Math.round(VSI * 3.28084);
+ } // nothing else required since VSI is already in meters ("smoothed")
+
+ if (VSI > 9999) VSI = 9999;
+ else if (VSI < -9999) VSI = -9999;
+}
+
+// update GPS-derived information
+function updateGPS(fix) {
+ if (!('fix' in fix) || fix.fix == 0 || fix.satellites < 4) return;
+
+ speed = 'N/A';
+ if (settings.speedUnits == 0) { // Knots
+ speed = Math.round(fix.speed * 0.539957);
+ } else if (settings.speedUnits == 1) { // km/h
+ speed = Math.round(fix.speed);
+ } else if (settings.speedUnits == 2) { // MPH
+ speed = Math.round(fix.speed * 0.621371);
+ }
+ if (speed > 9999) speed = 9999;
+
+ if (! settings.useBaro) { // use GPS altitude
+ altitude = 'N/A';
+ if (settings.altimeterUnits == 0) { // Feet
+ altitude = Math.round(fix.alt * 3.28084);
+ } else if (settings.altimeterUnits == 1) { // Meters
+ altitude = Math.round(fix.alt);
+ }
+ if (altitude > 99999) altitude = 99999;
+
+ updateVSI(fix.alt);
+ }
+
+ track = Math.round(fix.course);
+ if (isNaN(track)) track = '-';
+ else if (track < 10) track = '00'+track;
+ else if (track < 100) track = '0'+track;
+
+ lat = fix.lat;
+ lon = fix.lon;
+
+ // calculation from https://www.movable-type.co.uk/scripts/latlong.html
+ const latRad1 = lat * Math.PI/180;
+ const latRad2 = settings.destLat * Math.PI/180;
+ const lonRad1 = lon * Math.PI/180;
+ const lonRad2 = settings.destLon * Math.PI/180;
+
+ // distance (using "Equirectangular approximation")
+ let x = (lonRad2 - lonRad1) * Math.cos((latRad1 + latRad2) / 2);
+ let y = (latRad2 - latRad1);
+ let distanceNumber = Math.sqrt(x*x + y*y) * 6371; // in km - 6371 = mean Earth radius
+ if (settings.speedUnits == 0) { // NM
+ distanceNumber = distanceNumber * 0.539957;
+ } else if (settings.speedUnits == 2) { // miles
+ distanceNumber = distanceNumber * 0.621371;
+ }
+ if (distanceNumber > 99.9) {
+ distance = '>100';
+ } else {
+ distance = (Math.round(distanceNumber * 10) / 10).toString();
+ if (! distance.includes('.'))
+ distance += '.0';
+ }
+
+ // bearing
+ y = Math.sin(lonRad2 - lonRad1) * Math.cos(latRad2);
+ x = Math.cos(latRad1) * Math.sin(latRad2) -
+ Math.sin(latRad1) * Math.cos(latRad2) * Math.cos(lonRad2 - lonRad1);
+ let nonNormalisedBearing = Math.atan2(y, x);
+ bearing = Math.round((nonNormalisedBearing * 180 / Math.PI + 360) % 360);
+
+ if (bearing > 337 || bearing < 23) {
+ fromCardinal = 'S';
+ } else if (bearing < 68) {
+ fromCardinal = 'SW';
+ } else if (bearing < 113) {
+ fromCardinal = 'W';
+ } else if (bearing < 158) {
+ fromCardinal = 'NW';
+ } else if (bearing < 203) {
+ fromCardinal = 'N';
+ } else if (bearing < 248) {
+ fromCardinal = 'NE';
+ } else if (bearing < 293) {
+ fromCardinal = 'E';
+ } else{
+ fromCardinal = 'SE';
+ }
+
+ if (bearing < 10) bearing = '00'+bearing;
+ else if (bearing < 100) bearing = '0'+bearing;
+
+ relativeBearing = parseInt(bearing) - parseInt(track);
+ if (isNaN(relativeBearing)) relativeBearing = 0;
+ if (relativeBearing > 180) relativeBearing -= 360;
+ else if (relativeBearing < -180) relativeBearing += 360;
+
+ // ETA
+ if (speed) {
+ let ETE = distanceNumber * 3600 / speed;
+ let now = new Date();
+ ETAdate = new Date(now + (now.getTimezoneOffset() * 1000 * 60) + ETE*1000);
+ if (ETE < 86400) {
+ ETA = timeStr(ETAdate, false);
+ } else {
+ ETA = '>24h';
+ }
+ } else {
+ ETAdate = new Date();
+ ETA = '-';
+ }
+}
+
+
+// update barometric information
+function updatePressure(e) {
+ altitude = 'N/A';
+ if (settings.altimeterUnits == 0) { // Feet
+ altitude = Math.round(e.altitude * 3.28084);
+ } else if (settings.altimeterUnits == 1) { // Meters
+ altitude = Math.round(e.altitude); // altitude is given in meters
+ }
+ if (altitude > 99999) altitude = 99999;
+
+ updateVSI(e.altitude);
+}
+
+
+// (re-)draw all read-outs
+function draw(initial) {
+
+ g.setBgColor(COLOUR_BLACK);
+
+ // speed
+ if (speed != speedPrev || initial) {
+ g.setFontAlign(-1, -1).setFont("Vector", dataFontHeight).setColor(COLOUR_GREEN);
+ g.clearRect(0, 0, 55, dataFontHeight);
+ g.drawString(speed.toString(), 0, 0, false);
+ if (initial) {
+ g.setFontAlign(-1, -1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString(speedUnit, 0, dataFontHeight, false);
+ }
+ speedPrev = speed;
+ }
+
+
+ // distance
+ if (distance != distancePrev || initial) {
+ g.setFontAlign(1, -1).setFont("Vector", dataFontHeight).setColor(COLOUR_WHITE);
+ g.clearRect(g.getWidth() - 58, 0, g.getWidth(), dataFontHeight);
+ g.drawString(distance, g.getWidth(), 0, false);
+ if (initial) {
+ g.setFontAlign(1, -1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString(distanceUnit, g.getWidth(), dataFontHeight, false);
+ }
+ distancePrev = distance;
+ }
+
+
+ // track (+ static track/bearing content)
+ let trackY = 18;
+ let destInfoY = trackY + 53;
+ if (track != trackPrev || initial) {
+ g.setFontAlign(0, -1).setFont("Vector", dataFontHeight).setColor(COLOUR_WHITE);
+ g.clearRect(horizontalCenter - 29, trackY, horizontalCenter + 28, trackY + dataFontHeight);
+ g.drawString(track.toString() + "\xB0", horizontalCenter + 3, trackY, false);
+ if (initial) {
+ let y = trackY + dataFontHeight + 1;
+ g.setColor(COLOUR_YELLOW);
+ g.drawRect(horizontalCenter - 30, trackY - 3, horizontalCenter + 29, y);
+ g.drawLine(0, y, g.getWidth(), y);
+ y += dataFontHeight + 5;
+ g.drawLine(0, y, g.getWidth(), y);
+
+ g.setFontAlign(1, -1).setFont("Vector", secondaryFontHeight).setColor(COLOUR_MAGENTA);
+ g.drawString(settings.destID, horizontalCenter, destInfoY, false);
+ }
+ trackPrev = track;
+ }
+
+
+ // bearing
+ if (bearing != bearingPrev || relativeBearing != relativeBearingPrev || initial) {
+ let bearingY = trackY + 27;
+
+ g.clearRect(0, bearingY, g.getWidth(), bearingY + dataFontHeight);
+
+ g.setColor(COLOUR_YELLOW);
+ for (let i = Math.floor(relativeBearing * 2.5) % 25; i <= g.getWidth(); i += 25) {
+ g.drawLine(i, bearingY + 3, i, bearingY + 16);
+ }
+
+ let bearingX = horizontalCenter + relativeBearing * 2.5;
+ if (bearingX > g.getWidth() - 26) bearingX = g.getWidth() - 26;
+ else if (bearingX < 26) bearingX = 26;
+ g.setFontAlign(0, -1).setFont("Vector", dataFontHeight).setColor(COLOUR_MAGENTA);
+ g.drawString(bearing.toString() + "\xB0", bearingX + 3, bearingY, false);
+
+ g.clearRect(horizontalCenter + 42, destInfoY, horizontalCenter + 69, destInfoY + secondaryFontHeight);
+ g.setFontAlign(-1, -1).setFont("Vector", secondaryFontHeight).setColor(COLOUR_MAGENTA);
+ g.drawString(fromCardinal, horizontalCenter + 42, destInfoY, false);
+ if (initial) {
+ g.setFontAlign(-1, -1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString(' from', horizontalCenter, destInfoY, false);
+ }
+
+ bearingPrev = bearing;
+ relativeBearingPrev = relativeBearing;
+ }
+
+
+ let row3y = g.getHeight() - 48;
+
+ // QNH
+ if (settings.useBaro) {
+ if (QNH != QNHPrev || initial) {
+ let QNHy = row3y - secondaryFontHeight - 2;
+ g.setFontAlign(0, 1).setFont("Vector", secondaryFontHeight).setColor(COLOUR_WHITE);
+ g.clearRect(horizontalCenter - 29, QNHy - secondaryFontHeight, horizontalCenter + 22, QNHy);
+ g.drawString(QNH.toString(), horizontalCenter - 3, QNHy, false);
+ if (initial) {
+ g.setFontAlign(0, -1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString('QNH', horizontalCenter - 3, QNHy, false);
+ }
+ QNHPrev = QNH;
+ }
+ }
+
+
+ // VSI
+ if (VSI != VSIPrev || initial) {
+ g.setFontAlign(-1, 1).setFont("Vector", secondaryFontHeight).setColor(COLOUR_WHITE);
+ g.clearRect(0, row3y - secondaryFontHeight, 51, row3y);
+ g.drawString(VSI.toString(), 0, row3y, false);
+ if (initial) {
+ g.setFontAlign(-1, 1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString(altUnit + '/min', 0, row3y - secondaryFontHeight, false);
+ }
+
+ let VSIarrowX = 6;
+ let VSIarrowY = row3y - 42;
+ g.clearRect(VSIarrowX - 7, VSIarrowY - 10, VSIarrowX + 6, VSIarrowY + 10);
+ g.setColor(COLOUR_WHITE);
+ if (VSIraw > 30) { // climbing
+ g.fillRect(VSIarrowX - 1, VSIarrowY, VSIarrowX + 1, VSIarrowY + 10);
+ g.fillPoly([ VSIarrowX , VSIarrowY - 11,
+ VSIarrowX + 7, VSIarrowY,
+ VSIarrowX - 7, VSIarrowY]);
+ } else if (VSIraw < -30) { // descending
+ g.fillRect(VSIarrowX - 1, VSIarrowY - 10, VSIarrowX + 1, VSIarrowY);
+ g.fillPoly([ VSIarrowX , VSIarrowY + 11,
+ VSIarrowX + 7, VSIarrowY,
+ VSIarrowX - 7, VSIarrowY ]);
+ }
+ }
+
+
+ // altitude
+ if (altitude != altitudePrev || initial) {
+ g.setFontAlign(1, 1).setFont("Vector", secondaryFontHeight).setColor(COLOUR_WHITE);
+ g.clearRect(g.getWidth() - 65, row3y - secondaryFontHeight, g.getWidth(), row3y);
+ g.drawString(addThousandSeparator(altitude), g.getWidth(), row3y, false);
+ if (initial) {
+ g.setFontAlign(1, 1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString(altUnit, g.getWidth(), row3y - secondaryFontHeight, false);
+ }
+ altitudePrev = altitude;
+ }
+
+
+ // time
+ let now = new Date();
+ let nowUTC = new Date(now + (now.getTimezoneOffset() * 1000 * 60));
+ g.setFontAlign(-1, 1).setFont("Vector", dataFontHeight).setColor(COLOUR_LIGHT_BLUE);
+ let timeStrMetrics = g.stringMetrics(timeStr(now, false));
+ g.drawString(timeStr(now, false), 0, g.getHeight(), true);
+
+ let seconds = now.getSeconds().toString();
+ if (seconds.length == 1) seconds = '0' + seconds;
+ g.setFontAlign(-1, 1).setFont("Vector", secondaryFontHeight);
+ g.drawString(seconds, timeStrMetrics.width + 2, g.getHeight() - 1, true);
+
+ if (initial) {
+ g.setFontAlign(-1, 1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString('LOCAL', 0, g.getHeight() - dataFontHeight, false);
+ }
+
+
+ // ETE
+ let ETEy = g.getHeight() - dataFontHeight;
+ let ETE = '-';
+ if (ETA != '-') {
+ let ETEseconds = Math.floor((ETAdate - nowUTC) / 1000);
+ if (ETEseconds < 0) ETEseconds = 0;
+ ETE = ETEseconds % 60;
+ if (ETE < 10) ETE = '0' + ETE;
+ ETE = Math.floor(ETEseconds / 60) + ':' + ETE;
+ if (ETE.length > 6) ETE = '>999m';
+ }
+ g.clearRect(horizontalCenter - 35, ETEy - secondaryFontHeight, horizontalCenter + 29, ETEy);
+ g.setFontAlign(0, 1).setFont("Vector", secondaryFontHeight).setColor(COLOUR_WHITE);
+ g.drawString(ETE, horizontalCenter - 3, ETEy, false);
+ if (initial) {
+ g.setFontAlign(0, 1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString('ETE', horizontalCenter - 3, ETEy - secondaryFontHeight, false);
+ }
+
+
+ // ETA
+ if (ETA != ETAPrev || initial) {
+ g.clearRect(g.getWidth() - 63, g.getHeight() - dataFontHeight, g.getWidth(), g.getHeight());
+ g.setFontAlign(1, 1).setFont("Vector", dataFontHeight).setColor(COLOUR_WHITE);
+ g.drawString(ETA, g.getWidth(), g.getHeight(), false);
+ if (initial) {
+ g.setFontAlign(1, 1).setFont("Vector", labelFontHeight).setColor(COLOUR_CYAN);
+ g.drawString('UTC ETA', g.getWidth(), g.getHeight() - dataFontHeight, false);
+ }
+ ETAPrev = ETA;
+ }
+}
+
+
+function handleSwipes(directionLR, directionUD) {
+ if (directionUD == -1) { // up -> increase QNH
+ QNH = Math.round(Bangle.getOptions().seaLevelPressure);
+ QNH++;
+ Bangle.setOptions({'seaLevelPressure': QNH});
+ } else if (directionUD == 1) { // down -> decrease QNH
+ QNH = Math.round(Bangle.getOptions().seaLevelPressure);
+ QNH--;
+ Bangle.setOptions({'seaLevelPressure': QNH});
+ }
+}
+
+function handleTouch(button, xy) {
+ if ('handled' in xy && xy.handled) return;
+ Bangle.removeListener('touch', handleTouch);
+ if (settings.useBaro) {
+ Bangle.removeListener('swipe', handleSwipes);
+ }
+
+ // any touch -> show settings
+ clearInterval(updateTimeInterval);
+ Bangle.setGPSPower(false, APP_NAME);
+ if (settings.useBaro)
+ Bangle.setBarometerPower(false, APP_NAME);
+
+ eval(require("Storage").read(APP_NAME+'.settings.js'))( () => {
+ E.showMenu();
+ // "clear" values potentially affected by a settings change
+ speed = '-'; distance = '-';
+ altitude = '-'; VSI = '-';
+ // re-launch
+ start();
+ });
+}
+
+
+/*
+ * main
+ */
+function start() {
+
+ // read in the settings
+ settings = Object.assign({
+ useBaro: false,
+ speedUnits: 0, // KTS
+ altimeterUnits: 0, // FT
+ destID: 'KOSH',
+ destLat: 43.9844,
+ destLon: -88.5570,
+ }, require('Storage').readJSON(APP_NAME+'.json', true) || {});
+
+ // set units
+ if (settings.speedUnits == 0) { // Knots
+ speedUnit = 'KTS';
+ distanceUnit = 'NM';
+ } else if (settings.speedUnits == 1) { // km/h
+ speedUnit = 'KPH';
+ distanceUnit = 'KM';
+ } else if (settings.speedUnits == 2) { // MPH
+ speedUnit = 'MPH';
+ distanceUnit = 'SM';
+ }
+
+ if (settings.altimeterUnits == 0) { // Feet
+ altUnit = 'FT';
+ } else if (settings.altimeterUnits == 1) { // Meters
+ altUnit = 'M';
+ }
+
+ // initialise
+ g.reset();
+ g.setBgColor(COLOUR_BLACK);
+ g.clear();
+
+ // draw incl. static components
+ draw(true);
+
+ // enable timeout/interval and sensors
+ setTimeout(function() {
+ draw();
+ updateTimeInterval = setInterval(draw, 1000);
+ }, 1000 - (Date.now() % 1000));
+
+ Bangle.setGPSPower(true, APP_NAME);
+ Bangle.on('GPS', updateGPS);
+
+ if (settings.useBaro) {
+ Bangle.setBarometerPower(true, APP_NAME);
+ Bangle.on('pressure', updatePressure);
+ }
+
+ // handle interaction
+ if (settings.useBaro) {
+ Bangle.on('swipe', handleSwipes);
+ }
+ Bangle.on('touch', handleTouch);
+ setWatch(e => { Bangle.showClock(); }, BTN1); // exit on button press
+}
+
+start();
+
+
+/*
+// TMP for testing:
+//settings.speedUnits = 1;
+//settings.altimeterUnits = 1;
+QNH = 1013;
+updateGPS({"fix":1,"speed":228,"alt":3763,"course":329,"lat":36.0182,"lon":-75.6713});
+updatePressure({"altitude":3700});
+*/
diff --git a/apps/flightdash/flightdash.png b/apps/flightdash/flightdash.png
new file mode 100644
index 0000000000000000000000000000000000000000..8230bc0c1e29b1a07be15aae1ce6bd9cf600c972
GIT binary patch
literal 1020
zcmV You can upload a list of airports, which can then be used as the
+ navigation destinations in the Flight-Dash. It is recommended to only
+ upload up to 100 - 150 airports max. Due to memory contraints on the
+ Bangle, no more than 500 airports can be uploaded. The database of airports is based on OurAirports.
+
+ - or -
+
+
+ - or -
+
+
+ Only 1 of the above filters is applied, with higher up in the list taking precedence.
+
+
+ Filter Airports
+
+
+ Results:
+