Merge pull request #1770 from berkenbu/master
Bug fix for OpenWind, new scientific calculator appmaster
commit
306f738643
|
|
@ -1 +1,2 @@
|
|||
0.01: New App!
|
||||
0.02: Fix true wind computation, add swipe gesture to pause GPS
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ additionally displayed in red. In this mode, the speed over ground in knots is
|
|||
|
||||
## Controls
|
||||
|
||||
There are no controls in the main app, but there are two settings in the settings app that can be changed:
|
||||
In the main app, when true wind mode is enabled (see below), swiping left on the screen will temporarily disable GPS (to preserve battery); a small
|
||||
red satellite symbol will appear on the bottom right. Swiping right will turn GPS back on.
|
||||
The settings app provides the following two settings:
|
||||
|
||||
* True wind: enables or disables true wind calculations; enabling this will turn on GPS inside the app
|
||||
* Mounting angle: mounting relative to the boat of the wind instrument (in degrees)
|
||||
|
|
|
|||
|
|
@ -1,16 +1,20 @@
|
|||
OW_CHAR_UUID = '0000cc91-0000-1000-8000-00805f9b34fb';
|
||||
require("Font7x11Numeric7Seg").add(Graphics);
|
||||
gatt = {};
|
||||
cx = g.getWidth()/2;
|
||||
cy = 24+(g.getHeight()-24)/2;
|
||||
w = (g.getWidth()-24)/2;
|
||||
|
||||
gps_course = { spd: 0 };
|
||||
var gatt = {};
|
||||
var cx = g.getWidth()/2;
|
||||
var cy = 24+(g.getHeight()-24)/2;
|
||||
var w = (g.getWidth()-24)/2;
|
||||
var y1 = 24;
|
||||
var y2 = g.getHeight()-1;
|
||||
var gps_course = { spd: 0 };
|
||||
var course_marker_len = g.getWidth()/4;
|
||||
|
||||
var settings = require("Storage").readJSON('openwindsettings.json', 1) || {};
|
||||
|
||||
i = 0;
|
||||
hullpoly = [];
|
||||
var pause_gps = false;
|
||||
|
||||
var i = 0;
|
||||
var hullpoly = [];
|
||||
for (y=-1; y<=1; y+=0.1) {
|
||||
hullpoly[i++] = cx - (y<0 ? 1+y*0.15 : (Math.sqrt(1-0.7*y*y)-Math.sqrt(0.3))/(1-Math.sqrt(0.3)))*w*0.3;
|
||||
hullpoly[i++] = cy - y*w*0.7;
|
||||
|
|
@ -22,21 +26,22 @@ for (y=1; y>=-1; y-=0.1) {
|
|||
|
||||
function wind_updated(ev) {
|
||||
if (ev.target.uuid == "0xcc91") {
|
||||
awa = settings.mount_angle-ev.target.value.getInt16(1, true)*0.1;
|
||||
awa = settings.mount_angle+ev.target.value.getInt16(1, true)*0.1;
|
||||
if (awa<0) awa += 360;
|
||||
aws = ev.target.value.getInt16(3, true)*0.01;
|
||||
// console.log(awa, aws);
|
||||
//console.log(awa, aws);
|
||||
if (gps_course.spd > 0) {
|
||||
wv = { // wind vector (in fixed reference frame)
|
||||
lon: Math.sin(Math.PI*(gps_course.course+awa)/180)*aws,
|
||||
lat: Math.cos(Math.PI*(gps_course.course+awa)/180)*aws
|
||||
wv = { // wind vector (in "earth" reference frame)
|
||||
vlon: Math.sin(Math.PI*(gps_course.course+(awa+180))/180)*aws,
|
||||
vlat: Math.cos(Math.PI*(gps_course.course+(awa+180))/180)*aws
|
||||
};
|
||||
twv = { lon: wv.lon+gps_course.lon, lat: wv.lat+gps_course.lat };
|
||||
tws = Math.sqrt(Math.pow(twv.lon,2)+Math.pow(twv.lat, 2));
|
||||
twa = Math.atan2(twv.lat, twv.lon)*180/Math.PI-gps_course.course;
|
||||
twv = { vlon: wv.vlon+gps_course.vlon, vlat: wv.vlat+gps_course.vlat };
|
||||
tws = Math.sqrt(Math.pow(twv.vlon,2)+Math.pow(twv.vlat, 2));
|
||||
twa = 180+Math.atan2(twv.vlon, twv.vlat)*180/Math.PI-gps_course.course;
|
||||
if (twa<0) twa += 360;
|
||||
if (twa>360) twa -=360;
|
||||
}
|
||||
else {
|
||||
else {
|
||||
tws = -1;
|
||||
twa = 0;
|
||||
}
|
||||
|
|
@ -57,34 +62,37 @@ function draw_compass(awa, aws, twa, tws) {
|
|||
a = i*Math.PI/2+Math.PI/4;
|
||||
g.drawLineAA(cx+Math.cos(a)*w*0.85, cy+Math.sin(a)*w*0.85, cx+Math.cos(a)*w*0.99, cy+Math.sin(a)*w*0.99);
|
||||
}
|
||||
g.setColor(0, 1, 0).fillCircle(cx+Math.sin(Math.PI*awa/180)*w*0.9, cy+Math.cos(Math.PI*awa/180)*w*0.9, w*0.1);
|
||||
g.setColor(0, 1, 0).fillCircle(cx+Math.sin(Math.PI*awa/180)*w*0.9, cy-Math.cos(Math.PI*awa/180)*w*0.9, w*0.1);
|
||||
if (tws>0) g.setColor(1, 0, 0).fillCircle(cx+Math.sin(Math.PI*twa/180)*w*0.9, cy+Math.cos(Math.PI*twa/180)*w*0.9, w*0.1);
|
||||
g.setColor(0, 1, 0).setFont("7x11Numeric7Seg",w*0.06);
|
||||
g.setFontAlign(0, 0, 0).drawString(aws.toFixed(1), cx, cy-0.32*w);
|
||||
if (tws>0) g.setColor(1, 0, 0).drawString(tws.toFixed(1), cx, cy+0.32*w);
|
||||
if (settings.truewind && typeof gps_course.spd!=='undefined') {
|
||||
spd = gps_course.spd/1.852;
|
||||
g.setColor(g.theme.fg).setFont("7x11Numeric7Seg", w*0.03).setFontAlign(-1, 1, 0).drawString(spd.toFixed(1), 1, g.getHeight()-1);
|
||||
if (!pause_gps) {
|
||||
if (tws>0) g.setColor(1, 0, 0).drawString(tws.toFixed(1), cx, cy+0.32*w);
|
||||
if (settings.truewind && gps_course.spd!=-1) {
|
||||
spd = gps_course.spd/1.852;
|
||||
g.setColor(g.theme.fg).setFont("7x11Numeric7Seg", w*0.03).setFontAlign(-1, 1, 0).drawString(spd.toFixed(1), 1, g.getHeight()-1);
|
||||
}
|
||||
}
|
||||
if (pause_gps) g.setColor("#f00").drawImage(atob("DAwBEAKARAKQE4DwHkPqPRGKAEAA"),g.getWidth()-15, g.getHeight()-15);
|
||||
}
|
||||
|
||||
function parseDevice(d) {
|
||||
device = d;
|
||||
console.log("Found device");
|
||||
device.gatt.connect().then(function(ga) {
|
||||
console.log("Connected");
|
||||
gatt = ga;
|
||||
return ga.getPrimaryService("cc90");
|
||||
}).then(function(s) {
|
||||
return s.getCharacteristic("cc91");
|
||||
}).then(function(c) {
|
||||
c.on('characteristicvaluechanged', (event)=>wind_updated(event));
|
||||
return c.startNotifications();
|
||||
}).then(function() {
|
||||
console.log("Done!");
|
||||
}).catch(function(e) {
|
||||
console.log("ERROR"+e);
|
||||
});}
|
||||
device.gatt.connect().then(function(ga) {
|
||||
console.log("Connected");
|
||||
gatt = ga;
|
||||
return ga.getPrimaryService("cc90");
|
||||
}).then(function(s) {
|
||||
return s.getCharacteristic("cc91");
|
||||
}).then(function(c) {
|
||||
c.on('characteristicvaluechanged', (event)=>wind_updated(event));
|
||||
return c.startNotifications();
|
||||
}).then(function() {
|
||||
console.log("Done!");
|
||||
}).catch(function(e) {
|
||||
console.log("ERROR"+e);
|
||||
});}
|
||||
|
||||
function connection_setup() {
|
||||
NRF.setScan();
|
||||
|
|
@ -96,8 +104,10 @@ if (settings.truewind) {
|
|||
Bangle.on('GPS',function(fix) {
|
||||
if (fix.fix && fix.satellites>3 && fix.speed>2) { // only uses fixes w/ more than 3 sats and speed > 2kph
|
||||
gps_course =
|
||||
{ lon: Math.sin(Math.PI*fix.course/180)*fix.speed/1.852,
|
||||
lat: Math.cos(Math.PI*fix.course/180)*fix.speed/1.852,
|
||||
{ vlon: Math.sin(Math.PI*fix.course/180)*fix.speed/1.852,
|
||||
vlat: Math.cos(Math.PI*fix.course/180)*fix.speed/1.852,
|
||||
lat: fix.lat,
|
||||
lon: fix.lon,
|
||||
spd: fix.speed,
|
||||
course: fix.course
|
||||
};
|
||||
|
|
@ -107,6 +117,20 @@ if (settings.truewind) {
|
|||
Bangle.setGPSPower(1, "app");
|
||||
}
|
||||
|
||||
if (settings.truewind) {
|
||||
Bangle.on("swipe", (d)=>{
|
||||
if (d==-1 && !pause_gps) {
|
||||
pause_gps = true;
|
||||
Bangle.setGPSPower(0);
|
||||
draw_compass(0, 0, 0, 0);
|
||||
}
|
||||
else if (d==1 && pause_gps) {
|
||||
pause_gps = false;
|
||||
Bangle.setGPSPower(1, "app");
|
||||
draw_compass(0, 0, 0, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
draw_compass(0, 0, 0, 0);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "openwind",
|
||||
"name": "OpenWind",
|
||||
"shortName":"OpenWind",
|
||||
"version":"0.01",
|
||||
"version":"0.02",
|
||||
"description": "OpenWind",
|
||||
"icon": "openwind.png",
|
||||
"readme": "README.md",
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
# SciCalc
|
||||
|
||||
Simple scientific calculator. I needed one, so I wrote a basic one, no design frills. Input expressions are slightly post processed and then evaluated
|
||||
by the JS interpreter.
|
||||
|
||||
## Usage
|
||||
|
||||
Buttons are arranged on 3 separate screens, swiping left or right switches between them. Swiping down has the same effect as hitting the "=" button.
|
||||
|
||||
## Features
|
||||
|
||||
The calculator supports the following operations:
|
||||
|
||||
* basic arithmetic: +, -, *, /, ^ (raise to a power), +/- (invert sign), 1/x (inverse), use of parentheses
|
||||
* trigonometric fucntions: sin, cos, tan, asin, acos, atan
|
||||
* exponential exp, natural logarithm log, pow function (this one takes 2 comma separated arguments)
|
||||
* Pi is provided as a constant
|
||||
* a memory button "M" stores or recalls the last result (after hitting the "=" button or swiping down)
|
||||
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwxH+AH4A/AH4AJioAaF1wwSFzowRCQUZo4AWjIvVFy4ABF/4vXyGQAYov/R+sZFy8ZF6oAcF/4vvi4AeF/4SCjseAAMdAx8MAAYvVEAQABAx4v/R/TvvF96PUg8cAAMHd9QuCAAIv/R+rvvF96Pvd94vvR97vvF96Pvd94vvR97vsGDwuQGDouSAH4A/AGwA=="))
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
const W = g.getWidth();
|
||||
const H = g.getHeight();
|
||||
|
||||
const dispH = H/5;
|
||||
const butH = H-dispH;
|
||||
|
||||
const buttons = [[['7', '8', '9'],
|
||||
['4', '5', '6'],
|
||||
['1', '2', '3'],
|
||||
['E', '0', '.']],
|
||||
[['<', 'M', 'C'],
|
||||
['+', '-', '*'],
|
||||
['/', '(', ')'],
|
||||
['^', ',', '=']],
|
||||
[['Sin', 'Cos', 'Tan'],
|
||||
['Asi', 'Aco', 'Ata'],
|
||||
['Pi', '1/x', '+/-'],
|
||||
['Log', 'Exp', 'Pow']
|
||||
]];
|
||||
|
||||
var curPage = 0;
|
||||
var inputStr = '';
|
||||
var memory = '';
|
||||
var qResult = false;
|
||||
|
||||
function drawPage (p) {
|
||||
g.clearRect(0, dispH, W-1, H-1);
|
||||
g.setFont('Vector', butH/5).setFontAlign(0, 0, 0).setColor(g.theme.fg);
|
||||
for (x=0; x<3; ++x)
|
||||
for (y=0; y<4; ++y)
|
||||
g.drawString(buttons[p][y][x], (x+0.5)*W/3, dispH+(y+0.7)*butH/4);
|
||||
g.setColor(0.5, 0.5, 0.5);
|
||||
for (x=1; x<3; ++x) g.drawLine(x*W/3, dispH+0.2*butH/4-2, x*W/3, H-1);
|
||||
for (y=1; y<4; ++y) g.drawLine(0, dispH+(y+0.2)*butH/4, W-1, dispH+(y+0.2)*butH/4);
|
||||
g.setColor(g.theme.fg).drawLine(0, dispH+0.2*butH/4-2, W-1, dispH+0.2*butH/4-2);
|
||||
}
|
||||
|
||||
function updateDisp(s, len) {
|
||||
var fh = butH/5;
|
||||
if (s.toString().length>len) s = s.toString().substr(0,len);
|
||||
g.setFont("Vector", butH/5).setColor(g.theme.fg).setFontAlign(1, 0, 0);
|
||||
while (g.stringWidth(s) > W-1) {
|
||||
fh /= 1.05;
|
||||
g.setFont("Vector", fh);
|
||||
}
|
||||
g.clearRect(0, 0, W-1, dispH-1).drawString(s, W-2, dispH/2);
|
||||
g.setColor(g.theme.fg).drawLine(0, dispH+0.2*butH/4-2, W-1, dispH+0.2*butH/4-2);
|
||||
}
|
||||
|
||||
function processInp (s) {
|
||||
var idx = s.indexOf("^");
|
||||
if (idx > 0) s = "Math.pow(" + s.slice(0,idx) + "," + s.slice(idx+1, s.length) + ")";
|
||||
['Sin', 'Cos', 'Tan', 'Asin', 'Acos', 'Atan', 'Log', 'Exp', 'Pow'].forEach((x) => {
|
||||
var i = s.indexOf(x);
|
||||
while (i>-1) {
|
||||
s = s.slice(0,i)+"Math."+s.slice(i,i+1).toLowerCase()+s.slice(i+1, s.length);
|
||||
i = s.indexOf(x, i+6);
|
||||
}
|
||||
});
|
||||
idx = s.indexOf('Pi');
|
||||
if (idx>-1) s = s.slice(0,idx) + "Math.PI" + s.slice(idx+2, s.length);
|
||||
idx = 0;
|
||||
s.split('').forEach((x)=>{ if (x=='(') idx++; if (x==')') idx-- });
|
||||
s += ')'.repeat(idx);
|
||||
return s;
|
||||
}
|
||||
|
||||
function compute() {
|
||||
var res;
|
||||
console.log(processInp(inputStr));
|
||||
try { res = eval(processInp(inputStr)); }
|
||||
catch(e) { res = "error"; }
|
||||
inputStr = res;
|
||||
qResult = true;
|
||||
updateDisp(inputStr, 19);
|
||||
}
|
||||
|
||||
function touchHandler(e, d) {
|
||||
var x = Math.floor(d.x/(W/3));
|
||||
var y = Math.floor((d.y-dispH-0.2*butH/4)/(butH/4));
|
||||
var c = buttons[curPage][y][x];
|
||||
if (c=="=") { // do the computation
|
||||
compute();
|
||||
return;
|
||||
}
|
||||
else if (c=="<" && inputStr.length>0) inputStr = inputStr.slice(0, -1); // delete last character
|
||||
else if (c=='M' && qResult) memory = inputStr;
|
||||
else if (c=='M') inputStr += memory;
|
||||
else if (c=="C") inputStr = ''; // clear
|
||||
else {
|
||||
if ("Sin Cos Tan Log Exp Pow".indexOf(c)>-1 && c!='E') c += "(";
|
||||
if ("Asi Aco Ata".indexOf(c)>-1) c += "n(";
|
||||
if (c=='1/x') { inputStr = "1/("+inputStr+")"; compute(); return; }
|
||||
if (c=='+/-') { inputStr = "-("+inputStr+")"; compute(); return; }
|
||||
if (qResult && "+-*/^".indexOf(c)==-1) inputStr = c + inputStr + ")";
|
||||
else inputStr += c;
|
||||
}
|
||||
qResult = false;
|
||||
updateDisp(inputStr, 32);
|
||||
}
|
||||
|
||||
function swipeHandler(e,d) {
|
||||
curPage -= e;
|
||||
if (curPage>buttons.length-1) curPage = 0;
|
||||
if (curPage<0) curPage = buttons.length-1;
|
||||
drawPage(curPage);
|
||||
if (d==1) compute();
|
||||
}
|
||||
|
||||
Bangle.on("touch", touchHandler);
|
||||
Bangle.on("swipe", swipeHandler);
|
||||
g.clear();
|
||||
drawPage(curPage);
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{ "id": "scicalc",
|
||||
"name": "Scientific Calculator",
|
||||
"shortName":"SciCalc",
|
||||
"version":"0.01",
|
||||
"description": "Scientific calculator",
|
||||
"icon": "scicalc.png",
|
||||
"readme": "README.md",
|
||||
"tags": "app,tool",
|
||||
"allow_emulator": true,
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"scicalc.app.js","url":"app.js"},
|
||||
{"name":"scicalc.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 562 B |
Loading…
Reference in New Issue