Merge PR #470 - implement automatic DST toggling

Implements logic to automatically offset daylight saving time settings
when calculating timezone offsets. This should make the DST functions
work automatically with no need for user input in most cases.

Reviewed-by: Matheus Afonso Martins Moreira <matheus@matheusmoreira.com>
GitHub-Pull-Request: https://github.com/joeycastillo/Sensor-Watch/pull/470
This commit is contained in:
Matheus Afonso Martins Moreira 2024-09-08 13:41:52 -03:00
commit ac5bf8cfce
32 changed files with 295 additions and 165 deletions

View file

@ -44,6 +44,7 @@ of debounce time.
#include "filesystem.h" #include "filesystem.h"
#include "movement.h" #include "movement.h"
#include "shell.h" #include "shell.h"
#include "watch_utility.h"
#ifndef MOVEMENT_FIRMWARE #ifndef MOVEMENT_FIRMWARE
#include "movement_config.h" #include "movement_config.h"
@ -130,6 +131,11 @@ of debounce time.
#define MOVEMENT_DEFAULT_BIRTHDATE_DAY 0 #define MOVEMENT_DEFAULT_BIRTHDATE_DAY 0
#endif #endif
// Default to having DST get set
#ifndef MOVEMENT_DEFAULT_DST_ACTIVE
#define MOVEMENT_DEFAULT_DST_ACTIVE true
#endif
#if __EMSCRIPTEN__ #if __EMSCRIPTEN__
#include <emscripten.h> #include <emscripten.h>
#endif #endif
@ -141,7 +147,8 @@ const int32_t movement_le_inactivity_deadlines[8] = {INT_MAX, 600, 3600, 7200, 2
const int16_t movement_timeout_inactivity_deadlines[4] = {60, 120, 300, 1800}; const int16_t movement_timeout_inactivity_deadlines[4] = {60, 120, 300, 1800};
movement_event_t event; movement_event_t event;
const int16_t movement_timezone_offsets[] = { #define NUM_TIME_ZONES 41
const int16_t movement_timezone_offsets[NUM_TIME_ZONES] = {
0, // 0 : 0:00:00 (UTC) 0, // 0 : 0:00:00 (UTC)
60, // 1 : 1:00:00 (Central European Time) 60, // 1 : 1:00:00 (Central European Time)
120, // 2 : 2:00:00 (South African Standard Time) 120, // 2 : 2:00:00 (South African Standard Time)
@ -198,94 +205,50 @@ const int16_t movement_timezone_offsets[] = {
* having to separately change the hour and timezone info * having to separately change the hour and timezone info
* in the time set face. * in the time set face.
*/ */
const uint8_t movement_dst_jump_table[] = { const int16_t movement_timezone_dst_offsets[NUM_TIME_ZONES] = {
1, // 0 UTC + 1 = CET 60, // 0 UTC + 1 = CET
2, // 1 CET + 1 = SAST 120, // 1 CET + 1 = SAST
3, // 2 SAST + 1 = AST 189, // 2 SAST + 1 = AST
5, // 3 AST + 1 = GST 240, // 3 AST + 1 = GST
6, // 4 IST + 1 = AT 270, // 4 IST + 1 = AT
7, // 5 GST + 1 = PST 300, // 5 GST + 1 = PST
8, // 6 AT + 1 = IST 330, // 6 AT + 1 = IST
10, // 7 PST + 1 = KT 360, // 7 PST + 1 = KT
11, // 8 IST + 1 = MT 390, // 8 IST + 1 = MT
9, // 9 Nepal has no equivalent DST timezone, but they don't observe DST anyway 345, // 9 Nepal has no equivalent DST timezone, but they don't observe DST anyway
12, // 10 KT + 1 = TST 420, // 10 KT + 1 = TST
11, // 11 Myanmar has no equivalent DST timezone, but they don't observe DST anyway 390, // 11 Myanmar has no equivalent DST timezone, but they don't observe DST anyway
13, // 12 TST + 1 = CST 480, // 12 TST + 1 = CST
15, // 13 CST + 1 = JST 540, // 13 CST + 1 = JST
14, // 14 ACWST has no equivalent DST timezone, but they don't observe DST anyway 525, // 14 ACWST has no equivalent DST timezone, but they don't observe DST anyway
17, // 15 JST + 1 = AEST 600, // 15 JST + 1 = AEST
18, // 16 ACST + 1 = LHST 630, // 16 ACST + 1 = LHST
19, // 17 AEST + 1 = SIT 660, // 17 AEST + 1 = SIT
18, // 18 LHST has no equivalent DST timezone, but they don't observe DST anyway 630, // 18 LHST has no equivalent DST timezone, but they don't observe DST anyway
20, // 19 SIT + 1 = NZST 720, // 19 SIT + 1 = NZST
22, // 20 NZST + 1 = TT 780, // 20 NZST + 1 = TT
23, // 21 CST + 1 = CDT 825, // 21 CST + 1 = CDT
24, // 22 TT + 1 = LIT 840, // 22 TT + 1 = LIT
23, // 23 CDT is already a daylight timezone 825, // 23 CDT is already a daylight timezone
24, // 24 LIT has no equivalent DST timezone, but they don't observe DST anyway 840, // 24 LIT has no equivalent DST timezone, but they don't observe DST anyway
26, // 25 BIT + 1 = NT -660, // 25 BIT + 1 = NT
27, // 26 NT + 1 = HAST -600, // 26 NT + 1 = HAST
29, // 27 HAST + 1 = AST -540, // 27 HAST + 1 = AST
28, // 28 MIT has no equivalent DST timezone, but they don't observe DST anyway -570, // 28 MIT has no equivalent DST timezone, but they don't observe DST anyway
30, // 29 AST + 1 = PST -480, // 29 AST + 1 = PST
31, // 30 PST + 1 = MST -420, // 30 PST + 1 = MST
32, // 31 MST + 1 = CST -360, // 31 MST + 1 = CST
33, // 32 CST + 1 = EST -300, // 32 CST + 1 = EST
35, // 33 EST + 1 = AST -240, // 33 EST + 1 = AST
36, // 34 VST + 1 = NST -210, // 34 VST + 1 = NST
37, // 35 AST + 1 = BT -180, // 35 AST + 1 = BT
38, // 36 NST + 1 = NDT -150, // 36 NST + 1 = NDT
39, // 37 BT + 1 = 39 -120, // 37 BT + 1 = 39
38, // 38 NDT is already a daylight timezone -150, // 38 NDT is already a daylight timezone
40, // 39 FNT + 1 = AST -60, // 39 FNT + 1 = AST
0 // 40 AST + 1 = UTC 0 // 40 AST + 1 = UTC
}; };
const uint8_t movement_dst_inverse_jump_table[] = {
40, // 0
0, // 1
1, // 2
2, // 3
4, // 4
3, // 5
4, // 6
5, // 7
6, // 8
9, // 9
7, // 10
8, // 11
10, // 12
12, // 13
14, // 14
13, // 15
16, // 16
15, // 17
16, // 18
17, // 19
19, // 20
21, // 21
20, // 22
21, // 23
24, // 24
25, // 25
25, // 26
26, // 27
28, // 28
27, // 29
29, // 30
30, // 31
31, // 32
32, // 33
34, // 34
33, // 35
34, // 36
35, // 37
36, // 38
37, // 39
39 // 40
};
const char movement_valid_position_0_chars[] = " AaBbCcDdEeFGgHhIiJKLMNnOoPQrSTtUuWXYZ-='+\\/0123456789"; const char movement_valid_position_0_chars[] = " AaBbCcDdEeFGgHhIiJKLMNnOoPQrSTtUuWXYZ-='+\\/0123456789";
const char movement_valid_position_1_chars[] = " ABCDEFHlJLNORTtUX-='01378"; const char movement_valid_position_1_chars[] = " ABCDEFHlJLNORTtUX-='01378";
@ -323,6 +286,31 @@ static inline void _movement_disable_fast_tick_if_possible(void) {
} }
} }
static bool _check_and_act_on_daylight_savings(void) {
if (!movement_state.settings.bit.dst_active) return false;
watch_date_time date_time = watch_rtc_get_date_time();
// No need for all of the unix time calculations for times not at the beginning or end of the hour
if (date_time.unit.minute > 1 && date_time.unit.minute < 59) return false;
uint8_t dst_result = get_dst_status(date_time);
bool dst_skip_rolling_back = get_dst_skip_rolling_back();
if (dst_skip_rolling_back && (dst_result == DST_ENDED)) {
clear_dst_skip_rolling_back();
}
else if (dst_result == DST_ENDING && !dst_skip_rolling_back) {
date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24;
watch_rtc_set_date_time(date_time);
set_dst_skip_rolling_back();
return true;
}
else if (dst_result == DST_STARTING) {
date_time.unit.hour = (date_time.unit.hour + 1) % 24;
watch_rtc_set_date_time(date_time);
return true;
}
return false;
}
static void _movement_handle_background_tasks(void) { static void _movement_handle_background_tasks(void) {
for(uint8_t i = 0; i < MOVEMENT_NUM_FACES; i++) { for(uint8_t i = 0; i < MOVEMENT_NUM_FACES; i++) {
// For each face, if the watch face wants a background task... // For each face, if the watch face wants a background task...
@ -332,6 +320,7 @@ static void _movement_handle_background_tasks(void) {
watch_faces[i].loop(background_event, &movement_state.settings, watch_face_contexts[i]); watch_faces[i].loop(background_event, &movement_state.settings, watch_face_contexts[i]);
} }
} }
_check_and_act_on_daylight_savings();
movement_state.needs_background_tasks_handled = false; movement_state.needs_background_tasks_handled = false;
} }
@ -529,6 +518,12 @@ uint8_t movement_claim_backup_register(void) {
return movement_state.next_available_backup_register++; return movement_state.next_available_backup_register++;
} }
int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time) {
if (movement_state.settings.bit.dst_active && dst_occurring(date_time))
return movement_timezone_dst_offsets[timezone_idx];
return movement_timezone_offsets[timezone_idx];
}
void app_init(void) { void app_init(void) {
#if defined(NO_FREQCORR) #if defined(NO_FREQCORR)
watch_rtc_freqcorr_write(0, 0); watch_rtc_freqcorr_write(0, 0);
@ -551,6 +546,20 @@ void app_init(void) {
movement_state.birthdate.bit.year = MOVEMENT_DEFAULT_BIRTHDATE_YEAR; movement_state.birthdate.bit.year = MOVEMENT_DEFAULT_BIRTHDATE_YEAR;
movement_state.birthdate.bit.month = MOVEMENT_DEFAULT_BIRTHDATE_MONTH; movement_state.birthdate.bit.month = MOVEMENT_DEFAULT_BIRTHDATE_MONTH;
movement_state.birthdate.bit.day = MOVEMENT_DEFAULT_BIRTHDATE_DAY; movement_state.birthdate.bit.day = MOVEMENT_DEFAULT_BIRTHDATE_DAY;
movement_state.settings.bit.dst_active = MOVEMENT_DEFAULT_DST_ACTIVE;
#ifdef MAKEFILE_TIMEZONE
timezone_offsets = dst_occurring(watch_rtc_get_date_time()) ? movement_timezone_dst_offsets : movement_timezone_offsets;
for (int i = 0; i < NUM_TIME_ZONES; i++) {
if (timezone_offsets[i] == MAKEFILE_TIMEZONE) {
movement_state.settings.bit.time_zone = i;
break;
}
}
#else
movement_state.settings.bit.time_zone = 35; // Atlantic Time as default
#endif
movement_state.light_ticks = -1; movement_state.light_ticks = -1;
movement_state.alarm_ticks = -1; movement_state.alarm_ticks = -1;
movement_state.next_available_backup_register = 4; movement_state.next_available_backup_register = 4;
@ -559,11 +568,13 @@ void app_init(void) {
filesystem_init(); filesystem_init();
#if __EMSCRIPTEN__ #if __EMSCRIPTEN__
const int16_t* timezone_offsets;
int32_t time_zone_offset = EM_ASM_INT({ int32_t time_zone_offset = EM_ASM_INT({
return -new Date().getTimezoneOffset(); return -new Date().getTimezoneOffset();
}); });
for (int i = 0, count = sizeof(movement_timezone_offsets) / sizeof(movement_timezone_offsets[0]); i < count; i++) { timezone_offsets = dst_occurring(watch_rtc_get_date_time()) ? movement_timezone_dst_offsets : movement_timezone_offsets;
if (movement_timezone_offsets[i] == time_zone_offset) { for (int i = 0; i < NUM_TIME_ZONES; i++) {
if (timezone_offsets[i] == time_zone_offset) {
movement_state.settings.bit.time_zone = i; movement_state.settings.bit.time_zone = i;
break; break;
} }

View file

@ -130,8 +130,7 @@ typedef struct {
} movement_event_t; } movement_event_t;
extern const int16_t movement_timezone_offsets[]; extern const int16_t movement_timezone_offsets[];
extern const uint8_t movement_dst_jump_table[]; extern const int16_t movement_timezone_dst_offsets[];
extern const uint8_t movement_dst_inverse_jump_table[];
extern const char movement_valid_position_0_chars[]; extern const char movement_valid_position_0_chars[];
extern const char movement_valid_position_1_chars[]; extern const char movement_valid_position_1_chars[];
@ -321,5 +320,6 @@ void movement_play_alarm(void);
void movement_play_alarm_beeps(uint8_t rounds, BuzzerNote alarm_note); void movement_play_alarm_beeps(uint8_t rounds, BuzzerNote alarm_note);
uint8_t movement_claim_backup_register(void); uint8_t movement_claim_backup_register(void);
int16_t get_timezone_offset(uint8_t timezone_idx, watch_date_time date_time);
#endif // MOVEMENT_H_ #endif // MOVEMENT_H_

View file

@ -111,4 +111,11 @@ const watch_face_t watch_faces[] = {
#define MOVEMENT_DEFAULT_BIRTHDATE_MONTH 0 #define MOVEMENT_DEFAULT_BIRTHDATE_MONTH 0
#define MOVEMENT_DEFAULT_BIRTHDATE_DAY 0 #define MOVEMENT_DEFAULT_BIRTHDATE_DAY 0
/* Set if using DST
* Valid values are:
* false: Don't allow the watch to use DST
* true: Allow the watch to use DST
*/
#define MOVEMENT_DEFAULT_DST_ACTIVE true
#endif // MOVEMENT_CONFIG_H_ #endif // MOVEMENT_CONFIG_H_

View file

@ -61,7 +61,7 @@ bool beats_face_loop(movement_event_t event, movement_settings_t *settings, void
case EVENT_ACTIVATE: case EVENT_ACTIVATE:
case EVENT_TICK: case EVENT_TICK:
date_time = watch_rtc_get_date_time(); date_time = watch_rtc_get_date_time();
centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, movement_timezone_offsets[settings->bit.time_zone]); centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, get_timezone_offset(settings->bit.time_zone, date_time));
if (centibeats == state->last_centibeat_displayed) { if (centibeats == state->last_centibeat_displayed) {
// we missed this update, try again next subsecond // we missed this update, try again next subsecond
state->next_subsecond_update = (event.subsecond + 1) % BEAT_REFRESH_FREQUENCY; state->next_subsecond_update = (event.subsecond + 1) % BEAT_REFRESH_FREQUENCY;
@ -76,7 +76,7 @@ bool beats_face_loop(movement_event_t event, movement_settings_t *settings, void
case EVENT_LOW_ENERGY_UPDATE: case EVENT_LOW_ENERGY_UPDATE:
if (!watch_tick_animation_is_running()) watch_start_tick_animation(432); if (!watch_tick_animation_is_running()) watch_start_tick_animation(432);
date_time = watch_rtc_get_date_time(); date_time = watch_rtc_get_date_time();
centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, movement_timezone_offsets[settings->bit.time_zone]); centibeats = clock2beats(date_time.unit.hour, date_time.unit.minute, date_time.unit.second, event.subsecond, get_timezone_offset(settings->bit.time_zone, date_time));
sprintf(buf, "bt %4lu ", centibeats / 100); sprintf(buf, "bt %4lu ", centibeats / 100);
watch_display_string(buf, 0); watch_display_string(buf, 0);

View file

@ -62,7 +62,8 @@ void day_night_percentage_face_setup(movement_settings_t *settings, uint8_t watc
if (*context_ptr == NULL) { if (*context_ptr == NULL) {
*context_ptr = malloc(sizeof(day_night_percentage_state_t)); *context_ptr = malloc(sizeof(day_night_percentage_state_t));
day_night_percentage_state_t *state = (day_night_percentage_state_t *)*context_ptr; day_night_percentage_state_t *state = (day_night_percentage_state_t *)*context_ptr;
watch_date_time utc_now = watch_utility_date_time_convert_zone(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60, 0); watch_date_time date_time = watch_rtc_get_date_time();
watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0);
recalculate(utc_now, state); recalculate(utc_now, state);
} }
} }
@ -77,7 +78,7 @@ bool day_night_percentage_face_loop(movement_event_t event, movement_settings_t
char buf[12]; char buf[12];
watch_date_time date_time = watch_rtc_get_date_time(); watch_date_time date_time = watch_rtc_get_date_time();
watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0);
switch (event.event_type) { switch (event.event_type) {
case EVENT_ACTIVATE: case EVENT_ACTIVATE:

View file

@ -70,7 +70,7 @@ static void _h_to_hms(mars_clock_hms_t *date_time, double h) {
static void _update(movement_settings_t *settings, mars_time_state_t *state) { static void _update(movement_settings_t *settings, mars_time_state_t *state) {
char buf[11]; char buf[11];
watch_date_time date_time = watch_rtc_get_date_time(); watch_date_time date_time = watch_rtc_get_date_time();
uint32_t now = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); uint32_t now = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60);
// TODO: I'm skipping over some steps here. // TODO: I'm skipping over some steps here.
// https://www.giss.nasa.gov/tools/mars24/help/algorithm.html // https://www.giss.nasa.gov/tools/mars24/help/algorithm.html
double jdut = 2440587.5 + ((double)now / 86400.0); double jdut = 2440587.5 + ((double)now / 86400.0);

View file

@ -154,6 +154,7 @@ void world_clock2_face_activate(movement_settings_t *settings, void *context)
movement_request_tick_frequency(4); movement_request_tick_frequency(4);
break; break;
} }
state->tz = get_timezone_offset(settings->bit.time_zone, watch_rtc_get_date_time());
refresh_face = true; refresh_face = true;
} }
@ -183,8 +184,8 @@ static bool mode_display(movement_event_t event, movement_settings_t *settings,
/* Determine current time at time zone and store date/time */ /* Determine current time at time zone and store date/time */
date_time = watch_rtc_get_date_time(); date_time = watch_rtc_get_date_time();
timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); timestamp = watch_utility_date_time_to_unix_time(date_time, state->tz * 60);
date_time = watch_utility_date_time_from_unix_time(timestamp, movement_timezone_offsets[state->current_zone] * 60); date_time = watch_utility_date_time_from_unix_time(timestamp, state->tz * 60);
previous_date_time = state->previous_date_time; previous_date_time = state->previous_date_time;
state->previous_date_time = date_time.reg; state->previous_date_time = date_time.reg;
@ -289,7 +290,7 @@ static bool mode_settings(movement_event_t event, movement_settings_t *settings,
watch_clear_indicator(WATCH_INDICATOR_PM); watch_clear_indicator(WATCH_INDICATOR_PM);
refresh_face = false; refresh_face = false;
} }
result = div(movement_timezone_offsets[state->current_zone], 60); result = div(state->tz, 60);
hours = result.quot; hours = result.quot;
minutes = result.rem; minutes = result.rem;

View file

@ -104,6 +104,7 @@ typedef struct {
world_clock2_mode_t current_mode; world_clock2_mode_t current_mode;
uint8_t current_zone; uint8_t current_zone;
uint32_t previous_date_time; uint32_t previous_date_time;
int16_t tz;
} world_clock2_state_t; } world_clock2_state_t;
void world_clock2_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr); void world_clock2_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void **context_ptr);

