@@ -40,9 +40,9 @@
# endif
typedef enum {
spring_tide , // Less than 1.8 days away from a full or new moon.
neap_tide , // Less than 1.8 days away from a first or third quarter moon.
medium_tide , // The rest
TIDE_SPRING , // Less than 1.8 days away from a full or new moon.
TIDE_NEAP , // Less than 1.8 days away from a first or third quarter moon.
TIDE_MEDIUM , // The rest
} tide_amplitude_t ;
static tide_amplitude_t _get_tide_amplitude ( uint32_t time ) {
@@ -50,25 +50,25 @@ static tide_amplitude_t _get_tide_amplitude(uint32_t time) {
double moon_age = fmod ( ( ( double ) ( time - FIRST_MOON ) ) / 86400 , LUNAR_DAYS / 2 ) ;
if ( moon_age < = LUNAR_DAYS / 16 | | moon_age > = LUNAR_DAYS * 7 / 16 ) {
return spring_tide ;
return TIDE_SPRING ;
} else if ( moon_age > LUNAR_DAYS * 3 / 16 & & moon_age < LUNAR_DAYS * 5 / 16 ) {
return neap_tide ;
return TIDE_NEAP ;
} else {
return medium_tide ;
return TIDE_MEDIUM ;
}
}
typedef enum {
empty , // No tide data set.
current , // Default screen, showing the current tide.
future , // Screen showing the time of future high and low tides.
setting_hour , // Setting screen, setting the hour of the next high tide.
setting_min , // Setting screen, setting the minute of the next high tide.
TIDE_SCREEN_EMPTY , // No tide data set.
TIDE_SCREEN_CURRENT , // Default screen, showing the current tide.
TIDE_SCREEN_FUTURE , // Screen showing the time of future high and low tides.
TIDE_SCREEN_SETTING_HOUR , // Setting screen, setting the hour of the next high tide.
TIDE_SCREEN_SETTING_MIN , // Setting screen, setting the minute of the next high tide.
} tide_mode_t ;
typedef enum {
high_tide ,
low_tide ,
TIDE_HIGH ,
TIDE_LOW ,
} tide_type_t ;
typedef struct {
@@ -81,19 +81,20 @@ typedef struct {
} tide_state_t ;
void tide_face_setup ( uint8_t watch_face_index , void * * state_ptr ) {
( void ) watch_face_index ;
if ( * state_ptr = = NULL ) {
// Boot time initialization.
* state_ptr = malloc ( sizeof ( tide_state_t ) ) ;
tide_state_t * state = ( tide_state_t * ) * state_ptr ;
state - > mode = empty ;
state - > mode = TIDE_SCREEN_EMPTY ;
}
}
uint32_t _get_current_unix_time ( ) {
static uint32_t _get_current_unix_time ( ) {
return watch_utility_date_time_to_unix_time ( movement_get_utc_date_time ( ) , 0 ) ;
}
void _move_next_high_tide ( tide_state_t * state , uint32_t now ) {
static void _move_next_high_tide ( tide_state_t * state , uint32_t now ) {
while ( state - > next_high_tide > now + SEMI_DIURNAL_TIDAL_PERIOD ) {
state - > next_high_tide - = SEMI_DIURNAL_TIDAL_PERIOD ;
}
@@ -104,8 +105,8 @@ void _move_next_high_tide(tide_state_t* state, uint32_t now) {
void tide_face_activate ( void * context ) {
tide_state_t * state = ( tide_state_t * ) context ;
if ( state - > mode ! = empty ) {
state - > mode = current ;
if ( state - > mode ! = TIDE_SCREEN_EMPTY ) {
state - > mode = TIDE_SCREEN_CURRENT ;
}
// int64 so that the substraction below works (we need a signed number and int32 will overflow soon).
// using int64 everywhere for the unix time would probably be better.
@@ -113,7 +114,7 @@ void tide_face_activate(void* context) {
if ( llabs ( now - state - > next_high_tide ) > 60 * 86400 ) {
// We revert to the empty mode if the next high tide is more than 2
// months from now, to avoid accumulating too much errors.
state - > mode = empty ;
state - > mode = TIDE_SCREEN_EMPTY ;
return ;
}
_move_next_high_tide ( state , now ) ;
@@ -127,11 +128,13 @@ static void _draw_tide_amplitude(uint32_t time) {
const digit_mapping_t * digit_mapping =
watch_get_lcd_type ( ) = = WATCH_LCD_TYPE_CLASSIC ? Classic_LCD_Display_Mapping : Custom_LCD_Display_Mapping ;
switch ( _get_tide_amplitude ( time ) ) {
case spring_tide :
case TIDE_SPRING :
_set_pixel ( digit_mapping [ 9 ] . segment [ 0 ] ) ; // top horizontal bar on the bottom-right character.
case medium_tide :
// fall-through
case TIDE_MEDIUM :
_set_pixel ( digit_mapping [ 9 ] . segment [ 6 ] ) ; // mid horizontal bar on the bottom-right character.
case neap_tide :
// fall-through
case TIDE_NEAP :
_set_pixel ( digit_mapping [ 9 ] . segment [ 3 ] ) ; // bottom horizontal bar on the bottom-right character.
break ;
}
@@ -172,11 +175,11 @@ static void _draw_day_and_time(uint32_t time, bool show_day, bool show_hour, boo
static void _draw ( tide_state_t * state , uint32_t now , uint8_t subsecond ) {
watch_clear_display ( ) ;
switch ( state - > mode ) {
case empty :
case TIDE_SCREEN_EMPTY :
watch_display_text_with_fallback ( WATCH_POSITION_TOP , " TIDE " , " TI " ) ;
watch_display_text ( WATCH_POSITION_BOTTOM , " ---- " ) ;
break ;
case current : {
case TIDE_SCREEN_CURRENT : {
double tide_age = state - > next_high_tide - now ;
_draw_tide_amplitude ( now ) ;
double tide_percent = ( cos ( tide_age / SEMI_DIURNAL_TIDAL_PERIOD * M_PI * 2 ) + 1 ) * 50 ;
@@ -211,8 +214,8 @@ static void _draw(tide_state_t *state, uint32_t now, uint8_t subsecond) {
}
break ;
}
case future :
if ( state - > future_tide_type = = low_tide ) {
case TIDE_SCREEN_FUTURE :
if ( state - > future_tide_type = = TIDE_LOW ) {
watch_display_text_with_fallback ( WATCH_POSITION_TOP_LEFT , " LOW " , " LO " ) ;
} else {
watch_display_text_with_fallback ( WATCH_POSITION_TOP_LEFT , " HIG " , " HI " ) ;
@@ -220,15 +223,15 @@ static void _draw(tide_state_t *state, uint32_t now, uint8_t subsecond) {
_draw_day_and_time ( state - > future_tide_time , true , true , true ) ;
_draw_tide_amplitude ( state - > future_tide_time ) ;
break ;
case setting_hour :
case setting_min :
case TIDE_SCREEN_SETTING_HOUR :
case TIDE_SCREEN_SETTING_MIN :
if ( state - > start_setting ) {
watch_display_text_with_fallback ( WATCH_POSITION_TOP , " HIGH " , " HI " ) ;
} else {
watch_display_text_with_fallback ( WATCH_POSITION_TOP_LEFT , " HIG " , " HI " ) ;
}
_draw_day_and_time ( state - > next_high_tide , ! state - > start_setting ,
( state - > mode ! = setting_hour | | subsecond % 2 ) , ( state - > mode ! = setting_min | | subsecond % 2 ) ) ;
( state - > mode ! = TIDE_SCREEN_SETTING_HOUR | | subsecond % 2 ) , ( state - > mode ! = TIDE_SCREEN_SETTING_MIN | | subsecond % 2 ) ) ;
break ;
}
}
@@ -249,21 +252,21 @@ bool tide_face_loop(movement_event_t event, void* context) {
switch ( event . event_type ) {
case EVENT_ACTIVATE :
_draw ( state , now , event . subsecond ) ;
if ( state - > mode = = current ) {
if ( state - > mode = = TIDE_SCREEN_CURRENT ) {
state - > last_current_update_time = now ;
}
break ;
case EVENT_TICK :
switch ( state - > mode ) {
case current :
case TIDE_SCREEN_CURRENT :
if ( now - state - > last_current_update_time > = 60 ) {
_move_next_high_tide ( state , now ) ;
_draw ( state , now , event . subsecond ) ;
state - > last_current_update_time = now ;
}
break ;
case setting_hour :
case setting_min :
case TIDE_SCREEN_SETTING_HOUR :
case TIDE_SCREEN_SETTING_MIN :
_draw ( state , now , event . subsecond ) ;
break ;
default :
@@ -280,12 +283,12 @@ bool tide_face_loop(movement_event_t event, void* context) {
break ;
case EVENT_LIGHT_BUTTON_DOWN :
switch ( state - > mode ) {
case setting_hour :
state - > mode = setting_min ;
case TIDE_SCREEN_SETTING_HOUR :
state - > mode = TIDE_SCREEN_SETTING_MIN ;
_draw ( state , now , event . subsecond ) ;
break ;
case setting_min :
state - > mode = current ;
case TIDE_SCREEN_SETTING_MIN :
state - > mode = TIDE_SCREEN_CURRENT ;
_move_next_high_tide ( state , _get_current_unix_time ( ) ) ;
movement_request_tick_frequency ( 1 ) ;
_draw ( state , now , event . subsecond ) ;
@@ -296,18 +299,18 @@ bool tide_face_loop(movement_event_t event, void* context) {
}
break ;
case EVENT_LIGHT_LONG_PRESS :
if ( state - > mode = = future ) {
state - > mode = current ;
if ( state - > mode = = TIDE_SCREEN_FUTURE ) {
state - > mode = TIDE_SCREEN_CURRENT ;
_draw ( state , now , event . subsecond ) ;
state - > last_current_update_time = now ;
}
break ;
case EVENT_ALARM_BUTTON_DOWN :
switch ( state - > mode ) {
case setting_hour :
case TIDE_SCREEN_SETTING_HOUR :
_offset_next_high_tide ( state , 3600 ) ;
break ;
case setting_min :
case TIDE_SCREEN_SETTING_MIN :
_offset_next_high_tide ( state , 60 ) ;
break ;
default :
@@ -316,21 +319,21 @@ bool tide_face_loop(movement_event_t event, void* context) {
_draw ( state , now , event . subsecond ) ;
break ;
case EVENT_ALARM_BUTTON_UP :
// We react to UP event only so that we don’ t switch to a future day at the beginning of a long press.
// We react to UP event only so that we don’ t switch to a TIDE_SCREEN_FUTURE day at the beginning of a long press.
switch ( state - > mode ) {
case current :
case TIDE_SCREEN_CURRENT :
if ( state - > next_high_tide - now > SEMI_DIURNAL_TIDAL_PERIOD / 2 ) {
state - > future_tide_time = state - > next_high_tide - SEMI_DIURNAL_TIDAL_PERIOD / 2 ;
state - > future_tide_type = low_tide ;
state - > future_tide_type = TIDE_LOW ;
} else {
state - > future_tide_time = state - > next_high_tide ;
state - > future_tide_type = high_tide ;
state - > future_tide_type = TIDE_HIGH ;
}
state - > mode = future ;
state - > mode = TIDE_SCREEN_FUTURE ;
break ;
case future :
case TIDE_SCREEN_FUTURE :
state - > future_tide_time + = SEMI_DIURNAL_TIDAL_PERIOD / 2 ;
state - > future_tide_type = state - > future_tide_type = = low_tide ? high_tide : low_tide ;
state - > future_tide_type = state - > future_tide_type = = TIDE_LOW ? TIDE_HIGH : TIDE_LOW ;
break ;
default :
break ;
@@ -339,27 +342,27 @@ bool tide_face_loop(movement_event_t event, void* context) {
break ;
case EVENT_ALARM_LONG_PRESS :
switch ( state - > mode ) {
case empty :
case TIDE_SCREEN_EMPTY :
state - > next_high_tide = _get_current_unix_time ( ) ;
// fallthrough intended.
case current :
case future :
state - > mode = setting_hour ;
// fall- through
case TIDE_SCREEN_CURRENT :
case TIDE_SCREEN_FUTURE :
state - > mode = TIDE_SCREEN_SETTING_HOUR ;
state - > start_setting = true ;
movement_request_tick_frequency ( 4 ) ;
break ;
case setting_hour :
case setting_min :
case TIDE_SCREEN_SETTING_HOUR :
case TIDE_SCREEN_SETTING_MIN :
break ;
}
_draw ( state , now , event . subsecond ) ;
break ;
case EVENT_MODE_BUTTON_DOWN :
switch ( state - > mode ) {
case setting_hour :
case TIDE_SCREEN_SETTING_HOUR :
_offset_next_high_tide ( state , - 3600 ) ;
break ;
case setting_min :
case TIDE_SCREEN_SETTING_MIN :
_offset_next_high_tide ( state , - 60 ) ;
break ;
default :
@@ -370,19 +373,19 @@ bool tide_face_loop(movement_event_t event, void* context) {
case EVENT_MODE_BUTTON_UP :
case EVENT_MODE_LONG_PRESS :
switch ( state - > mode ) {
case setting_hour :
case setting_min :
case TIDE_SCREEN_SETTING_HOUR :
case TIDE_SCREEN_SETTING_MIN :
break ;
default :
return movement_default_loop_handler ( event ) ;
}
break ;
case EVENT_TIMEOUT :
if ( state - > mode = = setting_min | | state - > mode = = setting_hour ) {
state - > mode = current ;
if ( state - > mode = = TIDE_SCREEN_SETTING_MIN | | state - > mode = = TIDE_SCREEN_SETTING_HOUR ) {
state - > mode = TIDE_SCREEN_CURRENT ;
_draw ( state , now , event . subsecond ) ;
}
// Pass through intended:
// fall- through
// Delegate the resign behavior to the default loop handler.
default :
return movement_default_loop_handler ( event ) ;
@@ -394,7 +397,7 @@ bool tide_face_loop(movement_event_t event, void* context) {
void tide_face_resign ( void * context ) {
// Any cleanup needed before the watch face goes off-screen.
tide_state_t * state = ( tide_state_t * ) context ;
if ( state - > mode = = setting_hour | | state - > mode = = setting_min ) {
if ( state - > mode = = TIDE_SCREEN_SETTING_HOUR | | state - > mode = = TIDE_SCREEN_SETTING_MIN ) {
// Not strictly needed because it will be done upon re-entering the
// watch face. But let’ s leave a clean state.
_move_next_high_tide ( state , _get_current_unix_time ( ) ) ;