summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorroot <root@artemis.panaceas.org>2023-09-29 14:34:58 +0000
committerroot <root@artemis.panaceas.org>2023-09-29 14:34:58 +0000
commit2ab1dcfe27b7fe9891aec12be407654db2a9f839 (patch)
tree7b6ad5db6634352500b3fe7a17cabff43dddaa53
downloadSensor-Watch-pq-2ab1dcfe27b7fe9891aec12be407654db2a9f839.tar.gz
Sensor-Watch-pq-2ab1dcfe27b7fe9891aec12be407654db2a9f839.tar.bz2
Sensor-Watch-pq-2ab1dcfe27b7fe9891aec12be407654db2a9f839.zip
first
-rw-r--r--.gitignore1
-rw-r--r--main/default-to-gsm20
-rw-r--r--main/endstop0
-rw-r--r--main/fix-serial97
-rw-r--r--main/fix-wakeup42
-rw-r--r--main/jmm57
-rw-r--r--main/metric404
-rw-r--r--main/moon601
-rw-r--r--main/series8
-rw-r--r--main/siderial273
10 files changed, 1503 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8be5547
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+status
diff --git a/main/default-to-gsm b/main/default-to-gsm
new file mode 100644
index 0000000..98de0a3
--- /dev/null
+++ b/main/default-to-gsm
@@ -0,0 +1,20 @@
+diff --git a/movement/watch_faces/complication/sunrise_sunset_face.c b/movement/watch_faces/complication/sunrise_sunset_face.c
+index 1d08bb0..9bd3369 100644
+--- a/movement/watch_faces/complication/sunrise_sunset_face.c
++++ b/movement/watch_faces/complication/sunrise_sunset_face.c
+@@ -49,8 +49,15 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s
+ movement_location_t movement_location = (movement_location_t) watch_get_backup_data(1);
+
+ if (movement_location.reg == 0) {
++#if 0
+ watch_display_string("RI no Loc", 0);
+ return;
++#else
++ // Obviously we're at the east end of GSM
++ movement_location.bit.latitude = 5221;
++ movement_location.bit.longitude = 12;
++ watch_store_backup_data(movement_location.reg, 1);
++#endif
+ }
+
+ watch_date_time date_time = watch_rtc_get_date_time(); // the current local date / time
diff --git a/main/endstop b/main/endstop
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/main/endstop
diff --git a/main/fix-serial b/main/fix-serial
new file mode 100644
index 0000000..80b11c5
--- /dev/null
+++ b/main/fix-serial
@@ -0,0 +1,97 @@
+diff --git a/movement/filesystem.c b/movement/filesystem.c
+index 97e3545..d3c2f83 100644
+--- a/movement/filesystem.c
++++ b/movement/filesystem.c
+@@ -275,6 +275,10 @@ void filesystem_process_command(char *line) {
+ filesystem_append_file(filename, "\n", 1);
+ }
+ free(text);
++ } else if (strcmp(command, "format") == 0) {
++ lfs_unmount(&lfs);
++ lfs_format(&lfs, &cfg);
++ lfs_mount(&lfs, &cfg);
+ } else {
+ printf("%s: command not found\n", command);
+ }
+diff --git a/movement/movement.c b/movement/movement.c
+index 825f130..c7066f7 100644
+--- a/movement/movement.c
++++ b/movement/movement.c
+@@ -547,7 +547,29 @@ bool app_loop(void) {
+ tx = "";
+ });
+ #else
+- read(0, line, 256);
++#if 0
++ read(0, &line[ll], sizeof(line)-ll);
++#else
++ // JMM yuck just yuck, we do our best to patch this mess up
++ {
++ static char buf[sizeof(line) - 1];
++ static int bl; //again really signed?
++ int red;
++
++ red = read(0, &buf[bl], sizeof(buf) - 1);
++
++ if (red > 0) {
++ write(0, &buf[bl], red);
++ bl += red;
++ if (buf[bl-1] == '\r') {
++ for (red = 0; red < bl; ++red)
++ line[red] = buf[red] == '\r' ? '\n' : buf[red];
++ //memcpy(line,buf,bl-1);
++ bl = 0;
++ }
++ }
++ }
++#endif
+ #endif
+ if (strlen(line)) filesystem_process_command(line);
+ }
+diff --git a/watch-library/hardware/watch/watch_private.c b/watch-library/hardware/watch/watch_private.c
+index cd607b8..5081fdb 100644
+--- a/watch-library/hardware/watch/watch_private.c
++++ b/watch-library/hardware/watch/watch_private.c
+@@ -246,9 +246,18 @@ void _watch_enable_usb(void) {
+ // this function ends up getting called by printf to log stuff to the USB console.
+ int _write(int file, char *ptr, int len) {
+ (void)file;
++ int i; //it's 2023 boys and girls you can use size_t and ssize_t
+ if (hri_usbdevice_get_CTRLA_ENABLE_bit(USB)) {
+- tud_cdc_n_write(0, (void const*)ptr, len);
+- tud_cdc_n_write_flush(0);
++ //tud_cdc_n_write(0, (void const*)ptr, len);
++ for (i = 0; i < len; ++i)
++ {
++ if (ptr[i]=='\n') {
++ tud_cdc_n_write(0, (void const*)"\r\n", 2);
++ } else {
++ tud_cdc_n_write(0, (void const*)&ptr[i], 1);
++ }
++ tud_cdc_n_write_flush(0);
++ }
+ return len;
+ }
+
+@@ -262,6 +271,8 @@ int _read(int file, char *ptr, int len) {
+ int actual_length = strlen(buf);
+ if (actual_length) {
+ memcpy(ptr, buf, min(len, actual_length));
++ //JMM yuckity yuck yuck
++ buf[0] = 0;
+ return actual_length;
+ }
+ return 0;
+diff --git a/watch-library/shared/watch/watch.h b/watch-library/shared/watch/watch.h
+index 790f9a1..b280ae6 100644
+--- a/watch-library/shared/watch/watch.h
++++ b/watch-library/shared/watch/watch.h
+@@ -95,5 +95,6 @@ void watch_reset_to_bootloader(void);
+ * @return The number of bytes read, or zero if no bytes were read.
+ */
+ int read(int file, char *ptr, int len);
++int write(int file, char *ptr, int len);
+
+-#endif /* WATCH_H_ */
+\ No newline at end of file
++#endif /* WATCH_H_ */
diff --git a/main/fix-wakeup b/main/fix-wakeup
new file mode 100644
index 0000000..e3933a2
--- /dev/null
+++ b/main/fix-wakeup
@@ -0,0 +1,42 @@
+diff --git a/movement/movement.c b/movement/movement.c
+index ec20ce6..825f130 100644
+--- a/movement/movement.c
++++ b/movement/movement.c
+@@ -132,7 +132,7 @@ const char movement_valid_position_1_chars[] = " ABCDEFHlJLNORTtUX-='01378";
+ void cb_mode_btn_interrupt(void);
+ void cb_light_btn_interrupt(void);
+ void cb_alarm_btn_interrupt(void);
+-void cb_alarm_btn_extwake(void);
++void cb_btn_extwake(void);
+ void cb_alarm_fired(void);
+ void cb_fast_tick(void);
+ void cb_tick(void);
+@@ -382,6 +382,8 @@ void app_setup(void) {
+ }
+ if (movement_state.le_mode_ticks != -1) {
+ watch_disable_extwake_interrupt(BTN_ALARM);
++ watch_disable_extwake_interrupt(BTN_LIGHT);
++ watch_disable_extwake_interrupt(BTN_MODE);
+
+ watch_enable_external_interrupts();
+ watch_register_interrupt_callback(BTN_MODE, cb_mode_btn_interrupt, INTERRUPT_TRIGGER_BOTH);
+@@ -464,7 +466,9 @@ bool app_loop(void) {
+ // if we have timed out of our low energy mode countdown, enter low energy mode.
+ if (movement_state.le_mode_ticks == 0) {
+ movement_state.le_mode_ticks = -1;
+- watch_register_extwake_callback(BTN_ALARM, cb_alarm_btn_extwake, true);
++ watch_register_extwake_callback(BTN_ALARM, cb_btn_extwake, true);
++ watch_register_extwake_callback(BTN_MODE, cb_btn_extwake, true);
++ watch_register_extwake_callback(BTN_LIGHT, cb_btn_extwake, true);
+ event.event_type = EVENT_NONE;
+ event.subsecond = 0;
+
+@@ -600,7 +604,7 @@ void cb_alarm_btn_interrupt(void) {
+ event.event_type = _figure_out_button_event(pin_level, EVENT_ALARM_BUTTON_DOWN, &movement_state.alarm_down_timestamp);
+ }
+
+-void cb_alarm_btn_extwake(void) {
++void cb_btn_extwake(void) {
+ // wake up!
+ _movement_reset_inactivity_countdown();
+ }
diff --git a/main/jmm b/main/jmm
new file mode 100644
index 0000000..0bde666
--- /dev/null
+++ b/main/jmm
@@ -0,0 +1,57 @@
+diff --git a/movement/alt_fw/jmm.h b/movement/alt_fw/jmm.h
+new file mode 100644
+index 0000000..878b4a4
+--- /dev/null
++++ b/movement/alt_fw/jmm.h
+@@ -0,0 +1,28 @@
++#ifndef MOVEMENT_CONFIG_H_
++#define MOVEMENT_CONFIG_H_
++
++#include "movement_faces.h"
++
++#define MOVEMENT_DEFAULT_RED_COLOR 0xf
++#define MOVEMENT_DEFAULT_GREEN_COLOR 0xf
++
++const watch_face_t watch_faces[] = {
++ simple_clock_face,
++ stock_stopwatch_face,
++ world_clock_face,
++ totp_face_lfs,
++ moon_phase_face,
++ sunrise_sunset_face,
++ decimal_time_face,
++ beats_face,
++ astronomy_face,
++ preferences_face,
++ frequency_correction_face,
++ set_time_face,
++};
++
++#define MOVEMENT_NUM_FACES (sizeof(watch_faces) / sizeof(watch_face_t))
++
++#define SIGNAL_TUNE_DEFAULT
++
++#endif // MOVEMENT_CONFIG_H_
+diff --git a/movement/make/Makefile b/movement/make/Makefile
+index 625c772..db08929 100644
+--- a/movement/make/Makefile
++++ b/movement/make/Makefile
+@@ -1,3 +1,5 @@
++FIRMWARE=JMM
++COLOR=RED
+ # Leave this line at the top of the file; it has all the watch library sources and includes.
+ TOP = ../..
+ include $(TOP)/make.mk
+diff --git a/movement/movement.c b/movement/movement.c
+index 0c6ed31..ec20ce6 100644
+--- a/movement/movement.c
++++ b/movement/movement.c
+@@ -52,6 +52,8 @@
+ #include "alt_fw/the_stargazer.h"
+ #elif MOVEMENT_FIRMWARE == MOVEMENT_FIRMWARE_DEEP_SPACE_NOW
+ #include "alt_fw/deep_space_now.h"
++#elif MOVEMENT_FIRMWARE == MOVEMENT_FIRMWARE_JMM
++#include "alt_fw/jmm.h"
+ #endif
+
+ #include "movement_custom_signal_tunes.h"
diff --git a/main/metric b/main/metric
new file mode 100644
index 0000000..cbf0287
--- /dev/null
+++ b/main/metric
@@ -0,0 +1,404 @@
+diff --git a/movement/alt_fw/jmm.h b/movement/alt_fw/jmm.h
+index 878b4a4..5c2cee7 100644
+--- a/movement/alt_fw/jmm.h
++++ b/movement/alt_fw/jmm.h
+@@ -10,6 +10,7 @@ const watch_face_t watch_faces[] = {
+ simple_clock_face,
+ stock_stopwatch_face,
+ world_clock_face,
++ metric_face,
+ totp_face_lfs,
+ moon_phase_face,
+ sunrise_sunset_face,
+diff --git a/movement/make/Makefile b/movement/make/Makefile
+index db08929..e131bbb 100644
+--- a/movement/make/Makefile
++++ b/movement/make/Makefile
+@@ -120,6 +120,7 @@ SRCS += \
+ ../watch_faces/complication/flashlight_face.c \
+ ../watch_faces/clock/decimal_time_face.c \
+ ../watch_faces/clock/wyoscan_face.c \
++ ../watch_faces/complication/metric_face.c \
+ # New watch faces go above this line.
+
+ # Leave this line at the bottom of the file; it has all the targets for making your project.
+diff --git a/movement/movement_faces.h b/movement/movement_faces.h
+index ff34c06..94316ce 100644
+--- a/movement/movement_faces.h
++++ b/movement/movement_faces.h
+@@ -95,6 +95,7 @@
+ #include "flashlight_face.h"
+ #include "decimal_time_face.h"
+ #include "wyoscan_face.h"
++#include "metric_face.h"
+ // New includes go above this line.
+
+ #endif // MOVEMENT_FACES_H_
+diff --git a/movement/watch_faces/complication/equinox.c b/movement/watch_faces/complication/equinox.c
+new file mode 100644
+index 0000000..e8d862e
+--- /dev/null
++++ b/movement/watch_faces/complication/equinox.c
+@@ -0,0 +1,101 @@
++#define D2R(a) ((a)*(M_PI/180.))
++
++
++
++
++// shamelessly stolen from Meeus Astronmical Algorithms Chapter 27 table 27.B
++// chapter 25 is probably more tractable, but this is easier to code up
++
++# if 0
++static double mean_vernal_equinox (unsigned year)
++{
++ double y = year;
++ y -= 2000;
++ y *= 0.001;
++ return 2451623.80984 + 365242.37404 * y + 0.05169 * (y * y) - 0.00411 * (y * y * y) - 0.00057 * (y * y * y * y);
++}
++
++static double mean_summer_solstice (unsigned year)
++{
++ double y = year;
++ y -= 2000;
++ y *= 0.001;
++ return 2451716.56767 + 365241.62603 * y + 0.00325 * (y * y) + 0.00888 * (y * y * y) - 0.00030 * (y * y * y * y);
++}
++#endif
++
++static double mean_autumnal_equinox (unsigned year)
++{
++ double y = year;
++ y -= 2000;
++ y *= 0.001;
++ return 2451810.21715 + 365242.01767 * y - 0.11575 * (y * y) + 0.00337 * (y * y * y) + 0.00078 * (y * y * y * y);
++}
++
++#if 0
++static double mean_winter_solstice (unsigned year)
++{
++ double y = year;
++ y -= 2000;
++ y *= 0.001;
++ return 2451900.05952 + 365242.74049 * y - 0.06223 * (y * y) - 0.00823 * (y * y * y) + 0.00032 * (y * y * y * y);
++}
++#endif
++
++static double orbital_periodic_terms (double t)
++{
++#define N 24
++ const double A[N] = {485, 203, 199, 182, 156, 136, 77, 74, 70, 58, 52, 50, 45,
++ 44, 29, 18, 17, 16, 14, 12, 12, 12, 9, 8
++ };
++ const double B[N] = {D2R (324.96), D2R (337.23), D2R (342.08), D2R (27.85),
++ D2R (73.14), D2R (171.52), D2R (222.54), D2R (296.72),
++ D2R (243.58), D2R (119.81), D2R (297.17), D2R (21.02),
++ D2R (247.54), D2R (325.15), D2R (60.93), D2R (155.12),
++ D2R (288.79), D2R (198.04), D2R (199.76), D2R (95.39),
++ D2R (287.11), D2R (320.81), D2R (227.73), D2R (15.45)
++ };
++ const double C[N] = {D2R (1934.136), D2R (32964.467), D2R (20.186),
++ D2R (445267.112), D2R (45036.886), D2R (22518.443),
++ D2R (65928.934), D2R (3034.906), D2R (9037.513),
++ D2R (33718.147), D2R (150.678), D2R (2281.226),
++ D2R (29929.562), D2R (31555.956), D2R (4443.417),
++ D2R (67555.328), D2R (4562.452), D2R (62894.029),
++ D2R (31436.921), D2R (14577.848), D2R (31931.756),
++ D2R (34777.259), D2R (1222.114), D2R (16859.074)
++ };
++ double s = 0;
++ unsigned i;
++
++ for (i = 0; i < N; ++i)
++ s += A[i] * cos (B[i] + (C[i] * t));
++
++ return s;
++}
++
++
++static double mean_to_real (double j0)
++{
++
++ double t = (j0 - 2451545.) / 36525.;
++ double w = D2R ((35999.373 * t) - 2.47);
++ double dl = 1 + 0.0334 * cos (w) + 0.0007 * cos (2. * w);
++ double s = orbital_periodic_terms (t);
++
++#if 0
++ printf ("j0=%.6f\r\n", j0);
++ printf ("t=%.6f\r\n", t);
++ printf ("w=%.6f\r\n", w);
++ printf ("dl=%.6f\r\n", dl);
++ printf ("s=%.6f\r\n", s);
++#endif
++
++ return j0 + ((0.00001 * s) / dl);
++}
++
++static double autumnal_equinox (unsigned y)
++{
++ return mean_to_real (mean_autumnal_equinox (y));
++ // return mean_to_real (mean_summer_solstice (y));
++}
++
+diff --git a/movement/watch_faces/complication/metric_face.c b/movement/watch_faces/complication/metric_face.c
+new file mode 100644
+index 0000000..9eaafbf
+--- /dev/null
++++ b/movement/watch_faces/complication/metric_face.c
+@@ -0,0 +1,198 @@
++#include <stdlib.h>
++#include <string.h>
++#include <math.h>
++#include "watch.h"
++#include "watch_utility.h"
++#include "vsop87a_micro.h" // smaller size, less accurate
++#include "vsop87a_milli.h"
++#include "astrolib.h"
++#include "metric_face.h"
++
++#define RATIO 8
++
++#include "equinox.c"
++
++static const char *days[10] =
++ { "Pr", "Du", "Tr", "Qa", "Qi", "SH", "Sp", "Oc", "No", "De" };
++
++
++static uint32_t
++jd_to_timestamp (double tjd)
++{
++ tjd -= 2440587.5;
++ tjd *= 86400.;
++ return (uint32_t) floor (tjd);
++}
++
++static uint32_t
++timestamp_start_of_year (int y)
++{
++ double julian_equinox = autumnal_equinox (y);
++ julian_equinox += (2.3372305555 / 360); /* Offset of paris meridian from greenwich meridian */
++ return jd_to_timestamp (julian_equinox);
++}
++
++static int
++days_since_equinox (watch_date_time date_time)
++{
++ uint32_t now = watch_utility_date_time_to_unix_time (date_time, 0);
++ uint32_t start_of_year =
++ timestamp_start_of_year (date_time.unit.year + 2020);
++
++ start_of_year/=86400;
++ start_of_year*=86400;
++
++ if (start_of_year > now)
++ start_of_year =
++ timestamp_start_of_year (date_time.unit.year + 2020 - 1);
++
++ now -= start_of_year;
++
++ now /= 86400;
++
++ return (int) now;
++}
++
++static void
++calculate_metric_date (metric_state_t * state, watch_date_time date_time)
++{
++ int dse = days_since_equinox (date_time);
++
++ state->m_day = (dse % 30) + 1;
++ state->m_wday = (dse % 10);
++ state->c_day = date_time.unit.day;
++
++}
++
++
++static uint32_t
++date_time_to_ds (watch_date_time dt)
++{
++ uint32_t ret;
++ ret = dt.unit.hour;
++ ret *= 60;
++ ret += dt.unit.minute;
++ ret *= 60;
++ ret += dt.unit.second;
++ return ret;
++}
++
++static void
++_metric_face_sync (movement_settings_t * settings, metric_state_t * state)
++{
++ watch_date_time date_time = watch_rtc_get_date_time ();
++
++ (void) settings;
++
++ if (date_time.unit.day != state->c_day)
++ calculate_metric_date (state, date_time);
++
++ state->dt = date_time_to_ds (date_time) * RATIO;
++ state->resync_at = state->dt * (3600 * RATIO);
++ if (state->resync_at > (86400 * RATIO))
++ state->resync_at = (86400 * RATIO);
++
++}
++
++static void
++_metric_face_update (movement_event_t event, movement_settings_t * settings,
++ metric_state_t * state, int show_secs)
++{
++ uint64_t v;
++ char buf[14];
++
++ (void) event;
++
++ if (state->dt >= state->resync_at)
++ _metric_face_sync (settings, state);
++
++ v = state->dt;
++ v *= (uint64_t) 100000;
++ v /= (uint64_t) (RATIO * 86400);
++
++
++#if 0
++ {
++ watch_date_time date_time = watch_rtc_get_date_time ();
++ printf ("%d %d\n", state->dt, date_time_to_ds (date_time) * RATIO);
++ }
++#endif
++
++
++ sprintf (buf, "%s%2d %05d", days[state->m_wday], (int) state->m_day,
++ (int) v);
++
++ if (!show_secs)
++ {
++ buf[8] = ' ';
++ buf[9] = ' ';
++ }
++
++ watch_display_string (buf, 0);
++}
++
++void
++metric_face_setup (movement_settings_t * settings, uint8_t watch_face_index,
++ void **context_ptr)
++{
++ (void) settings;
++ (void) watch_face_index;
++ if (*context_ptr == NULL)
++ {
++ *context_ptr = malloc (sizeof (metric_state_t));
++ memset (*context_ptr, 0, sizeof (metric_state_t));
++ }
++}
++
++void
++metric_face_activate (movement_settings_t * settings, void *context)
++{
++ metric_state_t *state = (metric_state_t *) context;
++ (void) settings;
++ movement_request_tick_frequency (RATIO);
++ state->resync_at = 0;
++}
++
++bool
++metric_face_loop (movement_event_t event, movement_settings_t * settings,
++ void *context)
++{
++ (void) settings;
++ metric_state_t *state = (metric_state_t *) context;
++
++ switch (event.event_type)
++ {
++ case EVENT_ACTIVATE:
++ watch_set_colon ();
++ /*fall through */
++ case EVENT_TICK:
++ state->dt++;
++ _metric_face_update (event, settings, state, 1);
++ break;
++ case EVENT_LOW_ENERGY_UPDATE:
++ state->dt += RATIO * 60;
++ _metric_face_update (event, settings, state, 0);
++ break;
++ case EVENT_ALARM_BUTTON_UP:
++ break;
++ case EVENT_ALARM_LONG_PRESS:
++ break;
++ case EVENT_TIMEOUT:
++ state->resync_at = 0;
++ break;
++ default:
++ movement_default_loop_handler (event, settings);
++ break;
++ }
++
++ return true;
++}
++
++void
++metric_face_resign (movement_settings_t * settings, void *context)
++{
++ (void) settings;
++ metric_state_t *state = (metric_state_t *) context;
++ state->resync_at = 0;
++ state->c_day = 0;
++}
+diff --git a/movement/watch_faces/complication/metric_face.h b/movement/watch_faces/complication/metric_face.h
+new file mode 100644
+index 0000000..1968e38
+--- /dev/null
++++ b/movement/watch_faces/complication/metric_face.h
+@@ -0,0 +1,51 @@
++/*
++ * MIT License
++ *
++ * Copyright (c) 2022 Joey Castillo
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in all
++ * copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ */
++
++#ifndef METRIC_FACE_H_
++#define METRIC_FACE_H_
++
++#include "movement.h"
++
++typedef struct {
++ uint32_t dt;
++ uint32_t resync_at;
++ uint32_t m_day;
++ uint32_t m_wday;
++ uint32_t c_day;
++} metric_state_t;
++
++void metric_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
++void metric_face_activate(movement_settings_t *settings, void *context);
++bool metric_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
++void metric_face_resign(movement_settings_t *settings, void *context);
++
++#define metric_face ((const watch_face_t){ \
++ metric_face_setup, \
++ metric_face_activate, \
++ metric_face_loop, \
++ metric_face_resign, \
++ NULL, \
++})
++
++#endif // METRIC_FACE_H_
diff --git a/main/moon b/main/moon
new file mode 100644
index 0000000..0374f01
--- /dev/null
+++ b/main/moon
@@ -0,0 +1,601 @@
+diff --git a/movement/lib/sunriset/moonriset.c b/movement/lib/sunriset/moonriset.c
+new file mode 100644
+index 0000000..ded9f33
+--- /dev/null
++++ b/movement/lib/sunriset/moonriset.c
+@@ -0,0 +1,491 @@
++#include <math.h>
++#include <stdio.h>
++#include "sunriset.h"
++
++
++#define E0BASE ((21.448 / 60. + 26.) / 60. + 23.)
++#define MOON_RADIUS 3474.8
++
++#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
++
++#define days_since_2000_Jan_0(y,m,d) \
++ (367L*(y)-((7*((y)+(((m)+9)/12)))/4)+((275*(m))/9)+(d)-730530L)
++
++typedef struct
++{
++ int d;
++ int m;
++ int mprime;
++ int f;
++ int csin;
++ int ccos;
++} Table;
++
++static const Table table_47a[] = {
++ {0, 0, 1, 0, 6288774, -20905355},
++ {2, 0, -1, 0, 1274027, -3699111},
++ {2, 0, 0, 0, 658314, -2955968},
++ {0, 0, 2, 0, 213618, -569925},
++ {0, 1, 0, 0, -185116, 48888},
++ {0, 0, 0, 2, -114332, -3149},
++ {2, 0, -2, 0, 58793, 246158},
++ {2, -1, -1, 0, 57066, -152138},
++ {2, 0, 1, 0, 53322, -170733},
++ {2, -1, 0, 0, 45758, -204586},
++ {0, 1, -1, 0, -40923, -129620},
++ {1, 0, 0, 0, -34720, 108743},
++ {0, 1, 1, 0, -30383, 104755},
++ {2, 0, 0, -2, 15327, 10321},
++ {0, 0, 1, 2, -12528, 0},
++ {0, 0, 1, -2, 10980, 79661},
++ {4, 0, -1, 0, 10675, -34782},
++ {0, 0, 3, 0, 10034, -23210},
++ {4, 0, -2, 0, 8548, -21636},
++ {2, 1, -1, 0, -7888, 24208},
++ {2, 1, 0, 0, -6766, 30824},
++ {1, 0, -1, 0, -5163, -8379},
++ {1, 1, 0, 0, 4987, -16675},
++ {2, -1, 1, 0, 4036, -12831},
++ {2, 0, 2, 0, 3994, -10445},
++ {4, 0, 0, 0, 3861, -11650},
++ {2, 0, -3, 0, 3665, 14403},
++ {0, 1, -2, 0, -2689, -7003},
++ {2, 0, -1, 2, -2602, 0},
++ {2, -1, -2, 0, 2390, 10056},
++ {1, 0, 1, 0, -2348, 6322},
++ {2, -2, 0, 0, 2236, -9884},
++ {0, 1, 2, 0, -2120, 5751},
++ {0, 2, 0, 0, -2069, 0},
++ {2, -2, -1, 0, 2048, -4950},
++ {2, 0, 1, -2, -1773, 4130},
++ {2, 0, 0, 2, -1595, 0},
++ {4, -1, -1, 0, 1215, -3958},
++ {0, 0, 2, 2, -1110, 0},
++ {3, 0, -1, 0, -892, 3258},
++ {2, 1, 1, 0, -810, 2616},
++ {4, -1, -2, 0, 759, -1897},
++ {0, 2, -1, 0, -713, -2117},
++ {2, 2, -1, 0, -700, 2354},
++ {2, 1, -2, 0, 691, 0},
++ {2, -1, 0, -2, 596, 0},
++ {4, 0, 1, 0, 549, -1423},
++ {0, 0, 4, 0, 537, -1117},
++ {4, -1, 0, 0, 520, -1571},
++ {1, 0, -2, 0, -487, -1739},
++ {2, 1, 0, -2, -399, 0},
++ {0, 0, 2, -2, -381, -4421},
++ {1, 1, 1, 0, 351, 0},
++ {3, 0, -2, 0, -340, 0},
++ {4, 0, -3, 0, 330, 0},
++ {2, -1, 2, 0, 327, 0},
++ {0, 2, 1, 0, -323, 1165},
++ {1, 1, -1, 0, 299, 0},
++ {2, 0, 3, 0, 294, 0},
++ {2, 0, -1, -2, 0, 8752}
++};
++
++static const Table table_47b[] = {
++ {0, 0, 0, 1, 5128122, 0},
++ {0, 0, 1, 1, 280602, 0},
++ {0, 0, 1, -1, 277693, 0},
++ {2, 0, 0, -1, 173237, 0},
++ {2, 0, -1, 1, 55413, 0},
++ {2, 0, -1, -1, 46271, 0},
++ {2, 0, 0, 1, 32573, 0},
++ {0, 0, 2, 1, 17198, 0},
++ {2, 0, 1, -1, 9266, 0},
++ {0, 0, 2, -1, 8822, 0},
++ {2, -1, 0, -1, 8216, 0},
++ {2, 0, -2, -1, 4324, 0},
++ {2, 0, 1, 1, 4200, 0},
++ {2, 1, 0, -1, -3359, 0},
++ {2, -1, -1, 1, 2463, 0},
++ {2, -1, 0, 1, 2211, 0},
++ {2, -1, -1, -1, 2065, 0},
++ {0, 1, -1, -1, -1870, 0},
++ {4, 0, -1, -1, 1828, 0},
++ {0, 1, 0, 1, -1794, 0},
++ {0, 0, 0, 3, -1749, 0},
++ {0, 1, -1, 1, -1565, 0},
++ {1, 0, 0, 1, -1491, 0},
++ {0, 1, 1, 1, -1475, 0},
++ {0, 1, 1, -1, -1410, 0},
++ {0, 1, 0, -1, -1344, 0},
++ {1, 0, 0, -1, -1335, 0},
++ {0, 0, 3, 1, 1107, 0},
++ {4, 0, 0, -1, 1021, 0},
++ {4, 0, -1, 1, 833, 0},
++ {0, 0, 1, -3, 777, 0},
++ {4, 0, -2, 1, 671, 0},
++ {2, 0, 0, -3, 607, 0},
++ {2, 0, 2, -1, 596, 0},
++ {2, -1, 1, -1, 491, 0},
++ {2, 0, -2, 1, -451, 0},
++ {0, 0, 3, -1, 439, 0},
++ {2, 0, 2, 1, 422, 0},
++ {2, 0, -3, -1, 421, 0},
++ {2, 1, -1, 1, -366, 0},
++ {2, 1, 0, 1, -351, 0},
++ {4, 0, 0, 1, 331, 0},
++ {2, -1, 1, 1, 315, 0},
++ {2, -2, 0, -1, 302, 0},
++ {0, 0, 1, 3, -283, 0},
++ {2, 1, 1, -1, -229, 0},
++ {1, 1, 0, -1, 223, 0},
++ {1, 1, 0, 1, 223, 0},
++ {0, 1, -2, -1, -220, 0},
++ {2, 1, -1, -1, -220, 0},
++ {1, 0, 1, 1, -185, 0},
++ {2, -1, -2, -1, 181, 0},
++ {0, 1, 2, 1, -177, 0},
++ {4, 0, -2, -1, 176, 0},
++ {4, -1, -1, -1, 166, 0},
++ {1, 0, 1, -1, -164, 0},
++ {4, 0, 1, -1, 132, 0},
++ {1, 0, -1, -1, -119, 0},
++ {4, -1, 0, -1, 115, 0},
++ {2, -2, 0, 1, 107, 0},
++};
++
++
++static double
++deg2rad (double d)
++{
++ return d * (M_PI / 180.);
++}
++
++static double
++fold_deg2rad (double d)
++{
++ d /= 360.;
++ d = d - floor (d);
++ return d * 2.0 * M_PI;
++}
++
++
++static void
++compute_table (const Table * t, unsigned n, double *ret_sin, double *ret_cos,
++ double D, double M, double Mprime, double F, double E,
++ double E2)
++{
++ unsigned i;
++ double sigma_sin = 0;
++ double sigma_cos = 0;
++
++ for (i = 0; i < n; ++i)
++ {
++ double csin = t[i].csin;
++ double ccos = t[i].ccos;
++ double arg = 0;
++
++ switch (t[i].m)
++ {
++ case -2:
++ case 2:
++ csin *= E2;
++
++ if (t[i].ccos)
++ ccos *= E2;
++
++ break;
++
++ case -1:
++ case 1:
++ csin *= E;
++
++ if (t[i].ccos)
++ ccos *= E;
++
++ break;
++ }
++
++ if (t[i].d)
++ arg += D * (double) t[i].d;
++
++ if (t[i].m)
++ arg += M * (double) t[i].m;
++
++ if (t[i].mprime)
++ arg += Mprime * (double) t[i].mprime;
++
++ if (t[i].f)
++ arg += F * (double) t[i].f;
++
++ sigma_sin += csin * sin (arg);
++
++ if (t[i].ccos)
++ sigma_cos += ccos * cos (arg);
++ }
++
++ *ret_sin = sigma_sin;
++
++ if (ret_cos)
++ *ret_cos = sigma_cos;
++}
++
++
++static int
++moon_above_horizon (double jd, double lst, double lat, int limb, double altit)
++{
++ double T = (jd) / 36525.;
++ double L, Lprime, D, M, Mprime, F, A1, A2, A3, E, E2;
++ double sigma_l, sigma_r, sigma_b;
++ double lambda, beta, big_delta;
++ double omega, delta_psi, delta_epsilon;
++ double epsilon;
++ double alpha, delta;
++
++ //printf("j0=%f T=%f lst=%f alit=%f\n",jd+2451545.,T,lst,altit);
++
++
++ // Sun's mean longitude
++ L = fold_deg2rad (36000.7698 * T + 280.4665);
++
++ // Moon's mean longitude.
++ Lprime = fold_deg2rad ((((-(T / 65194000.) + 1. / 538841.) * T - 0.0015786) * T + 481267.88123421) * T + 218.3164477); // Meeus (47.1)
++
++ // Moon's mean elongation.
++ D = fold_deg2rad ((((T / 113065000. + 1. / 545868.) * T - 0.0018819) * T + 445267.1114034) * T + 297.8501921); //Meeus (47.2)
++
++ // Sun's mean anomaly.
++ M = fold_deg2rad (((T / 24490000. - 0.0001536) * T + 35999.0502909) * T + 357.5291092); // Meeus (47.3)
++
++ // Moon's mean anomaly.
++ Mprime = fold_deg2rad ((((-T / 14712000. + 1. / 69699.) * T + 0.0087414) * T + 477198.8675055) * T + 134.9633964); // Meeus (47.4)
++
++ // Moon's argument of latitude (mean distance
++ // from ascending node).
++
++ F = fold_deg2rad ((((T / 863310000. - 1. / 3526000.) * T - 0.0036539) * T + 483202.0175233) * T + 93.2720950); // Meeus (47.5)
++
++ // further arguments
++ A1 = fold_deg2rad (131.849 * T + 119.75); // Venus
++ A2 = fold_deg2rad (479264.290 * T + 53.09); // Jupiter
++ A3 = fold_deg2rad (481266.484 * T + 313.45); // undocumented
++
++ // Eccentricity correction factor.
++ E = (-0.0000074 * T - 0.002516) * T + 1.; // Meeus (47.6)
++ E2 = E * E;
++
++
++ compute_table (table_47a, ARRAY_SIZE (table_47a), &sigma_l, &sigma_r, D,
++ M, Mprime, F, E, E2);
++ compute_table (table_47b, ARRAY_SIZE (table_47b), &sigma_b, (void *) 0, D,
++ M, Mprime, F, E, E2);
++
++ sigma_l += 3958. * sin (A1) + 1962. * sin (Lprime - F) + 318. * sin (A2);
++ sigma_b +=
++ -2235. * sin (Lprime) + 382. * sin (A3) + 175. * sin (A1 - F) +
++ 175. * sin (A1 + F) + 127. * sin (Lprime - Mprime) -
++ 115. * sin (Lprime + Mprime);
++
++ // Coordinates of Moon (finally!)
++
++ lambda = deg2rad (sigma_l / 1000000.) + Lprime;
++ beta = deg2rad (sigma_b / 1000000.);
++ big_delta = sigma_r / 1000. + 385000.56;
++
++#if 0
++ printf ("JDE=%+f\n", j0);
++ printf ("T =%+f\n", T);
++ printf ("L' =%+f\n", Lprime * (180 / M_PI));
++ printf ("D =%+f\n", D * (180 / M_PI));
++ printf ("M =%+f\n", M * (180 / M_PI));
++ printf ("M' =%+f\n", Mprime * (180 / M_PI));
++ printf ("F =%+f\n", F * (180 / M_PI));
++ printf ("A1 =%+f\n", A1 * (180 / M_PI));
++ printf ("A2 =%+f\n", A2 * (180 / M_PI));
++ printf ("A3 =%+f\n", A3 * (180 / M_PI));
++ printf ("E =%+f\n", E);
++ printf ("Sl =%+f\n", sigma_l);
++ printf ("Sb =%+f\n", sigma_b);
++ printf ("Sr =%+f\n", sigma_r);
++
++ printf ("l=%+f\n", lambda * (180 / M_PI));
++ printf ("b=%+f\n", beta * (180 / M_PI));
++ printf ("D=%+f\n", big_delta);
++#endif
++
++
++ omega =
++ fold_deg2rad (((T / 450000 + .0020708) * T - 1934.136261) * T +
++ 125.04452);
++
++ //budget - not doing the whole of chapter 22
++ delta_psi =
++ deg2rad ((-17.20 * sin (omega) - 1.32 * sin (2 * L) -
++ 0.23 * sin (2 * Lprime) + 0.21 * sin (2 * omega)) / 3600.);
++ delta_epsilon =
++ deg2rad ((9.20 * cos (omega) + 0.57 * cos (2 * L) +
++ 0.10 * cos (2 * Lprime) - 0.09 * cos (2 * omega)) / 3600);
++
++ lambda += delta_psi;
++
++ epsilon =
++ deg2rad (((0.001813 * T - 0.00059) * T - 46.8150) * T / 3600 +
++ E0BASE);
++ epsilon += delta_epsilon;
++
++#if 0
++ printf ("delta_psi=%+f\n", delta_psi * (180 / M_PI));
++ printf ("delta_epsilon=%+f\n", delta_epsilon * (180 / M_PI));
++ printf ("epsilon=%+f\n", epsilon * (180 / M_PI));
++#endif
++
++
++ alpha =
++ atan2 (sin (lambda) * cos (epsilon) - tan (beta) * sin (epsilon),
++ cos (lambda));
++ delta =
++ asin (sin (beta) * cos (epsilon) +
++ cos (beta) * sin (epsilon) * sin (lambda));
++
++#if 0
++ printf ("alpha=%+f\n", alpha * (180. / M_PI));
++ printf ("delta=%+f\n", delta * (180. / M_PI));
++#endif
++
++ double H = deg2rad (lst) - alpha;
++
++ lat = deg2rad (lat);
++
++ double h =
++ asin (sin (lat) * sin (delta) + cos (lat) * cos (delta) * cos (H));
++
++// printf("rawh=%f\n",h);
++
++ if (limb)
++ h += atan2 (MOON_RADIUS / 2, big_delta);
++
++ h += deg2rad (altit);
++
++ return h > 0;
++}
++
++#if 0
++double
++correct_refraction (double a)
++{
++
++
++ if (a > ((-2.0 / 180.) * (M_PI)))
++ {
++ //double deg = a * (180./M_PI);
++ //double correction = deg2rad( (1.02 / tan (deg2rad (deg + 10.3/(deg + 5.11))) + .0019279)/60.);
++ //a+= deg2rad (correction / 60.);
++ a += 0.0002967 / tan (a + 0.00312536 / (a + 0.08901179));
++ }
++ return a;
++}
++#endif
++
++static double
++localSiderealTime (double jd, double longitude)
++{
++ double lSideTime = (15.0L * (6.697374558L + 0.06570982441908L * jd +
++ remainder (jd, 1) * 24 + 12 +
++ 0.000026 * (jd / 36525) * (jd / 36525))
++ - longitude) / 360;
++ lSideTime -= floor (lSideTime);
++ lSideTime *= 360; // Convert to degrees.
++//printf("lst %f %f => %f\n",jd+2451545.,longitude,lSideTime);
++ return (lSideTime);
++}
++
++
++int
++__moonriset__ (int year, int month, int day, int hour, int min, double lon,
++ double lat, double altit, int upper_limb, double *when)
++{
++ double jd, jo, jt, o, step;
++ double lst;
++ int up, start_up = -1;
++
++ jd = days_since_2000_Jan_0 (year, month, day) - 1.5;
++
++
++ jo = (double) min;
++ jo = (jo / 60.) + (double) hour;
++ jo = (jo / 24.);
++
++ jt = jd + jo;
++
++
++ step = 0.1;
++
++ for (o = 0; o < 2.5; o += step)
++ {
++ jt = jd + jo + o;
++
++
++ lst = localSiderealTime (jt, lon);
++ up = moon_above_horizon (jt, lst, lat, upper_limb, altit);
++
++//printf("%f %f %d %d\n",jt,jt-jd,start_up,up);
++
++ if (start_up == -1)
++ {
++ start_up = up;
++ continue;
++ }
++
++ if (up != start_up)
++ {
++ if (step < 30. / 86400.)
++ break;
++ o -= step;
++ step *= 0.5;
++ }
++ }
++
++ if (up == start_up)
++ {
++ return -1;
++ }
++//printf("moon goes to state %d at jd=%f jo=%f o=%f s=%f l=%f\n",up,jd,jo,o,jd+jo+o,jt);
++ *when = 24. * (o + jo);
++ return up;
++}
++
++
++#if 0
++static int
++show_time (char *p, double s)
++{
++ double h, m;
++
++ if (s < 0)
++ s += 24.;
++
++ h = floor (s);
++ s = (s - h) * 60.;
++ m = floor (s);
++ s = (s - m) * 60.;
++
++ printf ("%s %02d:%02d:%08.5f\n", p, (int) h, (int) m, s);
++}
++
++
++int
++main (int argc, char *argv[])
++{
++ double rise, set;
++
++ moonriset (2023, 9, 23, 15, 36, 0.1280, 52.2202, -19.152 / 60., 1, &rise,
++ &set);
++ show_time ("RISE ", rise);
++ show_time ("SET ", set);
++
++ moonriset (2023, 9, 24, 0, 00, 0.1280, 52.2202, -19.152 / 60., 1, &rise,
++ &set);
++ show_time ("RISE ", rise);
++ show_time ("SET ", set);
++
++
++}
++
++
++
++
++
++#endif
+diff --git a/movement/lib/sunriset/sunriset.h b/movement/lib/sunriset/sunriset.h
+index 7b2b6c8..8a79e88 100644
+--- a/movement/lib/sunriset/sunriset.h
++++ b/movement/lib/sunriset/sunriset.h
+@@ -22,9 +22,13 @@ double __daylen__( int year, int month, int day, double lon, double lat,
+ double altit, int upper_limb );
+
+ int __sunriset__( int year, int month, int day, double lon, double lat,
+- double altit, int upper_limb, double *rise, double *set );
++ double altit, int upper_limb, double *rise, double *set);
++
++int __moonriset__( int year, int month, int day, int hour, int min, double lon, double lat,
++ double altit, int upper_limb, double *when);
+
+ void sun_RA_dec( double d, double *RA, double *dec, double *r );
++void moon_RA_dec( double d, double *RA, double *dec, double *r );
+
+ double revolution( double x );
+
+@@ -65,6 +69,12 @@ double GMST0( double d );
+ __daylen__( year, month, day, lon, lat, -18.0, 0 )
+
+
++
++/* This macro computes times for moonrise/moonset. */
++/* It's right according to meeus but it's not matching anytihng else */
++#define moon_rise_set(year,month, day, hour,min, lon, lat, when) \
++ __moonriset__( year, month, day, hour,min, lon, lat, -19.152/60.0, 1,when)
++
+ /* This macro computes times for sunrise/sunset. */
+ /* Sunrise/set is considered to occur when the Sun's upper limb is */
+ /* 35 arc minutes below the horizon (this accounts for the refraction */
+diff --git a/movement/make/Makefile b/movement/make/Makefile
+index 7110be2..db93b19 100644
+--- a/movement/make/Makefile
++++ b/movement/make/Makefile
+@@ -41,6 +41,7 @@ SRCS += \
+ ../lib/TOTP/TOTP.c \
+ ../lib/base32/base32.c \
+ ../lib/sunriset/sunriset.c \
++ ../lib/sunriset/moonriset.c \
+ ../lib/vsop87/vsop87a_milli.c \
+ ../lib/astrolib/astrolib.c \
+ ../lib/morsecalc/calc.c \
+diff --git a/movement/watch_faces/complication/sunrise_sunset_face.c b/movement/watch_faces/complication/sunrise_sunset_face.c
+index 2a929c8..9bd3369 100644
+--- a/movement/watch_faces/complication/sunrise_sunset_face.c
++++ b/movement/watch_faces/complication/sunrise_sunset_face.c
+@@ -78,6 +78,46 @@ static void _sunrise_sunset_face_update(movement_settings_t *settings, sunrise_s
+ // 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;
+
++
++ if (state->rise_index == 2) {
++ int wot;
++ watch_clear_display();
++ watch_start_character_blink('C', 100);
++
++ wot = moon_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day,scratch_time.unit.hour,scratch_time.unit.minute, lon, lat, &rise);
++ watch_stop_blink();
++
++ rise += hours_from_utc;
++ if (rise>24.) {
++ scratch_time.unit.day++;
++ rise -= 24.;
++ }
++ minutes = 60.0 * fmod(rise, 1);
++ seconds = 60.0 * fmod(minutes, 1);
++
++ scratch_time.unit.hour = floor(rise);
++
++ if (seconds < 30) scratch_time.unit.minute = floor(minutes);
++ else scratch_time.unit.minute = ceil(minutes);
++
++ if (scratch_time.unit.minute == 60) {
++ scratch_time.unit.minute = 0;
++ scratch_time.unit.hour = (scratch_time.unit.hour + 1) % 24;
++ }
++
++ watch_set_colon();
++
++ if (!settings->bit.clock_mode_24h) {
++ if (watch_utility_convert_to_12_hour(&scratch_time)) watch_set_indicator(WATCH_INDICATOR_PM);
++ else watch_clear_indicator(WATCH_INDICATOR_PM);
++ }
++
++ sprintf(buf, "LU%2d%2d%02d%s", scratch_time.unit.day, scratch_time.unit.hour, scratch_time.unit.minute,wot ? "RI":"SE");
++ watch_display_string(buf, 0);
++ return;
++ }
++
++
+ // 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++) {
+ uint8_t result = sun_rise_set(scratch_time.unit.year + WATCH_RTC_REFERENCE_YEAR, scratch_time.unit.month, scratch_time.unit.day, lon, lat, &rise, &set);
+@@ -369,7 +409,7 @@ bool sunrise_sunset_face_loop(movement_event_t event, movement_settings_t *setti
+ _sunrise_sunset_face_advance_digit(state);
+ _sunrise_sunset_face_update_settings_display(event, context);
+ } else {
+- state->rise_index = (state->rise_index + 1) % 2;
++ state->rise_index = (state->rise_index + 1) % 3;
+ _sunrise_sunset_face_update(settings, state);
+ }
+ break;
diff --git a/main/series b/main/series
new file mode 100644
index 0000000..43d4273
--- /dev/null
+++ b/main/series
@@ -0,0 +1,8 @@
+jmm
+default-to-gsm
+fix-wakeup
+fix-serial
+metric
+siderial
+moon
+endstop
diff --git a/main/siderial b/main/siderial
new file mode 100644
index 0000000..2a2b894
--- /dev/null
+++ b/main/siderial
@@ -0,0 +1,273 @@
+diff --git a/movement/alt_fw/jmm.h b/movement/alt_fw/jmm.h
+index 5c2cee7..061d058 100644
+--- a/movement/alt_fw/jmm.h
++++ b/movement/alt_fw/jmm.h
+@@ -12,6 +12,7 @@ const watch_face_t watch_faces[] = {
+ world_clock_face,
+ metric_face,
+ totp_face_lfs,
++ siderial_face,
+ moon_phase_face,
+ sunrise_sunset_face,
+ decimal_time_face,
+diff --git a/movement/make/Makefile b/movement/make/Makefile
+index e131bbb..7110be2 100644
+--- a/movement/make/Makefile
++++ b/movement/make/Makefile
+@@ -121,6 +121,7 @@ SRCS += \
+ ../watch_faces/clock/decimal_time_face.c \
+ ../watch_faces/clock/wyoscan_face.c \
+ ../watch_faces/complication/metric_face.c \
++ ../watch_faces/complication/siderial_face.c \
+ # New watch faces go above this line.
+
+ # Leave this line at the bottom of the file; it has all the targets for making your project.
+diff --git a/movement/movement_faces.h b/movement/movement_faces.h
+index 94316ce..c605743 100644
+--- a/movement/movement_faces.h
++++ b/movement/movement_faces.h
+@@ -96,6 +96,7 @@
+ #include "decimal_time_face.h"
+ #include "wyoscan_face.h"
+ #include "metric_face.h"
++#include "siderial_face.h"
+ // New includes go above this line.
+
+ #endif // MOVEMENT_FACES_H_
+diff --git a/movement/watch_faces/complication/siderial_face.c b/movement/watch_faces/complication/siderial_face.c
+new file mode 100644
+index 0000000..b60f4a9
+--- /dev/null
++++ b/movement/watch_faces/complication/siderial_face.c
+@@ -0,0 +1,175 @@
++#include <stdlib.h>
++#include <string.h>
++#include <math.h>
++#include "watch.h"
++#include "watch_utility.h"
++#include "vsop87a_micro.h" // smaller size, less accurate
++#include "vsop87a_milli.h"
++#include "astrolib.h"
++#include "siderial_face.h"
++
++#define TICK_MAX 14400
++
++
++static uint32_t
++date_time_to_ds (watch_date_time dt)
++{
++ uint32_t ret;
++ ret = dt.unit.hour;
++ ret *= 60;
++ ret += dt.unit.minute;
++ ret *= 60;
++ ret += dt.unit.second;
++ return ret;
++}
++
++static void
++_siderial_face_recalculate (movement_settings_t * settings,
++ siderial_state_t * state)
++{
++ watch_date_time date_time = watch_rtc_get_date_time ();
++ movement_location_t movement_location;
++
++ double jd, days;
++ uint32_t timestamp = watch_utility_date_time_to_unix_time (date_time,
++ movement_timezone_offsets
++ [settings->bit.
++ time_zone] *
++ 60);
++
++ state->ref_ds = date_time_to_ds (date_time);
++
++ date_time = watch_utility_date_time_from_unix_time (timestamp, 0);
++ 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);
++
++ jd -= .5;
++
++ if (state->lst)
++ {
++ movement_location = (movement_location_t) watch_get_backup_data (1);
++ jd += ((double) movement_location.bit.longitude) / 36000.;
++ }
++
++
++ state->ref_st = (uint32_t) (modf (jd, &days) * 86400. + .5);
++ state->ticks = 0;
++}
++
++static void
++_siderial_face_update (movement_event_t event, movement_settings_t * settings,
++ siderial_state_t * state, int show_secs)
++{
++ watch_date_time date_time = watch_rtc_get_date_time ();
++ uint32_t ds = date_time_to_ds (date_time);
++ char buf[14];
++ int sh, sm, ss;
++
++ (void) event;
++
++ if (state->ticks >= TICK_MAX)
++ _siderial_face_recalculate (settings, state);
++
++
++ ss = state->ref_st + (int) (.5 +
++ (86164.0905 / 86400.) * (double) (ds -
++ state->ref_ds));
++ if (ss > 86400.)
++ ss -= 86400.;
++#if 0 //The compiler is too dumb to realize the bounds so generates a Werror for the sprintf
++ sm = ss / 60;
++ ss -= sm * 60;
++ sh = sm / 60;
++ sm -= sh * 60;
++#else
++ sm = ss / 60;
++ ss %= 60;
++ sh = sm / 60;
++ sm %= 60;
++ sh %= 64;
++#endif
++
++ sprintf (buf, "ST %c%02d%02d%02d", state->lst ? 'L' : ' ', sh, sm, ss);
++
++ if (!show_secs)
++ {
++ buf[8] = ' ';
++ buf[9] = ' ';
++ }
++
++ watch_display_string (buf, 0);
++}
++
++void
++siderial_face_setup (movement_settings_t * settings, uint8_t watch_face_index,
++ void **context_ptr)
++{
++ (void) settings;
++ (void) watch_face_index;
++ if (*context_ptr == NULL)
++ {
++ *context_ptr = malloc (sizeof (siderial_state_t));
++ memset (*context_ptr, 0, sizeof (siderial_state_t));
++ }
++}
++
++void
++siderial_face_activate (movement_settings_t * settings, void *context)
++{
++ siderial_state_t *state = (siderial_state_t *) context;
++ (void) settings;
++ movement_request_tick_frequency (2);
++ state->ticks = TICK_MAX;
++}
++
++bool
++siderial_face_loop (movement_event_t event, movement_settings_t * settings,
++ void *context)
++{
++ (void) settings;
++ siderial_state_t *state = (siderial_state_t *) context;
++
++ switch (event.event_type)
++ {
++ case EVENT_ACTIVATE:
++ watch_set_colon ();
++ watch_set_indicator (WATCH_INDICATOR_24H);
++ /*fall through */
++ case EVENT_TICK:
++ state->ticks++;
++ _siderial_face_update (event, settings, state, 1);
++ break;
++ case EVENT_LOW_ENERGY_UPDATE:
++ state->ticks += 60;
++ _siderial_face_update (event, settings, state, 0);
++ break;
++ case EVENT_ALARM_BUTTON_UP:
++ state->lst ^= 1;
++ state->ticks = TICK_MAX;
++ _siderial_face_update (event, settings, state, 1);
++ break;
++ case EVENT_ALARM_LONG_PRESS:
++ break;
++ case EVENT_TIMEOUT:
++ state->ticks = TICK_MAX;
++ break;
++ default:
++ movement_default_loop_handler (event, settings);
++ break;
++ }
++
++ return true;
++}
++
++void
++siderial_face_resign (movement_settings_t * settings, void *context)
++{
++ (void) settings;
++ siderial_state_t *state = (siderial_state_t *) context;
++ state->ticks = TICK_MAX;
++}
+diff --git a/movement/watch_faces/complication/siderial_face.h b/movement/watch_faces/complication/siderial_face.h
+new file mode 100644
+index 0000000..a4ae67f
+--- /dev/null
++++ b/movement/watch_faces/complication/siderial_face.h
+@@ -0,0 +1,50 @@
++/*
++ * MIT License
++ *
++ * Copyright (c) 2022 Joey Castillo
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this software and associated documentation files (the "Software"), to deal
++ * in the Software without restriction, including without limitation the rights
++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
++ * copies of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in all
++ * copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ */
++
++#ifndef SIDERIAL_FACE_H_
++#define SIDERIAL_FACE_H_
++
++#include "movement.h"
++
++typedef struct {
++ int lst;
++ uint32_t ticks;
++ uint32_t ref_ds;
++ uint32_t ref_st;
++} siderial_state_t;
++
++void siderial_face_setup(movement_settings_t *settings, uint8_t watch_face_index, void ** context_ptr);
++void siderial_face_activate(movement_settings_t *settings, void *context);
++bool siderial_face_loop(movement_event_t event, movement_settings_t *settings, void *context);
++void siderial_face_resign(movement_settings_t *settings, void *context);
++
++#define siderial_face ((const watch_face_t){ \
++ siderial_face_setup, \
++ siderial_face_activate, \
++ siderial_face_loop, \
++ siderial_face_resign, \
++ NULL, \
++})
++
++#endif // SIDERIAL_FACE_H_