View file

@ -60,15 +60,16 @@ static bool world_clock_face_do_display_mode(movement_event_t event, movement_se
watch_date_time date_time; watch_date_time date_time;
switch (event.event_type) { switch (event.event_type) {
case EVENT_ACTIVATE: case EVENT_ACTIVATE:
if (settings->bit.clock_mode_24h && !settings->bit.clock_24h_leading_zero) watch_set_indicator(WATCH_INDICATOR_24H); state->tz = get_timezone_offset(settings->bit.time_zone, watch_rtc_get_date_time());
if (settings->bit.clock_mode_24h) watch_set_indicator(WATCH_INDICATOR_24H);
watch_set_colon(); watch_set_colon();
state->previous_date_time = 0xFFFFFFFF; state->previous_date_time = 0xFFFFFFFF;
// fall through // fall through
case EVENT_TICK: case EVENT_TICK:
case EVENT_LOW_ENERGY_UPDATE: case EVENT_LOW_ENERGY_UPDATE:
date_time = watch_rtc_get_date_time(); date_time = watch_rtc_get_date_time();
timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); timestamp = watch_utility_date_time_to_unix_time(date_time, state->tz * 60);
date_time = watch_utility_date_time_from_unix_time(timestamp, movement_timezone_offsets[state->settings.bit.timezone_index] * 60); date_time = watch_utility_date_time_from_unix_time(timestamp, state->tz * 60);
previous_date_time = state->previous_date_time; previous_date_time = state->previous_date_time;
state->previous_date_time = date_time.reg; state->previous_date_time = date_time.reg;
@ -176,8 +177,8 @@ static bool _world_clock_face_do_settings_mode(movement_event_t event, movement_
sprintf(buf, "%c%c %3d%02d ", sprintf(buf, "%c%c %3d%02d ",
movement_valid_position_0_chars[state->settings.bit.char_0], movement_valid_position_0_chars[state->settings.bit.char_0],
movement_valid_position_1_chars[state->settings.bit.char_1], movement_valid_position_1_chars[state->settings.bit.char_1],
(int8_t) (movement_timezone_offsets[state->settings.bit.timezone_index] / 60), (int8_t) (state->tz / 60),
(int8_t) (movement_timezone_offsets[state->settings.bit.timezone_index] % 60) * (movement_timezone_offsets[state->settings.bit.timezone_index] < 0 ? -1 : 1)); (int8_t) (state->tz % 60) * (state->tz < 0 ? -1 : 1));
watch_set_colon(); watch_set_colon();
watch_clear_indicator(WATCH_INDICATOR_PM); watch_clear_indicator(WATCH_INDICATOR_PM);

View file

@ -62,6 +62,7 @@ typedef struct {
uint8_t backup_register; uint8_t backup_register;
uint8_t current_screen; uint8_t current_screen;
uint32_t previous_date_time; uint32_t previous_date_time;
int16_t tz;
} world_clock_state_t; } world_clock_state_t;
void world_clock_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr); void world_clock_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);

