solar clock: First Commit
parent
29f34ad567
commit
03716d5b80
25
apps.json
25
apps.json
|
|
@ -265,6 +265,31 @@
|
||||||
{"name":"slidingtext.dtfmt.js","url":"slidingtext.dtfmt.js"}
|
{"name":"slidingtext.dtfmt.js","url":"slidingtext.dtfmt.js"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{ "id": "solarclock",
|
||||||
|
"name": "Solar Clock",
|
||||||
|
"icon": "solar_clock.png",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "The solar clock will use your current or chosen location to work out the the current sun phase of the day",
|
||||||
|
"tags": "clock",
|
||||||
|
"type":"clock",
|
||||||
|
"allow_emulator":false,
|
||||||
|
"readme": "README.md",
|
||||||
|
"storage": [
|
||||||
|
{"name":"solarclock.app.js","url":"solar_clock.js"},
|
||||||
|
{"name":"solarclock.img","url":"solar_clock-icon.js","evaluate":true},
|
||||||
|
{"name":"solar_colors.js","url":"solar_colors.js"},
|
||||||
|
{"name":"solar_controller.js","url":"solar_controller.js"},
|
||||||
|
{"name":"solar_date_utils.js","url":"solar_date_utils.js"},
|
||||||
|
{"name":"solar_graphic_utils.js","url":"solar_graphic_utils.js"},
|
||||||
|
{"name":"solar_location.js","url":"solar_location.js"},
|
||||||
|
{"name":"solar_math_utils.js","url":"solar_math_utils.js"},
|
||||||
|
{"name":"solar_loc.Iceland.json","url":"solar_loc.Iceland.json"},
|
||||||
|
{"name":"solar_loc.Kauai.json","url":"solar_loc.Kauai.json"},
|
||||||
|
{"name":"solar_loc.Tokyo.json","url":"solar_loc.Tokyo.json"},
|
||||||
|
{"name":"solar_loc.local.json","url":"solar_loc.local.json"}
|
||||||
|
{"name":"solar_locations.json","url":"solar_locations.json"}
|
||||||
|
]
|
||||||
|
},
|
||||||
{ "id": "sweepclock",
|
{ "id": "sweepclock",
|
||||||
"name": "Sweep Clock",
|
"name": "Sweep Clock",
|
||||||
"icon": "sweepclock.png",
|
"icon": "sweepclock.png",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: Initial Release
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
# Solar Clock
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
require("heatshrink").decompress(atob("lEowkA/4AGmYIHABHzmVCCaE0kUin4TPmUimQTQ+UzmcvJ6EjCaP/kYABCaEymYTl+Q7SMgITTmQTQPAK0RMgITm+QTS+ciPCcikQpPY4MjmYTO+czmcyHh4TCmcvJ54nCPCBjBJx4oECc8zJ6ATTn48RE4YTTHh4SDH4ImRFBwTGFBgTGFBgSGFBYmHUgITRmcyFBASFAoUjE5PzkQLBHJxiDAQP/GxAA=="))
|
||||||
|
|
@ -0,0 +1,466 @@
|
||||||
|
const DateUtils = require("solar_date_utils.js");
|
||||||
|
const Math2 = require("solar_math_utils.js");
|
||||||
|
const GraphicUtils = require("solar_graphic_utils.js");
|
||||||
|
const Colors = require("solar_colors.js");
|
||||||
|
const LocationUtils = require("solar_location.js");
|
||||||
|
|
||||||
|
var screen_info = {
|
||||||
|
screen_width : g.getWidth(),
|
||||||
|
screen_start_x : 0,
|
||||||
|
screen_centre_x: g.getWidth()/2,
|
||||||
|
screen_height : (g.getHeight()-100),
|
||||||
|
screen_start_y : 100,
|
||||||
|
screen_centre_y : 90 + (g.getHeight()-100)/2,
|
||||||
|
screen_bg_color : Colors.BLACK,
|
||||||
|
sun_radius: 8,
|
||||||
|
sun_x : null,
|
||||||
|
sun_y : null,
|
||||||
|
sunrise_y : null,
|
||||||
|
}
|
||||||
|
const img_width=40;
|
||||||
|
const img_height=30;
|
||||||
|
var img_buffer = Graphics.createArrayBuffer(img_width,img_height,8);
|
||||||
|
var img = {width:img_width,height:img_height,bpp:8,transparent:0,buffer:img_buffer.buffer};
|
||||||
|
var img_info = {
|
||||||
|
x: null,
|
||||||
|
y: null,
|
||||||
|
img: img,
|
||||||
|
img_buffer: img_buffer
|
||||||
|
}
|
||||||
|
const COSINE_COLOUR= Colors.GREY;
|
||||||
|
const HORIZON_COLOUR = Colors.GREY;
|
||||||
|
const SolarController = require("solar_controller.js");
|
||||||
|
var controller = new SolarController();
|
||||||
|
var curr_mode = null;
|
||||||
|
var last_sun_draw_time = null;
|
||||||
|
var draw_full_cosine = true;
|
||||||
|
|
||||||
|
function draw_sun(now, day_info) {
|
||||||
|
|
||||||
|
var now_fraction = (now.getTime() - day_info.day_start.getTime())/DateUtils.DAY_MILLIS;
|
||||||
|
var now_x = now_fraction * screen_info.screen_width;
|
||||||
|
if(screen_info.sun_x != null && Math.abs(now_x- screen_info.sun_x) < 1){
|
||||||
|
console.log("no sun movement");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// now calculate thew new sun coordinates
|
||||||
|
var now_radians = Math2.TWO_PI *(now_x - screen_info.screen_centre_x)/screen_info.screen_width;
|
||||||
|
var now_y = screen_info.screen_centre_y - (screen_info.screen_height * Math.cos(now_radians) / 2);
|
||||||
|
if(Math.abs(now_x - screen_info.sun_x) > 5){
|
||||||
|
clear_sun();
|
||||||
|
}
|
||||||
|
// update the screen info with the new sun info
|
||||||
|
screen_info.sun_x = now_x;
|
||||||
|
screen_info.sun_y = now_y;
|
||||||
|
last_sun_draw_time = now;
|
||||||
|
|
||||||
|
if(draw_full_cosine){
|
||||||
|
//console.log("drawing full cosine");
|
||||||
|
GraphicUtils.draw_cosine(screen_info.screen_start_x,
|
||||||
|
screen_info.screen_width,
|
||||||
|
COSINE_COLOUR,
|
||||||
|
screen_info);
|
||||||
|
draw_full_cosine = false;
|
||||||
|
}
|
||||||
|
if(curr_mode == null) {
|
||||||
|
GraphicUtils.draw_sunrise_line(HORIZON_COLOUR, day_info, screen_info);
|
||||||
|
}
|
||||||
|
// decide on the new sun drawing mode and draw
|
||||||
|
curr_mode = controller.mode(now,day_info,screen_info);
|
||||||
|
img_info.img_buffer.clear();
|
||||||
|
img_info.img_buffer.setColor(screen_info.screen_bg_color[0],
|
||||||
|
screen_info.screen_bg_color[1],
|
||||||
|
screen_info.screen_bg_color[2],
|
||||||
|
);
|
||||||
|
img_info.img_buffer.fillRect(0,0,img_width, img_height);
|
||||||
|
img_info.x = screen_info.sun_x - img_info.img.width/2;
|
||||||
|
img_info.y = screen_info.sun_y - img_info.img.height/2;
|
||||||
|
|
||||||
|
var cosine_dist = screen_info.sun_radius/Math.sqrt(2);
|
||||||
|
GraphicUtils.draw_cosine(img_info.x,
|
||||||
|
screen_info.sun_x - cosine_dist,
|
||||||
|
COSINE_COLOUR,
|
||||||
|
screen_info,
|
||||||
|
img_info);
|
||||||
|
GraphicUtils.draw_cosine(screen_info.sun_x + cosine_dist,
|
||||||
|
screen_info.sun_x + img_width,
|
||||||
|
COSINE_COLOUR,
|
||||||
|
screen_info,
|
||||||
|
img_info);
|
||||||
|
|
||||||
|
curr_mode.draw(now,day_info,screen_info,img_info);
|
||||||
|
|
||||||
|
var sunrise_dist = Math.abs(screen_info.sunrise_y-screen_info.sun_y);
|
||||||
|
if( sunrise_dist <= img_height) {
|
||||||
|
GraphicUtils.draw_sunrise_line(HORIZON_COLOUR, day_info, screen_info,img_info);
|
||||||
|
} else if(sunrise_dist <= img_height*2.5) {
|
||||||
|
GraphicUtils.draw_sunrise_line(HORIZON_COLOUR, day_info, screen_info);
|
||||||
|
}
|
||||||
|
// we draw a blank where the image is going to be drawn to clear out the area
|
||||||
|
g.setColor(screen_info.screen_bg_color[0],screen_info.screen_bg_color[1],screen_info.screen_bg_color[2]);
|
||||||
|
g.fillRect(img_info.x,img_info.y-2,img_info.x+img_width,img_info.y + img_height + 2);
|
||||||
|
g.drawImage(img,img_info.x,img_info.y);
|
||||||
|
// paint the cosine curve back to the normal color where it just came from
|
||||||
|
GraphicUtils.draw_cosine(img_info.x - 3,
|
||||||
|
img_info.x,
|
||||||
|
COSINE_COLOUR,
|
||||||
|
screen_info);
|
||||||
|
GraphicUtils.draw_cosine(img_info.x + img_width,
|
||||||
|
img_info.x + img_width + 3,
|
||||||
|
COSINE_COLOUR,
|
||||||
|
screen_info);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear_sun(){
|
||||||
|
g.setColor(screen_info.screen_bg_color[0],screen_info.screen_bg_color[1],screen_info.screen_bg_color[2]);
|
||||||
|
g.fillRect(img_info.x,img_info.y,img_info.x+img_width,img_info.y + img_width);
|
||||||
|
GraphicUtils.draw_cosine(img_info.x - 4,
|
||||||
|
img_info.x + img_width + 4,
|
||||||
|
COSINE_COLOUR,
|
||||||
|
screen_info);
|
||||||
|
GraphicUtils.draw_sunrise_line(HORIZON_COLOUR, day_info, screen_info);
|
||||||
|
screen_info.sun_x = null;
|
||||||
|
screen_info.sun_y = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var last_time = null;
|
||||||
|
var last_offset = null;
|
||||||
|
var last_date = null;
|
||||||
|
|
||||||
|
const time_color = Colors.WHITE;
|
||||||
|
const date_color = Colors.YELLOW;
|
||||||
|
const DATE_Y_COORD = 35;
|
||||||
|
const DATE_X_COORD = 10;
|
||||||
|
const TIME_X_COORD = 140;
|
||||||
|
const TIME_Y_COORD = 35;
|
||||||
|
const OFFSET_Y_COORD = 70;
|
||||||
|
const LOCATION_Y_COORD = 55;
|
||||||
|
|
||||||
|
function write_date(now){
|
||||||
|
var new_date = require('locale').dow(now,1) + " " + Math2.format00(now.getDate());
|
||||||
|
//console.log("writing date:" + new_date)
|
||||||
|
g.setFont("Vector",15);
|
||||||
|
g.setFontAlign(-1,-1,0);
|
||||||
|
if(last_date != null){
|
||||||
|
if(new_date == last_date){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g.setColor(screen_info.screen_bg_color[0],
|
||||||
|
screen_info.screen_bg_color[1],
|
||||||
|
screen_info.screen_bg_color[2]);
|
||||||
|
g.drawString(last_date, DATE_X_COORD,DATE_Y_COORD);
|
||||||
|
}
|
||||||
|
g.setColor(date_color[0],date_color[1],date_color[2]);
|
||||||
|
g.drawString(new_date, DATE_X_COORD,DATE_Y_COORD);
|
||||||
|
last_date = new_date;
|
||||||
|
}
|
||||||
|
|
||||||
|
var last_status_msg = ""
|
||||||
|
var last_gps_coords_msg_n = "";
|
||||||
|
var last_gps_coords_msg_e = "";
|
||||||
|
const GPS_MSG_X_COORD = 70;
|
||||||
|
const GPS_MSG_Y = 220;
|
||||||
|
const GPS_MSG_COORDS_Y_E = 80;
|
||||||
|
const GPS_MSG_COORDS_Y_N = 90;
|
||||||
|
|
||||||
|
function write_GPS_status(){
|
||||||
|
var gps_coords = location.getCoordinates();
|
||||||
|
var gps_coords_msg_n;
|
||||||
|
var gps_coords_msg_e;
|
||||||
|
if(gps_coords != null){
|
||||||
|
gps_coords_msg_n = "N: " + gps_coords[0];
|
||||||
|
gps_coords_msg_n = gps_coords_msg_n.substr(0,Math.min(gps_coords_msg_n.length - 1,10));
|
||||||
|
gps_coords_msg_e = "E: " + gps_coords[1];
|
||||||
|
gps_coords_msg_e = gps_coords_msg_e.substr(0,Math.min(gps_coords_msg_e.length - 1,10));
|
||||||
|
} else {
|
||||||
|
gps_coords_msg_n = "";
|
||||||
|
gps_coords_msg_e = "";
|
||||||
|
}
|
||||||
|
var status_msg = "";
|
||||||
|
if(location.isGPSLocation()) {
|
||||||
|
if(gps_coords == null) {
|
||||||
|
if (location.getGPSPower() > 0) {
|
||||||
|
status_msg = "Finding GPS Position";
|
||||||
|
} else {
|
||||||
|
status_msg = "ERROR GPS Position not found";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (location.getGPSPower() > 0) {
|
||||||
|
status_msg = "Updating GPS Position";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.setFont("Vector",11);
|
||||||
|
g.setFontAlign(-1,-1,0);
|
||||||
|
if(last_status_msg != status_msg) {
|
||||||
|
g.setColor(screen_info.screen_bg_color[0],
|
||||||
|
screen_info.screen_bg_color[1],
|
||||||
|
screen_info.screen_bg_color[2]);
|
||||||
|
g.drawString(last_status_msg, GPS_MSG_X_COORD, GPS_MSG_Y);
|
||||||
|
g.setColor(Colors.YELLOW[0],Colors.YELLOW[1],Colors.YELLOW[2]);
|
||||||
|
g.drawString(status_msg, GPS_MSG_X_COORD, GPS_MSG_Y);
|
||||||
|
last_status_msg = status_msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(last_gps_coords_msg_e != gps_coords_msg_e) {
|
||||||
|
g.setColor(screen_info.screen_bg_color[0],
|
||||||
|
screen_info.screen_bg_color[1],
|
||||||
|
screen_info.screen_bg_color[2]);
|
||||||
|
g.drawString(last_gps_coords_msg_e, DATE_X_COORD, GPS_MSG_COORDS_Y_E);
|
||||||
|
g.drawString(last_gps_coords_msg_n, DATE_X_COORD, GPS_MSG_COORDS_Y_N);
|
||||||
|
g.setColor(Colors.WHITE[0],Colors.WHITE[1],Colors.WHITE[2]);
|
||||||
|
|
||||||
|
g.drawString(gps_coords_msg_e, DATE_X_COORD, GPS_MSG_COORDS_Y_E);
|
||||||
|
g.drawString(gps_coords_msg_n, DATE_X_COORD, GPS_MSG_COORDS_Y_N);
|
||||||
|
last_gps_coords_msg_e = gps_coords_msg_e;
|
||||||
|
last_gps_coords_msg_n = gps_coords_msg_n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_time(now){
|
||||||
|
var new_time = format_time(now);
|
||||||
|
g.setFont("Vector",35);
|
||||||
|
g.setFontAlign(-1,-1,0);
|
||||||
|
if(last_time != null){
|
||||||
|
g.setColor(screen_info.screen_bg_color[0],screen_info.screen_bg_color[1],screen_info.screen_bg_color[2]);
|
||||||
|
g.drawString(last_time, TIME_X_COORD,TIME_Y_COORD);
|
||||||
|
}
|
||||||
|
g.setColor(time_color[0],time_color[1],time_color[2]);
|
||||||
|
g.drawString(new_time, TIME_X_COORD,TIME_Y_COORD);
|
||||||
|
last_time = new_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
function format_time(now){
|
||||||
|
var time = new Date(now.getTime() - time_offset);
|
||||||
|
var hours = time.getHours() % 12;
|
||||||
|
if(hours < 1){
|
||||||
|
hours = 12;
|
||||||
|
}
|
||||||
|
return Math2.format00(hours) + ":" + Math2.format00(time.getMinutes());
|
||||||
|
}
|
||||||
|
|
||||||
|
function write_offset(){
|
||||||
|
var new_offset = format_offset();
|
||||||
|
g.setFont("Vector",15);
|
||||||
|
g.setFontAlign(-1,-1,0);
|
||||||
|
if(last_offset != null){
|
||||||
|
g.setColor(screen_info.screen_bg_color[0],screen_info.screen_bg_color[1],screen_info.screen_bg_color[2]);
|
||||||
|
g.drawString(last_offset, TIME_X_COORD,OFFSET_Y_COORD);
|
||||||
|
}
|
||||||
|
g.setColor(time_color[0],time_color[1],time_color[2]);
|
||||||
|
g.drawString(new_offset, TIME_X_COORD,OFFSET_Y_COORD);
|
||||||
|
last_offset = new_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
function format_offset(){
|
||||||
|
if(time_offset == 0)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
var hours_offset = Math.abs(time_offset) / DateUtils.HOUR_MILLIS;
|
||||||
|
var mins_offset = Math.abs(time_offset) / DateUtils.MIN_MILLIS;
|
||||||
|
var mins_offset_from_hour = mins_offset % 60;
|
||||||
|
//console.log("mins offset=" + mins_offset + " mins_offset_from_hour=" + mins_offset_from_hour);
|
||||||
|
var sign = "+";
|
||||||
|
if(time_offset < 0)
|
||||||
|
sign = "-";
|
||||||
|
|
||||||
|
return sign + Math2.format00(hours_offset) + ":" + Math2.format00(mins_offset_from_hour);
|
||||||
|
}
|
||||||
|
|
||||||
|
let time_offset = 0;
|
||||||
|
let last_draw_time = null;
|
||||||
|
var day_info = null;
|
||||||
|
var location = LocationUtils.load_locations();
|
||||||
|
var last_location_name = null;
|
||||||
|
|
||||||
|
function write_location_name() {
|
||||||
|
var new_location_name = location.getName();
|
||||||
|
g.setFont("Vector", 20);
|
||||||
|
g.setFontAlign(-1, -1, 0);
|
||||||
|
if (last_location_name != null) {
|
||||||
|
g.setColor(screen_info.screen_bg_color[0], screen_info.screen_bg_color[1], screen_info.screen_bg_color[2]);
|
||||||
|
g.drawString(last_location_name, DATE_X_COORD, LOCATION_Y_COORD);
|
||||||
|
}
|
||||||
|
g.setColor(time_color[0], time_color[1], time_color[2]);
|
||||||
|
if (new_location_name != "local") {
|
||||||
|
g.drawString(new_location_name, DATE_X_COORD, LOCATION_Y_COORD);
|
||||||
|
}
|
||||||
|
last_location_name = new_location_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
location.addUpdateListener(
|
||||||
|
(loc)=>{
|
||||||
|
console.log("location update:" + JSON.stringify(loc));
|
||||||
|
clear_sun();
|
||||||
|
GraphicUtils.draw_sunrise_line(screen_info.screen_bg_color, day_info, screen_info);
|
||||||
|
day_info = null;
|
||||||
|
screen_info.sunrise_y = null;
|
||||||
|
curr_mode = null;
|
||||||
|
draw_clock();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function dayInfo(now) {
|
||||||
|
if (day_info == null || now > day_info.day_end) {
|
||||||
|
var coords = location.getCoordinates();
|
||||||
|
if(coords != null) {
|
||||||
|
day_info = DateUtils.sunrise_sunset(now, coords[0], coords[1], location.getUTCOffset());
|
||||||
|
//console.log("day info:" + JSON.stringify(day_info));
|
||||||
|
} else {
|
||||||
|
day_info = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return day_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
function time_now() {
|
||||||
|
var timezone_offset_hours = location.getUTCOffset();
|
||||||
|
if(timezone_offset_hours != null) {
|
||||||
|
var local_offset_hours = -new Date().getTimezoneOffset()/60;
|
||||||
|
var timezone_offset_millis =
|
||||||
|
(timezone_offset_hours - local_offset_hours) * DateUtils.HOUR_MILLIS;
|
||||||
|
return new Date(Date.now() + time_offset + timezone_offset_millis);
|
||||||
|
} else {
|
||||||
|
return new Date(Date.now() + time_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function draw_clock(){
|
||||||
|
var start_time = Date.now();
|
||||||
|
var now = time_now();
|
||||||
|
|
||||||
|
var day_info = dayInfo(now);
|
||||||
|
if(day_info != null) {
|
||||||
|
draw_sun(now, day_info);
|
||||||
|
}
|
||||||
|
write_time(now);
|
||||||
|
write_date(now);
|
||||||
|
write_offset();
|
||||||
|
write_location_name();
|
||||||
|
write_GPS_status();
|
||||||
|
last_draw_time = now;
|
||||||
|
log_memory_used();
|
||||||
|
var time_taken = Date.now() - start_time;
|
||||||
|
console.log("drawing clock:" + now.toISOString() + " time taken:" + time_taken );
|
||||||
|
}
|
||||||
|
|
||||||
|
function log_memory_used() {
|
||||||
|
var memory = process.memory();
|
||||||
|
console.log("memory used:" + memory.usage +
|
||||||
|
" total:" + memory.total + "->" +
|
||||||
|
" ->" + memory.usage/memory.total
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function button1pressed(){
|
||||||
|
console.log("button 1 pressed");
|
||||||
|
time_offset = 0;
|
||||||
|
clear_sun();
|
||||||
|
day_info = null;
|
||||||
|
draw_clock();
|
||||||
|
}
|
||||||
|
|
||||||
|
function button3pressed(){
|
||||||
|
console.log("button 3 pressed");
|
||||||
|
time_offset = 0;
|
||||||
|
location.nextLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
function button4pressed(){
|
||||||
|
time_offset -= DateUtils.HOUR_MILLIS/4;
|
||||||
|
draw_clock();
|
||||||
|
setTimeout(()=>{
|
||||||
|
if(BTN4.read()){
|
||||||
|
button4pressed();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
50
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function button5pressed(){
|
||||||
|
time_offset += DateUtils.HOUR_MILLIS/4;
|
||||||
|
draw_clock();
|
||||||
|
setTimeout(()=>{
|
||||||
|
if(BTN5.read()){
|
||||||
|
button5pressed();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
50
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The interval reference for updating the clock
|
||||||
|
let interval_ref = null;
|
||||||
|
function clear_timers(){
|
||||||
|
if(interval_ref != null) {
|
||||||
|
clearInterval(interval_ref);
|
||||||
|
interval_ref = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function start_timers(){
|
||||||
|
var date = new Date();
|
||||||
|
var secs = date.getSeconds();
|
||||||
|
var nextMinuteStart = 60 - secs;
|
||||||
|
setTimeout(schedule_draw_clock,nextMinuteStart * 1000);
|
||||||
|
draw_clock();
|
||||||
|
}
|
||||||
|
function schedule_draw_clock(){
|
||||||
|
clear_timers();
|
||||||
|
if (Bangle.isLCDOn()) {
|
||||||
|
interval_ref = setInterval(() => {
|
||||||
|
if (!Bangle.isLCDOn()) {
|
||||||
|
console.log("draw clock callback - skipped redraw");
|
||||||
|
} else {
|
||||||
|
draw_clock();
|
||||||
|
}
|
||||||
|
}, DateUtils.MIN_MILLIS
|
||||||
|
);
|
||||||
|
draw_clock();
|
||||||
|
} else {
|
||||||
|
console.log("scheduleDrawClock - skipped not visible");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Bangle.on('lcdPower', (on) => {
|
||||||
|
if (on) {
|
||||||
|
console.log("lcdPower: on");
|
||||||
|
draw_clock();
|
||||||
|
start_timers();
|
||||||
|
} else {
|
||||||
|
console.log("lcdPower: off");
|
||||||
|
clear_timers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Bangle.on('faceUp',function(up){
|
||||||
|
if (up && !Bangle.isLCDOn()) {
|
||||||
|
clear_timers();
|
||||||
|
Bangle.setLCDPower(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
g.clear();
|
||||||
|
Bangle.loadWidgets();
|
||||||
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
|
start_timers();
|
||||||
|
|
||||||
|
function button2pressed(){
|
||||||
|
controller = null;
|
||||||
|
|
||||||
|
location.shutdown();
|
||||||
|
location = null;
|
||||||
|
|
||||||
|
Bangle.showLauncher();
|
||||||
|
}
|
||||||
|
setWatch(button2pressed, BTN2,{repeat:false,edge:"falling"});
|
||||||
|
setWatch(button1pressed, BTN1,{repeat:true,edge:"falling"});
|
||||||
|
setWatch(button3pressed, BTN3,{repeat:true,edge:"falling"});
|
||||||
|
setWatch(button4pressed, BTN4,{repeat:true,edge:"rising"});
|
||||||
|
setWatch(button5pressed, BTN5,{repeat:true,edge:"rising"});
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
|
|
@ -0,0 +1,14 @@
|
||||||
|
const _BLACK= [0.0,0.0,0.0];
|
||||||
|
const _GREY= [0.5,0.5,0.5];
|
||||||
|
const _WHITE= [1.0,1.0,1.0];
|
||||||
|
const _YELLOW= [1.0,1.0,0.0];
|
||||||
|
const _RED= [1.0,0.0,0.0];
|
||||||
|
|
||||||
|
const Colors = {
|
||||||
|
BLACK: _BLACK,
|
||||||
|
GREY: _GREY,
|
||||||
|
WHITE: _WHITE,
|
||||||
|
YELLOW: _YELLOW,
|
||||||
|
RED: _RED
|
||||||
|
};
|
||||||
|
module.exports = Colors;
|
||||||
|
|
@ -0,0 +1,247 @@
|
||||||
|
const Math2 = require("solar_math_utils.js");
|
||||||
|
const DateUtils = require("solar_date_utils.js");
|
||||||
|
const GraphicUtils = require("solar_graphic_utils.js");
|
||||||
|
const Colors = require("solar_colors.js");
|
||||||
|
|
||||||
|
const CORONA_GAP = 2;
|
||||||
|
const CORONA_MIN_LENGTH = 2;
|
||||||
|
const CORONA_LINES = 12;
|
||||||
|
const CORONA_RADIUS = 14;
|
||||||
|
const SUNSET_START_HEIGHT = 10;
|
||||||
|
const SUNSET_COLOUR = Colors.RED;
|
||||||
|
const SUNRISE_COLOUR = [1,0.6,0];
|
||||||
|
const MIDDAY_COLOUR = [1,1,0.7];
|
||||||
|
const NIGHT_COLOUR = Colors.BLACK;
|
||||||
|
|
||||||
|
function daytime_sun_color(now,day_info){
|
||||||
|
var now_fraction_of_day =DateUtils.now_fraction_of_day(now,day_info);
|
||||||
|
if(now > day_info.sunset_date){
|
||||||
|
return SUNSET_COLOUR;
|
||||||
|
} else if(now < day_info.sunrise_date){
|
||||||
|
return SUNRISE_COLOUR;
|
||||||
|
} else if(now < day_info.solar_noon) {
|
||||||
|
var sunrise_fraction = (day_info.sunrise_date - day_info.day_start) / DateUtils.DAY_MILLIS;
|
||||||
|
var rise_to_midday_fraction = (now_fraction_of_day - sunrise_fraction) / (0.5 - sunrise_fraction);
|
||||||
|
return Math2.interpolate(SUNRISE_COLOUR, MIDDAY_COLOUR, rise_to_midday_fraction);
|
||||||
|
} else {
|
||||||
|
var sunset_fraction = (day_info.day_end - day_info.sunset_date) / DateUtils.DAY_MILLIS;
|
||||||
|
var midday_to_sunset_fraction = (now_fraction_of_day - 0.5)/(0.5 - sunset_fraction);
|
||||||
|
//console.log("sunset_fraction=" + sunset_fraction + " midday_to_sunset_fraction=" + midday_to_sunset_fraction)
|
||||||
|
return Math2.interpolate(MIDDAY_COLOUR,SUNSET_COLOUR,midday_to_sunset_fraction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw_night_sun(sun_x,sun_y,sun_radius,img_info){
|
||||||
|
var draw_info = GraphicUtils.draw_info(img_info);
|
||||||
|
draw_info.buff.setColor(Colors.WHITE[0],Colors.WHITE[1],Colors.WHITE[2]);
|
||||||
|
draw_info.buff.fillCircle(sun_x - draw_info.offset_x,
|
||||||
|
sun_y - draw_info.offset_y,
|
||||||
|
sun_radius);
|
||||||
|
draw_info.buff.setColor(NIGHT_COLOUR[0],NIGHT_COLOUR[1],NIGHT_COLOUR[2]);
|
||||||
|
draw_info.buff.fillCircle(sun_x - draw_info.offset_x,
|
||||||
|
sun_y - draw_info.offset_y,
|
||||||
|
sun_radius-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw_partial_sun(time, day_info, screen_info,img_info){
|
||||||
|
var sun_height = screen_info.sunrise_y - screen_info.sun_y;
|
||||||
|
if(sun_height > screen_info.sun_radius){
|
||||||
|
var sun_color = daytime_sun_color(time,day_info);
|
||||||
|
var draw_info = GraphicUtils.draw_info(img_info);
|
||||||
|
draw_info.buff.setColor(sun_color[0],sun_color[1],sun_color[2]);
|
||||||
|
draw_info.buff.fillCircle(screen_info.sun_x - draw_info.offset_x,
|
||||||
|
screen_info.sun_y - draw_info.offset_y,
|
||||||
|
screen_info.sun_radius
|
||||||
|
);
|
||||||
|
} else if(sun_height < -screen_info.sun_radius){
|
||||||
|
draw_night_sun(screen_info.sun_x,screen_info.sun_y,screen_info.sun_radius, img_info);
|
||||||
|
} else {
|
||||||
|
var draw_info = GraphicUtils.draw_info(img_info);
|
||||||
|
draw_info.buff.setColor(NIGHT_COLOUR[0],NIGHT_COLOUR[1],NIGHT_COLOUR[2]);
|
||||||
|
draw_info.buff.fillCircle(screen_info.sun_x - draw_info.offset_x,
|
||||||
|
screen_info.sun_y - draw_info.offset_y,
|
||||||
|
screen_info.sun_radius-1);
|
||||||
|
var sun_color = daytime_sun_color(time,day_info);
|
||||||
|
draw_info.buff.setColor(sun_color[0],sun_color[1],sun_color[2]);
|
||||||
|
draw_info.buff.drawCircle(screen_info.sun_x - draw_info.offset_x,
|
||||||
|
screen_info.sun_y - draw_info.offset_y,
|
||||||
|
screen_info.sun_radius);
|
||||||
|
GraphicUtils.fill_circle_partial_y(screen_info.sun_x,
|
||||||
|
screen_info.sun_y,
|
||||||
|
screen_info.sun_radius,
|
||||||
|
screen_info.sun_y - screen_info.sun_radius,
|
||||||
|
screen_info.sunrise_y,
|
||||||
|
img_info
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function draw_random_background(screen_info,
|
||||||
|
img_info,
|
||||||
|
rgb_init,
|
||||||
|
rgb_step
|
||||||
|
){
|
||||||
|
var draw_info = GraphicUtils.draw_info(img_info);
|
||||||
|
var rgb = rgb_init;
|
||||||
|
var sky_to = Math.min(screen_info.sunrise_y-1, img_info.y + img_info.img.height - 3);
|
||||||
|
for(var sky_y=img_info.y+3;sky_y<sky_to ; sky_y++){
|
||||||
|
for(var i=0;i<rgb.length;i++){
|
||||||
|
if(rgb_step[i]>0)
|
||||||
|
rgb[i] = Math2.random_walk(rgb[i],rgb_step[i],1,0);
|
||||||
|
}
|
||||||
|
draw_info.buff.setColor(rgb[0],rgb[1],rgb[2]);
|
||||||
|
draw_info.buff.moveTo(screen_info.sun_x +
|
||||||
|
Math.random()*img_info.img.width/8 -
|
||||||
|
0.4*img_info.img.width -
|
||||||
|
draw_info.offset_x,
|
||||||
|
sky_y - draw_info.offset_y);
|
||||||
|
draw_info.buff.lineTo(screen_info.sun_x +
|
||||||
|
0.4*img_info.img.width -
|
||||||
|
Math.random()*img_info.img.width/8 -
|
||||||
|
draw_info.offset_x,
|
||||||
|
sky_y - draw_info.offset_y);
|
||||||
|
}
|
||||||
|
draw_info.buff.setColor(NIGHT_COLOUR[0],NIGHT_COLOUR[1],NIGHT_COLOUR[2]);
|
||||||
|
draw_info.buff.fillCircle(screen_info.sun_x - draw_info.offset_x,
|
||||||
|
screen_info.sun_y - draw_info.offset_y,
|
||||||
|
screen_info.sun_radius+1);
|
||||||
|
}
|
||||||
|
class SolarMode {
|
||||||
|
test(time, day_info, screen_info){ throw "test undefined";}
|
||||||
|
draw(time, day_info, screen_info, img_buffer_info){
|
||||||
|
throw "sun drawing undefined";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class NightMode extends SolarMode {
|
||||||
|
toString(){return "NightMode";}
|
||||||
|
test(time, day_info, screen_info, img_info) {
|
||||||
|
return (time < day_info.sunrise_date || time > day_info.sunset_date);
|
||||||
|
}
|
||||||
|
draw(time, day_info, screen_info, img_info){
|
||||||
|
draw_night_sun(screen_info.sun_x,screen_info.sun_y,screen_info.sun_radius, img_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class DayLightMode extends SolarMode {
|
||||||
|
toString(){
|
||||||
|
return "DayLightMode";
|
||||||
|
}
|
||||||
|
test(time, day_info, screen_info){
|
||||||
|
var sun_height = screen_info.sunrise_y - screen_info.sun_y;
|
||||||
|
/*console.log("DayLightMode " +
|
||||||
|
"time=" + time.toISOString() +
|
||||||
|
" sunset_date=" + day_info.sunset_date.toISOString() +
|
||||||
|
" sunrise_date=" + day_info.sunrise_date.toISOString()
|
||||||
|
);*/
|
||||||
|
return time < day_info.sunset_date &&
|
||||||
|
time > day_info.sunrise_date &&
|
||||||
|
sun_height >= screen_info.sun_radius * 2 + SUNSET_START_HEIGHT;
|
||||||
|
}
|
||||||
|
_calc_corona_radius(now, day_info){
|
||||||
|
if(now < day_info.sunset_date &&
|
||||||
|
now > day_info.sunrise_date){
|
||||||
|
var now_fraction_of_day =DateUtils.now_fraction_of_day(now,day_info);
|
||||||
|
var sunset_fraction = (day_info.sunset_date.getTime() - day_info.day_start.getTime())/DateUtils.DAY_MILLIS;
|
||||||
|
var now_fraction_from_midday =
|
||||||
|
1 - Math.abs(now_fraction_of_day-0.5)/(sunset_fraction-0.5);
|
||||||
|
return CORONA_RADIUS * now_fraction_from_midday;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_drawCorona(corona_radius,sun_x,sun_y,sun_radius, draw_info){
|
||||||
|
var thickness_rads = (Math2.TWO_PI/CORONA_LINES)/3;
|
||||||
|
var from_radius = sun_radius + CORONA_GAP;
|
||||||
|
if(corona_radius > from_radius + CORONA_MIN_LENGTH) {
|
||||||
|
for (var i = 0; i < CORONA_LINES; i++) {
|
||||||
|
var to_x1 = sun_x - draw_info.offset_x + from_radius * Math.cos(i * Math2.TWO_PI / CORONA_LINES + thickness_rads);
|
||||||
|
var to_y1 = sun_y - draw_info.offset_y + from_radius * Math.sin(i * Math2.TWO_PI / CORONA_LINES + thickness_rads);
|
||||||
|
var to_x2 = sun_x - draw_info.offset_x + from_radius * Math.cos(i * Math2.TWO_PI / CORONA_LINES - thickness_rads);
|
||||||
|
var to_y2 = sun_y - draw_info.offset_y + from_radius * Math.sin(i * Math2.TWO_PI / CORONA_LINES - thickness_rads);
|
||||||
|
var to_x3 = sun_x - draw_info.offset_x + corona_radius * Math.cos(i * Math2.TWO_PI / CORONA_LINES);
|
||||||
|
var to_y3 = sun_y - draw_info.offset_y + corona_radius * Math.sin(i * Math2.TWO_PI / CORONA_LINES);
|
||||||
|
draw_info.buff.fillPoly([to_x1, to_y1, to_x2, to_y2, to_x3, to_y3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
draw(now, day_info, screen_info, img_info){
|
||||||
|
var sun_color = daytime_sun_color(now,day_info);
|
||||||
|
var corona_radius = this._calc_corona_radius(now, day_info);
|
||||||
|
var draw_info = GraphicUtils.draw_info(img_info);
|
||||||
|
draw_info.buff.setColor(sun_color[0],sun_color[1],sun_color[2]);
|
||||||
|
if(corona_radius > screen_info.sun_radius){
|
||||||
|
this._drawCorona(corona_radius,
|
||||||
|
screen_info.sun_x,
|
||||||
|
screen_info.sun_y,
|
||||||
|
screen_info.sun_radius,
|
||||||
|
draw_info);
|
||||||
|
}
|
||||||
|
draw_info.buff.fillCircle(screen_info.sun_x - draw_info.offset_x,
|
||||||
|
screen_info.sun_y - draw_info.offset_y,
|
||||||
|
screen_info.sun_radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class TwiLightMode extends SolarMode {
|
||||||
|
toString(){
|
||||||
|
return "TwilightMode";
|
||||||
|
}
|
||||||
|
test(time, day_info, screen_info){
|
||||||
|
if(screen_info.sunrise_y == null) {
|
||||||
|
console.log("warning no sunrise_defined");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var sun_height = screen_info.sunrise_y - screen_info.sun_y;
|
||||||
|
/*console.log("TwilightMode " +
|
||||||
|
"time=" + time.toISOString() +
|
||||||
|
" sun_height=" + sun_height +
|
||||||
|
" sun_radius=" + screen_info.sun_radius
|
||||||
|
);*/
|
||||||
|
if(sun_height > -screen_info.sun_radius &&
|
||||||
|
sun_height < screen_info.sun_radius * 2 + SUNSET_START_HEIGHT
|
||||||
|
){
|
||||||
|
//console.log("selected TwilightMode");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
draw(time, day_info, screen_info, img_info){
|
||||||
|
if(time < day_info.solar_noon) {
|
||||||
|
draw_random_background(screen_info,
|
||||||
|
img_info,
|
||||||
|
[0,0.8,1],
|
||||||
|
[0.05,0.05,0.0]);
|
||||||
|
} else {
|
||||||
|
draw_random_background(screen_info,
|
||||||
|
img_info,
|
||||||
|
[1,0.75,Math.random()],
|
||||||
|
[0,0.05,0.05]);
|
||||||
|
}
|
||||||
|
draw_partial_sun(time,day_info,screen_info,img_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class SolarControllerImpl {
|
||||||
|
constructor(){
|
||||||
|
this.solar_modes = [new TwiLightMode(), new DayLightMode()];
|
||||||
|
this.default_mode = new NightMode();
|
||||||
|
this.last = null;
|
||||||
|
}
|
||||||
|
toString(){
|
||||||
|
return "SolarControllerImpl";
|
||||||
|
}
|
||||||
|
mode(time, day_info, screen_info){
|
||||||
|
if(this.last != null){
|
||||||
|
if(this.last.test(time,day_info,screen_info)){
|
||||||
|
return this.last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(var i=0; i<this.solar_modes.length; i++){
|
||||||
|
if(this.solar_modes[i].test(time,day_info,screen_info) ){
|
||||||
|
this.last = this.solar_modes[i];
|
||||||
|
return this.last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//console.log("defaulting");
|
||||||
|
this.last = this.default_mode;
|
||||||
|
return this.last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = SolarControllerImpl;
|
||||||
|
|
@ -0,0 +1,132 @@
|
||||||
|
var Math2 = require("solar_math_utils.js");
|
||||||
|
|
||||||
|
const _MIN_MILLIS = 1000 * 60 ;
|
||||||
|
const _HOUR_MILLIS = _MIN_MILLIS * 60;
|
||||||
|
const _DAY_MILLIS = _HOUR_MILLIS * 24;
|
||||||
|
|
||||||
|
function _start_of_julian_day(now){
|
||||||
|
var sod_of_day = new Date(now.getTime());
|
||||||
|
var local_offset_hours = sod_of_day.getTimezoneOffset()/60;
|
||||||
|
//console.log("local_offset_hours=" + local_offset_hours);
|
||||||
|
sod_of_day.setHours(12 + local_offset_hours,0,0,0);
|
||||||
|
return sod_of_day;
|
||||||
|
}
|
||||||
|
function _date_to_julian_date(now){
|
||||||
|
var year = now.getFullYear();
|
||||||
|
var month = now.getMonth() + 1;
|
||||||
|
var day = now.getDate();
|
||||||
|
|
||||||
|
var julian_date_full = (1461 * (year + 4800 + (month-14)/12))/4 +(367 * (month-2 - 12*((month - 14)/12)))/12-(3 * ((year + 4900 + (month - 14)/12)/100))/4 + day - 32075;
|
||||||
|
var julian_date = (julian_date_full| 0);
|
||||||
|
return julian_date;
|
||||||
|
}
|
||||||
|
function _to_time(now,day_fraction){
|
||||||
|
var datetime = new Date(now.getTime());
|
||||||
|
var hours = (day_fraction * 24) |0;
|
||||||
|
var remainder = day_fraction - hours/24;
|
||||||
|
var mins = (remainder * 24 * 60 | 0);
|
||||||
|
var remainder = remainder - mins/(24*60);
|
||||||
|
var secs = (remainder * 24 * 60 * 60 | 0);
|
||||||
|
var remainder = remainder - secs /(24 * 60 * 60);
|
||||||
|
var millis = remainder * 24 * 60 * 60 * 1000;
|
||||||
|
datetime.setHours(hours, mins, secs,millis);
|
||||||
|
return datetime;
|
||||||
|
}
|
||||||
|
const DateUtils = {
|
||||||
|
DAY_MILLIS : _DAY_MILLIS,
|
||||||
|
HOUR_MILLIS : _HOUR_MILLIS,
|
||||||
|
MIN_MILLIS: _MIN_MILLIS,
|
||||||
|
sunrise_sunset: (now,longitude,latitude, utc_offset)=>{
|
||||||
|
|
||||||
|
var sod_julian = _start_of_julian_day(now);
|
||||||
|
var julian_date = _date_to_julian_date(sod_julian);
|
||||||
|
//console.log("julian date=" + julian_date);
|
||||||
|
//var n = julian_date - 2451545.0 + 0.0008;
|
||||||
|
var julian_century = (julian_date-2451545)/36525;
|
||||||
|
//console.log("julian_century=" + julian_century);
|
||||||
|
var geom_mean_long_sun_degrees = (280.46646+julian_century*(36000.76983 + julian_century*0.0003032)) % 360;
|
||||||
|
//console.log("geom_mean_long_sun=" + geom_mean_long_sun_degrees);
|
||||||
|
var geom_mean_anomaly_sun_degrees = 357.52911+julian_century*(35999.05029 - 0.0001537*julian_century);
|
||||||
|
//console.log("solar_mean_anomaly_sun=" + geom_mean_anomaly_sun_degrees);
|
||||||
|
var eccent_earth_orbit = 0.016708634-julian_century*(0.000042037+0.0000001267*julian_century);
|
||||||
|
//console.log("eccent_earth_orbit=" + eccent_earth_orbit);
|
||||||
|
var sun_eq_of_ctr = Math.sin(Math2.to_radians(geom_mean_anomaly_sun_degrees))*
|
||||||
|
(1.914602-julian_century*(0.004817+0.000014*julian_century))+
|
||||||
|
Math.sin(Math2.to_radians(2*geom_mean_anomaly_sun_degrees))*(0.019993-0.000101*julian_century)+
|
||||||
|
Math.sin(Math2.to_radians(3*geom_mean_anomaly_sun_degrees))*0.000289;
|
||||||
|
//console.log("sun_eq_of_ctr=" + sun_eq_of_ctr);
|
||||||
|
var sun_true_long_degrees = geom_mean_long_sun_degrees + sun_eq_of_ctr;
|
||||||
|
//console.log("sun_true_long_degrees=" + sun_true_long_degrees);
|
||||||
|
var sun_true_anom_degrees = geom_mean_anomaly_sun_degrees + sun_eq_of_ctr;
|
||||||
|
//console.log("sun_true_anom_degrees=" + sun_true_anom_degrees);
|
||||||
|
var sun_rad_vector_AUs = (1.000001018*(1-eccent_earth_orbit*eccent_earth_orbit))/(1+eccent_earth_orbit*Math.cos(Math2.to_radians(sun_true_anom_degrees)))
|
||||||
|
//console.log("sun_rad_vector_AUs=" + sun_rad_vector_AUs);
|
||||||
|
var sun_app_long_degress = sun_true_long_degrees-0.00569-0.00478*Math.sin(Math2.to_radians(125.04-1934.136*julian_century));
|
||||||
|
//console.log("sun_app_long_degress=" + sun_app_long_degress);
|
||||||
|
var mean_obliq_ecliptic_degrees = 23+(26+((21.448-julian_century*(46.815+julian_century*(0.00059-julian_century*0.001813))))/60)/60;
|
||||||
|
//console.log("mean_obliq_ecliptic_degrees=" + mean_obliq_ecliptic_degrees);
|
||||||
|
var obliq_corr_degrees = mean_obliq_ecliptic_degrees+0.00256*Math.cos(Math2.to_radians(125.04-1934.136*julian_century))
|
||||||
|
//console.log("obliq_corr_degrees=" + obliq_corr_degrees);
|
||||||
|
|
||||||
|
var sun_declin_degrees = Math2.to_degrees(
|
||||||
|
Math.asin(Math.sin(Math2.to_radians(obliq_corr_degrees))*Math.sin(Math2.to_radians(sun_app_long_degress)))
|
||||||
|
);
|
||||||
|
//console.log("sun_declin_degrees=" + sun_declin_degrees);
|
||||||
|
var var_y = Math.tan(Math2.to_radians(obliq_corr_degrees/2))*Math.tan(Math2.to_radians(obliq_corr_degrees/2));
|
||||||
|
//console.log("var_y=" + var_y);
|
||||||
|
var eq_of_time = 4*Math2.to_degrees(
|
||||||
|
var_y*Math.sin(2*Math2.to_radians(geom_mean_long_sun_degrees))-
|
||||||
|
2*eccent_earth_orbit*Math.sin(Math2.to_radians(geom_mean_anomaly_sun_degrees))+
|
||||||
|
4*eccent_earth_orbit*var_y*Math.sin(Math2.to_radians(geom_mean_anomaly_sun_degrees))*Math.cos(2*Math2.to_radians(geom_mean_long_sun_degrees))-
|
||||||
|
0.5*var_y*var_y*Math.sin(4*Math2.to_radians(geom_mean_long_sun_degrees))-
|
||||||
|
1.25*eccent_earth_orbit*eccent_earth_orbit*Math.sin(2*Math2.to_radians(geom_mean_anomaly_sun_degrees))
|
||||||
|
);
|
||||||
|
//console.log("eq_of_time=" + eq_of_time);
|
||||||
|
|
||||||
|
var HA_sunrise_degrees = Math2.to_degrees(
|
||||||
|
Math.acos(
|
||||||
|
Math.cos(Math2.to_radians(90.833))/(Math.cos(Math2.to_radians(latitude))*Math.cos(Math2.to_radians(sun_declin_degrees)))-
|
||||||
|
Math.tan(Math2.to_radians(latitude))*Math.tan(Math2.to_radians(sun_declin_degrees))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
//console.log("HA_sunrise_degrees=" + HA_sunrise_degrees);
|
||||||
|
|
||||||
|
var local_offset_hours = new Date().getTimezoneOffset()/60;
|
||||||
|
if(utc_offset == null){
|
||||||
|
utc_offset = -local_offset_hours;
|
||||||
|
}
|
||||||
|
var timezone_offset_hours = (utc_offset - local_offset_hours);
|
||||||
|
console.log("timezone_offset_hours=" + timezone_offset_hours +
|
||||||
|
" longitude" + longitude +
|
||||||
|
" utc_offset=" + utc_offset
|
||||||
|
);
|
||||||
|
|
||||||
|
var solar_noon = (720-4*longitude-eq_of_time+timezone_offset_hours*60)/1440;
|
||||||
|
var solar_noon_datetime = _to_time(now,solar_noon);
|
||||||
|
|
||||||
|
console.log("solar_noon=" + solar_noon + "->" + solar_noon_datetime.toISOString());
|
||||||
|
|
||||||
|
var sunrise_time_LST = (solar_noon*1440-HA_sunrise_degrees*4)/1440;
|
||||||
|
var sunrise_time_LST_datetime = _to_time(now,sunrise_time_LST);
|
||||||
|
console.log("sunrise_time_LST=" + sunrise_time_LST +
|
||||||
|
"->" + sunrise_time_LST_datetime.toISOString());
|
||||||
|
|
||||||
|
var sunset_time_LST =(solar_noon*1440+HA_sunrise_degrees*4)/1440;
|
||||||
|
var sunset_time_LST_datetime = _to_time(now,sunset_time_LST);
|
||||||
|
console.log("sunset_time_LST=" + sunset_time_LST +
|
||||||
|
"->" + sunset_time_LST_datetime.toISOString());
|
||||||
|
return {
|
||||||
|
day_start: new Date(solar_noon_datetime.getTime() - _DAY_MILLIS / 2),
|
||||||
|
sunrise_date: sunrise_time_LST_datetime,
|
||||||
|
//sunrise_fraction: sunrise_time_LST,
|
||||||
|
sunset_date: sunset_time_LST_datetime,
|
||||||
|
//sunset_fraction: sunset_time_LST,
|
||||||
|
solar_noon: solar_noon_datetime,
|
||||||
|
day_end: new Date(solar_noon_datetime.getTime() + _DAY_MILLIS / 2)
|
||||||
|
};
|
||||||
|
},
|
||||||
|
now_fraction_of_day: (now,day_info)=>{
|
||||||
|
return (now.getTime() - day_info.day_start.getTime())/_DAY_MILLIS;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
module.exports = DateUtils;
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
var DateUtils = require("solar_date_utils.js");
|
||||||
|
var Math2 = require("solar_math_utils.js");
|
||||||
|
|
||||||
|
function _draw_info(img_info){
|
||||||
|
if (img_info == null) {
|
||||||
|
return {
|
||||||
|
buff: g,
|
||||||
|
offset_x: 0,
|
||||||
|
offset_y: 0
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
buff: img_info.img_buffer,
|
||||||
|
offset_x: img_info.x,
|
||||||
|
offset_y: img_info.y
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
const GraphicUtils = {
|
||||||
|
draw_info : (img_info)=>_draw_info(img_info),
|
||||||
|
draw_cosine : (from_x,to_x, line_colour, screen_info, img_info)=>{
|
||||||
|
//console.log("draw_cosine from_x=" + from_x + " to_x=" + to_x);
|
||||||
|
var draw_info = _draw_info(img_info);
|
||||||
|
|
||||||
|
draw_info.buff.reset();
|
||||||
|
draw_info.buff.setColor(line_colour[0],line_colour[1],line_colour[2]);
|
||||||
|
first = true;
|
||||||
|
for(var x=from_x; x<to_x;x++){
|
||||||
|
var radians = Math2.TWO_PI *((x-screen_info.screen_start_x) - screen_info.screen_centre_x)/(screen_info.screen_width);
|
||||||
|
var y = screen_info.screen_centre_y - screen_info.screen_height * Math.cos(radians)/2;
|
||||||
|
if(first) {
|
||||||
|
draw_info.buff.moveTo(x-draw_info.offset_x, y-draw_info.offset_y);
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
draw_info.buff.lineTo(x-draw_info.offset_x,y - draw_info.offset_y);
|
||||||
|
}
|
||||||
|
//console.log("(x,y)=" + x +"," + y + " radians=" +radians );
|
||||||
|
}
|
||||||
|
draw_info.buff.reset();
|
||||||
|
},
|
||||||
|
|
||||||
|
draw_sunrise_line : (sunrise_colour, day_info, screen_info, img_info)=> {
|
||||||
|
var rise_y = screen_info.sunrise_y;
|
||||||
|
if (rise_y == null && day_info != null) {
|
||||||
|
var rise_fraction = (day_info.sunrise_date.getTime() - day_info.day_start.getTime()) / DateUtils.DAY_MILLIS;
|
||||||
|
var rise_radians = Math2.TWO_PI * (rise_fraction - 0.5);
|
||||||
|
var rise_y = screen_info.screen_centre_y - (screen_info.screen_height * Math.cos(rise_radians) / 2);
|
||||||
|
//console.log("rise_y=" + rise_y + " rise_fraction=" + rise_fraction + " rise_radian=" + rise_radians);
|
||||||
|
screen_info.sunrise_y = rise_y;
|
||||||
|
}
|
||||||
|
if(rise_y != null) {
|
||||||
|
var draw_info = _draw_info(img_info);
|
||||||
|
draw_info.buff.setColor(sunrise_colour[0], sunrise_colour[1], sunrise_colour[2]);
|
||||||
|
draw_info.buff.drawLine(screen_info.screen_start_x - draw_info.offset_x,
|
||||||
|
rise_y - draw_info.offset_y,
|
||||||
|
screen_info.screen_width,
|
||||||
|
rise_y - draw_info.offset_y);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
fill_circle_partial_y : (center_x,center_y,radius,from_y,to_y, img_info)=>{
|
||||||
|
from_y = (from_y | 0);
|
||||||
|
to_y = (to_y | 0);
|
||||||
|
if(from_y > to_y){
|
||||||
|
var tmp_y = from_y;
|
||||||
|
from_y = to_y;
|
||||||
|
to_y = tmp_y;
|
||||||
|
}
|
||||||
|
var draw_info = _draw_info(img_info);
|
||||||
|
for(var y=from_y; y<=to_y; y++){
|
||||||
|
// now (y-center_y)^2 + (x-center_x)^2 = radius^2
|
||||||
|
// so (x-center_x)^2 = radius^2 - (y-center_y)^2
|
||||||
|
// so (x-center_x) = sqrt( radius^2 - (y-center_y)^2 )
|
||||||
|
// so x = center_x +/- sqrt( radius^2 - (y-center_y)^2 )
|
||||||
|
var x_dist2 =radius*radius - (y-center_y)*(y-center_y);
|
||||||
|
if(x_dist2 >= 0) {
|
||||||
|
var x_dist = Math.sqrt(x_dist2);
|
||||||
|
var from_x = (center_x - x_dist | 0);
|
||||||
|
var to_x = center_x + x_dist ;
|
||||||
|
var to_x_rounded = (to_x | 0);
|
||||||
|
if(to_x - to_x_rounded > 0.01)
|
||||||
|
to_x_rounded += 1;
|
||||||
|
|
||||||
|
draw_info.buff.drawLine(from_x-draw_info.offset_x,
|
||||||
|
y-draw_info.offset_y,
|
||||||
|
to_x_rounded-draw_info.offset_x,
|
||||||
|
y-draw_info.offset_y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = GraphicUtils;
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"coordinates": [-21.9,64.133],
|
||||||
|
"utc_offset": 0
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"coordinates": [-159.4879501,22.2224610],
|
||||||
|
"utc_offset": -10
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"coordinates": [139.6503,35.6762],
|
||||||
|
"utc_offset": 9
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"coordinates": [-0.12574, 51.50853]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
const storage = require("Storage");
|
||||||
|
class LocationManager {
|
||||||
|
constructor(locations) {
|
||||||
|
this.idx=0;
|
||||||
|
this.locations = locations;
|
||||||
|
this.listeners = [];
|
||||||
|
this.in_use = true;
|
||||||
|
this.gps_queried = false;
|
||||||
|
this.gpsPower = 0;
|
||||||
|
this.location_info = null;
|
||||||
|
}
|
||||||
|
init(){
|
||||||
|
this.location_info = storage.readJSON("solar_loc." + this.getName() + ".json");
|
||||||
|
if(this.isGPSLocation() && !this.gps_queried) {
|
||||||
|
console.log("updating local location");
|
||||||
|
this._gpsUpdate();
|
||||||
|
this.gps_queried = true;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setGPSPower(power){
|
||||||
|
this.gpsPower = power;
|
||||||
|
Bangle.setGPSPower(this.gpsPower);
|
||||||
|
}
|
||||||
|
getGPSPower(){return this.gpsPower;}
|
||||||
|
_gpsUpdate(){
|
||||||
|
this.setGPSPower(1);
|
||||||
|
Bangle.on('GPS', (g) => {
|
||||||
|
if (!this.in_use)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (g.fix) {
|
||||||
|
var loc_info = {
|
||||||
|
last_update: new Date(),
|
||||||
|
coordinates: [g.lon, g.lat]
|
||||||
|
};
|
||||||
|
console.log("Received gps fixing:" + JSON.stringify(loc_info));
|
||||||
|
storage.writeJSON("solar_loc.local.json", this.location_info);
|
||||||
|
if(this.isGPSLocation()){
|
||||||
|
this.location_info = loc_info;
|
||||||
|
this.notifyUpdate();
|
||||||
|
}
|
||||||
|
this.setGPSPower(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
isGPSLocation(){return this.getName() == 'local';}
|
||||||
|
addUpdateListener(listener){this.listeners.push(listener);}
|
||||||
|
nextLocation() {
|
||||||
|
if(this.locations.length > 1) {
|
||||||
|
this.idx += 1;
|
||||||
|
this.idx = this.idx % this.locations.length;
|
||||||
|
console.log("location now:" + this.getName());
|
||||||
|
this.init();
|
||||||
|
this.notifyUpdate();
|
||||||
|
} else {
|
||||||
|
console.log("no extra locations found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyUpdate(){
|
||||||
|
for(var i=0; i<this.listeners.length; i++){
|
||||||
|
this.listeners[i](this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getUTCOffset(){return this.location_info.utc_offset;}
|
||||||
|
getName(){return this.locations[this.idx];}
|
||||||
|
getCoordinates(){return this.location_info.coordinates;}
|
||||||
|
shutdown(){
|
||||||
|
this.in_use=false;
|
||||||
|
this.setGPSPower(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const LOCATIONS_FILE = "solar_locations.json";
|
||||||
|
const LocationUtils = {
|
||||||
|
load_locations : ()=>{
|
||||||
|
var locations = storage.readJSON(LOCATIONS_FILE);
|
||||||
|
console.log("loaded locations:" + locations);
|
||||||
|
var mgr = new LocationManager(locations);
|
||||||
|
mgr.init();
|
||||||
|
return mgr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = LocationUtils;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
["local","Tokyo","Iceland","Kauai"]
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
const _TWO_PI = 2 * Math.PI;
|
||||||
|
const Maths2 = {
|
||||||
|
TWO_PI: _TWO_PI,
|
||||||
|
to_radians: (degrees)=> _TWO_PI * degrees / 360,
|
||||||
|
to_degrees: (radians)=> 360 * radians/ (_TWO_PI),
|
||||||
|
interpolate: (vector1, vector2, fraction)=>{
|
||||||
|
var result = [];
|
||||||
|
for(var i=0; i< vector1.length; i++){
|
||||||
|
var value = vector1[i] + (vector2[i] - vector1[i]) * fraction;
|
||||||
|
result.push(value);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
format00: (num)=>{
|
||||||
|
var value = (num | 0);
|
||||||
|
if(value > 99 || value < 0)
|
||||||
|
throw "must be between in range 0-99";
|
||||||
|
if(value < 10)
|
||||||
|
return "0" + value.toString();
|
||||||
|
else
|
||||||
|
return value.toString();
|
||||||
|
},
|
||||||
|
random_walk: (value,step,max,min)=>{
|
||||||
|
if(Math.random()>0.5){
|
||||||
|
value -= step;
|
||||||
|
} else {
|
||||||
|
value += step;
|
||||||
|
}
|
||||||
|
value = Math.min(value,max);
|
||||||
|
value = Math.max(value,min);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module.exports = Maths2;
|
||||||
Loading…
Reference in New Issue