View file

@ -80,7 +80,7 @@ static void _astronomy_face_recalculate(movement_settings_t *settings, astronomy
#endif #endif
watch_date_time date_time = watch_rtc_get_date_time(); watch_date_time date_time = watch_rtc_get_date_time();
uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60);
date_time = watch_utility_date_time_from_unix_time(timestamp, 0); date_time = watch_utility_date_time_from_unix_time(timestamp, 0);
double jd = astro_convert_date_to_julian_date(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR, date_time.unit.month, date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second); double jd = astro_convert_date_to_julian_date(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR, date_time.unit.month, date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second);

View file

@ -45,8 +45,8 @@ static void abort_quick_ticks(countdown_state_t *state) {
} }
} }
static inline int32_t get_tz_offset(movement_settings_t *settings) { static inline int32_t get_tz_offset(movement_settings_t *settings, watch_date_time date_time) {
return movement_timezone_offsets[settings->bit.time_zone] * 60; return get_timezone_offset(settings->bit.time_zone, date_time) * 60;
} }
static inline void store_countdown(countdown_state_t *state) { static inline void store_countdown(countdown_state_t *state) {
@ -70,13 +70,15 @@ static inline void button_beep(movement_settings_t *settings) {
} }
static void schedule_countdown(countdown_state_t *state, movement_settings_t *settings) { static void schedule_countdown(countdown_state_t *state, movement_settings_t *settings) {
watch_date_time now = watch_rtc_get_date_time();
int16_t tz = get_tz_offset(settings, now);
// Calculate the new state->now_ts but don't update it until we've updated the target - // Calculate the new state->now_ts but don't update it until we've updated the target -
// avoid possible race where the old target is compared to the new time and immediately triggers // avoid possible race where the old target is compared to the new time and immediately triggers
uint32_t new_now = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), get_tz_offset(settings)); uint32_t new_now = watch_utility_date_time_to_unix_time(now, tz);
state->target_ts = watch_utility_offset_timestamp(new_now, state->hours, state->minutes, state->seconds); state->target_ts = watch_utility_offset_timestamp(new_now, state->hours, state->minutes, state->seconds);
state->now_ts = new_now; state->now_ts = new_now;
watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, get_tz_offset(settings)); watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, tz);
movement_schedule_background_task_for_face(state->watch_face_index, target_dt); movement_schedule_background_task_for_face(state->watch_face_index, target_dt);
} }
@ -203,7 +205,7 @@ void countdown_face_activate(movement_settings_t *settings, void *context) {
countdown_state_t *state = (countdown_state_t *)context; countdown_state_t *state = (countdown_state_t *)context;
if(state->mode == cd_running) { if(state->mode == cd_running) {
watch_date_time now = watch_rtc_get_date_time(); watch_date_time now = watch_rtc_get_date_time();
state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now));
watch_set_indicator(WATCH_INDICATOR_SIGNAL); watch_set_indicator(WATCH_INDICATOR_SIGNAL);
} }
watch_set_colon(); watch_set_colon();

View file

@ -59,8 +59,9 @@ static void _update(movement_settings_t *settings, moon_phase_state_t *state, ui
(void)state; (void)state;
char buf[11]; char buf[11];
watch_date_time date_time = watch_rtc_get_date_time(); watch_date_time date_time = watch_rtc_get_date_time();
uint32_t now = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60) + offset; int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time);
date_time = watch_utility_date_time_from_unix_time(now, movement_timezone_offsets[settings->bit.time_zone] * 60); uint32_t now = watch_utility_date_time_to_unix_time(date_time, tz * 60) + offset;
date_time = watch_utility_date_time_from_unix_time(now, tz * 60);
double currentfrac = fmod(now - FIRST_MOON, LUNAR_SECONDS) / LUNAR_SECONDS; double currentfrac = fmod(now - FIRST_MOON, LUNAR_SECONDS) / LUNAR_SECONDS;
double currentday = currentfrac * LUNAR_DAYS; double currentday = currentfrac * LUNAR_DAYS;
uint8_t phase_index = 0; uint8_t phase_index = 0;
@ -138,6 +139,7 @@ bool moon_phase_face_loop(movement_event_t event, movement_settings_t *settings,
switch (event.event_type) { switch (event.event_type) {
case EVENT_ACTIVATE: case EVENT_ACTIVATE:
_update(settings, state, state->offset); _update(settings, state, state->offset);
break; break;
case EVENT_TICK: case EVENT_TICK:

View file

@ -48,7 +48,7 @@ static const char orrery_celestial_body_names[NUM_AVAILABLE_BODIES][3] = {
static void _orrery_face_recalculate(movement_settings_t *settings, orrery_state_t *state) { static void _orrery_face_recalculate(movement_settings_t *settings, orrery_state_t *state) {
watch_date_time date_time = watch_rtc_get_date_time(); watch_date_time date_time = watch_rtc_get_date_time();
uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); uint32_t timestamp = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60);
date_time = watch_utility_date_time_from_unix_time(timestamp, 0); date_time = watch_utility_date_time_from_unix_time(timestamp, 0);
double jd = astro_convert_date_to_julian_date(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR, date_time.unit.month, date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second); double jd = astro_convert_date_to_julian_date(date_time.unit.year + WATCH_RTC_REFERENCE_YEAR, date_time.unit.month, date_time.unit.day, date_time.unit.hour, date_time.unit.minute, date_time.unit.second);
double et = astro_convert_jd_to_julian_millenia_since_j2000(jd); double et = astro_convert_jd_to_julian_millenia_since_j2000(jd);

View file

@ -134,7 +134,8 @@ static void _planetary_solar_phases(movement_settings_t *settings, planetary_hou
state->no_location = false; state->no_location = false;
watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time
watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time);
watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, tz * 60, 0); // the current date / time in UTC
watch_date_time scratch_time; // scratchpad, contains different values at different times watch_date_time scratch_time; // scratchpad, contains different values at different times
watch_date_time midnight; watch_date_time midnight;
scratch_time.reg = midnight.reg = utc_now.reg; scratch_time.reg = midnight.reg = utc_now.reg;
@ -147,7 +148,7 @@ static void _planetary_solar_phases(movement_settings_t *settings, planetary_hou
double lon = (double)lon_centi / 100.0; double lon = (double)lon_centi / 100.0;
// save UTC offset // save UTC offset
state->utc_offset = ((double)movement_timezone_offsets[settings->bit.time_zone]) / 60.0; state->utc_offset = ((double)tz) / 60.0;
// calculate sunrise and sunset of current day in decimal hours after midnight // calculate sunrise and sunset of current day in decimal hours after midnight
sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset); sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
@ -238,7 +239,7 @@ static void _planetary_hours(movement_settings_t *settings, planetary_hours_stat
// get current time // get current time
watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time
watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0); // the current date / time in UTC
current_hour_epoch = watch_utility_date_time_to_unix_time(utc_now, 0); current_hour_epoch = watch_utility_date_time_to_unix_time(utc_now, 0);
// set the current planetary hour as default screen // set the current planetary hour as default screen

View file

@ -37,7 +37,7 @@
// STATIC FUNCTIONS AND CONSTANTS ///////////////////////////////////////////// // STATIC FUNCTIONS AND CONSTANTS /////////////////////////////////////////////
/** @brief Planetary rulers in the Chaldean order from slowest to fastest /** @brief Planetary rulers in the Chaldean order from slowest to fastest
* @details Planetary rulers in the Chaldean order from slowest to fastest: * @details Planetary rulers in the Chaldean order from slowest to fastest:
* Jupiter, Mars, Sun, Venus, Mercury, Moon * Jupiter, Mars, Sun, Venus, Mercury, Moon
*/ */
static const char planets[7][3] = {"Sa", "Ju", "Ma", "So", "Ve", "Me", "Lu"}; // Latin static const char planets[7][3] = {"Sa", "Ju", "Ma", "So", "Ve", "Me", "Lu"}; // Latin
@ -129,7 +129,8 @@ static void _planetary_solar_phase(movement_settings_t *settings, planetary_time
state->no_location = false; state->no_location = false;
watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time
watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time);
watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, tz * 60, 0); // the current date / time in UTC
watch_date_time scratch_time; // scratchpad, contains different values at different times watch_date_time scratch_time; // scratchpad, contains different values at different times
watch_date_time midnight; watch_date_time midnight;
scratch_time.reg = midnight.reg = utc_now.reg; scratch_time.reg = midnight.reg = utc_now.reg;
@ -142,7 +143,7 @@ static void _planetary_solar_phase(movement_settings_t *settings, planetary_time
double lon = (double)lon_centi / 100.0; double lon = (double)lon_centi / 100.0;
// save UTC offset // save UTC offset
state->utc_offset = ((double)movement_timezone_offsets[settings->bit.time_zone]) / 60.0; state->utc_offset = ((double)tz) / 60.0;
// get UNIX epoch time // get UNIX epoch time
now_epoch = watch_utility_date_time_to_unix_time(utc_now, 0); now_epoch = watch_utility_date_time_to_unix_time(utc_now, 0);
@ -150,7 +151,7 @@ static void _planetary_solar_phase(movement_settings_t *settings, planetary_time
// calculate sunrise and sunset of current day in decimal hours after midnight // calculate sunrise and sunset of current day in decimal hours after midnight
sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset); sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &sunrise, &sunset);
// calculate sunrise and sunset UNIX timestamps // calculate sunrise and sunset UNIX timestamps
sunrise_epoch = midnight_epoch + sunrise * 3600; sunrise_epoch = midnight_epoch + sunrise * 3600;
sunset_epoch = midnight_epoch + sunset * 3600; sunset_epoch = midnight_epoch + sunset * 3600;
@ -191,7 +192,7 @@ static void _planetary_solar_phase(movement_settings_t *settings, planetary_time
state->phase_end = sunrise_epoch; state->phase_end = sunrise_epoch;
} }
// calculate the duration of a planetary second during this solar phase // calculate the duration of a planetary second during this solar phase
// and convert to Hertz so we can call a faster tick rate // and convert to Hertz so we can call a faster tick rate
state->freq = (1 / ((double)( state->phase_end - state->phase_start ) / 43200)); state->freq = (1 / ((double)( state->phase_end - state->phase_start ) / 43200));
} }
@ -207,11 +208,12 @@ static void _planetary_time(movement_event_t event, movement_settings_t *setting
uint8_t weekday, planet, planetary_hour; uint8_t weekday, planet, planetary_hour;
double hour_duration, current_hour, current_minute, current_second; double hour_duration, current_hour, current_minute, current_second;
bool set_leading_zero = false; bool set_leading_zero = false;
watch_date_time date_time = watch_rtc_get_date_time();
watch_set_colon(); watch_set_colon();
// get current time and convert to UTC // get current time and convert to UTC
state->scratch = watch_utility_date_time_convert_zone(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60, 0); state->scratch = watch_utility_date_time_convert_zone(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60, 0);
// when current phase ends calculate the next phase // when current phase ends calculate the next phase
if ( watch_utility_date_time_to_unix_time(state->scratch, 0) >= state->phase_end ) { if ( watch_utility_date_time_to_unix_time(state->scratch, 0) >= state->phase_end ) {
@ -261,11 +263,11 @@ static void _planetary_time(movement_event_t event, movement_settings_t *setting
if ( state->ruler == 0 ) strncpy(ruler, planets[planet], 3); if ( state->ruler == 0 ) strncpy(ruler, planets[planet], 3);
if ( state->ruler == 1 ) strncpy(ruler, planetes[planet], 3); if ( state->ruler == 1 ) strncpy(ruler, planetes[planet], 3);
if ( state->ruler == 2 ) strncpy(ruler, " ", 3); if ( state->ruler == 2 ) strncpy(ruler, " ", 3);
// display planetary time with ruler of the hour or ruler of the day // display planetary time with ruler of the hour or ruler of the day
if ( state->day_ruler ) sprintf(buf, "%s d%2d%02d%02d", ruler, state->scratch.unit.hour, state->scratch.unit.minute, state->scratch.unit.second); if ( state->day_ruler ) sprintf(buf, "%s d%2d%02d%02d", ruler, state->scratch.unit.hour, state->scratch.unit.minute, state->scratch.unit.second);
else sprintf(buf, "%s h%2d%02d%02d", ruler, state->scratch.unit.hour, state->scratch.unit.minute, state->scratch.unit.second); else sprintf(buf, "%s h%2d%02d%02d", ruler, state->scratch.unit.hour, state->scratch.unit.minute, state->scratch.unit.second);
watch_display_string(buf, 0); watch_display_string(buf, 0);
if (set_leading_zero) if (set_leading_zero)
watch_display_string("0", 4); watch_display_string("0", 4);
@ -301,7 +303,7 @@ void planetary_time_face_activate(movement_settings_t *settings, void *context)
#endif #endif
planetary_time_state_t *state = (planetary_time_state_t *)context; planetary_time_state_t *state = (planetary_time_state_t *)context;
// calculate phase // calculate phase
_planetary_solar_phase(settings, state); _planetary_solar_phase(settings, state);
} }

View file

@ -33,8 +33,8 @@
#define sl_SELECTIONS 6 #define sl_SELECTIONS 6
#define DEFAULT_MINUTES { 5,4,1,0,0,0 } #define DEFAULT_MINUTES { 5,4,1,0,0,0 }
static inline int32_t get_tz_offset(movement_settings_t *settings) { static inline int32_t get_tz_offset(movement_settings_t *settings, watch_date_time date_time) {
return movement_timezone_offsets[settings->bit.time_zone] * 60; return get_timezone_offset(settings->bit.time_zone, date_time) * 60;
} }
static int lap = 0; static int lap = 0;
@ -165,7 +165,8 @@ static void ring(sailing_state_t *state, movement_settings_t *settings) {
return; return;
} }
state->nextbeep_ts = state->target_ts - beepseconds[beepflag+1]; state->nextbeep_ts = state->target_ts - beepseconds[beepflag+1];
watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->nextbeep_ts, get_tz_offset(settings)); watch_date_time now = watch_rtc_get_date_time();
watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->nextbeep_ts, get_tz_offset(settings, now));
movement_schedule_background_task_for_face(state->watch_face_index, target_dt); movement_schedule_background_task_for_face(state->watch_face_index, target_dt);
//background task is set, now we have time to play the tune. If this is cancelled accidentally, the next alarm will still ring. Sound is implemented non-blocking, so that neither buttons nor display output are compromised. //background task is set, now we have time to play the tune. If this is cancelled accidentally, the next alarm will still ring. Sound is implemented non-blocking, so that neither buttons nor display output are compromised.
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
@ -194,7 +195,7 @@ static void start(sailing_state_t *state, movement_settings_t *settings) {//gets
} }
if (state->index > 5 || state->minutes[state->index] == 0) { if (state->index > 5 || state->minutes[state->index] == 0) {
watch_date_time now = watch_rtc_get_date_time(); watch_date_time now = watch_rtc_get_date_time();
state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now));
state->target_ts = state->now_ts; state->target_ts = state->now_ts;
if (alarmflag != 0){ if (alarmflag != 0){
watch_buzzer_play_sequence(long_beep, NULL); watch_buzzer_play_sequence(long_beep, NULL);
@ -205,7 +206,7 @@ static void start(sailing_state_t *state, movement_settings_t *settings) {//gets
movement_request_tick_frequency(1); //synchronises tick with the moment the button was pressed. Solves 1s offset between sound and display, solves up to +-0.5s offset between button action and display. movement_request_tick_frequency(1); //synchronises tick with the moment the button was pressed. Solves 1s offset between sound and display, solves up to +-0.5s offset between button action and display.
state->mode = sl_running; state->mode = sl_running;
watch_date_time now = watch_rtc_get_date_time(); watch_date_time now = watch_rtc_get_date_time();
state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now));
state->target_ts = watch_utility_offset_timestamp(state->now_ts, 0, state->minutes[state->index], 0); state->target_ts = watch_utility_offset_timestamp(state->now_ts, 0, state->minutes[state->index], 0);
ring(state, settings); ring(state, settings);
} }
@ -253,11 +254,11 @@ void sailing_face_activate(movement_settings_t *settings, void *context) {
sailing_state_t *state = (sailing_state_t *)context; sailing_state_t *state = (sailing_state_t *)context;
if(state->mode == sl_running) { if(state->mode == sl_running) {
watch_date_time now = watch_rtc_get_date_time(); watch_date_time now = watch_rtc_get_date_time();
state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now));
} }
if(state->mode == sl_counting) { if(state->mode == sl_counting) {
watch_date_time now = watch_rtc_get_date_time(); watch_date_time now = watch_rtc_get_date_time();
state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now));
watch_set_indicator(WATCH_INDICATOR_LAP); watch_set_indicator(WATCH_INDICATOR_LAP);
} }
switch (alarmflag) { switch (alarmflag) {

View file

@ -123,9 +123,10 @@ static watch_date_time jde_to_date_time(double JDE) {
} }
static void calculate_datetimes(solstice_state_t *state, movement_settings_t *settings) { static void calculate_datetimes(solstice_state_t *state, movement_settings_t *settings) {
watch_date_time date_time = watch_rtc_get_date_time();
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
// TODO: handle DST changes // TODO: handle DST changes
state->datetimes[i] = jde_to_date_time(calculate_solstice_equinox(2020 + state->year, i) + (movement_timezone_offsets[settings->bit.time_zone] / (60.0*24.0))); state->datetimes[i] = jde_to_date_time(calculate_solstice_equinox(2020 + state->year, i) + (get_timezone_offset(settings->bit.time_zone, date_time) / (60.0*24.0)));
} }
} }

View file

@ -62,7 +62,7 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s
} }
watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time
watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60, 0); // the current date / time in UTC watch_date_time utc_now = watch_utility_date_time_convert_zone(date_time, state->tz * 60, 0); // the current date / time in UTC
watch_date_time scratch_time; // scratchpad, contains different values at different times watch_date_time scratch_time; // scratchpad, contains different values at different times
scratch_time.reg = utc_now.reg; scratch_time.reg = utc_now.reg;
@ -77,7 +77,7 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s
// sunriset returns the rise/set times as signed decimal hours in UTC. // sunriset returns the rise/set times as signed decimal hours in UTC.
// this can mean hours below 0 or above 31, which won't fit into a watch_date_time struct. // this can mean hours below 0 or above 31, which won't fit into a watch_date_time struct.
// to deal with this, we set aside the offset in hours, and add it back before converting it to a watch_date_time. // to deal with this, we set aside the offset in hours, and add it back before converting it to a watch_date_time.
double hours_from_utc = ((double)movement_timezone_offsets[settings->bit.time_zone]) / 60.0; double hours_from_utc = ((double)state->tz) / 60.0;
// we loop twice because if it's after sunset today, we need to recalculate to display values for tomorrow. // we loop twice because if it's after sunset today, we need to recalculate to display values for tomorrow.
for(int i = 0; i < 2; i++) { for(int i = 0; i < 2; i++) {
@ -334,6 +334,7 @@ void sunrise_sunset_face_activate(movement_settings_t *settings, void *context)
movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1); movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
state->working_latitude = _sunrise_sunset_face_struct_from_latlon(movement_location.bit.latitude); state->working_latitude = _sunrise_sunset_face_struct_from_latlon(movement_location.bit.latitude);
state->working_longitude = _sunrise_sunset_face_struct_from_latlon(movement_location.bit.longitude); state->working_longitude = _sunrise_sunset_face_struct_from_latlon(movement_location.bit.longitude);
state->tz = get_timezone_offset(settings->bit.time_zone, watch_rtc_get_date_time());
} }
bool sunrise_sunset_face_loop(movement_event_t event, movement_settings_t *settings, void *context) { bool sunrise_sunset_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {

View file

@ -52,6 +52,7 @@ typedef struct {
uint8_t rise_index; uint8_t rise_index;
uint8_t active_digit; uint8_t active_digit;
bool location_changed; bool location_changed;
int16_t tz;
watch_date_time rise_set_expires; watch_date_time rise_set_expires;
sunrise_sunset_lat_lon_settings_t working_latitude; sunrise_sunset_lat_lon_settings_t working_latitude;
sunrise_sunset_lat_lon_settings_t working_longitude; sunrise_sunset_lat_lon_settings_t working_longitude;

View file

@ -36,8 +36,8 @@ static const int8_t _sound_seq_start[] = {BUZZER_NOTE_C8, 2, 0};
static uint8_t _beeps_to_play; // temporary counter for ring signals playing static uint8_t _beeps_to_play; // temporary counter for ring signals playing
static inline int32_t _get_tz_offset(movement_settings_t *settings) { static inline int32_t _get_tz_offset(movement_settings_t *settings, watch_date_time date_time) {
return movement_timezone_offsets[settings->bit.time_zone] * 60; return get_timezone_offset(settings->bit.time_zone, date_time) * 60;
} }
static void _signal_callback() { static void _signal_callback() {
@ -50,7 +50,8 @@ static void _signal_callback() {
static void _start(timer_state_t *state, movement_settings_t *settings, bool with_beep) { static void _start(timer_state_t *state, movement_settings_t *settings, bool with_beep) {
if (state->timers[state->current_timer].value == 0) return; if (state->timers[state->current_timer].value == 0) return;
watch_date_time now = watch_rtc_get_date_time(); watch_date_time now = watch_rtc_get_date_time();
state->now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings)); int32_t tz = _get_tz_offset(settings, now);
state->now_ts = watch_utility_date_time_to_unix_time(now, tz);
if (state->mode == pausing) if (state->mode == pausing)
state->target_ts = state->now_ts + state->paused_left; state->target_ts = state->now_ts + state->paused_left;
else else
@ -58,7 +59,7 @@ static void _start(timer_state_t *state, movement_settings_t *settings, bool wit
state->timers[state->current_timer].unit.hours, state->timers[state->current_timer].unit.hours,
state->timers[state->current_timer].unit.minutes, state->timers[state->current_timer].unit.minutes,
state->timers[state->current_timer].unit.seconds); state->timers[state->current_timer].unit.seconds);
watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, _get_tz_offset(settings)); watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, tz);
state->mode = running; state->mode = running;
movement_schedule_background_task_for_face(state->watch_face_index, target_dt); movement_schedule_background_task_for_face(state->watch_face_index, target_dt);
watch_set_indicator(WATCH_INDICATOR_BELL); watch_set_indicator(WATCH_INDICATOR_BELL);
@ -210,7 +211,7 @@ void timer_face_activate(movement_settings_t *settings, void *context) {
watch_set_colon(); watch_set_colon();
if(state->mode == running) { if(state->mode == running) {
watch_date_time now = watch_rtc_get_date_time(); watch_date_time now = watch_rtc_get_date_time();
state->now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings)); state->now_ts = watch_utility_date_time_to_unix_time(now, _get_tz_offset(settings, now));
watch_set_indicator(WATCH_INDICATOR_BELL); watch_set_indicator(WATCH_INDICATOR_BELL);
} else { } else {
state->pausing_seconds = 1; state->pausing_seconds = 1;

View file

@ -30,8 +30,8 @@
static uint8_t focus_min = 25; static uint8_t focus_min = 25;
static uint8_t break_min = 5; static uint8_t break_min = 5;
static inline int32_t get_tz_offset(movement_settings_t *settings) { static inline int32_t get_tz_offset(movement_settings_t *settings, watch_date_time date_time) {
return movement_timezone_offsets[settings->bit.time_zone] * 60; return get_timezone_offset(settings->bit.time_zone, date_time) * 60;
} }
static uint8_t get_length(tomato_state_t *state) { static uint8_t get_length(tomato_state_t *state) {
@ -47,12 +47,13 @@ static uint8_t get_length(tomato_state_t *state) {
static void tomato_start(tomato_state_t *state, movement_settings_t *settings) { static void tomato_start(tomato_state_t *state, movement_settings_t *settings) {
watch_date_time now = watch_rtc_get_date_time(); watch_date_time now = watch_rtc_get_date_time();
int32_t tz = get_tz_offset(settings, now);
int8_t length = (int8_t) get_length(state); int8_t length = (int8_t) get_length(state);
state->mode = tomato_run; state->mode = tomato_run;
state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); state->now_ts = watch_utility_date_time_to_unix_time(now, tz);
state->target_ts = watch_utility_offset_timestamp(state->now_ts, 0, length, 0); state->target_ts = watch_utility_offset_timestamp(state->now_ts, 0, length, 0);
watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, get_tz_offset(settings)); watch_date_time target_dt = watch_utility_date_time_from_unix_time(state->target_ts, tz);
movement_schedule_background_task(target_dt); movement_schedule_background_task(target_dt);
watch_set_indicator(WATCH_INDICATOR_BELL); watch_set_indicator(WATCH_INDICATOR_BELL);
} }
@ -126,7 +127,7 @@ void tomato_face_activate(movement_settings_t *settings, void *context) {
tomato_state_t *state = (tomato_state_t *)context; tomato_state_t *state = (tomato_state_t *)context;
if (state->mode == tomato_run) { if (state->mode == tomato_run) {
watch_date_time now = watch_rtc_get_date_time(); watch_date_time now = watch_rtc_get_date_time();
state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings)); state->now_ts = watch_utility_date_time_to_unix_time(now, get_tz_offset(settings, now));
watch_set_indicator(WATCH_INDICATOR_BELL); watch_set_indicator(WATCH_INDICATOR_BELL);
} }
watch_set_colon(); watch_set_colon();

View file

@ -157,8 +157,9 @@ static void totp_generate_and_display(totp_state_t *totp_state) {
totp_display(totp_state); totp_display(totp_state);
} }
static inline uint32_t totp_compute_base_timestamp(movement_settings_t *settings) { static uint32_t totp_compute_base_timestamp(movement_settings_t *settings) {
return watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60); watch_date_time date_time = watch_rtc_get_date_time();
return watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60);
} }
void totp_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) { void totp_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr) {

View file

@ -254,7 +254,8 @@ void totp_face_lfs_activate(movement_settings_t *settings, void *context) {
} }
#endif #endif
totp_state->timestamp = watch_utility_date_time_to_unix_time(watch_rtc_get_date_time(), movement_timezone_offsets[settings->bit.time_zone] * 60); watch_date_time date_time = watch_rtc_get_date_time();
totp_state->timestamp = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60);
totp_face_set_record(totp_state, 0); totp_face_set_record(totp_state, 0);
} }

View file

@ -442,7 +442,7 @@ static void start_reading(accelerometer_data_acquisition_state_t *state, movemen
accelerometer_data_acquisition_record_t record; accelerometer_data_acquisition_record_t record;
watch_date_time date_time = watch_rtc_get_date_time(); watch_date_time date_time = watch_rtc_get_date_time();
state->starting_timestamp = watch_utility_date_time_to_unix_time(date_time, movement_timezone_offsets[settings->bit.time_zone] * 60); state->starting_timestamp = watch_utility_date_time_to_unix_time(date_time, get_timezone_offset(settings->bit.time_zone, date_time) * 60);
record.header.info.record_type = ACCELEROMETER_DATA_ACQUISITION_HEADER; record.header.info.record_type = ACCELEROMETER_DATA_ACQUISITION_HEADER;
record.header.info.range = ACCELEROMETER_RANGE; record.header.info.range = ACCELEROMETER_RANGE;
record.header.info.temperature = lis2dw_get_temperature(); record.header.info.temperature = lis2dw_get_temperature();

View file

@ -60,13 +60,6 @@ static void _handle_alarm_button(movement_settings_t *settings, watch_date_time
if (settings->bit.time_zone > 40) settings->bit.time_zone = 0; if (settings->bit.time_zone > 40) settings->bit.time_zone = 0;
break; break;
case 7: // daylight savings time case 7: // daylight savings time
if (settings->bit.dst_active) { // deactivate DST
date_time.unit.hour = (date_time.unit.hour + 24 - 1) % 24;
settings->bit.time_zone = movement_dst_inverse_jump_table[settings->bit.time_zone];
} else { // activate DST
date_time.unit.hour = (date_time.unit.hour + 1) % 24;
settings->bit.time_zone = movement_dst_jump_table[settings->bit.time_zone];
}
settings->bit.dst_active = !settings->bit.dst_active; settings->bit.dst_active = !settings->bit.dst_active;
break; break;
} }
@ -135,8 +128,9 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v
return movement_default_loop_handler(event, settings); return movement_default_loop_handler(event, settings);
} }
char buf[11]; char buf[13];
bool set_leading_zero = false; bool set_leading_zero = false;
if (current_page < 3) { if (current_page < 3) {
watch_set_colon(); watch_set_colon();
if (settings->bit.clock_mode_24h) { if (settings->bit.clock_mode_24h) {
@ -156,17 +150,18 @@ bool set_time_face_loop(movement_event_t event, movement_settings_t *settings, v
watch_clear_indicator(WATCH_INDICATOR_PM); watch_clear_indicator(WATCH_INDICATOR_PM);
sprintf(buf, "%s %2d%02d%02d", set_time_face_titles[current_page], date_time.unit.year + 20, date_time.unit.month, date_time.unit.day); sprintf(buf, "%s %2d%02d%02d", set_time_face_titles[current_page], date_time.unit.year + 20, date_time.unit.month, date_time.unit.day);
} else if (current_page < 7) { // zone } else if (current_page < 7) { // zone
char dst_char = (settings->bit.dst_active && dst_occurring(watch_rtc_get_date_time())) ? 'd' : ' ';
if (event.subsecond % 2) { if (event.subsecond % 2) {
watch_clear_colon(); watch_clear_colon();
sprintf(buf, "%s ", set_time_face_titles[current_page]); sprintf(buf, "%s %c", set_time_face_titles[current_page], dst_char);
} else { } else {
int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time);
watch_set_colon(); watch_set_colon();
sprintf(buf, "%s %3d%02d ", set_time_face_titles[current_page], (int8_t) (movement_timezone_offsets[settings->bit.time_zone] / 60), (int8_t) (movement_timezone_offsets[settings->bit.time_zone] % 60) * (movement_timezone_offsets[settings->bit.time_zone] < 0 ? -1 : 1)); sprintf(buf, "%s %3d%02d %c", set_time_face_titles[current_page], (int8_t) (tz / 60), (int8_t) (tz % 60) * (tz < 0 ? -1 : 1), dst_char);
} }
} else { // daylight savings } else { // daylight savings
watch_clear_colon(); watch_clear_colon();
if (settings->bit.dst_active) sprintf(buf, "%s dsT y", set_time_face_titles[current_page]); sprintf(buf, "%s dsT %c", set_time_face_titles[current_page], settings->bit.dst_active ? 'y' : 'n');
else sprintf(buf, "%s dsT n", set_time_face_titles[current_page]);
} }
// blink up the parameter we're setting // blink up the parameter we're setting

View file

@ -132,8 +132,9 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s
} }
break; break;
} }
if (current_page != 2) // Do not set time when we are at seconds, it was already set previously if (current_page != 2) { // Do not set time when we are at seconds, it was already set previously
watch_rtc_set_date_time(date_time_settings); watch_rtc_set_date_time(date_time_settings);
}
break; break;
case EVENT_ALARM_LONG_UP://Setting seconds on long release case EVENT_ALARM_LONG_UP://Setting seconds on long release
@ -171,11 +172,16 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s
if (settings->bit.time_zone > 40) settings->bit.time_zone = 0; if (settings->bit.time_zone > 40) settings->bit.time_zone = 0;
break; break;
} }
if (date_time_settings.unit.day > days_in_month(date_time_settings.unit.month, date_time_settings.unit.year + WATCH_RTC_REFERENCE_YEAR)) if (date_time_settings.unit.day > days_in_month(date_time_settings.unit.month, date_time_settings.unit.year + WATCH_RTC_REFERENCE_YEAR))
date_time_settings.unit.day = 1; date_time_settings.unit.day = 1;
if (current_page != 2) // Do not set time when we are at seconds, it was already set previously
if (current_page != 2) { // Do not set time when we are at seconds, it was already set previously
watch_rtc_set_date_time(date_time_settings); watch_rtc_set_date_time(date_time_settings);
}
//TODO: Do not update whole RTC, just what we are changing //TODO: Do not update whole RTC, just what we are changing
break; break;
case EVENT_TIMEOUT: case EVENT_TIMEOUT:
movement_move_to_face(0); movement_move_to_face(0);
@ -231,12 +237,13 @@ bool set_time_hackwatch_face_loop(movement_event_t event, movement_settings_t *s
watch_clear_colon(); watch_clear_colon();
sprintf(buf, "%s ", set_time_hackwatch_face_titles[current_page]); sprintf(buf, "%s ", set_time_hackwatch_face_titles[current_page]);
} else { } else {
int16_t tz = get_timezone_offset(settings->bit.time_zone, date_time_settings);
watch_set_colon(); watch_set_colon();
sprintf(buf, sprintf(buf,
"%s %3d%02d ", "%s %3d%02d ",
set_time_hackwatch_face_titles[current_page], set_time_hackwatch_face_titles[current_page],
(int8_t)(movement_timezone_offsets[settings->bit.time_zone] / 60), (int8_t)(tz / 60),
(int8_t)(movement_timezone_offsets[settings->bit.time_zone] % 60) * (movement_timezone_offsets[settings->bit.time_zone] < 0 ? -1 : 1)); (int8_t)(tz % 60) * (tz < 0 ? -1 : 1));
} }
} }

View file

@ -30,6 +30,19 @@ ext_irq_cb_t btn_alarm_callback;
ext_irq_cb_t a2_callback; ext_irq_cb_t a2_callback;
ext_irq_cb_t a4_callback; ext_irq_cb_t a4_callback;
static bool dst_skip_rolling_back;
bool get_dst_skip_rolling_back(void) {
return dst_skip_rolling_back;
}
void set_dst_skip_rolling_back(void) {
dst_skip_rolling_back = true;
}
void clear_dst_skip_rolling_back(void) {
dst_skip_rolling_back = false;
}
bool _watch_rtc_is_enabled(void) { bool _watch_rtc_is_enabled(void) {
return RTC->MODE2.CTRLA.bit.ENABLE; return RTC->MODE2.CTRLA.bit.ENABLE;
} }
@ -60,6 +73,7 @@ void watch_rtc_set_date_time(watch_date_time date_time) {
_sync_rtc(); // Double sync as without it at high Hz faces setting time is unrealiable (specifically, set_time_hackwatch) _sync_rtc(); // Double sync as without it at high Hz faces setting time is unrealiable (specifically, set_time_hackwatch)
RTC->MODE2.CLOCK.reg = date_time.reg; RTC->MODE2.CLOCK.reg = date_time.reg;
_sync_rtc(); _sync_rtc();
clear_dst_skip_rolling_back();
} }
watch_date_time watch_rtc_get_date_time(void) { watch_date_time watch_rtc_get_date_time(void) {

View file

@ -157,5 +157,11 @@ void watch_rtc_enable(bool en);
*/ */
void watch_rtc_freqcorr_write(int16_t value, int16_t sign); void watch_rtc_freqcorr_write(int16_t value, int16_t sign);
/** @brief Returns if we're currently at a point where the we rolled back for DST and need to ignore the next DST segment
*/
bool get_dst_skip_rolling_back(void);
void set_dst_skip_rolling_back(void);
void clear_dst_skip_rolling_back(void);
/// @} /// @}
#endif #endif

View file

@ -83,6 +83,44 @@ uint8_t is_leap(uint16_t y)
return !(y%4) && ((y%100) || !(y%400)); return !(y%4) && ((y%100) || !(y%400));
} }
uint8_t get_dst_status(watch_date_time date_time) {
watch_date_time dst_start_time;
watch_date_time dst_end_time;
uint32_t unix_dst_start_time;
uint32_t unix_dst_end_time;
uint32_t unix_curr_time;
dst_start_time.unit.year = date_time.unit.year;
dst_start_time.unit.month = 3;
dst_start_time.unit.hour = 2;
dst_start_time.unit.minute = 0;
dst_start_time.unit.second = 0;
dst_start_time.unit.day = 15 - watch_utility_get_iso8601_weekday_number(dst_start_time.unit.year + WATCH_RTC_REFERENCE_YEAR, dst_start_time.unit.month, 1);
unix_dst_start_time = watch_utility_date_time_to_unix_time(dst_start_time, 0);
dst_end_time.unit.year = date_time.unit.year;
dst_end_time.unit.month = 11;
dst_end_time.unit.hour = 2;
dst_end_time.unit.minute = 0;
dst_end_time.unit.second = 0;
dst_end_time.unit.day = 15 - watch_utility_get_iso8601_weekday_number(dst_end_time.unit.year + WATCH_RTC_REFERENCE_YEAR, dst_end_time.unit.month, 1);
unix_dst_end_time = watch_utility_date_time_to_unix_time(dst_end_time, 0);
unix_curr_time = watch_utility_date_time_to_unix_time(date_time, 0);
unix_curr_time -= date_time.unit.second;
if (date_time.unit.second > 45) // In emu, it's been seen that we may trigger at 59sec rather than exactly 0 each time
unix_curr_time += 60;
if (unix_curr_time == unix_dst_start_time) return DST_STARTING;
if (unix_curr_time == unix_dst_end_time) return DST_ENDING;
if (unix_curr_time > unix_dst_end_time || unix_curr_time < unix_dst_start_time) return DST_ENDED;
return DST_OCCURRING;
}
bool dst_occurring(watch_date_time date_time) {
return get_dst_status(date_time) <= DST_OCCURRING;
}
uint16_t watch_utility_days_since_new_year(uint16_t year, uint8_t month, uint8_t day) { uint16_t watch_utility_days_since_new_year(uint16_t year, uint8_t month, uint8_t day) {
uint16_t DAYS_SO_FAR[] = { uint16_t DAYS_SO_FAR[] = {
0, // Jan 0, // Jan

View file

@ -45,6 +45,13 @@ typedef struct {
uint32_t days; // 0-4294967295 uint32_t days; // 0-4294967295
} watch_duration_t; } watch_duration_t;
typedef enum {
DST_STARTING,
DST_OCCURRING,
DST_ENDING,
DST_ENDED
} dst_t;
/** @brief Returns a two-letter weekday for the given timestamp, suitable for display /** @brief Returns a two-letter weekday for the given timestamp, suitable for display
* in positions 0-1 of the watch face * in positions 0-1 of the watch face
* @param date_time The watch_date_time whose weekday you want. * @param date_time The watch_date_time whose weekday you want.
@ -78,6 +85,17 @@ uint16_t watch_utility_days_since_new_year(uint16_t year, uint8_t month, uint8_t
*/ */
uint8_t is_leap(uint16_t year); uint8_t is_leap(uint16_t year);
/** @brief Returns off of dst_t based off if DST is occurring, srted, ended, or none of those.
* @param date_time The watch_date_time that you wish to convert.
* @return DST_OCCURRING, DST_HAPPENING, DST_ENDING, DST_ENDED
*/
uint8_t get_dst_status(watch_date_time date_time);
/** @brief Returns true if it's DST and false otherwise.
* @param date_time The watch_date_time that you wish to convert.
*/
bool dst_occurring(watch_date_time date_time);
/** @brief Returns the UNIX time (seconds since 1970) for a given date/time in UTC. /** @brief Returns the UNIX time (seconds since 1970) for a given date/time in UTC.
* @param date_time The watch_date_time that you wish to convert. * @param date_time The watch_date_time that you wish to convert.
* @param year The year of the date you wish to convert. * @param year The year of the date you wish to convert.

View file

@ -39,6 +39,19 @@ ext_irq_cb_t btn_alarm_callback;
ext_irq_cb_t a2_callback; ext_irq_cb_t a2_callback;
ext_irq_cb_t a4_callback; ext_irq_cb_t a4_callback;
static bool dst_skip_rolling_back;
bool get_dst_skip_rolling_back(void) {
return dst_skip_rolling_back;
}
void set_dst_skip_rolling_back(void) {
dst_skip_rolling_back = true;
}
void clear_dst_skip_rolling_back(void) {
dst_skip_rolling_back = false;
}
bool _watch_rtc_is_enabled(void) { bool _watch_rtc_is_enabled(void) {
return true; return true;
} }
@ -57,6 +70,7 @@ void watch_rtc_set_date_time(watch_date_time date_time) {
const date = new Date(year, month - 1, day, hour, minute, second); const date = new Date(year, month - 1, day, hour, minute, second);
return date - Date.now(); return date - Date.now();
}, date_time.reg); }, date_time.reg);
clear_dst_skip_rolling_back();
} }
watch_date_time watch_rtc_get_date_time(void) { watch_date_time watch_rtc_get_date_time(void) {